git2r/0000755000175000017500000000000014146643373011446 5ustar nileshnileshgit2r/MD50000644000175000017500000012117114146643373011761 0ustar nileshnilesh64c4485ec82248fb64ee3681e412ae9f *DESCRIPTION 3873adb1c538f1527f2ee35bbfd02ef8 *NAMESPACE ea557f69e584fc472ed2e8d9eac712e3 *NEWS.md 797b8718caed7353ce0ec190207cc5e7 *R/blame.R a7f91fbc1c6a48b134622179dfa4b5c3 *R/blob.R 8a49695ede4af53eae9c6692322e9884 *R/branch.R 912d2f06cad541952b37731595598954 *R/bundle_r_package.R cbf32a0e95393840f72b17e46c88676d *R/checkout.R 3ec322f31a20ac3fbb650543691ca5a0 *R/commit.R e04a3583d4db89278f94ed8f1de52a06 *R/config.R cfc05de640905bf42c3eef9e8d733380 *R/contributions.R 2b03b95884253f2d65c968118c2ff8d8 *R/credential.R 20549ad29d193e781329e163e1afdbd9 *R/diff.R cf2114d7b3b49447a6fe050ce1b2c6d7 *R/fetch.R 0dbc7ad2d53b169031e3a074b691157f *R/git2r.R 1032af9159d6221d4c439f0ef9a21aa2 *R/index.R 027eec93446c942d562c5b9dc278c3f8 *R/libgit2.R 02f2f8b09df96a3ec7efe99c0be8a535 *R/merge.R bb13facd826816b7890e5f9026acc79b *R/note.R 9bf7cbaf3111abd1cf5598feed02431a *R/odb.R 952494199a5442d99f5fb21fe0b019bf *R/plot.R d6347c61d02a4eb46f0d0aef93b059f7 *R/pull.R d69f5e41c8715f5db959208d525b2783 *R/punch_card.R 483b50ea6b21ea9cb85df7dd81a50c3e *R/push.R 049f4b69bfe38b11c7179876fc1494ef *R/reference.R fd75336e4ba8ff173c44320c95d8c228 *R/reflog.R 65dcd4ad378091af841b530e95f61e41 *R/refspec.R a2581ce31be01acad63b76fe458a1cbd *R/remote.R b40fd8ff30898d0550312401104a4a83 *R/repository.R 57ea55818ddd38354a615da213c80ba9 *R/reset.R 51f7c0a6d7b60d56e4b8766032c6e0e8 *R/revparse.R b1f5b6cf02fe9f3eb77f37de322bef3b *R/sha.R baa54b99191c1c6a7c4a5d4294e6edf5 *R/signature.R 38eb504cee4b3c587c53e0ee789216e5 *R/stash.R f90a70e276517a6b09a1770300f5bbec *R/status.R 0a183197718efa1d2630da88756e4f62 *R/tag.R 750282092d65155ddc7beb5728a6d951 *R/time.R 8cbc22194e5ffc2a0f5313e83158fbe6 *R/tree.R cb6c3d09641d6b9a2213578c13d2a8ce *R/when.R 3adca5c6db3ead545edd2b61e5a49a91 *cleanup 91d2f98e937a786ef04072c582ea3d49 *configure 23a446aed46e1973df177993d0440c6f *configure.ac 8695928f5b2fc912cfc6f2c17282514c *inst/AUTHORS 9b5534b9aec12805ebdb9d393bcb01df *inst/CITATION 43e011b97b3d57c82da0bb4b1b93753d *inst/COPYING 475a618f9d558eea3c83d5860c2b3c55 *inst/COPYRIGHTS b331149fca2d7a121a72bedecf2b4deb *man/add.Rd d5267a2fa4c1dd6a4c19bc02b0ce3434 *man/ahead_behind.Rd 4f9de567ee9cd924e7a8b0c92c617ae5 *man/as.data.frame.git_repository.Rd 14791c824a3de0fc664e7753342414d8 *man/as.data.frame.git_tree.Rd c5da14f94fd7441d9368aff8446ecafd *man/as.list.git_tree.Rd 9409c8b875b52aaee969dc4e787d9aad *man/blame.Rd 15c60de0ebc7011960f78851ab2f4420 *man/blob_create.Rd 0f075ca210ecb7194407ff808670ad2c *man/branch_create.Rd e308c1de4bc95e056936de7e9d1c5018 *man/branch_delete.Rd 8e6b4f5cdc6f28109dcb5af77ceb66cd *man/branch_get_upstream.Rd 10740b906b8a05c8467ad243ba14f508 *man/branch_remote_name.Rd b8f9fd0a0a01cd8c3a162a238185c6ed *man/branch_remote_url.Rd b9050ab922f933324b19775eedca56fd *man/branch_rename.Rd 3949b2dd0e69a58e179a64c49b52a4b6 *man/branch_set_upstream.Rd 8f2fac4a8c84a16164d6a0a003ab9ae1 *man/branch_target.Rd dbc33c95c27a680f33526fbdf169abe3 *man/branches.Rd ed8ff151e968286185df78d45cc83bfe *man/bundle_r_package.Rd 03ab18767469316b997a26b1e32965e9 *man/checkout.Rd a8929e27f5921b7733f0137e0406387f *man/clone.Rd 39175724df5e169b629deaef431def06 *man/commit.Rd c518e42a71db69521a96f2b425dbde54 *man/commits.Rd 377b3ee874bfd019408521bc92826942 *man/config.Rd 18a0809751ea252c48a065d9201ba4d1 *man/content.Rd 916c66ab567489629f260079e89117d0 *man/contributions.Rd f38a93b3884ca35938443e1fcef67385 *man/cred_env.Rd 2905f003036501cf8be6ecfb3936ebfa *man/cred_ssh_key.Rd 92423531e5b43498b2fdf66c2ae45bdc *man/cred_token.Rd ca3a5483751a14780d2514530f78c353 *man/cred_user_pass.Rd c6a4941cf08cfe908fff2c9384f7c904 *man/default_signature.Rd 36796c3a8580c06fb5e62958233f4b8a *man/descendant_of.Rd c01a1399874918e9a977f6667a1b4e26 *man/diff-methods.Rd 4cb82d68b325bbb580eec72b4d919ced *man/discover_repository.Rd 15413bac86f90043f7a9ce78005acf7f *man/fetch.Rd fd3a198a0bc7c4badd9b72df537e0ad4 *man/fetch_heads.Rd 00b90bd41aa95c93290df1733d14088f *man/git2r.Rd 4e69c35a4cbbc009ad78c4a32b86b9a8 *man/git_config_files.Rd f1114088cb7dea281ac0b4215ae97652 *man/git_time.Rd ef19202ae4f329968d55beb12f03cbfd *man/hash.Rd 0bb5696d8e70a6a60f2fbe9849cd3f8a *man/hashfile.Rd 3242f386a71929cd8d53a73d6e0fe96c *man/head.git_repository.Rd 3d817e50995bb65670ce134e777b3d72 *man/in_repository.Rd c5b3e61452ff6e296a4578cb8c5c923c *man/index_remove_bypath.Rd e5605585bdbe25fbbd04f785ed3aab7b *man/init.Rd cd85ef7784fad166d026729b621a25aa *man/is_bare.Rd cbbab70be97bfedd3494fa57140eb934 *man/is_binary.Rd 83517d2e86446b39c24679c56133e6e8 *man/is_blob.Rd 4c6d2b1c5e421ec9101e753a7c427152 *man/is_branch.Rd e1542a2e4ddd1d0918d33d1bdf372367 *man/is_commit.Rd c964e65abae418b7d709f9ccdc865d31 *man/is_detached.Rd ed9cfa42feb7e5231e39b34d6182b27e *man/is_empty.Rd 08a2a68b47f696eade54485d0f69b404 *man/is_head.Rd 4d37c8eb6de3ac2626fd3926a10f785e *man/is_local.Rd ab24429add3d56dbeeed3b81a6f20e67 *man/is_merge.Rd 4a0f5bb3ef3eeef0fbe97452d30d3fec *man/is_shallow.Rd b7a1f6bba46e39e04504037e0aa040d1 *man/is_tag.Rd 008c1a40f4f27053d1ffa9109d09c843 *man/is_tree.Rd a894855ebfa55bb79bac03227c9073f2 *man/last_commit.Rd 0cb99de7c8f70b73b743977613545109 *man/length.git_blob.Rd ee72b371ae4ec50c4bd2d004f39fd6b0 *man/length.git_diff.Rd 811d9a9de11579c753be15aac2560671 *man/length.git_tree.Rd 856072dcdfff2daca80283c38fd9b9b8 *man/libgit2_features.Rd f5b36b56e9898afc109a4167f2069167 *man/libgit2_version.Rd b698c5cb6b6c81da0e617764c495b3a6 *man/lookup.Rd b3c0dedeb66b768fc35cd4f4d4d83098 *man/lookup_commit.Rd 02c27b96ba7dcb4344b1405c05d74d13 *man/ls_tree.Rd 03d42586eef5535aa589177ee07ba360 *man/merge.Rd d3fc915175dc2c511f5b2c3d927b90a0 *man/merge_base.Rd 6343c8d7abb2a3d194319fb317afdfbd *man/note_create.Rd 1be9116c3be76656280d226f4765fcf3 *man/note_default_ref.Rd d32e381f70ba32cc11e21e80308611d1 *man/note_remove.Rd 2b80093593f380bc687f99b959a278c3 *man/notes.Rd 20c766579e5adc05e4497a68461ef56b *man/odb_blobs.Rd b77d0aab30c7cc08b6324beeea4dda70 *man/odb_objects.Rd 51f489dc8719c6b241d7daf174f7ffd9 *man/parents.Rd f5a07524bc918be81e40fdfc4a476304 *man/plot.git_repository.Rd ea0235e21065ff3f5d533184694d1081 *man/print.git_reflog_entry.Rd c0e7b2fb8ae792ca269ab7673d833d4e *man/pull.Rd 3d235431994af0b8a92aa9b00ef637b3 *man/punch_card.Rd ad93997dde39fbe5a21b240e71d65929 *man/push.Rd a02ce2d84f936ab1ae8154080f3a49f1 *man/reexports.Rd a4cdc735d5a0023d743c6a9ae4cd267a *man/references.Rd c1f0e3d0e911c4f4d00422cc5d99cf5d *man/reflog.Rd a6442cf9c888a63ce072a7b77b0177ce *man/remote_add.Rd d5b4f6525762a4cdf01c8b70e3e74c69 *man/remote_ls.Rd 1b6691fb52d638be4f44137234ba03eb *man/remote_remove.Rd 5ff725a2213ff83eb3aec0734b67b120 *man/remote_rename.Rd 8bd38203528c1b20f80bc26139ffadf3 *man/remote_set_url.Rd 8e66dbd022ef80e36eafdebf21aecf10 *man/remote_url.Rd 99c329f0d3fd299c5def2cc5a9e3768f *man/remotes.Rd 60cdfb2c9b60f9290f9d5e29564c4b2e *man/repository.Rd a232cb46703996c971c3a62eb79b6436 *man/repository_head.Rd 58b0b2d3dcf2e849dbd307ed6039f882 *man/reset.Rd a07c6d55e844daf3765257d8b8c24bff *man/revparse_single.Rd ec762f6d8b9e4c949323891f09f973f0 *man/rm_file.Rd cade2bb43df51c5bad476f08a1a3f952 *man/sha.Rd 7dc1454b285b1ccaca9e2d47eaa4bf96 *man/ssh_path.Rd dfb4cee2ca9b914e6632f1c7e86c1179 *man/ssl_cert_locations.Rd e3c124effca07b0412f98d27997575bc *man/stash.Rd ec288256c07afd75d77259b069b52506 *man/stash_apply.Rd cf67311200161cc4e1163910bb853a03 *man/stash_drop.Rd bd2f6f49b6eb3d967ea27bbb70f74dee *man/stash_list.Rd 239cd5748325f55bdcbce09a6a761d7f *man/stash_pop.Rd 4a4b3510bd2565b4e4e110b00b4adf24 *man/status.Rd 50feba2426bbc5044ba4fd5439e0fdea *man/sub-.git_tree.Rd fd1ae5b9d1290cde0147a7b0dcd25804 *man/summary.git_repository.Rd 8b0c34745c6a15f1dc7611fd062b3204 *man/summary.git_stash.Rd dda0ebd536d6ad0fc4ccfdefe87b6130 *man/summary.git_tree.Rd 27f8736fed19acc108c3056d7ad89293 *man/tag.Rd 690a8e5559addc2f81c3c9eb6830c013 *man/tag_delete.Rd df047e615d762624ec7cf191e8dee9d0 *man/tags.Rd 5916b74836adab701bdc0ce41925d0a5 *man/tree.Rd c6d600aac2889b197b4aa95913a8e0a4 *man/when.Rd ba3a1a4f454f3d9d5fc01c5a3b568c85 *man/workdir.Rd fbd21714f2fcdb394e2958c148349fee *src/Makevars.in 683135e27036e3e85ad6f65d34f4dc07 *src/Makevars.win f43c33c27fc938eba4ceda7bff119c5b *src/Makevars_libgit2.in 4718460ae676f220fec2823b035cff18 *src/git2r-win.def ff9a46c8f7898557ab65f5273fc1277c *src/git2r.c 4c2fc7c7753fec85dea399714239c9bb *src/git2r_S3.c 183ad80d4db8ce859f02791788a7a575 *src/git2r_S3.h b604ec6a77c6b2702b3659fb0e1a5d9a *src/git2r_arg.c bb13ede6c3df8bc1c9c5261d95b4a110 *src/git2r_arg.h 8490c4b4ef78d188048a5cd8959e7f44 *src/git2r_blame.c 0590f03a2973f3b7ead4bfee123c1097 *src/git2r_blame.h 014fecd21c8035446fdf3c810e0d6f2c *src/git2r_blob.c 8e1e05b59d833860c48edcea3aedbab2 *src/git2r_blob.h f9e50a2b10bf91f799aae75a328efed2 *src/git2r_branch.c 7d8eedf2fdd2c55dcb6e05ed5c6f618a *src/git2r_branch.h 7fcc6a561266e639d9b5fea69e8a125c *src/git2r_checkout.c 89161937429aab6567363d9d4e59113d *src/git2r_checkout.h 3c9f145c594debce8a0a169ce24d5ea3 *src/git2r_clone.c 76406656a419de911c6ddc3ad17b908d *src/git2r_clone.h 81680b06a0ff02c1c9efa7f68c942fd8 *src/git2r_commit.c 1a61abdcd5d7558ce07148454a9fe096 *src/git2r_commit.h dfa305f80cf153eeb0e78a32c321d2a0 *src/git2r_config.c 2a5cd9432062a0893957344f471503b5 *src/git2r_config.h 4ab9675f35d74c20bcc43d557ab214a6 *src/git2r_cred.c 798b3d8996de780a641d9096797f24c1 *src/git2r_cred.h 8590c684cbf4ce62c72f5b5f0f141c93 *src/git2r_deprecated.h be576f4fa70c470a068eb51a9bc93cc4 *src/git2r_diff.c a5c67c5ad4f9e0f6d9ebabc8ab230dd9 *src/git2r_diff.h 691a2d92648714d016bc445787a1aa2c *src/git2r_error.c b2d94c84cf719b7092f5a8f7773fabfb *src/git2r_error.h 8694df11705526fb132063243e77b92c *src/git2r_graph.c c3a13beeacf07e857036cbe877d7dae9 *src/git2r_graph.h 082132d69048651e9738bd3e705f39fa *src/git2r_index.c 07277b9d8d7993129165ccf33ecd3b6b *src/git2r_index.h 659b5f551e4bc46390a2390db9627df5 *src/git2r_libgit2.c 5dc19c8872d38e8136b240417fec0a88 *src/git2r_libgit2.h df67bc567842b4b3519a7c1379a05434 *src/git2r_merge.c e9fabfe4d16bcc226f6a47f2b492ad95 *src/git2r_merge.h fb59599bd5c762e694abb20c0c1f05ce *src/git2r_note.c 00c8c66f54b30fac6733aad059256cdf *src/git2r_note.h 837c33e396b670ab939f0649cc61aef2 *src/git2r_object.c 5cfce52edcef1af271df02583927c19e *src/git2r_object.h a856f8b13e3c6d6520cda9ada300c725 *src/git2r_odb.c 25046d116d8caa2140a277ec719c7856 *src/git2r_odb.h 4ddaf8afd7be13e7f8396b1e2ed08898 *src/git2r_oid.c c19b39cc1c34f5b3579a165d8f4973d6 *src/git2r_oid.h 8c58438ff8c83e938977d662ce5af7a8 *src/git2r_push.c 9f877e9f2bb03fa3478acb19c55bd054 *src/git2r_push.h 634869894b5ff5f3c6c0bc321ced2c8d *src/git2r_reference.c 5803da458ac3c586000d7381f1f3c497 *src/git2r_reference.h bdd422221a4a80378e33a2829fcda246 *src/git2r_reflog.c 8b6e7c2e4207bdb8490e4d3b5284cd67 *src/git2r_reflog.h 2aa52801d0b1ea6519e5d7d3f3096eb5 *src/git2r_remote.c ccdd219017dfc1dbf7304963dada6880 *src/git2r_remote.h 480fd18a5e9be78a74628b1281f5ed60 *src/git2r_repository.c 31b03fe3a62562b1a34bd8c592c3a721 *src/git2r_repository.h b29762e46e35611df8551d97d77aa153 *src/git2r_reset.c 1f1475f57ee2f1607cd65c99cf54c9b6 *src/git2r_reset.h 4f2d8ff6ba63f42be1e937dc7071e915 *src/git2r_revparse.c a5605827f68d65572c28ac647cbf0f55 *src/git2r_revparse.h 8e188f2ba14a8694dbe5299ebce06049 *src/git2r_revwalk.c 0530bdddca0a35a7dcba2f43d5699b19 *src/git2r_revwalk.h 9c1c001aad9e65ddc3fe536df5bb318b *src/git2r_signature.c c644c059e5a3755fd2a42cedd35e8d7e *src/git2r_signature.h 9f3f7847ce6079631b506e3a4923e398 *src/git2r_stash.c 3e9721fb8188f18df721813efe34d6a7 *src/git2r_stash.h ede0f9785f99dc2c4a59e600c4d35707 *src/git2r_status.c bf57139eb87e930232eae7507ea17964 *src/git2r_status.h 949eb33c52c6c152019cb78cd58b51de *src/git2r_tag.c f8f7433e00e95aaf5719a476e8806ede *src/git2r_tag.h 07be81145b9244080ecf32129c9d8261 *src/git2r_transfer.c b572bf78aeb66273ecc62448380f77fc *src/git2r_transfer.h 8f10887a3b9ba7125cd9b764eafa267b *src/git2r_tree.c fca98773c5f867d7c6f98afe6a8e3a4e *src/git2r_tree.h 20d989143ee48a92dacde4f06bbcb59a *src/libgit2/deps/http-parser/COPYING d9363e053604e43fd0b79e032de246e8 *src/libgit2/deps/http-parser/http_parser.c 0a232503f91c017704fab71390173109 *src/libgit2/deps/http-parser/http_parser.h 4fbd65380cdd255951079008b364516c *src/libgit2/deps/regex/COPYING f7fc3344cd375342c3920fda8389dd65 *src/libgit2/deps/regex/config.h 1ecc27857da8666d57f079b8e1523858 *src/libgit2/deps/regex/regcomp.c bc42286ad63637dff794f2bc3cc7bb32 *src/libgit2/deps/regex/regex.c fb5ba5b89906a5e900eee8936c6d85df *src/libgit2/deps/regex/regex.h 05cd7281a126a9432116821e50d10e8a *src/libgit2/deps/regex/regex_internal.c 3ad4585cc6cae987edbc397afdae48da *src/libgit2/deps/regex/regex_internal.h 760a9fff756040fde62ccf8f1141feb7 *src/libgit2/deps/regex/regexec.c 7d21ca7e0ba19e890278a4076ddd7f22 *src/libgit2/include/git2.h 87d59ef9058553223c2a42ea1310dab9 *src/libgit2/include/git2/annotated_commit.h 3ee86cc71fb480e7f0b378ecb31fa187 *src/libgit2/include/git2/apply.h a4a9bbd8e2045797b886b4609ff077c2 *src/libgit2/include/git2/attr.h bfe308784d3c2b5251aaeec220969cf7 *src/libgit2/include/git2/blame.h c5a0bb87f452513227886ff1c582f8c1 *src/libgit2/include/git2/blob.h 6bdafe9d90d7e8bd58e1949c0549f9f4 *src/libgit2/include/git2/branch.h a4f6b6490db43d03946db0be8242febf *src/libgit2/include/git2/buffer.h 25da2bc7e44dae12cf00ed3b3b6d42ed *src/libgit2/include/git2/cert.h 031fa26e88ffe7d4957dd41075c69a61 *src/libgit2/include/git2/checkout.h 32a1e1f1d100228c0f441c1bb95d5fa4 *src/libgit2/include/git2/cherrypick.h 192db62f918e9dc712a484e7c2b23378 *src/libgit2/include/git2/clone.h 550ce817e821fa07e8f5843a2bb8f908 *src/libgit2/include/git2/commit.h 18c4e5bb08da6319d09e0bef5082e746 *src/libgit2/include/git2/common.h 2a84e3e329bf9494f2b99da952013e14 *src/libgit2/include/git2/config.h 6b9947b455880cd3989816cb83b33978 *src/libgit2/include/git2/cred_helpers.h 633fe8c3e043c2cdc13cbc1743c58419 *src/libgit2/include/git2/credential.h 87a88e2feb0a94dc131b3ed0725e2dc6 *src/libgit2/include/git2/credential_helpers.h 7f5e5291a58b5ee94290e2d73f0c2475 *src/libgit2/include/git2/deprecated.h 3c49819ef57c3bd11a51502c2bedce16 *src/libgit2/include/git2/describe.h ffdb8e09996d5738477925fea71feb95 *src/libgit2/include/git2/diff.h c58f159551a189b2745bfabb325182f0 *src/libgit2/include/git2/email.h 54d4d32edd3bbbaed5dba8c069d59ea0 *src/libgit2/include/git2/errors.h 4f96320ea591f3db670ab7fbdcf284d6 *src/libgit2/include/git2/filter.h fe34d6c0f5a2d0e97fb8ef224d83dc9f *src/libgit2/include/git2/global.h 7bea25a214c2912070b5ba09979385f6 *src/libgit2/include/git2/graph.h 3b1e7cc906d3190ebd25f67fa1b2e03f *src/libgit2/include/git2/ignore.h 569497c4d0c4d584d6dd3578926062a7 *src/libgit2/include/git2/index.h 134bf87fc3a226eca4a3bffa32908f22 *src/libgit2/include/git2/indexer.h e3c4ce0115d18d2fcf9fd38b959a5c9f *src/libgit2/include/git2/mailmap.h 59b9f83a9dcf2bd5e07073e20c5e19fb *src/libgit2/include/git2/merge.h 38e8259cc5bc1b7eaabffe4fb1f84913 *src/libgit2/include/git2/message.h 18d0b5a0e06dda3781ecab63d13725ab *src/libgit2/include/git2/net.h ae8707ed104b06ab7fa5e2e5d1720c07 *src/libgit2/include/git2/notes.h f5b158024eb0550916e6774a1245d7a3 *src/libgit2/include/git2/object.h b7b22af6224eec1d06856fbc7a46f744 *src/libgit2/include/git2/odb.h 8cfcf4829c6956a443a542d7b72d5a77 *src/libgit2/include/git2/odb_backend.h 80f45fdbbc45c42d00a63909ff4ff870 *src/libgit2/include/git2/oid.h 3e149a6499b55e1932c1e56b7b6e6f9e *src/libgit2/include/git2/oidarray.h d184a68e205d49a5b9c0022081b54c00 *src/libgit2/include/git2/pack.h 982d727761125c4749a1e8dbe6e3d541 *src/libgit2/include/git2/patch.h cacecfa6ad5604b1bbcb3c65e782cbff *src/libgit2/include/git2/pathspec.h e8e1411fec4e9b4dd20e1c05d36638c5 *src/libgit2/include/git2/proxy.h 97af3d6fc445781e15b6aca8f87a1e1e *src/libgit2/include/git2/rebase.h 15c63b4d0d57a09871b3c91f286eba05 *src/libgit2/include/git2/refdb.h bead33d7ed800b97c78e8e97c102dbab *src/libgit2/include/git2/reflog.h ad2f9ab3002f2d5a4a3b76fdc080f036 *src/libgit2/include/git2/refs.h 7cfd028f706b5ff941d9ebe3f6fdfc9d *src/libgit2/include/git2/refspec.h 77d472642d3004d51b7c899c2fc2d635 *src/libgit2/include/git2/remote.h 7a79ca65c195c2f5a26a45ca79c0f532 *src/libgit2/include/git2/repository.h f50b51bf0308a1641827a2c9b0485730 *src/libgit2/include/git2/reset.h 3260554daa8f7b9bbd6014579a4d4af2 *src/libgit2/include/git2/revert.h d2e84f4d573ac9d12100e4500c1ff1cc *src/libgit2/include/git2/revparse.h d1f146a2d85e718920a535769ecf5344 *src/libgit2/include/git2/revwalk.h 5551a5f1f4c4b4cbe9fe2741b61b9ded *src/libgit2/include/git2/signature.h 8df8ea249002738a53db8549fa4e98d0 *src/libgit2/include/git2/stash.h 7bc35ca1b406796c85590326090be513 *src/libgit2/include/git2/status.h 4705e03340f1e647d9094fa56d055616 *src/libgit2/include/git2/strarray.h af0e4e256d2072f602fad29edc711406 *src/libgit2/include/git2/submodule.h 9c53e49cd963500d6ac84ec78bb88765 *src/libgit2/include/git2/sys/alloc.h b20f4efff55de5a248cffbebc41d31cd *src/libgit2/include/git2/sys/commit.h d3ea587389177d48a0dd7bf8fee2ec85 *src/libgit2/include/git2/sys/commit_graph.h 77065422264d58784bf92aa8793df268 *src/libgit2/include/git2/sys/config.h faed577825c6365a878a672bf5da1014 *src/libgit2/include/git2/sys/cred.h 1cca3209928cbb1b31bd76c5f86620fc *src/libgit2/include/git2/sys/credential.h c21f458e1d80f0b83690a586a20cabed *src/libgit2/include/git2/sys/diff.h 5a57e50d02944cd1dfa100d0d3a101e5 *src/libgit2/include/git2/sys/email.h e06cfab79aa7febf6dbedf276440b425 *src/libgit2/include/git2/sys/filter.h 528ce9e7374369cdef155a57c3427ab4 *src/libgit2/include/git2/sys/hashsig.h 70b69e459f5ea9f71807a43174a17896 *src/libgit2/include/git2/sys/index.h d9192e3555c3fee13755afb74cd48ee2 *src/libgit2/include/git2/sys/mempack.h 1d6d7e3e15e5654ffe1b6ca7f37e6662 *src/libgit2/include/git2/sys/merge.h ecd5443e3171a5109f5297baf1338908 *src/libgit2/include/git2/sys/midx.h 018afcf826e5a2a3bf764e17984a7da4 *src/libgit2/include/git2/sys/odb_backend.h 35100b3dc3308443df5ca45008ae0ab5 *src/libgit2/include/git2/sys/openssl.h bd094a6ac714d5cbbaaac78ff006bc31 *src/libgit2/include/git2/sys/path.h 858c8f95479a8564cd73c4d84ab9f5c0 *src/libgit2/include/git2/sys/refdb_backend.h 5b7105b9bd458c44870a17a8574e1584 *src/libgit2/include/git2/sys/reflog.h 5ef00738bc010797724ea40412cb1eb9 *src/libgit2/include/git2/sys/refs.h 587b671ac545501ad102ce789dbd0e3a *src/libgit2/include/git2/sys/repository.h 6b4d38baf592497c7e5b2df13f13d19b *src/libgit2/include/git2/sys/stream.h 023cb4781347a4855d3e8ff9526a5545 *src/libgit2/include/git2/sys/transport.h 8a6ca389b4b1c48a5788c0e0fec1e6f0 *src/libgit2/include/git2/tag.h 541e6dea059d42d126c17f2190fb8a94 *src/libgit2/include/git2/trace.h 55ada48853b27492711568a400379302 *src/libgit2/include/git2/transaction.h 1d8c414434a9b767d623195c01252656 *src/libgit2/include/git2/transport.h d18f9d66c9333e46d1dbfa40dba7829f *src/libgit2/include/git2/tree.h 8e8c95b1f105056a2d577efd8fec458a *src/libgit2/include/git2/types.h 1463152bbe9ddad4795d957157956fcb *src/libgit2/include/git2/version.h e9b18a3b1d4d18b9f075e4a9bb14c870 *src/libgit2/include/git2/worktree.h 78a48ae0010be874d1f8cbd353a6b95c *src/libgit2/src/alloc.c ff8eb36422016c97a9456b2fccd66015 *src/libgit2/src/alloc.h 4a9c022243ad8c900a9b2f89bfc1a79f *src/libgit2/src/allocators/failalloc.c 2974076297d3b8f0b17c8182edd1364a *src/libgit2/src/allocators/failalloc.h 3b683e24ede5274b5a2a281e5c166af6 *src/libgit2/src/allocators/stdalloc.c 5c6d4d608411693d24f66f6b492d1cd1 *src/libgit2/src/allocators/stdalloc.h b129a255bc1f20a70a898ae8f8f8df5a *src/libgit2/src/allocators/win32_leakcheck.c 96fc281fc4d3deb2e8e17eac6fb66452 *src/libgit2/src/allocators/win32_leakcheck.h 685c8aa51b61c4c2521e926d47022043 *src/libgit2/src/annotated_commit.c a28b1d43148529cda316e68eede35642 *src/libgit2/src/annotated_commit.h 44bff0ed09b725cb66732a16b84bdcab *src/libgit2/src/apply.c 2582dd7701f1c40644e5263ff2fda9e7 *src/libgit2/src/apply.h df6d5af12aa71e194225e0582a030f0a *src/libgit2/src/array.h d0855efee73ca99bd67681317ff49626 *src/libgit2/src/assert_safe.h c43d9fd362cf2a9993d597f15cd05e44 *src/libgit2/src/attr.c dee6edde38dac6fe5bdbba88c9074e6e *src/libgit2/src/attr.h c87880c60e5921bc1b185a8df4799b57 *src/libgit2/src/attr_file.c 93479cdd90e16c89d904efe381d6a394 *src/libgit2/src/attr_file.h f33a900b97441f666302fffdd76992c1 *src/libgit2/src/attrcache.c 98e347aa2826cc223ed64a51fa80e927 *src/libgit2/src/attrcache.h 93d6655d87a8606fe606aedfa86ed7d1 *src/libgit2/src/bitvec.h 696f2a263c2a97376a23537d284725bf *src/libgit2/src/blame.c cd64f167fe460508c26cc3f4f22f96cb *src/libgit2/src/blame.h b97fcf50beecfec95cd891862fef6bcc *src/libgit2/src/blame_git.c 03327443d37fab50256dffdf86eb0965 *src/libgit2/src/blame_git.h a883096c80fa3e4b3e621169ddde500f *src/libgit2/src/blob.c d36d8c8aa5bcfcdece84f866c36d6dab *src/libgit2/src/blob.h 0fd780012bfa6e8f096a0a590be6bf17 *src/libgit2/src/branch.c 8c1e7705b12a2533e9e6cb81dac4df26 *src/libgit2/src/branch.h 017fa1da8d0513b166c82d9bd0fee3b5 *src/libgit2/src/buffer.c 43ad45af0edbb32e0d3e14870770a7e0 *src/libgit2/src/buffer.h ec21e19b289a8816aea6a485a44d03e2 *src/libgit2/src/cache.c 61eaa9f7944af90c3181cd0e3a5fad56 *src/libgit2/src/cache.h ce8893c906dfdc35f1b0bea5514cc7a2 *src/libgit2/src/cc-compat.h 841d15d9b86f6053d3a03ed747f315ec *src/libgit2/src/checkout.c 9b40cb51278171193441aaba1a17218d *src/libgit2/src/checkout.h 2aa2f857a8c387e90a5a8c0adee9f31a *src/libgit2/src/cherrypick.c afc49897900d138653899a2f2ff442f1 *src/libgit2/src/clone.c 8235c134aaed4d7ce801c51440dc810a *src/libgit2/src/clone.h 9262df3df1e41fceed17efe6224cc6ae *src/libgit2/src/commit.c 2297c753fbb09b7c7f34c328c022e278 *src/libgit2/src/commit.h 2277de7087b21fd1aae0ad62cb90aba3 *src/libgit2/src/commit_graph.c e5b90bdefbb44eb23265ba1acf7ebdd8 *src/libgit2/src/commit_graph.h 9355a0238e23475f18f5184bb9c7ccdb *src/libgit2/src/commit_list.c 5141d12a6b9a6a78947ccbbff26ac112 *src/libgit2/src/commit_list.h 545719dd679f8c7d11d0d313cff6df5a *src/libgit2/src/common.h 819814e86563c134fb74b23ea08859a8 *src/libgit2/src/config.c 35803dc8c54b9944f6689cecfc3842cf *src/libgit2/src/config.h b654a2d7a721efc048ca7054088ff12f *src/libgit2/src/config_backend.h 58661d87adacb83a7a840f5b90305164 *src/libgit2/src/config_cache.c 7a0d681f684977faff8a4d12db9593b2 *src/libgit2/src/config_entries.c 494f54c78d8608e6bbc83bc6534761ee *src/libgit2/src/config_entries.h 0bda95e314c716438240b93ace266940 *src/libgit2/src/config_file.c 0df15b17e8a62aae9a4b9c86cab1db95 *src/libgit2/src/config_mem.c 8e2a5bfa6586d4fdb28ebb190520633d *src/libgit2/src/config_parse.c 15f831f20fbf7e7c18c4b8a62620ec03 *src/libgit2/src/config_parse.h 65a2520bf8dbe2b30785b09f11c2ef14 *src/libgit2/src/config_snapshot.c 76f4c103a8ebdd8e21ebecff53a63578 *src/libgit2/src/crlf.c fcad81e3ce4a8874a36038978af95c0e *src/libgit2/src/date.c e04615ebda31e9ce996471645f9a845e *src/libgit2/src/delta.c 556d99a82704627f1dec80bbbabba87d *src/libgit2/src/delta.h ec8d59663b55344cd6c8d8b8ff5753e0 *src/libgit2/src/describe.c 9d173684cd327db0f70b9bc6278599f3 *src/libgit2/src/diff.c 287db5177a5ae270d3fb1b9addc9bb68 *src/libgit2/src/diff.h ab2e225f121fa6d2ae09ba02a6b992b9 *src/libgit2/src/diff_driver.c beeef329f781689b83779c37234b57d6 *src/libgit2/src/diff_driver.h 84519a0c7397840c3c682bb45116f657 *src/libgit2/src/diff_file.c beba030f865fb5bfa411deb761589c9f *src/libgit2/src/diff_file.h 5b35f791c156249b3cf83c599382673a *src/libgit2/src/diff_generate.c fb163b8f3dfa0ccaa161c500efd21f41 *src/libgit2/src/diff_generate.h 6fed7bc839073cf8d4ebfd4d8d231e21 *src/libgit2/src/diff_parse.c af7be5f396b0180b1bbd8b9b228cab22 *src/libgit2/src/diff_parse.h d8b7d42d4799fcca071b5534e66e55b6 *src/libgit2/src/diff_print.c 84abad3fa2c2b6cdb03c394cf5fa2e1b *src/libgit2/src/diff_stats.c 6d57edb54b9b2d18e3e8b2892fbf0696 *src/libgit2/src/diff_tform.c a06cbfd4d605b5b1e5a6348e7085b891 *src/libgit2/src/diff_tform.h 4d7ad67dc1113b61f837332b7518cf62 *src/libgit2/src/diff_xdiff.c 7b325d951ec3085d9954cd872500b407 *src/libgit2/src/diff_xdiff.h 79189446b628b8a64d022f69efe1f1cc *src/libgit2/src/email.c aa0c984e95a1b3ec036a23f59482e3ab *src/libgit2/src/email.h acea80ce677056b60a4a09c919da179a *src/libgit2/src/errors.c df081e5fc01f7cab1c0e1f2b75f4a0b5 *src/libgit2/src/errors.h 6ff96f5f95c11a017dd9fa2b35870383 *src/libgit2/src/fetch.c 0415665c1ed6a7837725529eb5b1018c *src/libgit2/src/fetch.h 2e6c3cf82153437149698bfc83e03734 *src/libgit2/src/fetchhead.c 3ac96a36098810374155c4a69dc56874 *src/libgit2/src/fetchhead.h 785b95fedd464e6a4f69d0d98108ca15 *src/libgit2/src/filebuf.c 8f7d229f109865507a28a5e8287af97f *src/libgit2/src/filebuf.h b29986bf48395eef168134bcf3d6457f *src/libgit2/src/filter.c c0ff042b13683df961170015a8f0c77c *src/libgit2/src/filter.h 4de1370604d3b5d99622b80912776892 *src/libgit2/src/futils.c e75d4312121b325a8cb2303fd0cb6e69 *src/libgit2/src/futils.h 3d6a2bc41f855f6d3ac4d214330c0195 *src/libgit2/src/graph.c ef7c0af69351dc8ea37bf75e832c7fa4 *src/libgit2/src/hash.c 4972fdf9f061fc0bd497eb08ce4c3b0f *src/libgit2/src/hash.h f05fe31b398009bc20081b7ddd041adf *src/libgit2/src/hash/sha1.h d5b43d4c76c66401080dd9e1fb2dd504 *src/libgit2/src/hash/sha1/collisiondetect.c a894578444c74c8cdbc52d8109fd10a3 *src/libgit2/src/hash/sha1/collisiondetect.h ac96a75f8b5ff2331f636a6420fe432e *src/libgit2/src/hash/sha1/openssl.c 3f3c6b5bd1c1833bcc5c479d5bffb6e4 *src/libgit2/src/hash/sha1/openssl.h 429697323e8c31f1f003f90a49ffe402 *src/libgit2/src/hash/sha1/sha1dc/sha1.c 21c70909bcd057283a6a2b023256dafc *src/libgit2/src/hash/sha1/sha1dc/sha1.h 14bd8908eb52a8a4a426faf9c538d077 *src/libgit2/src/hash/sha1/sha1dc/ubc_check.c 6633a9728e2b0278c0a60eb3f77651a3 *src/libgit2/src/hash/sha1/sha1dc/ubc_check.h 9ac7c4af2e266052bc2af0ce8765c903 *src/libgit2/src/hashsig.c d677e94a671a1fadcd6627b4c386bff4 *src/libgit2/src/ident.c 60c1991cc8df26ebf58b72160785042c *src/libgit2/src/idxmap.c 6bf1a4a36a91b107ab618797ee2b3fa2 *src/libgit2/src/idxmap.h 12bdc86d93ea307e454941f39445efb5 *src/libgit2/src/ignore.c b1cff5c92d6c11f2e9ffc591b32e2f19 *src/libgit2/src/ignore.h f5ffb898a1339c3e1430a4b11975b700 *src/libgit2/src/index.c 7e87066f055363762bf290b30b68399e *src/libgit2/src/index.h f38496fc306720c27d94432105f27845 *src/libgit2/src/indexer.c 107ec5ceb74e68cb57bf24b8c9ca9e66 *src/libgit2/src/indexer.h 0e85aa5a8a87d867a5e9d6683386222d *src/libgit2/src/integer.h 58ed0160d3dfc7e68134604de9901898 *src/libgit2/src/iterator.c 5cbec4f778159c2df3f8a2fb0f12deef *src/libgit2/src/iterator.h 0638ff474167d2e58a935bc90887353b *src/libgit2/src/khash.h e5755b6f45d095c6e763d35b957f6481 *src/libgit2/src/libgit2.c 36b7e7c4a9a1ffa5ab66caac6cea8b52 *src/libgit2/src/libgit2.h 90a4c85dc7986a44328e7e3896f69014 *src/libgit2/src/mailmap.c 66b63cc62aa17f8a7b2f563ee281ba5a *src/libgit2/src/mailmap.h b8c06db01e6eb4f88d251cbe3ebcbc25 *src/libgit2/src/map.h 48564ffcecca824358877f03d3e7e0fa *src/libgit2/src/merge.c 466407875d39e3814034d0c5b0af8314 *src/libgit2/src/merge.h f6893d0141b2fdfaaa1d54de758331ad *src/libgit2/src/merge_driver.c 7b8bce8e499a50c8c01ec4c2e2e1034e *src/libgit2/src/merge_driver.h e8cdca60346e5d074acc880efd1ea66f *src/libgit2/src/merge_file.c c3c4dc0f42223bc4337f57488cada3e0 *src/libgit2/src/message.c 6166420f26f3c52ae8db63ec340899a4 *src/libgit2/src/message.h 98436ecee843f2717add212398d9ea8c *src/libgit2/src/midx.c 7bf6622cf10d1d827dc38568601697d3 *src/libgit2/src/midx.h ace173ce9ba72fc39afd9d5f2826213d *src/libgit2/src/mwindow.c 074dcec303d663db5d6c6ff1a46c65fa *src/libgit2/src/mwindow.h cc8e6b865c677999a6100d1b949e9a6a *src/libgit2/src/net.c 657acb40a97efc6edc6730222ce556e6 *src/libgit2/src/net.h 2c8792b53fe51b1c7f6f1f4496e7f80f *src/libgit2/src/netops.c 56bb466a1f4d1cb18efe00a7a7cc21f9 *src/libgit2/src/netops.h 20fa6d8f9dd799cdb0ffde53e9a18971 *src/libgit2/src/notes.c 43424e790704016a8fe028df20009bab *src/libgit2/src/notes.h 78bb8c152d719cbb07afeab4fceb1e51 *src/libgit2/src/object.c 3f2948d93815e0731ed63bf1308fd770 *src/libgit2/src/object.h df2941c8733a4f72f8cbf71111dda6a1 *src/libgit2/src/object_api.c 86c992446208bb4d91e686c966f38dc8 *src/libgit2/src/odb.c 096a189a9932cdf07fd15300dacbd472 *src/libgit2/src/odb.h a2ec4578a15f3857031c5d3ad7650856 *src/libgit2/src/odb_loose.c 951ec66c071a4f3a66183119da8482c6 *src/libgit2/src/odb_mempack.c 91b7cb062309bf374ae8adefeed32361 *src/libgit2/src/odb_pack.c 99d0a05dc83667d43a3b5c98f7b071e8 *src/libgit2/src/offmap.c aa734f8a2da05935996220e451d9e845 *src/libgit2/src/offmap.h 695bb1092f9269c87d53928678a5da4e *src/libgit2/src/oid.c 08d8f66109fe29f77e3e91e894cd81a9 *src/libgit2/src/oid.h 9a582c4d5aed2f3ea8b831d1bb03089e *src/libgit2/src/oidarray.c d4cab7dde89df13b08eec0f912b68eee *src/libgit2/src/oidarray.h daaa1e9314b12af34df6716651ca711b *src/libgit2/src/oidmap.c e6b3cda45957ccde69f376fb3536606e *src/libgit2/src/oidmap.h fbc18072535401d799ed818252d580d2 *src/libgit2/src/pack-objects.c ac4359065bd50fbf076e9941d74ef6db *src/libgit2/src/pack-objects.h 68de1d84bce01a216412d6f77db38a25 *src/libgit2/src/pack.c dae31f078b436fdb3aded2d4d2843a7d *src/libgit2/src/pack.h 2286f9bcb8ae30ea8cb481a088bb0cc2 *src/libgit2/src/parse.c f017d5d85e2b66972bd97c9f0e2b1f48 *src/libgit2/src/parse.h cdf81cad141b17e88ad93fc2e575dc0a *src/libgit2/src/patch.c 59706e320d105777d7fe67b281b1316d *src/libgit2/src/patch.h 6c93b8b83cb9f9265bd8f11727aeb88f *src/libgit2/src/patch_generate.c d67744ede42e614e7aea95a8e88b5eb4 *src/libgit2/src/patch_generate.h 5b40c65221314f8a6ba4f3ec4697ab06 *src/libgit2/src/patch_parse.c 5b70e6a39b5159d48d8a772cb781c490 *src/libgit2/src/patch_parse.h 0f030e9b1339cb4b0e34c8266f9acf05 *src/libgit2/src/path.c 5c01dac2dcaba8d2af5b299de83adc5a *src/libgit2/src/path.h 0f2542b4707fd6c78d5b5a3e49306e44 *src/libgit2/src/pathspec.c 66a1cc51a099ef0d9cf6cfb04dc42526 *src/libgit2/src/pathspec.h db4f35060bb21a239c338247f706a12f *src/libgit2/src/pool.c b49bc2418cc5fb52b9d9521c125ae601 *src/libgit2/src/pool.h 222a2304c40fe1b099ddea6e6f84bd32 *src/libgit2/src/posix.c 8a7efe242d8bfbb2830cf3a42b708dbe *src/libgit2/src/posix.h 994f1d3b5dda2e4f30f1d69efe3b8a60 *src/libgit2/src/pqueue.c 55a8ef0429841f9730be8ef63fa125a4 *src/libgit2/src/pqueue.h de6112080355e13fa7477ca71d72c6a8 *src/libgit2/src/proxy.c c813798d45282a651a0d90ca188e18cd *src/libgit2/src/proxy.h 81a94e6b9c114c361df8071004cf6ede *src/libgit2/src/push.c 9ef6afb27559a0b551f0c38cbf791ccb *src/libgit2/src/push.h 4435100d332c2840d3c0046c7ac01d2c *src/libgit2/src/reader.c a5ce25baea83533fa254271ab7297d47 *src/libgit2/src/reader.h a41491656270b5e1f0bc9f49e45ece2c *src/libgit2/src/rebase.c 6616f339816da8657880c0fb72aa8498 *src/libgit2/src/refdb.c e5d627b95a85bb2e2ecfc4f9e2c49bbd *src/libgit2/src/refdb.h 1914e94a2f4434116927147cef357f29 *src/libgit2/src/refdb_fs.c 31c374966de0bd276836018569f12ca6 *src/libgit2/src/reflog.c b87cfc1afaae103e87d40b559667053a *src/libgit2/src/reflog.h be56bca1ede668587ca90160696b0344 *src/libgit2/src/refs.c 50d564969deb78289b7cf9315744e7a9 *src/libgit2/src/refs.h 34aa5a02217da5d8b2ca486a213e9389 *src/libgit2/src/refspec.c 17d174d989a7971e70b1c8f6f759ce0f *src/libgit2/src/refspec.h afccfc7b1912dc2ce0166516a8128704 *src/libgit2/src/regexp.c 9da430a195e5f240633014a3013e8a9b *src/libgit2/src/regexp.h f29f13145054f239fb2dbaac79a50697 *src/libgit2/src/remote.c 6f3208995716bd372fe84b3eb438f765 *src/libgit2/src/remote.h bb97d27e951ecf221616baea38bacfae *src/libgit2/src/repo_template.h fa9a9bb85aef2d812a2f429c5b50f7a2 *src/libgit2/src/repository.c 18aa65bb23f2233240bc00fa05b6fc04 *src/libgit2/src/repository.h f79fc4cd3028ceab03d674b1598c9dd2 *src/libgit2/src/reset.c 05498c198ed473effdbf85001118e08e *src/libgit2/src/revert.c 819ae7034d9aebd9ae71141f3a1e4ce6 *src/libgit2/src/revparse.c fe9468e006bafe455b0fff98a58052ae *src/libgit2/src/revwalk.c 9c91e0d6f61984ccab51f951fed7ed4d *src/libgit2/src/revwalk.h 0de9f9114f696d55aa98d7cf92a3aa65 *src/libgit2/src/runtime.c 65f00858ed4d69d9eba3d3f786a71d36 *src/libgit2/src/runtime.h 5f97e3ad7b698416504b35e95c099afd *src/libgit2/src/settings.h c531063c0edadf687c2dcf9787cae46e *src/libgit2/src/signature.c ae2450a5a00b5b638983675f1f8af191 *src/libgit2/src/signature.h dbbbcf3a42b05588c6bad980eefe1ca2 *src/libgit2/src/sortedcache.c 90d0e1b202f76580af88ce4a1494d11b *src/libgit2/src/sortedcache.h ff5ef3dbf7f1aa8ad8bed2f4ab4d7f40 *src/libgit2/src/stash.c 45ff5445d0827f7fbabf2e6b9ba4b3a0 *src/libgit2/src/status.c c7d4ad139216eb42b5bc1ba33798fe7c *src/libgit2/src/status.h 99624557a437e61a6c781ebc3f483116 *src/libgit2/src/strarray.c 2490ffab5606567bcd776562a107100c *src/libgit2/src/stream.h ebe87b2969a010f0c5ec1ea548aa371b *src/libgit2/src/streams/mbedtls.c cca3332b2deba32e07cfe43a0fcabbc2 *src/libgit2/src/streams/mbedtls.h 770340786d79195627ce71c7e12db730 *src/libgit2/src/streams/openssl.c 16cec18436c6af39f3e4be24aeffe156 *src/libgit2/src/streams/openssl.h 6ce64afc8a67002eed51a0bc53d18088 *src/libgit2/src/streams/openssl_dynamic.c fa343245492600c7f30d3a243bed6623 *src/libgit2/src/streams/openssl_dynamic.h 717a34be015168b44691be206402a25b *src/libgit2/src/streams/openssl_legacy.c 6f9f2bdf23d7f02cdf23c75a53db743e *src/libgit2/src/streams/openssl_legacy.h f110fba58da29daacde6bae21c2ac06b *src/libgit2/src/streams/registry.c 4ab3586d3c4d5744f5c357b9c21bfdd3 *src/libgit2/src/streams/registry.h 97dd05513f685d40ca65cc9c505149ab *src/libgit2/src/streams/socket.c ce8e433250a62a8f3acde5d842bc6467 *src/libgit2/src/streams/socket.h 49d37cea0bca7983aac2d45da4d7a169 *src/libgit2/src/streams/stransport.c 2c09de677a65f77c638a08e313697898 *src/libgit2/src/streams/stransport.h 1b81bd2f228204a1fb0886c0fc210dc9 *src/libgit2/src/streams/tls.c 0d9d070b1061548df9aae79e425e978d *src/libgit2/src/streams/tls.h bedf9cf88083e416ad20116db405e1d5 *src/libgit2/src/strmap.c bb0ab274a332e2d80746da717eb05f4a *src/libgit2/src/strmap.h e273cf0b693f64646805204f59b1762b *src/libgit2/src/strnlen.h 9bdfc097de5452890cd143dfde518d0f *src/libgit2/src/submodule.c f34541eb23a68541eadb3410cae60548 *src/libgit2/src/submodule.h b6526d02426206e7c2952a2dbb7d9032 *src/libgit2/src/sysdir.c 4b5ead2baa86449b4f27e98b5fdebc10 *src/libgit2/src/sysdir.h 636c93dc7adf70893778133f9ed957f4 *src/libgit2/src/tag.c c1c98db2ed2ea3f21ce314e48cd9d2ef *src/libgit2/src/tag.h d90f4e8a571cfd21221c3727b526b63a *src/libgit2/src/thread.c bed80cde5aa86edf6f00253add643b74 *src/libgit2/src/thread.h b80eab72c7aeae691a8b4b9d0913ff37 *src/libgit2/src/threadstate.c 3e2e2321f3d495349994304932db9c69 *src/libgit2/src/threadstate.h 0f12aaa747cc0cf3d5655125472fbe84 *src/libgit2/src/trace.c bacef5c7fc04e0eb1df9979b4e8d8a66 *src/libgit2/src/trace.h 852e7a5465a542dfafb97b6ba28dc310 *src/libgit2/src/trailer.c 869baadb5d3ede194ba25abfbf1085f1 *src/libgit2/src/transaction.c fb9e2b90a7b7e235cf9954f40f23ace1 *src/libgit2/src/transaction.h 70c03583ea260ffe4c1ab1b142dae7ff *src/libgit2/src/transport.c 29ad51d0293d982dc03018291ede242a *src/libgit2/src/transports/auth.c cbf9b48a342ea21ccd2b57ef0e6a03b1 *src/libgit2/src/transports/auth.h c2730585a62ba5a44f6914f2e3dad5c7 *src/libgit2/src/transports/auth_negotiate.h a282a38ee523a9318087b413bf5f34ee *src/libgit2/src/transports/auth_ntlm.c 68687e35741f9353378886f6a5f53200 *src/libgit2/src/transports/auth_ntlm.h 5b6a00403b6e7af5a7cfd78619434022 *src/libgit2/src/transports/credential.c e00ed4e6e9f8d5dbf2c4ad519ae721f0 *src/libgit2/src/transports/credential_helpers.c cedb1aab83e3d2670f3739c71d22f0ca *src/libgit2/src/transports/git.c 9f20eeebb6eb1a2378f5484968d34089 *src/libgit2/src/transports/http.c b6e8725e8199507574629c94c93fb82e *src/libgit2/src/transports/http.h 052e85da15258d2325e58cd31f00feb5 *src/libgit2/src/transports/httpclient.c 197650b081f3f39a7bc980b5299ec7e6 *src/libgit2/src/transports/httpclient.h 6b6151dc69fe2e699214673ac2a23749 *src/libgit2/src/transports/local.c 0238d067b7b237b6aa923af0da7558b4 *src/libgit2/src/transports/smart.c 66775d303baabf03be3d197d8d0ccb1c *src/libgit2/src/transports/smart.h 346bbbe6a508ae35ac093d261335d328 *src/libgit2/src/transports/smart_pkt.c c7513bb14c4d21e740f64d1a574c8aa4 *src/libgit2/src/transports/smart_protocol.c 24a2823d2f79988419a75cdbf61b75fd *src/libgit2/src/transports/ssh.c 7772089c95ff23af72787dfb93e9a6b1 *src/libgit2/src/transports/ssh.h dc1088a6eb0fc6cf292b41614c1a3c55 *src/libgit2/src/transports/winhttp.c dfbb7605f47b028e330ba5b77e3a0084 *src/libgit2/src/tree-cache.c 6f7ff2aa0536f676ee3b7b608dac213d *src/libgit2/src/tree-cache.h ce446d9a28eb3df90557d603bfe21aec *src/libgit2/src/tree.c b6e70dd4120b00ae83f30809aac154de *src/libgit2/src/tree.h e27841898c487c03274a8345baf3e422 *src/libgit2/src/tsort.c 48afdfe206c2b66476a198531e959910 *src/libgit2/src/unix/map.c 3737d82f5d12d8314b4ebf61366e3d0e *src/libgit2/src/unix/posix.h c6a0ceabebf4c1159baac62e2c4441fc *src/libgit2/src/unix/pthread.h 3c5b85aeb3bb2d2483bc4911e688b47c *src/libgit2/src/unix/realpath.c 760a7c86d0f86b9d9a825dc30516ee55 *src/libgit2/src/userdiff.h 8fe5c27275d27838f53d5e5bef7aa9b7 *src/libgit2/src/utf8.c 1f0575c5c90375a70bf6dd1d26624a12 *src/libgit2/src/utf8.h 47cd9fc692806cb671dcdd163d45f2c5 *src/libgit2/src/util.c bbd086d509cc6e2c77d85a63bc83388b *src/libgit2/src/util.h 6cff1530561052a5d4fdadf522b6b251 *src/libgit2/src/varint.c dc74d714a3595cc232434f62a3693477 *src/libgit2/src/varint.h 251ba913fba328be4082455cb864398a *src/libgit2/src/vector.c a21deee0e531e3e6eca5585475375def *src/libgit2/src/vector.h ea8738007484688a49c730cd49faba04 *src/libgit2/src/wildmatch.c 3df73f5b4f07efb903f1311e048225cd *src/libgit2/src/wildmatch.h 763ea3b9acde23b54cb4496de6283861 *src/libgit2/src/worktree.c 934bb99cdd72ff320bcc6e3fe3de6ccf *src/libgit2/src/worktree.h febd9a4cfaf0f362067bcfd37a533893 *src/libgit2/src/xdiff/xdiff.h b150ffa4c29b886cdd1acf3d9ed19445 *src/libgit2/src/xdiff/xdiffi.c d7650bda93bed570818d4420fc699ebd *src/libgit2/src/xdiff/xdiffi.h 33effe2ad82e89510f69e06d56190740 *src/libgit2/src/xdiff/xemit.c a6f80630bd86179645cf3330edc2bfd3 *src/libgit2/src/xdiff/xemit.h cf40b633e1ab930654f1974d0c60db9e *src/libgit2/src/xdiff/xhistogram.c 9a47aafe5eba86a3294e4e99343c5047 *src/libgit2/src/xdiff/xinclude.h d591e0442d508c7d81142aa1581435b9 *src/libgit2/src/xdiff/xmacros.h c613203651d7b98dfae0016aa3dd6cad *src/libgit2/src/xdiff/xmerge.c 91b3b2a69a3e81391cc2213da3baac87 *src/libgit2/src/xdiff/xpatience.c 8a94cb6c35c82920a2d6872a8a4c7977 *src/libgit2/src/xdiff/xprepare.c a9726a13f78e5d5f5d8a99765d236d03 *src/libgit2/src/xdiff/xprepare.h 4be97a01eee799e84a73e1dcb9f87e40 *src/libgit2/src/xdiff/xtypes.h d7889689a442e6af8ef4aca098be7b6a *src/libgit2/src/xdiff/xutils.c fc55e92966e2a695b353f946684e3bf6 *src/libgit2/src/xdiff/xutils.h 149a996039183a7e8ae20376126018b2 *src/libgit2/src/zstream.c fe13776f54afc96bdbfe2c16a11cb27a *src/libgit2/src/zstream.h d8b1b9016d1bd8fb5475da0a75e09330 *tests/add-force.R 716ee40965b81152aabcfe04aecd82b6 *tests/bare_repository.R 3c412237ca119f8ce8a21eee1a34839d *tests/blame.R ab171d28872213174c6ba1889040b906 *tests/blob.R 1cca18076ec9986b76272854d13a1d99 *tests/branch.R 777db6253c82dc9d9d1dd80cd7d751b5 *tests/bundle.R 13a6b90900e5359b478bcf0e59535b90 *tests/checkout-named-branch.R d3bedace90eee4720362b9efb4e8730f *tests/checkout.R a683916aac9047b969dba03fc7d85cbd *tests/checkout_branch.R 4909d12e6c29174542facf3335a2fcdd *tests/checkout_commit.R 63592b75eb742d26aa4c08926c144656 *tests/checkout_tag.R 3afa67791f1a8d6c0e3fd05369a4e07e *tests/clone_bare.R 8080191187f8a9099e437709e967c182 *tests/clone_branch.R 7550594f3434fb2909115630a9a8e021 *tests/clone_checkout.R 10fa9d7b5bb807ff10850514e9100a9e *tests/commit.R 77398848e7ffd4385f295128de95400e *tests/commits_path.R ce4d55c426020ecdb6f9ada92cd7a309 *tests/config.R 45c9dae53d016567eb19455ad6e20148 *tests/diff.R da7912a446b6b432dbbfd10ddf0a4a7c *tests/fast_forward_merge.R 407b0ddbaebde99c3111c9dfa9c5f3c5 *tests/fetch.R 572bbf83ba0d48af555405f5b8dabc42 *tests/graph.R 7a42f6a2fb0e6b3ec6d9570af36072c5 *tests/index.R e85148cca7a5b2efc3abf40ab1ff0e5f *tests/invalid-conf-var.R d32bbe3db1f7c2505217ef1039d29964 *tests/libgit2.R c53a4e099a568ce4817ba4932e9b1ce2 *tests/ls_tree.R 8d2fd6bfd9d1d44346b14999ed0feeb0 *tests/merge.R 1e50df2cbdf1200cdd376ec607a3088f *tests/merge_named_branch.R e12ce05a7e13aef47559cf5788d8a803 *tests/normal_merge.R ab0cb22ce6fc41bbd30f9dde2e1f7ec7 *tests/note.R b353c99dbbbe02183ff16c2c3b70f283 *tests/odb_blobs.R a825d13adcbacdd93e88d9beb3835570 *tests/pre-process-path.R 6e8af381d1893a4674eb8fd19c22530c *tests/pull.R c763e509fafe2626a6c8631c6a455732 *tests/push-force.R 6634ea7fc5e7aa7448335429fda8b060 *tests/push.R e5d4985ccab31ef30229b975a62025a4 *tests/reference.R 25248b2c5630ce3845ed6cdff346f446 *tests/reflog.R ddfafbe1f06cfb91a4b17e5e1584ed81 *tests/refspec.R f0be6a17819c0510c8d1450968518d8d *tests/remotes.R 445aee7b201cc1c453d8a7674f9d8efa *tests/remove.R defbaa61666c30f01103a2db8a6be5e0 *tests/repository.R 2c5ab65cd741c1585159c69b31091fa6 *tests/reset.R 7df3aa95bde62bf7cd8e77b096dcf42a *tests/revparse.R bc7cee14a58d7a0b7e1e65663fa1e422 *tests/signature.R 29b80ca08125b97f7f653f6ed28c99e4 *tests/stash.R d8d3cec2311690520ecb30b226ebb5ff *tests/status.R 9c41625df72427f0029e4ec72c7072ae *tests/tag.R 6f41f340230d3299221ff98816a3a9cc *tests/time.R 586cb03c091b4f23c49545ac608f834a *tests/tree.R 8a4603d96c6d324f5cdef12f70715963 *tests/util/check.R e66834eb5770695344be37918ca147c7 *tests/when.R 7656db1151b730dc6c51cbfa83136071 *tools/config.guess f1d8f489b65ababc19b36bb9cc38670a *tools/config.rpath e55224754deb973527e64156e8c47acd *tools/config.sub 1480a568a780a9e28d3487b9d9b71991 *tools/iconv.m4 f6b34e2198c1cf50c8038f5704f664c4 *tools/install-sh 07b6485b5a80e254d2b846b9ec68ded8 *tools/lib-ld.m4 aec3a802919e7bbafeb1260f6ea88c1c *tools/lib-link.m4 667accfc31d0bd3efd23efdccf69f5f3 *tools/lib-prefix.m4 f56e89fee26d40334bb9a1474895b187 *tools/missing c497ed2a59f927f916cfd7edc62307cd *tools/pkg.m4 6f80c54aaec370f071da4609a9a5a372 *tools/version.c 327f451d66fbab809bb322e0e1bac006 *tools/winlibs.R git2r/NEWS.md0000644000175000017500000005336514145546122012551 0ustar nileshnilesh# git2r 0.29.0 (2021-11-18) ## CHANGES * Added a 'branch' argument to the 'init' function to make it possible to specify the branch name. * Updated the build configuration script on Windows and MacOS to use libgit2 version 1.3.0. * Updated the bundled libgit2 source code to version 1.3.0. * Renamed the NEWS file to NEWS.md and changed to use markdown format style. # git2r 0.28.0 (2021-01-10) ## IMPROVEMENTS * Updated to use libgit2 version 1.1.0 on Windows. * Fix handling of a symbolic reference when checking out previous branch. * Added a configure option '--without-libgit2' to ignore presence of a system libgit2 library and instead use the internal git2r libgit2 library. Usage: R CMD INSTALL --configure-args='--without-libgit2' git2r_x.y.z.tar.gz * Updated some tests to work with libgit2 version 1.1.0. # git2r 0.27.1 (2020-05-03) ## CHANGES * Fixed the CITATION file to pass 'R CMD check' without a NOTE. # git2r 0.27.0 (2020-05-01) ## IMPROVEMENTS * Updated the bundled libgit2 source code to version '0.28.5'. * Updated the build configuration script to be able to build git2r with a system installation of libgit2 version >= 1.0. * Updated to use libgit2 version 1.0.0 on Windows. * The build configuration script checks for minimum required version of libssh2 (version >= 1.8). Issue #420. * Updated to use roxygen2 version 7.1.0 to build the documentation. * Make it easier to view and change the timezone (John Blischak in #408). * Fixed 'ls_tree' to handle content in subfolder, see description in PR #402. * The 'branch_create' function has been changed to use the 'last_commit()' function as default to determine the commit to which the new branch should point. # git2r 0.26.1 (2019-06-30) ## BUG FIXES * Fixed the broken build on Solaris. # git2r 0.26.0 (2019-06-29) ## IMPROVEMENTS * Updated the bundled libgit2 source code to version '0.28.2'. * Added the 'force' argument to the 'tag' function to overwrite an existing tag. * Allow a zero length tag message. * Make it possible to create a lighweight tag. * Added the 'ref' argument to the 'commits' function to give a reference to list commits from. * Added the utility function 'lookup_commit' to lookup a commit related to a git object. * The 'path' argument was added to the 'commits' function to make it possible to specify that only commits modifying this file ('path') will be returned to reproduce 'git log' with '--no-follow', see the documentation. (Peter Carbonetto and John Blischak in PR #372) ## BUG FIXES * Removed the timezone offset from the commit time to fix an incorrect time in GMT when reading information from a repository (Thierry Onkelinx in PR #393). # git2r 0.25.2 (2019-03-20) ## CHANGES * Improved the build configuration script: if the system installation of libgit2 is to old, use the bundled libgit2 instead of raising an error. ## BUG FIXES * Fixed the broken build on Solaris. # git2r 0.25.1 (2019-03-17) ## BUG FIXES * Fixed significant warning from 'R CMD check' # git2r 0.25.0 (2019-03-17) ## CHANGES * Updated the bundled libgit2 source code to version '0.28.1'. * Added additional parameters to the 'diff' function to control the output, see the documentation. * Added getPass option to the password argument in 'cred_user_pass' (Annie Wang in PR #383) * Changed the 'print' functions to return its argument invisibly. * Changed the 'git_config_files' function to return a 'data.frame' * Changed the 'ahead_behind' function to accept a tag or a branch for the local and upstrean commit. * Changed the 'descendent_of' function to accept a tag or a branch for the 'commit' and 'ancestor' commit. ## BUG FIXES * Fixed memory protection errors in the git2r C source code reported by the 'rchk' tool. * Fixed listing of 'commits' from a shallow repository. * Fixed the configuration script to include the missing macro 'AM_ICONV'. # git2r 0.24.0 (2019-01-07) This is a bug-fix release. ## BUG FIXES * Fixed memory protection errors in the git2r C source code reported by the 'rchk' tool. * Raise an error if the path argument to the 'hashfile' function is NA. # git2r 0.23.0 (2018-07-17) ## IMPROVEMENTS * Updated the bundled libgit2 source code to v0.27.3 (504bd54). ## BREAKING CHANGE * On macOS, git2r no longer enables SSH transport by default. This is due to the complexity to build the dependencies for SSH transport in an R package when macOS no longer ships the OpenSSL headers. However, you can install git2r from source on macOS (see the 'R Installation and Administration' manual) with SSH transport enabled if you first install the libgit2 library, for example, using the Homebrew package manager. Another possibility is to let the build configuration automatically download the libgit2 library from the Homebrew package manager with: install.packages('git2r', type='source', configure.vars='autobrew=yes') # git2r 0.22.1 (2018-07-10) ## NEW FEATURES * Added the 'git_config_files' method to locate configuration files. * Added the 'stash_pop' method to apply a single stashed state from the stash list and remove it from the list if successful. * Added the 'stash_apply' method to apply a single stashed state from the stash list. ## IMPROVEMENTS * Updated the bundled libgit2 source code to v0.27.2 (8d36dc6). * git2r can now build against a system installation of libgit2 (Elliott Sales de Andrade in PR #345, #344 and #336). * Refactoring of the configuration scripts to use a prebuilt libgit2 on macOS and Windows (Thanks Jeroen). * Ensure that git2r writes the config file to the correct location on Windows (John Blischak in PR #320). * Better default location to find ssh keys in 'cred_ssh_key()' (Ian Lyttle in PR #317). ## BUG FIXES * If a merge results in no change, the returned 'git_merge_result' now returns 'FALSE' for 'fast_forward' and 'conflicts' and 'NA' for 'sha'. Previously it returned 'logical(0)' for 'fast_forward' and 'conflicts' and 'character(0)' for 'sha'. ## BREAKING CHANGES * Changed from S4 classes to S3 classes to simplify the design and facilitate future development. * Removed the trailing slash from the directory name when reporting repository path or workdir. * Removed the 'libgit2_sha' method. Use the 'libgit2_version' method instead. * Changed the 'stash_drop' argument 'index' from zero-based to one-based i.e. use index = 1 to drop the first stash. # git2r 0.21.0 (2018-01-04) * Added methods 'odb_blobs' and 'odb_objects' with missing repository signature. Internally, they use 'getwd' and 'discover_repository' to open a repository. ## BUG FIXES * The bundled libgit2 source code has been reverted to libgit2 v0.26.0 (15e1193) from 14 June 2017 (same as in git2r v0.19.0) to fix memory alignment errors. # git2r 0.20.0 (2017-12-17) ## IMPROVEMENTS * Updated the bundled libgit2 source code to commit (fa8cf14) from 16 December 2017. * Improvements to the build configuration script. ## BUG FIXES * Fixed the internal callback for remote host authentication from hanging indefinitely when querying an ssh-agent for credentials. Now, the callback signals an error instead of trying again if the authentication failed the first time. # git2r 0.19.0 (2017-07-19) ## IMPROVEMENTS * Updated the bundled libgit2 source code to commit (15e1193) (v0.26.0) from 14 June 2017. * Added 'checkout' argument to 'clone()'. Allows to control whether checkout of HEAD is performed after the clone is complete. Setting 'checkout=FALSE' has similar effect as the git command line option '--no-checkout'. Andrzej K. Oles in #282. ## BUG FIXES * Fixed memory protection errors in the git2r C source code reported by the 'rchk' tool. * Added missing calls to 'R_forceSymbols' and 'R_useDynamicSymbols' in the C init function. * Enable argument 'all' to commit multiple modified (or deleted) files. John Blischak in #283 * Changed the configure script to determine the architecture of the machine earlier in order to fix an unsupported architecture error encountered on CentOS (#268, #288). # git2r 0.18.0 (2017-01-01) ## BUG FIXES * This is a bug-fix release to solve an error introduced in the build configuration on mac in version 0.17.0. The package failed with 'unable to load shared object', see issue #267. # git2r 0.17.0 (2016-12-29) ## IMPROVEMENTS * Updated the bundled libgit2 source code to commit (6b0510e) from 20 December 2016. * Static linking of LibSSH2 on mac to support redistributable binary package with SSH transport enabled. Version 1.8.0 of LibSSH2 is downloaded and built from 'https://www.libssh2.org/download/'. # git2r 0.16.0 (2016-11-20) ## IMPROVEMENTS * Updated libgit2 source code to commit (6b0510e) from 17 November 2016. * Add the option 'all_untracked' to the 'status' method to show individual files in untracked directories if the 'untracked' option is TRUE. * Add the 'tag_delete' method to delete an existing tag reference. * Update build configuration to support OpenSSL 1.1.0. * If the the 'getPass' package is installed the 'cred_ssh_key' method to create a new passphrase-protected ssh key credential object will call the 'getPass::getPass()' method if the private key is passphrase protected to allow for interactive input of the passphrase. The 'getPass' package is a suggested package. (Peter Meissner in PR #254) * Add 'path' argument to the 'reset' method to enable path-specific unstage, i.e. resets the index entries for all paths to their state at HEAD ## BUG FIXES * Build configuration: use portable string equality comparison operator. This fixes the build e.g. for those without Bash as /bin/sh. (Sander Maijers in PR #243). # git2r 0.15.0 (2016-05-11) ## IMPROVEMENTS * Build configuration: 'pkg-config' is now used to find 'libssl', if possible (Elias Pipping in PR #231). * Added a method to coerce a 'git_commit' object to a 'data.frame'. * Added the method 'is_branch' to check if an object is a 'git_branch'. ## BUG FIXES * Build configuration: fixed installation with parallel make (Kirill Müller in PR #228). # git2r 0.14.0 (2016-03-13) ## IMPROVEMENTS * Updated libgit2 source code to commit (785d8c48) from 2016-03-04. This is release v0.24.0 of libgit2. * Refactoring of the build scripts. * Added a check that the configuration key is valid when setting a configuration variable and output a warning if the key is invalid. * The status method now prints "working directory clean" instead of nothing when the working directory is clean. * Added the 'refspec' argument to the 'fetch' method to specify the refs to fetch and which local refs to update. * Added a workaround to the 'commit' method to list commits in a shallow clone since the libgit2 library does not yet support this. # git2r 0.13.1 (2015-12-10) ## BUG FIXES * This is a bug-fix release to solve problems introduced in version 0.12.1: - The bundled libgit2 source code has been reverted to commit (98f7bd2) from 2015-08-05 (same as in v0.11.0) to fix memory alignment errors (clang-UBSAN and gcc-UBSAN). - OpenSSL is now used again on OS X to provide the cryptographic support for HTTPS connections to fix a significant compiler warning (arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]) on r-devel-osx-x86_64-clang. - Several fixes to the build configuration on non-Windows platforms. # git2r 0.12.1 (2015-12-05) ## NEW FEATURES * Add 'remote_ls' method to list references in a remote repository akin to the `git ls-remote` command. * Add 'remote_set_url' method to set the remote's url in the configuration. * Add 'cred_token' S4 class to hold the name of the environmental variable with the secret. Default value for the name is GITHUB_PAT. * It is now possible to checkout a specific file with the 'checkout' method. * Add 'ssl_cert_locations' method to set libgit2 global option 'GIT_OPT_SET_SSL_CERT_LOCATIONS' * Add 'ceiling' argument to 'discover_repository' method to prevent search from walking up the parent directories. ## CHANGES * Improvments to the cred_* functions documentation. * Add the following default arguments to the 'cred_ssh_key' method: publickey = '~/.ssh/id_rsa.pub' and privatekey = '~/.ssh/id_rsa' * On OSX, cascade CPPFLAGS and LDFLAGS to libssh2 build to allow libssh2 to be built against a user-installed openssl, discovered by configure or from R's Makeconf. Necessary to build on OS X ≥ 10.11 * On OS X, SecureTransport is now used to provide the cryptographic support for HTTPS connections insead of OpenSSL. * The search for libssh2 during configuration (non Windows) is now done via pkg-config. * Update OpenSSL on Windows to v1.0.2d * Update libgit2 source code to commit (3f5877d) from 2015-11-12. ## BUG FIXES * Add missing credentials argument to pull method. * Fix config failure when user.name or user.email are passed as variables. * Include 'configure.ac' in the distribution. # git2r 0.11.0 (2015-08-12) ## NEW FEATURES * Add punch card plot. * Add branch argument to clone with name of the branch to checkout. * Add 'force' argument to 'add' method to add ignored files. * The following methods can now be called without the repository argument: 'branches', 'references', 'remotes', 'tags' and 'workdir'. When these methods are called without the repository argument, the repository is searched for with 'discover_repository' in the current working directory. * Add name of branch to each item in branch_list. * Add name of tag to each item in tags list. * Add S4 class 'cred_env' to pass credentials in environment variables. * SSH transport on Windows. This requires 'LibSSH2' and 'OpenSSL'. These two libraries are downloaded from 'https://github.com/rwinlib' during configuration of the package. * Static linking of LibSSH2 on OSX to support redistributable binary package with SSH transport enabled. Version 1.6.0 of LibSSH2 is downloaded and built from 'https://github.com/libssh2/libssh2'. ## IMPROVEMENTS * Better summary output from S4 classes 'git_commit' and 'git_repository'. * Updated libgit2 source code to commit (98f7bd2) from 2015-08-05. ## BUG FIXES * Add imports to DESCRIPTION to fix CRAN notes. * Fix plot function to use the repository argument 'x' * Update configuration to build on OpenBSD. * Fix checkout branch in empty repository. * Fix path argument in rm_file. * Internal refactoring of C code that raise error to prevent segfault. # git2r 0.10.1 (2015-05-07) ## CHANGES * Rename 'bundle_repo' method to 'bundle_r_package' # git2r 0.10.0 (2015-05-07) ## NEW FEATURES * Added method libgit2_sha that returns the commit id of the libgit2 library that the bundled source code is based on. * Added the method in_repository to determine if a directory is in a git repository. ## CHANGES * Add brief summary of the five latest commits when summarizing a git_respository. * Added argument 'n' to the commits method to limit the number of commits in the output. * Added the following methods with missing repository signature; commits, is_shallow, is_empty, is_detached, repository and status. Internally, these methods use getwd and discover_repository to open a repository. * Changed configuration to raise error if the OpenSSL library is not found on non-Windows systems. * Changed configuration to raise error if the iconv library is not found on OSX. * Removed print of the configuration in the config method. Changed to return S3 class git_config. * Removed print of the status in the status method. Changed to return S3 class git_status. ## BUG FIXES * Use OPENSSL_INCLUDES variable to build on Solaris. * Use bundled regex library on Solaris. git2 0.9 (2015-04-25) ## CHANGES * Single quote 'libgit2' and 'Git' in Description field git2 0.8 (2015-04-24) ## CHANGES * Added bare argument to clone method to create a bare repository * Added force argument to push to force local revision to the remote repo * Updated libgit2 source code (2a0f67f) * Internal refactoring of push ## NEW FEATURES * Added method rm_file to remove files * Added 'all' argument to commit method to stage modified and deleted files * Added shortcut to checkout previous branch with "-" which is synonymous with "@{-1}" * Added session argument to commit method to add sessionInfo to commit message * Added session argument to tag method to add sessionInfo to tag message * Added method to coerce POSIXlt to S4 class git_time * Added method 'revparse_single' to find object specified by revision * Added plot method git2 0.7 (2015-02-23) ## CHANGES * Update libgit2 source code to commit (366e53d) * Fix configuration of compiler options when the OpenSSL library is found on non-Windows platforms # git2r 0.6 (2015-02-18) ## CHANGES * Update Title and Description field in DESCRIPTION file. # git2r 0.5 (2015-02-17) ## CHANGES * Update libgit2 source code to commit (a291790) * Use Alice and Bob as placeholder names in examples. * Add COPYRIGHTS file to list all copyright holders. * Fix significant compiler warnings from R CMD check with pedantic flag. # git2r 0.4 (2015-01-13) ## CHANGES * Fix build on Windows # git2r 0.3 (2015-01-13) ## CHANGES * Internal refactoring of merge method and merge tests. * Update libgit2 source code to version v0.22.0 ## BUG FIXES * Fix build on OSX. # git2r 0.2 (2015-01-05) ## NEW FEATURES * Add method 'odb_objects' to list all objects available in the database as a data.frame * Add method 'odb_blobs' to list all blobs reachable from the commits in the object database. ## DOCUMENTATION * Added examples to all exported methods. ## CHANGES * Removed ggplot2 dependency. Moved plot functionality to the ggit package (https://github.com/ropensci/ggit). * Renamed note_list method to notes * Removed markdown_link method * Renamed diff and merge arguments ## IMPROVEMENTS * Better performance when summarizing contributions. * Improved build of package. ## BUG FIXES * Fixed memory leaks. * Fixed use of allocVector without protection. * Added unload hook to unload DLL. * Fix tree and blob tests to use writeChar instead of writeLines to have more control over line endings. # git2r 0.1 (2014-09-09) ## NEW FEATURES * Many new features and methods added, see the documention for a description of the methods below: - Blob: content, blob_create, hash, hashfile, is_binary, is_blob, length, show, summary. - Branch: branch_create, branch_delete, branch_get_upstream, branch_remote_name, branch_remote_url, branch_rename, branch_set_upstream and branch_target. - Commit: is_commit and parents. - Diff: diff and diff_print. - Fetch: fetch and fetch_heads. - Libgit2: libgit2_features and libgit2_version. - Merge: merge. - Note: note_create, note_default_ref, note_list and note_remove. - Pull: pull. - Push: push. - Remote: remote_add, remote_remove, remote_rename and remote_url. - Repository: discover_repository and is_shallow - Reset: reset. - Stash: stash, stash_drop, stash_list, show and summary. * Improved error messages to give more detailed information including which function raised the error. ## NEW S4 CLASSES * The following new S4 classes to handle the libgit2 data structures: - cred_ssh_key - cred_user_pass - git_blame - git_blame_hunk - git_blob - git_diff - git_diff_file - git_diff_hunk - git_diff_line - git_fetch_head - git_merge_result - git_note - git_reflog_entry - git_stash - git_transfer_progress - git_tree ## CHANGES * Renamed methods: - is.bare to is_bare - is.empty to is_empty - is.head to is_head - is.local to is_local * Rename hex to sha for the 40-character SHA-1 hash in method arguments and S4 class slots. # git2r 0.0.8 (2014-03-20) ## NEW FEATURES * Added method to clone repository * Added method config to set user.name and user.email in a repository * Added method status to display state of a repository # git2r 0.0.7 (2014-03-16) ## NEW FEATURES * Added method to create a commit ## CHANGES * Improved error checking # git2r 0.0.6 (2014-02-21) ## NEW FEATURES * Added method init to create a new Git repository ## CHANGES * Removed usage of testthat package when testing the package * Removed bundled zlib in src/zlib and instead link against zlib shipped with R. * Dropped usage of external pointers, S4 git_repository now keeps track of the path of the repository. # git2r 0.0.5 (2014-01-01) ## CHANGES * Renamed S4 class repository to git_repository ## NEW FEATURES * Added method commits to list all commits in repository * Added S4 class git_commit to hold infformation of a commit * Added S4 class git_time to hold time of an action * Added slot walker to S4 class git_repository # git2r 0.0.4 (2013-12-30) ## NEW FEATURES * Added method remote_url to get the url a remote in a repository * Added method workdir to get workdir of a repository * Added method remotes to list remotes of a repository * Added S4 class git_signature to hold information of an action signature (e.g. for committers, taggers, etc) ## CHANGES * Renamed S4 class tag to git_tag * Renamed S4 class branch to git_branch * Renamed S4 class reference to git_reference # git2r 0.0.3 (2013-12-29) ## NEW FEATURES * Added method branches to list branches * Added method head to retrieve head * Added method is.head to check if a branch is head * Added method is.local to check if a branch is local * Added S4 class branch to hold information of a git branch * Added method to show a reference * Added method to list all references in a repository * Added S4 class reference to hold information of a git reference # git2r 0.0.2 (2013-12-28) ## NEW FEATURES * Added is.bare method to check if a repository is bare * Added is.empty method to check if a repository is empty # git2r 0.0.1 (2013-12-28) ## NEW FEATURES * Added S4 class repository to work with a git repository * Initial package structure git2r/DESCRIPTION0000644000175000017500000000316714146643373013163 0ustar nileshnileshPackage: git2r Title: Provides Access to Git Repositories Description: Interface to the 'libgit2' library, which is a pure C implementation of the 'Git' core methods. Provides access to 'Git' repositories to extract data and running some basic 'Git' commands. Version: 0.29.0 License: GPL-2 Copyright: See COPYRIGHTS file. URL: https://docs.ropensci.org/git2r/ (website), https://github.com/ropensci/git2r BugReports: https://github.com/ropensci/git2r/issues Maintainer: Stefan Widgren Author: See AUTHORS file. Imports: graphics, utils Depends: R (>= 3.1) Suggests: getPass Type: Package Biarch: true NeedsCompilation: yes SystemRequirements: By default, git2r uses a system installation of the libgit2 headers and library. However, if a system installation is not available, builds and uses a bundled version of the libgit2 source. zlib headers and library. OpenSSL headers and library (non-macOS). LibSSH2 (optional on non-Windows) to enable the SSH transport. Collate: 'blame.R' 'blob.R' 'branch.R' 'bundle_r_package.R' 'checkout.R' 'commit.R' 'config.R' 'contributions.R' 'credential.R' 'diff.R' 'fetch.R' 'git2r.R' 'index.R' 'libgit2.R' 'merge.R' 'note.R' 'odb.R' 'plot.R' 'pull.R' 'punch_card.R' 'push.R' 'reference.R' 'reflog.R' 'refspec.R' 'remote.R' 'repository.R' 'reset.R' 'revparse.R' 'sha.R' 'signature.R' 'stash.R' 'status.R' 'tag.R' 'time.R' 'tree.R' 'when.R' Encoding: UTF-8 RoxygenNote: 7.1.2 Packaged: 2021-11-18 22:18:17 UTC; stefan Repository: CRAN Date/Publication: 2021-11-22 07:30:03 UTC git2r/man/0000755000175000017500000000000014145546030012210 5ustar nileshnileshgit2r/man/blame.Rd0000644000175000017500000000433614145546030013565 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blame.R \name{blame} \alias{blame} \title{Get blame for file} \usage{ blame(repo = ".", path = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{path}{Path to the file to consider} } \value{ git_blame object with the following entries: \describe{ \item{path}{ The path to the file of the blame } \item{hunks}{ List of blame hunks } \item{repo}{ The git_repository that contains the file } } \describe{ \item{lines_in_hunk}{ The number of lines in this hunk } \item{final_commit_id}{ The sha of the commit where this line was last changed } \item{final_start_line_number}{ The 1-based line number where this hunk begins, in the final version of the file } \item{final_signature}{ Final committer } \item{orig_commit_id}{ The sha of the commit where this hunk was found. This will usually be the same as 'final_commit_id'. } \item{orig_start_line_number}{ The 1-based line number where this hunk begins in the file named by 'orig_path' in the commit specified by 'orig_commit_id'. } \item{orig_signature}{ Origin committer } \item{orig_path}{ The path to the file where this hunk originated, as of the commit specified by 'orig_commit_id' } \item{boundary}{ TRUE iff the hunk has been tracked to a boundary commit. } \item{repo}{ The \code{git_repository} object that contains the blame hunk } } } \description{ Get blame for file } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a first user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Create a second user and change the file config(repo, user.name = "Bob", user.email = "bob@example.org") writeLines(c("Hello world!", "HELLO WORLD!", "HOLA"), file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Second commit message") ## Check blame blame(repo, "example.txt") } } git2r/man/ahead_behind.Rd0000644000175000017500000000307014145546030015052 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{ahead_behind} \alias{ahead_behind} \title{Ahead Behind} \usage{ ahead_behind(local = NULL, upstream = NULL) } \arguments{ \item{local}{a git_commit object. Can also be a tag or a branch, and in that case the commit will be the target of the tag or branch.} \item{upstream}{a git_commit object. Can also be a tag or a branch, and in that case the commit will be the target of the tag or branch.} } \value{ An integer vector of length 2 with number of commits that the upstream commit is ahead and behind the local commit } \description{ Count the number of unique commits between two commit objects. } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message 1") tag_1 <- tag(repo, "Tagname1", "Tag message 1") # Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Commit message 2") tag_2 <- tag(repo, "Tagname2", "Tag message 2") ahead_behind(commit_1, commit_2) ahead_behind(tag_1, tag_2) } } git2r/man/libgit2_features.Rd0000644000175000017500000000056414145546030015736 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/libgit2.R \name{libgit2_features} \alias{libgit2_features} \title{Compile time options for libgit2.} \usage{ libgit2_features() } \value{ A list with threads, https and ssh set to TRUE/FALSE. } \description{ Compile time options for libgit2. } \examples{ libgit2_features() } \keyword{methods} git2r/man/merge_base.Rd0000644000175000017500000000254714145546030014600 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/merge.R \name{merge_base} \alias{merge_base} \title{Find a merge base between two commits} \usage{ merge_base(one = NULL, two = NULL) } \arguments{ \item{one}{One of the commits} \item{two}{The other commit} } \value{ git_commit } \description{ Find a merge base between two commits } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Master branch", file.path(path, "master_branch.txt")) add(repo, "master_branch.txt") commit_1 <- commit(repo, "Commit message 1") ## Create first branch, checkout, add file and commit branch_1 <- branch_create(commit_1, "branch_1") checkout(branch_1) writeLines("Branch 1", file.path(path, "branch_1.txt")) add(repo, "branch_1.txt") commit_2 <- commit(repo, "Commit message branch_1") ## Create second branch, checkout, add file and commit branch_2 <- branch_create(commit_1, "branch_2") checkout(branch_2) writeLines("Branch 2", file.path(path, "branch_2.txt")) add(repo, "branch_2.txt") commit_3 <- commit(repo, "Commit message branch_2") ## Check that merge base equals commit_1 stopifnot(identical(merge_base(commit_2, commit_3), commit_1)) } } git2r/man/summary.git_tree.Rd0000644000175000017500000000142114145546030015773 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{summary.git_tree} \alias{summary.git_tree} \title{Summary of tree} \usage{ \method{summary}{git_tree}(object, ...) } \arguments{ \item{object}{The tree \code{object}} \item{...}{Additional arguments affecting the summary produced.} } \value{ None (invisible 'NULL'). } \description{ Summary of tree } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") summary(tree(last_commit(repo))) } } git2r/man/branch_remote_url.Rd0000644000175000017500000000225414145546030016174 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_remote_url} \alias{branch_remote_url} \title{Remote url of a branch} \usage{ branch_remote_url(branch = NULL) } \arguments{ \item{branch}{The branch} } \value{ character string with remote url } \description{ Remote url of a branch } \examples{ \dontrun{ ## Initialize two temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo, "origin", "refs/heads/master") ## Get remote url of tracking branch to branch 'master' branch_remote_url(branch_get_upstream(repository_head(repo))) } } git2r/man/stash_pop.Rd0000644000175000017500000000276514145546030014511 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stash.R \name{stash_pop} \alias{stash_pop} \title{Pop stash} \usage{ stash_pop(object = ".", index = 1) } \arguments{ \item{object}{path to a repository, or a \code{git_repository} object, or the stash \code{object} to pop. Default is a \code{path = '.'} to a reposiory.} \item{index}{The index to the stash to pop. Only used when \code{object} is a path to a repository or a \code{git_repository} object. Default is \code{index = 1}.} } \value{ invisible NULL } \description{ Apply a single stashed state from the stash list and remove it from the list if successful. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") # Create a file, add and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, 'test.txt') commit(repo, "Commit message") # Change file writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) # Create stash in repository stash(repo) # Change file writeLines(c("Hello world!", "HeLlO wOrLd!"), file.path(path, "test.txt")) # Create stash in repository stash(repo) # View stashes stash_list(repo) # Read file readLines(file.path(path, "test.txt")) # Pop latest git_stash object in repository stash_pop(stash_list(repo)[[1]]) # Read file readLines(file.path(path, "test.txt")) # View stashes stash_list(repo) } } git2r/man/hash.Rd0000644000175000017500000000111214145546030013415 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blob.R \name{hash} \alias{hash} \title{Determine the sha from a blob string} \usage{ hash(data = NULL) } \arguments{ \item{data}{The string vector to hash.} } \value{ A string vector with the sha for each string in data. } \description{ The blob is not written to the object database. } \examples{ \dontrun{ identical(hash(c("Hello, world!\n", "test content\n")), c("af5626b4a114abcb82d63db7c8082c3c4756e51b", "d670460b4b4aece5915caf5c68d12f560a9fe3e4")) } } git2r/man/as.data.frame.git_repository.Rd0000644000175000017500000000306514145546030020170 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{as.data.frame.git_repository} \alias{as.data.frame.git_repository} \title{Coerce Git repository to a \code{data.frame}} \usage{ \method{as.data.frame}{git_repository}(x, ...) } \arguments{ \item{x}{The repository \code{object}} \item{...}{Additional arguments. Not used.} } \value{ \code{data.frame} } \description{ The commits in the repository are coerced to a \code{data.frame} } \details{ The \code{data.frame} have the following columns: \describe{ \item{sha}{ The 40 character hexadecimal string of the SHA-1 } \item{summary}{ the short "summary" of the git commit message. } \item{message}{ the full message of a commit } \item{author}{ full name of the author } \item{email}{ email of the author } \item{when}{ time when the commit happened } } } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create three files and commit writeLines("First file", file.path(path, "example-1.txt")) writeLines("Second file", file.path(path, "example-2.txt")) writeLines("Third file", file.path(path, "example-3.txt")) add(repo, "example-1.txt") commit(repo, "Commit first file") add(repo, "example-2.txt") commit(repo, "Commit second file") add(repo, "example-3.txt") commit(repo, "Commit third file") ## Coerce commits to a data.frame df <- as.data.frame(repo) df } } git2r/man/branch_rename.Rd0000644000175000017500000000173214145546030015266 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_rename} \alias{branch_rename} \title{Rename a branch} \usage{ branch_rename(branch = NULL, name = NULL, force = FALSE) } \arguments{ \item{branch}{Branch to rename} \item{name}{The new name for the branch} \item{force}{Overwrite existing branch. Default is FALSE} } \value{ invisible renamed \code{git_branch} object } \description{ Rename a branch } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Rename 'master' branch to 'dev' branches(repo) branch_rename(repository_head(repo), "dev") branches(repo) } } git2r/man/is_commit.Rd0000644000175000017500000000142414145546030014463 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{is_commit} \alias{is_commit} \title{Check if object is a git_commit object} \usage{ is_commit(object) } \arguments{ \item{object}{Check if object is a git_commit object} } \value{ TRUE if object is a git_commit, else FALSE } \description{ Check if object is a git_commit object } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") ## Check if commit is_commit(commit_1) } } git2r/man/repository_head.Rd0000644000175000017500000000157614145546030015710 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{repository_head} \alias{repository_head} \title{Get HEAD for a repository} \usage{ repository_head(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ NULL if unborn branch or not found. A git_branch if not a detached head. A git_commit if detached head } \description{ Get HEAD for a repository } \examples{ \dontrun{ ## Create and initialize a repository in a temporary directory path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Commit message") ## Get HEAD of repository repository_head(repo) } } git2r/man/discover_repository.Rd0000644000175000017500000000353614145546030016623 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{discover_repository} \alias{discover_repository} \title{Find path to repository for any file} \usage{ discover_repository(path = ".", ceiling = NULL) } \arguments{ \item{path}{A character vector specifying the path to a file or folder} \item{ceiling}{The default is to not use the ceiling argument and start the lookup from path and walk across parent directories. When ceiling is 0, the lookup is only in path. When ceiling is 1, the lookup is in both the path and the parent to path.} } \value{ Character vector with path (terminated by a file separator) to repository or NULL if this cannot be established. } \description{ Find path to repository for any file } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example-1.txt")) add(repo, "example-1.txt") commit(repo, "First commit message") ## Create a second file. The file is not added for version control ## in the repository. dir.create(file.path(path, "example")) file_2 <- file.path(path, "example/example-2.txt") writeLines("Not under version control", file_2) ## Find the path to the repository using the path to the second file discover_repository(file_2) ## Demonstrate the 'ceiling' argument wd <- workdir(repo) dir.create(file.path(wd, "temp")) ## Lookup repository in 'file.path(wd, "temp")'. Should return NULL discover_repository(file.path(wd, "temp"), ceiling = 0) ## Lookup repository in parent to 'file.path(wd, "temp")'. ## Should not return NULL discover_repository(file.path(wd, "temp"), ceiling = 1) } } git2r/man/libgit2_version.Rd0000644000175000017500000000057614145546030015610 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/libgit2.R \name{libgit2_version} \alias{libgit2_version} \title{Version of the libgit2 library} \usage{ libgit2_version() } \value{ A list with major, minor and rev } \description{ Version of the libgit2 library that the bundled source code is based on } \examples{ libgit2_version() } \keyword{methods} git2r/man/hashfile.Rd0000644000175000017500000000141314145546030014261 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blob.R \name{hashfile} \alias{hashfile} \title{Determine the sha from a blob in a file} \usage{ hashfile(path = NULL) } \arguments{ \item{path}{The path vector with files to hash.} } \value{ A vector with the sha for each file in path. } \description{ The blob is not written to the object database. } \examples{ \dontrun{ ## Create a file. NOTE: The line endings from writeLines gives ## LF (line feed) on Unix/Linux and CRLF (carriage return, line feed) ## on Windows. The example use writeChar to have more control. path <- tempfile() f <- file(path, "wb") writeChar("Hello, world!\n", f, eos = NULL) close(f) ## Generate hash hashfile(path) identical(hashfile(path), hash("Hello, world!\n")) } } git2r/man/is_tree.Rd0000644000175000017500000000150214145546030014127 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{is_tree} \alias{is_tree} \title{Check if object is S3 class git_tree} \usage{ is_tree(object) } \arguments{ \item{object}{Check if object is S3 class git_tree} } \value{ TRUE if object is S3 class git_tree, else FALSE } \description{ Check if object is S3 class git_tree } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") tree_1 <- tree(commit_1) ## Check if tree is_tree(commit_1) is_tree(tree_1) } } \keyword{methods} git2r/man/is_blob.Rd0000644000175000017500000000147714145546030014121 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blob.R \name{is_blob} \alias{is_blob} \title{Check if object is S3 class git_blob} \usage{ is_blob(object) } \arguments{ \item{object}{Check if object is S3 class git_blob} } \value{ TRUE if object is S3 class git_blob, else FALSE } \description{ Check if object is S3 class git_blob } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") blob_1 <- tree(commit_1)["example.txt"] ## Check if blob is_blob(commit_1) is_blob(blob_1) } } git2r/man/stash_apply.Rd0000644000175000017500000000356514145546030015037 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stash.R \name{stash_apply} \alias{stash_apply} \title{Apply stash} \usage{ stash_apply(object = ".", index = 1) } \arguments{ \item{object}{path to a repository, or a \code{git_repository} object, or the stash \code{object} to pop. Default is a \code{path = '.'} to a reposiory.} \item{index}{The index to the stash to apply. Only used when \code{object} is a path to a repository or a \code{git_repository} object. Default is \code{index = 1}.} } \value{ invisible NULL } \description{ Apply a single stashed state from the stash list. } \details{ If local changes in the working directory conflict with changes in the stash then an error will be raised. In this case, the index will always remain unmodified and all files in the working directory will remain unmodified. However, if you are restoring untracked files or ignored files and there is a conflict when applying the modified files, then those files will remain in the working directory. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") # Create a file, add and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, 'test.txt') commit(repo, "Commit message") # Change file writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) # Create stash in repository stash(repo) # Change file writeLines(c("Hello world!", "HeLlO wOrLd!"), file.path(path, "test.txt")) # Create stash in repository stash(repo) # View stashes stash_list(repo) # Read file readLines(file.path(path, "test.txt")) # Apply latest git_stash object in repository stash_apply(stash_list(repo)[[1]]) # Read file readLines(file.path(path, "test.txt")) # View stashes stash_list(repo) } } git2r/man/push.Rd0000644000175000017500000000511414145546030013457 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/push.R \name{push} \alias{push} \title{Push} \usage{ push( object = ".", name = NULL, refspec = NULL, force = FALSE, credentials = NULL, set_upstream = FALSE ) } \arguments{ \item{object}{path to repository, or a \code{git_repository} or \code{git_branch}.} \item{name}{The remote's name. Default is NULL.} \item{refspec}{The refspec to be pushed. Default is NULL.} \item{force}{Force your local revision to the remote repo. Use it with care. Default is FALSE.} \item{credentials}{The credentials for remote repository access. Default is NULL. To use and query an ssh-agent for the ssh key credentials, let this parameter be NULL (the default).} \item{set_upstream}{Set the current local branch to track the remote branch. Default is FALSE.} } \value{ invisible(NULL) } \description{ Push } \examples{ \dontrun{ ## Initialize two temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) ## Clone the bare repository. This creates remote-tracking ## branches for each branch in the cloned repository. repo <- clone(path_bare, path_repo) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository push(repo, "origin", "refs/heads/master") ## Now, unset the remote-tracking branch to NULL to demonstrate ## the 'set_upstream' argument. Then push with 'set_upstream = TRUE' ## to add the upstream tracking branch to branch 'master' again. branch_get_upstream(repository_head(repo)) branch_set_upstream(repository_head(repo), NULL) branch_get_upstream(repository_head(repo)) push(repo, "origin", "refs/heads/master", set_upstream = TRUE) branch_get_upstream(repository_head(repo)) ## Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "Second commit message") ## Push commits from repository to bare repository push(repo) ## List commits in repository and bare repository commits(repo) commits(repo_bare) } } \seealso{ \code{\link{cred_user_pass}}, \code{\link{cred_ssh_key}} } git2r/man/stash.Rd0000644000175000017500000000302714145546030013623 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stash.R \name{stash} \alias{stash} \title{Stash} \usage{ stash( repo = ".", message = as.character(Sys.time()), index = FALSE, untracked = FALSE, ignored = FALSE, stasher = NULL ) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{message}{Optional description. Defaults to current time.} \item{index}{All changes already added to the index are left intact in the working directory. Default is FALSE} \item{untracked}{All untracked files are also stashed and then cleaned up from the working directory. Default is FALSE} \item{ignored}{All ignored files are also stashed and then cleaned up from the working directory. Default is FALSE} \item{stasher}{Signature with stasher and time of stash} } \value{ invisible \code{git_stash} object if anything to stash else NULL } \description{ Stash } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") # Create a file, add and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, 'test.txt') commit(repo, "Commit message") # Change file writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) # Check status of repository status(repo) # Create stash in repository stash(repo) # Check status of repository status(repo) # View stash stash_list(repo) } } git2r/man/cred_token.Rd0000644000175000017500000000267014145546030014621 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/credential.R \name{cred_token} \alias{cred_token} \title{Create a new personal access token credential object} \usage{ cred_token(token = "GITHUB_PAT") } \arguments{ \item{token}{The name of the environmental variable that holds the personal access token for the authentication. Default is \code{GITHUB_PAT}.} } \value{ A list of class \code{cred_token} with entry: \describe{ \item{token}{ The name of the environmental variable that holds the personal access token for the authentication. } } } \description{ The personal access token is stored in an envrionmental variable. Environmental variables can be written to the file \code{.Renviron}. This file is read by \emph{R} during startup, see \code{\link[base]{Startup}}. On GitHub, personal access tokens function like ordinary OAuth access tokens. They can be used instead of a password for Git over HTTPS, see \url{https://help.github.com/articles/creating-an-access-token-for-command-line-use} } \examples{ \dontrun{ ## Create a personal access token credential object. ## This example assumes that the token is stored in ## the 'GITHUB_PAT' environmental variable. repo <- repository("git2r") cred <- cred_token() push(repo, credentials = cred) } } \seealso{ Other git credential functions: \code{\link{cred_env}()}, \code{\link{cred_ssh_key}()}, \code{\link{cred_user_pass}()} } \concept{git credential functions} git2r/man/head.git_repository.Rd0000644000175000017500000000162514145546030016465 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{head.git_repository} \alias{head.git_repository} \title{Get HEAD for a repository} \usage{ \method{head}{git_repository}(x, ...) } \arguments{ \item{x}{The repository \code{x} to check head} \item{...}{Additional arguments. Unused.} } \value{ NULL if unborn branch or not found. A git_branch if not a detached head. A git_commit if detached head } \description{ Get HEAD for a repository } \examples{ \dontrun{ ## Create and initialize a repository in a temporary directory path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Commit message") ## Get HEAD of repository repository_head(repo) } } git2r/man/diff-methods.Rd0000644000175000017500000001051114145546030015046 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/diff.R \name{diff.git_repository} \alias{diff.git_repository} \alias{diff.git_tree} \title{Changes between commits, trees, working tree, etc.} \usage{ \method{diff}{git_repository}( x, index = FALSE, as_char = FALSE, filename = NULL, context_lines = 3, interhunk_lines = 0, old_prefix = "a", new_prefix = "b", id_abbrev = NULL, path = NULL, max_size = NULL, ... ) \method{diff}{git_tree}( x, new_tree = NULL, index = FALSE, as_char = FALSE, filename = NULL, context_lines = 3, interhunk_lines = 0, old_prefix = "a", new_prefix = "b", id_abbrev = NULL, path = NULL, max_size = NULL, ... ) } \arguments{ \item{x}{A \code{git_repository} object or the old \code{git_tree} object to compare to.} \item{index}{\describe{ \item{\emph{When object equals a git_repository}}{ Whether to compare the index to HEAD. If FALSE (the default), then the working tree is compared to the index. } \item{\emph{When object equals a git_tree}}{ Whether to use the working directory (by default), or the index (if set to TRUE) in the comparison to \code{object}. } }} \item{as_char}{logical: should the result be converted to a character string?. Default is FALSE.} \item{filename}{If as_char is TRUE, then the diff can be written to a file with name filename (the file is overwritten if it exists). Default is NULL.} \item{context_lines}{The number of unchanged lines that define the boundary of a hunk (and to display before and after). Defaults to 3.} \item{interhunk_lines}{The maximum number of unchanged lines between hunk boundaries before the hunks will be merged into one. Defaults to 0.} \item{old_prefix}{The virtual "directory" prefix for old file names in hunk headers. Default is "a".} \item{new_prefix}{The virtual "directory" prefix for new file names in hunk headers. Defaults to "b".} \item{id_abbrev}{The abbreviation length to use when formatting object ids. Defaults to the value of 'core.abbrev' from the config, or 7 if NULL.} \item{path}{A character vector of paths / fnmatch patterns to constrain diff. Default is NULL which include all paths.} \item{max_size}{A size (in bytes) above which a blob will be marked as binary automatically; pass a negative value to disable. Defaults to 512MB when max_size is NULL.} \item{...}{Not used.} \item{new_tree}{The new git_tree object to compare, or NULL. If NULL, then we use the working directory or the index (see the \code{index} argument).} } \value{ A \code{git_diff} object if as_char is FALSE. If as_char is TRUE and filename is NULL, a character string, else NULL. } \description{ Changes between commits, trees, working tree, etc. } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add, commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message") ## Change the file writeLines(c("Hello again!", "Here is a second line", "And a third"), file.path(path, "test.txt")) ## diff between index and workdir diff_1 <- diff(repo) summary(diff_1) cat(diff(repo, as_char=TRUE)) ## Diff between index and HEAD is empty diff_2 <- diff(repo, index=TRUE) summary(diff_2) cat(diff(repo, index=TRUE, as_char=TRUE)) ## Diff between tree and working dir, same as diff_1 diff_3 <- diff(tree(commits(repo)[[1]])) summary(diff_3) cat(diff(tree(commits(repo)[[1]]), as_char=TRUE)) ## Add changes, diff between index and HEAD is the same as diff_1 add(repo, "test.txt") diff_4 <- diff(repo, index=TRUE) summary(diff_4) cat(diff(repo, index=TRUE, as_char=TRUE)) ## Diff between tree and index diff_5 <- diff(tree(commits(repo)[[1]]), index=TRUE) summary(diff_5) cat(diff(tree(commits(repo)[[1]]), index=TRUE, as_char=TRUE)) ## Diff between two trees commit(repo, "Second commit") tree_1 <- tree(commits(repo)[[2]]) tree_2 <- tree(commits(repo)[[1]]) diff_6 <- diff(tree_1, tree_2) summary(diff_6) cat(diff(tree_1, tree_2, as_char=TRUE)) ## Binary files set.seed(42) writeBin(as.raw((sample(0:255, 1000, replace=TRUE))), con=file.path(path, "test.bin")) add(repo, "test.bin") diff_7 <- diff(repo, index=TRUE) summary(diff_7) cat(diff(repo, index=TRUE, as_char=TRUE)) } } git2r/man/is_empty.Rd0000644000175000017500000000154114145546030014331 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{is_empty} \alias{is_empty} \title{Check if repository is empty} \usage{ is_empty(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ \code{TRUE} if repository is empty else \code{FALSE}. } \description{ Check if repository is empty } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Check if it's an empty repository is_empty(repo) ## Commit a file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Check if it's an empty repository is_empty(repo) } } git2r/man/pull.Rd0000644000175000017500000000606114145546030013456 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pull.R \name{pull} \alias{pull} \title{Pull} \usage{ pull(repo = ".", credentials = NULL, merger = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{credentials}{The credentials for remote repository access. Default is NULL. To use and query an ssh-agent for the ssh key credentials, let this parameter be NULL (the default).} \item{merger}{Who made the merge, if the merge is non-fast forward merge that creates a merge commit. The \code{default_signature} for \code{repo} is used if this parameter is \code{NULL}.} } \value{ A list of class \code{git_merge_result} with entries: \describe{ \item{up_to_date}{ TRUE if the merge is already up-to-date, else FALSE. } \item{fast_forward}{ TRUE if a fast-forward merge, else FALSE. } \item{conflicts}{ TRUE if the index contain entries representing file conflicts, else FALSE. } \item{sha}{ If the merge created a merge commit, the sha of the merge commit. NA if no merge commit created. } } } \description{ Pull } \examples{ \dontrun{ ## Initialize repositories path_bare <- tempfile(pattern="git2r-") path_repo_1 <- tempfile(pattern="git2r-") path_repo_2 <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) repo_bare <- init(path_bare, bare = TRUE) repo_1 <- clone(path_bare, path_repo_1) ## Config first user and commit a file config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "First commit message") ## Push commits from first repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo_1, "origin", "refs/heads/master") ## Clone to second repository repo_2 <- clone(path_bare, path_repo_2) config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "Second commit message") ## Push commits from first repository to bare repository push(repo_1) ## Pull changes to repo_2 pull(repo_2) ## Change file again and commit. This time in repository 2 lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") writeLines(lines, file.path(path_repo_2, "example.txt")) add(repo_2, "example.txt") commit(repo_2, "Third commit message") ## Push commits from second repository to bare repository push(repo_2) ## Pull changes to repo_1 pull(repo_1) ## List commits in repositories commits(repo_1) commits(repo_2) commits(repo_bare) } } git2r/man/workdir.Rd0000644000175000017500000000121414145546030014156 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{workdir} \alias{workdir} \title{Workdir of repository} \usage{ workdir(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ Character vector with the path of the workdir. If the repository is bare, \code{NULL} will be returned. } \description{ Workdir of repository } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) ## Get the path of the workdir for repository workdir(repo) } } git2r/man/tags.Rd0000644000175000017500000000135214145546030013436 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag.R \name{tags} \alias{tags} \title{Tags} \usage{ tags(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ list of tags in repository } \description{ Tags } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Create tag tag(repo, "Tagname", "Tag message") ## List tags tags(repo) } } git2r/man/sub-.git_tree.Rd0000644000175000017500000000272214145546030015151 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{[.git_tree} \alias{[.git_tree} \title{Extract object from tree} \usage{ \method{[}{git_tree}(x, i) } \arguments{ \item{x}{The tree \code{object}} \item{i}{The index (integer or logical) of the tree object to extract. If negative values, all elements except those indicated are selected. A character vector to match against the names of objects to extract.} } \value{ Git object } \description{ Lookup a tree entry by its position in the tree } \examples{ \dontrun{ ##' Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) dir.create(file.path(path, "subfolder")) repo <- init(path) ##' Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ##' Create three files and commit writeLines("First file", file.path(path, "example-1.txt")) writeLines("Second file", file.path(path, "subfolder/example-2.txt")) writeLines("Third file", file.path(path, "example-3.txt")) add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) new_commit <- commit(repo, "Commit message") ##' Pick a tree in the repository tree_object <- tree(new_commit) ##' Display tree tree_object ##' Select item by name tree_object["example-1.txt"] ##' Select first item in tree tree_object[1] ##' Select first three items in tree tree_object[1:3] ##' Select all blobs in tree tree_object[vapply(as(tree_object, 'list'), is_blob, logical(1))] } } git2r/man/git_config_files.Rd0000644000175000017500000000230114145546030015765 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/config.R \name{git_config_files} \alias{git_config_files} \title{Locate the path to configuration files} \usage{ git_config_files(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ a \code{data.frame} with one row per potential configuration file where \code{NA} means not found. } \description{ Potential configuration files: \describe{ \item{system}{ Locate the path to the system configuration file. If '/etc/gitconfig' doesn't exist, it will look for '\%PROGRAMFILES\%'. } \item{xdg}{ Locate the path to the global xdg compatible configuration file. The xdg compatible configuration file is usually located in '$HOME/.config/git/config'. This method will try to guess the full path to that file, if the file exists. } \item{global}{ The user or global configuration file is usually located in '$HOME/.gitconfig'. This method will try to guess the full path to that file, if the file exists. } \item{local}{ Locate the path to the repository specific configuration file, if the file exists. } } } git2r/man/tag.Rd0000644000175000017500000000274414145546030013261 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag.R \name{tag} \alias{tag} \title{Create tag targeting HEAD commit in repository} \usage{ tag( object = ".", name = NULL, message = NULL, session = FALSE, tagger = NULL, force = FALSE ) } \arguments{ \item{object}{The repository \code{object}.} \item{name}{Name for the tag.} \item{message}{The tag message. Specify a tag message to create an annotated tag. A lightweight tag is created if the message parameter is \code{NULL}.} \item{session}{Add sessionInfo to tag message. Default is FALSE.} \item{tagger}{The tagger (author) of the tag} \item{force}{Overwrite existing tag. Default = FALSE} } \value{ invisible(\code{git_tag}) object } \description{ Create tag targeting HEAD commit in repository } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file filename <- file.path(path, "example.txt") writeLines("Hello world!", filename) add(repo, "example.txt") commit(repo, "First commit message") ## Create an annotated tag tag(repo, "v1.0", "Tag message") ## List tags tags(repo) ## Make a change to the text file and commit. writeLines(c("Hello world!", "HELLO WORLD!"), filename) add(repo, "example.txt") commit(repo, "Second commit message") ## Create a lightweight tag tag(repo, "v2.0") ## List tags tags(repo) } } git2r/man/sha.Rd0000644000175000017500000000265114145546030013256 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sha.R \name{sha} \alias{sha} \alias{sha.git_blob} \alias{sha.git_branch} \alias{sha.git_commit} \alias{sha.git_note} \alias{sha.git_reference} \alias{sha.git_reflog_entry} \alias{sha.git_tag} \alias{sha.git_tree} \alias{sha.git_fetch_head} \alias{sha.git_merge_result} \title{Get the SHA-1 of a git object} \usage{ sha(object) \method{sha}{git_blob}(object) \method{sha}{git_branch}(object) \method{sha}{git_commit}(object) \method{sha}{git_note}(object) \method{sha}{git_reference}(object) \method{sha}{git_reflog_entry}(object) \method{sha}{git_tag}(object) \method{sha}{git_tree}(object) \method{sha}{git_fetch_head}(object) \method{sha}{git_merge_result}(object) } \arguments{ \item{object}{a git object to get the SHA-1 from.} } \value{ The 40 character hexadecimal string of the SHA-1. } \description{ Get the 40 character hexadecimal string of the SHA-1. } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message 1") ## Get the SHA-1 of the last commit sha(last_commit(repo)) } } git2r/man/reset.Rd0000644000175000017500000000452014145546030013622 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reset.R \name{reset} \alias{reset} \title{Reset current HEAD to the specified state} \usage{ reset(object, reset_type = c("soft", "mixed", "hard"), path = NULL) } \arguments{ \item{object}{Either a \code{git_commit}, a \code{git_repository} or a character vector. If \code{object} is a \code{git_commit}, HEAD is moved to the \code{git_commit}. If \code{object} is a \code{git_repository}, resets the index entries in the \code{path} argument to their state at HEAD. If \code{object} is a character vector with paths, resets the index entries in \code{object} to their state at HEAD if the current working directory is in a repository.} \item{reset_type}{If object is a 'git_commit', the kind of reset operation to perform. 'soft' means the HEAD will be moved to the commit. 'mixed' reset will trigger a 'soft' reset, plus the index will be replaced with the content of the commit tree. 'hard' reset will trigger a 'mixed' reset and the working directory will be replaced with the content of the index.} \item{path}{If object is a 'git_repository', resets the index entries for all paths to their state at HEAD.} } \value{ invisible NULL } \description{ Reset current HEAD to the specified state } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit_1 <- commit(repo, "Commit message") ## Change and stage the file writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-1.txt")) add(repo, "test-1.txt") status(repo) ## Unstage file reset(repo, path = "test-1.txt") status(repo) ## Make one more commit add(repo, "test-1.txt") commit(repo, "Next commit message") ## Create one more file writeLines("Hello world!", file.path(path, "test-2.txt")) ## 'soft' reset to first commit and check status reset(commit_1) status(repo) ## 'mixed' reset to first commit and check status commit(repo, "Next commit message") reset(commit_1, "mixed") status(repo) ## 'hard' reset to first commit and check status add(repo, "test-1.txt") commit(repo, "Next commit message") reset(commit_1, "hard") status(repo) } } git2r/man/remote_set_url.Rd0000644000175000017500000000246014145546030015531 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remote.R \name{remote_set_url} \alias{remote_set_url} \title{Set the remote's url in the configuration} \usage{ remote_set_url(repo = ".", name = NULL, url = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{name}{The name of the remote} \item{url}{The \code{url} to set} } \value{ NULL, invisibly } \description{ This assumes the common case of a single-url remote and will otherwise raise an error. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name="Alice", user.email="alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Add a remote remote_add(repo, "playground", "https://example.org/git2r/playground") remotes(repo) remote_url(repo, "playground") ## Rename a remote remote_rename(repo, "playground", "foobar") remotes(repo) remote_url(repo, "foobar") ## Set remote url remote_set_url(repo, "foobar", "https://example.org/git2r/foobar") remotes(repo) remote_url(repo, "foobar") ## Remove a remote remote_remove(repo, "foobar") remotes(repo) } } git2r/man/is_merge.Rd0000644000175000017500000000340014145546030014266 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{is_merge} \alias{is_merge} \title{Is merge} \usage{ is_merge(commit = NULL) } \arguments{ \item{commit}{a git_commit object.} } \value{ TRUE if commit has more than one parent, else FALSE } \description{ Determine if a commit is a merge commit, i.e. has more than one parent. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines(c("First line in file 1.", "Second line in file 1."), file.path(path, "example-1.txt")) add(repo, "example-1.txt") commit(repo, "First commit message") ## Create and add one more file writeLines(c("First line in file 2.", "Second line in file 2."), file.path(path, "example-2.txt")) add(repo, "example-2.txt") commit(repo, "Second commit message") ## Create a new branch 'fix' checkout(repo, "fix", create = TRUE) ## Update 'example-1.txt' (swap words in first line) and commit writeLines(c("line First in file 1.", "Second line in file 1."), file.path(path, "example-1.txt")) add(repo, "example-1.txt") commit(repo, "Third commit message") checkout(repo, "master") ## Update 'example-2.txt' (swap words in second line) and commit writeLines(c("First line in file 2.", "line Second in file 2."), file.path(path, "example-2.txt")) add(repo, "example-2.txt") commit(repo, "Fourth commit message") ## Merge 'fix' merge(repo, "fix") ## Display parents of last commit parents(lookup(repo, branch_target(repository_head(repo)))) ## Check that last commit is a merge is_merge(lookup(repo, branch_target(repository_head(repo)))) } } git2r/man/length.git_blob.Rd0000644000175000017500000000151414145546030015541 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blob.R \name{length.git_blob} \alias{length.git_blob} \title{Size in bytes of the contents of a blob} \usage{ \method{length}{git_blob}(x) } \arguments{ \item{x}{The blob \code{object}} } \value{ a non-negative integer } \description{ Size in bytes of the contents of a blob } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") blob_1 <- tree(commit_1)["example.txt"] ## Get length in size of bytes of the content of the blob length(blob_1) } } git2r/man/revparse_single.Rd0000644000175000017500000000246514145546030015676 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/revparse.R \name{revparse_single} \alias{revparse_single} \title{Revparse} \usage{ revparse_single(repo = ".", revision = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{revision}{The revision string, see http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions} } \value{ a \code{git_commit} or \code{git_tag} or \code{git_tree} object } \description{ Find object specified by revision. } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "First commit message") # Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Second commit message") revparse_single(repo, "HEAD^") revparse_single(repo, "HEAD:test.txt") } } git2r/man/remote_remove.Rd0000644000175000017500000000235714145546030015356 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remote.R \name{remote_remove} \alias{remote_remove} \title{Remove a remote} \usage{ remote_remove(repo = ".", name = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{name}{The name of the remote to remove} } \value{ NULL, invisibly } \description{ All remote-tracking branches and configuration settings for the remote will be removed. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name="Alice", user.email="alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Add a remote remote_add(repo, "playground", "https://example.org/git2r/playground") remotes(repo) remote_url(repo, "playground") ## Rename a remote remote_rename(repo, "playground", "foobar") remotes(repo) remote_url(repo, "foobar") ## Set remote url remote_set_url(repo, "foobar", "https://example.org/git2r/foobar") remotes(repo) remote_url(repo, "foobar") ## Remove a remote remote_remove(repo, "foobar") remotes(repo) } } git2r/man/as.list.git_tree.Rd0000644000175000017500000000222114145546030015652 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{as.list.git_tree} \alias{as.list.git_tree} \title{Coerce entries in a git_tree to a list of entry objects} \usage{ \method{as.list}{git_tree}(x, ...) } \arguments{ \item{x}{The tree \code{object}} \item{...}{Unused} } \value{ list of entry objects } \description{ Coerce entries in a git_tree to a list of entry objects } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) dir.create(file.path(path, "subfolder")) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create three files and commit writeLines("First file", file.path(path, "example-1.txt")) writeLines("Second file", file.path(path, "subfolder/example-2.txt")) writeLines("Third file", file.path(path, "example-3.txt")) add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) commit(repo, "Commit message") ## Inspect size of each blob in tree invisible(lapply(as(tree(last_commit(repo)), "list"), function(obj) { if (is_blob(obj)) summary(obj) NULL })) } } git2r/man/branch_delete.Rd0000644000175000017500000000144414145546030015261 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_delete} \alias{branch_delete} \title{Delete a branch} \usage{ branch_delete(branch = NULL) } \arguments{ \item{branch}{The branch} } \value{ invisible NULL } \description{ Delete a branch } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") ## Create a 'dev' branch dev <- branch_create(commit_1, name = "dev") branches(repo) ## Delete 'dev' branch branch_delete(dev) branches(repo) } } git2r/man/remote_ls.Rd0000644000175000017500000000166614145546030014501 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remote.R \name{remote_ls} \alias{remote_ls} \title{List references in a remote repository} \usage{ remote_ls(name = NULL, repo = NULL, credentials = NULL) } \arguments{ \item{name}{Character vector with the "remote" repository URL to query or the name of the remote if a \code{repo} argument is given.} \item{repo}{an optional repository object used if remotes are specified by name.} \item{credentials}{The credentials for remote repository access. Default is NULL. To use and query an ssh-agent for the ssh key credentials, let this parameter be NULL (the default).} } \value{ Character vector for each reference with the associated commit IDs. } \description{ Displays references available in a remote repository along with the associated commit IDs. Akin to the 'git ls-remote' command. } \examples{ \dontrun{ remote_ls("https://github.com/ropensci/git2r") } } git2r/man/reexports.Rd0000644000175000017500000000107614145546030014536 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/diff.R, R/merge.R, R/repository.R, R/tree.R \docType{import} \name{reexports} \alias{reexports} \alias{diff} \alias{merge} \alias{head} \alias{as.data.frame} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{base}{\code{\link[base]{as.data.frame}}, \code{\link[base]{diff}}, \code{\link[base]{merge}}} \item{utils}{\code{\link[utils]{head}}} }} git2r/man/remote_rename.Rd0000644000175000017500000000233314145546030015322 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remote.R \name{remote_rename} \alias{remote_rename} \title{Rename a remote} \usage{ remote_rename(repo = ".", oldname = NULL, newname = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{oldname}{Old name of the remote} \item{newname}{New name of the remote} } \value{ NULL, invisibly } \description{ Rename a remote } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name="Alice", user.email="alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Add a remote remote_add(repo, "playground", "https://example.org/git2r/playground") remotes(repo) remote_url(repo, "playground") ## Rename a remote remote_rename(repo, "playground", "foobar") remotes(repo) remote_url(repo, "foobar") ## Set remote url remote_set_url(repo, "foobar", "https://example.org/git2r/foobar") remotes(repo) remote_url(repo, "foobar") ## Remove a remote remote_remove(repo, "foobar") remotes(repo) } } git2r/man/cred_user_pass.Rd0000644000175000017500000000214614145546030015503 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/credential.R \name{cred_user_pass} \alias{cred_user_pass} \title{Create a new plain-text username and password credential object} \usage{ cred_user_pass(username = NULL, password = NULL) } \arguments{ \item{username}{The username of the credential} \item{password}{The password of the credential. If getPass is installed and the only input is username, \code{getPass::getPass()} will be called to allow for interactive and obfuscated interactive input of the password.} } \value{ A list of class \code{cred_user_pass} with entries: \describe{ \item{username}{ The username of the credential } \item{password}{ The password of the credential } } } \description{ Create a new plain-text username and password credential object } \examples{ \dontrun{ ## Create a plain-text username and password credential object cred_user_pass("Random Developer", "SecretPassword") } } \seealso{ Other git credential functions: \code{\link{cred_env}()}, \code{\link{cred_ssh_key}()}, \code{\link{cred_token}()} } \concept{git credential functions} git2r/man/is_binary.Rd0000644000175000017500000000177214145546030014465 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blob.R \name{is_binary} \alias{is_binary} \title{Is blob binary} \usage{ is_binary(blob = NULL) } \arguments{ \item{blob}{The blob \code{object}.} } \value{ TRUE if binary data, FALSE if not. } \description{ Is blob binary } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") ## Check if binary b_text <- tree(commit_1)["example.txt"] is_binary(b_text) ## Commit plot file (binary) x <- 1:100 y <- x^2 png(file.path(path, "plot.png")) plot(y ~ x, type = "l") dev.off() add(repo, "plot.png") commit_2 <- commit(repo, "Second commit message") ## Check if binary b_png <- tree(commit_2)["plot.png"] is_binary(b_png) } } git2r/man/ls_tree.Rd0000644000175000017500000000375414145546030014145 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{ls_tree} \alias{ls_tree} \title{List the contents of a tree object} \usage{ ls_tree(tree = NULL, repo = ".", recursive = TRUE) } \arguments{ \item{tree}{default (\code{NULL}) is the tree of the last commit in \code{repo}. Can also be a \code{git_tree} object or a character that identifies a tree in the repository (see \sQuote{Examples}).} \item{repo}{never used if \code{tree} is a \code{git_tree} object. A \code{git_repository} object, or a path (default = '.') to a repository.} \item{recursive}{default is to recurse into sub-trees.} } \value{ A data.frame with the following columns: \describe{ \item{mode}{UNIX file attribute of the tree entry} \item{type}{type of object} \item{sha}{sha of the object} \item{path}{path relative to the root tree} \item{name}{filename of the tree entry} \item{len}{object size of blob (file) entries. NA for other objects.} } } \description{ Traverse the entries in a tree and its subtrees. Akin to the 'git ls-tree' command. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) dir.create(file.path(path, "subfolder")) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create three files and commit writeLines("First file", file.path(path, "example-1.txt")) writeLines("Second file", file.path(path, "subfolder/example-2.txt")) writeLines("Third file", file.path(path, "example-3.txt")) add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) commit(repo, "Commit message") ## Traverse tree entries and its subtrees. ## Various approaches that give identical result. ls_tree(tree = tree(last_commit(path))) ls_tree(tree = tree(last_commit(repo))) ls_tree(repo = path) ls_tree(repo = repo) ## Skip content in subfolder ls_tree(repo = repo, recursive = FALSE) ## Start in subfolder ls_tree(tree = "HEAD:subfolder", repo = repo) } } git2r/man/remotes.Rd0000644000175000017500000000222314145546030014154 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remote.R \name{remotes} \alias{remotes} \title{Get the configured remotes for a repo} \usage{ remotes(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ Character vector with remotes } \description{ Get the configured remotes for a repo } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name="Alice", user.email="alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Add a remote remote_add(repo, "playground", "https://example.org/git2r/playground") remotes(repo) remote_url(repo, "playground") ## Rename a remote remote_rename(repo, "playground", "foobar") remotes(repo) remote_url(repo, "foobar") ## Set remote url remote_set_url(repo, "foobar", "https://example.org/git2r/foobar") remotes(repo) remote_url(repo, "foobar") ## Remove a remote remote_remove(repo, "foobar") remotes(repo) } } git2r/man/reflog.Rd0000644000175000017500000000310614145546030013755 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reflog.R \name{reflog} \alias{reflog} \title{List and view reflog information} \usage{ reflog(repo = ".", refname = "HEAD") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{refname}{The name of the reference to list. 'HEAD' by default.} } \value{ S3 class \code{git_reflog} with git_reflog_entry objects. } \description{ List and view reflog information } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Second commit message") ## Change file again and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Third commit message") ## View reflog reflog(repo) } } git2r/man/default_signature.Rd0000644000175000017500000000151614145546030016207 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/signature.R \name{default_signature} \alias{default_signature} \title{Get the signature} \usage{ default_signature(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ A \code{git_signature} object with entries: } \description{ Get the signature according to the repository's configuration } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Get the default signature default_signature(repo) ## Change user config(repo, user.name = "Bob", user.email = "bob@example.org") ## Get the default signature default_signature(repo) } } git2r/man/branch_set_upstream.Rd0000644000175000017500000000275414145546030016537 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_set_upstream} \alias{branch_set_upstream} \title{Set remote tracking branch} \usage{ branch_set_upstream(branch = NULL, name) } \arguments{ \item{branch}{The branch to configure} \item{name}{remote-tracking or local branch to set as upstream. Pass NULL to unset.} } \value{ invisible NULL } \description{ Set the upstream configuration for a given local branch } \examples{ \dontrun{ ## Initialize two temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo, "origin", "refs/heads/master") ## Unset remote remote tracking branch branch_get_upstream(repository_head(repo)) branch_set_upstream(repository_head(repo), NULL) branch_get_upstream(repository_head(repo)) ## Set remote tracking branch branch_set_upstream(repository_head(repo), "origin/master") branch_get_upstream(repository_head(repo)) } } git2r/man/is_tag.Rd0000644000175000017500000000145614145546030013753 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag.R \name{is_tag} \alias{is_tag} \title{Check if object is a git_tag object} \usage{ is_tag(object) } \arguments{ \item{object}{Check if object is a git_tag object} } \value{ TRUE if object is a git_tag, else FALSE } \description{ Check if object is a git_tag object } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Create tag tag(repo, "Tagname", "Tag message") is_tag(tags(repo)[[1]]) is_tag(last_commit(repo)) } } git2r/man/when.Rd0000644000175000017500000000313714145546030013444 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/when.R \name{when} \alias{when} \title{When} \usage{ when(object, tz = "GMT", origin = "1970-01-01", usetz = TRUE) } \arguments{ \item{object}{the \code{object} to extract the time slot from.} \item{tz}{time zone specification to be used for the conversion, \emph{if one is required}. System-specific (see \link[base]{time zones}), but \code{""} is the current time zone, and \code{"GMT"} is UTC (Universal Time, Coordinated). Invalid values are most commonly treated as UTC, on some platforms with a warning.} \item{origin}{a date-time object, or something which can be coerced by \code{as.POSIXct(tz = "GMT")} to such an object.} \item{usetz}{logical. Should the time zone abbreviation be appended to the output? This is used in printing times, and more reliable than using \code{"\%Z"}.} } \value{ A \code{character} vector of length one. } \description{ Help method to extract the time as a character string from a git_commit, git_signature, git_tag and git_time object. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a first user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Create tag tag(repo, "Tagname", "Tag message") when(commits(repo)[[1]]) when(tags(repo)[[1]]) when(tags(repo)[[1]], tz = Sys.timezone()) } } \seealso{ \code{\link{git_time}} } git2r/man/is_branch.Rd0000644000175000017500000000146314145546030014433 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{is_branch} \alias{is_branch} \title{Check if object is \code{git_branch}} \usage{ is_branch(object) } \arguments{ \item{object}{Check if object is of class \code{git_branch}} } \value{ TRUE if object is class \code{git_branch}, else FALSE } \description{ Check if object is \code{git_branch} } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") branch <- branches(repo)[[1]] ## Check if branch is_branch(branch) } } git2r/man/odb_objects.Rd0000644000175000017500000000207114145546030014754 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/odb.R \name{odb_objects} \alias{odb_objects} \title{List all objects available in the database} \usage{ odb_objects(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ A data.frame with the following columns: \describe{ \item{sha}{The sha of the object} \item{type}{The type of the object} \item{len}{The length of the object} } } \description{ List all objects available in the database } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message 1") ## Create tag tag(repo, "Tagname", "Tag message") ## List objects in repository odb_objects(repo) } } git2r/man/remote_add.Rd0000644000175000017500000000234514145546030014606 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remote.R \name{remote_add} \alias{remote_add} \title{Add a remote to a repo} \usage{ remote_add(repo = ".", name = NULL, url = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{name}{Short name of the remote repository} \item{url}{URL of the remote repository} } \value{ NULL, invisibly } \description{ Add a remote to a repo } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name="Alice", user.email="alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Add a remote remote_add(repo, "playground", "https://example.org/git2r/playground") remotes(repo) remote_url(repo, "playground") ## Rename a remote remote_rename(repo, "playground", "foobar") remotes(repo) remote_url(repo, "foobar") ## Set remote url remote_set_url(repo, "foobar", "https://example.org/git2r/foobar") remotes(repo) remote_url(repo, "foobar") ## Remove a remote remote_remove(repo, "foobar") remotes(repo) } } git2r/man/length.git_tree.Rd0000644000175000017500000000055614145546030015567 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{length.git_tree} \alias{length.git_tree} \title{Number of entries in tree} \usage{ \method{length}{git_tree}(x) } \arguments{ \item{x}{The tree \code{object}} } \value{ a non-negative integer or double (which will be rounded down) } \description{ Number of entries in tree } git2r/man/note_default_ref.Rd0000644000175000017500000000133714145546030016010 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/note.R \name{note_default_ref} \alias{note_default_ref} \title{Default notes reference} \usage{ note_default_ref(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ Character vector of length one with name of default notes reference } \description{ Get the default notes reference for a repository } \examples{ \dontrun{ ## Create and initialize a repository in a temporary directory path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## View default notes reference note_default_ref(repo) } } git2r/man/notes.Rd0000644000175000017500000000301414145546030013625 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/note.R \name{notes} \alias{notes} \title{List notes} \usage{ notes(repo = ".", ref = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{ref}{Reference to read from. Default (ref = NULL) is to call \code{note_default_ref}.} } \value{ list with git_note objects } \description{ List all the notes within a specified namespace. } \examples{ \dontrun{ ## Create and initialize a repository in a temporary directory path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "Commit message 1") ## Create another commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "example.txt")) add(repo, "example.txt") commit_2 <- commit(repo, "Commit message 2") ## Create note in default namespace note_create(commit_1, "Note-1") note_create(commit_1, "Note-2", force = TRUE) ## Create note in named (review) namespace note_create(commit_1, "Note-3", ref="refs/notes/review") note_create(commit_2, "Note-4", ref="review") ## Create note on blob and tree note_create(tree(commit_1), "Note-5") note_create(tree(commit_1)["example.txt"], "Note-6") ## List notes in default namespace notes(repo) ## List notes in 'review' namespace notes(repo, "review") } } git2r/man/is_local.Rd0000644000175000017500000000235214145546030014266 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{is_local} \alias{is_local} \title{Check if branch is local} \usage{ is_local(branch) } \arguments{ \item{branch}{The branch \code{object} to check if it's local} } \value{ \code{TRUE} if branch is local, else \code{FALSE}. } \description{ Check if branch is local } \examples{ \dontrun{ ## Initialize repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Config first user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo, "origin", "refs/heads/master") ## List branches branches(repo) ## Check if first branch is_local is_local(branches(repo)[[1]]) ## Check if second branch is_local is_local(branches(repo)[[2]]) } } git2r/man/clone.Rd0000644000175000017500000000457114145546030013606 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{clone} \alias{clone} \title{Clone a remote repository} \usage{ clone( url = NULL, local_path = NULL, bare = FALSE, branch = NULL, checkout = TRUE, credentials = NULL, progress = TRUE ) } \arguments{ \item{url}{The remote repository to clone} \item{local_path}{Local directory to clone to.} \item{bare}{Create a bare repository. Default is FALSE.} \item{branch}{The name of the branch to checkout. Default is NULL which means to use the remote's default branch.} \item{checkout}{Checkout HEAD after the clone is complete. Default is TRUE.} \item{credentials}{The credentials for remote repository access. Default is NULL. To use and query an ssh-agent for the ssh key credentials, let this parameter be NULL (the default).} \item{progress}{Show progress. Default is TRUE.} } \value{ A \code{git_repository} object. } \description{ Clone a remote repository } \examples{ \dontrun{ ## Initialize repository path_repo_1 <- tempfile(pattern="git2r-") path_repo_2 <- tempfile(pattern="git2r-") dir.create(path_repo_1) dir.create(path_repo_2) repo_1 <- init(path_repo_1) ## Config user and commit a file config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit writeLines( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "First commit message") ## Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "Second commit message") ## Change file again and commit. lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") writeLines(lines, file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "Third commit message") ## Clone to second repository repo_2 <- clone(path_repo_1, path_repo_2) ## List commits in repositories commits(repo_1) commits(repo_2) } } \seealso{ \link{repository}, \code{\link{cred_user_pass}}, \code{\link{cred_ssh_key}} } git2r/man/branch_get_upstream.Rd0000644000175000017500000000230614145546030016514 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_get_upstream} \alias{branch_get_upstream} \title{Get remote tracking branch} \usage{ branch_get_upstream(branch = NULL) } \arguments{ \item{branch}{The branch} } \value{ \code{git_branch} object or NULL if no remote tracking branch. } \description{ Get remote tracking branch, given a local branch. } \examples{ \dontrun{ ## Initialize two temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo, "origin", "refs/heads/master") ## Get remote tracking branch branch_get_upstream(repository_head(repo)) } } git2r/man/tag_delete.Rd0000644000175000017500000000233214145546030014574 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag.R \name{tag_delete} \alias{tag_delete} \title{Delete an existing tag reference} \usage{ tag_delete(object = ".", name = NULL) } \arguments{ \item{object}{Can be either the path (default is ".") to a repository, or a \code{git_repository} object, or a \code{git_tag} object. or the tag name.} \item{name}{If the \code{object} argument is a path to a repository or a \code{git_repository}, the name of the tag to delete.} } \value{ \code{invisible(NULL)} } \description{ Delete an existing tag reference } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit a text file writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Create two tags tag(repo, "Tag1", "Tag message 1") t2 <- tag(repo, "Tag2", "Tag message 2") ## List the two tags in the repository tags(repo) ## Delete the two tags in the repository tag_delete(repo, "Tag1") tag_delete(t2) ## Show the empty list with tags in the repository tags(repo) } } git2r/man/in_repository.Rd0000644000175000017500000000145114145546030015405 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{in_repository} \alias{in_repository} \title{Determine if a directory is in a git repository} \usage{ in_repository(path = ".") } \arguments{ \item{path}{The path to the directory.} } \value{ TRUE if directory is in a git repository else FALSE } \description{ The lookup start from path and walk across parent directories if nothing has been found. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Check if path is in a git repository in_repository(path) ## Check if working directory is in a git repository setwd(path) in_repository() } } git2r/man/last_commit.Rd0000644000175000017500000000171714145546030015020 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{last_commit} \alias{last_commit} \title{Last commit} \usage{ last_commit(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \description{ Get last commit in the current branch. } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Get last commit last_commit(repo) last_commit(path) ## Coerce the last commit to a data.frame as.data.frame(last_commit(path), "data.frame") ## Summary of last commit in repository summary(last_commit(repo)) } } git2r/man/references.Rd0000644000175000017500000000250614145546030014623 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reference.R \name{references} \alias{references} \title{Get all references that can be found in a repository.} \usage{ references(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ Character vector with references } \description{ Get all references that can be found in a repository. } \examples{ \dontrun{ ## Initialize two temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo, "origin", "refs/heads/master") ## Add tag to HEAD tag(repo, "v1.0", "First version") ## Create a note note_create(commits(repo)[[1]], "My note") ## List all references in repository references(repo) } } git2r/man/content.Rd0000644000175000017500000000150014145546030014145 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blob.R \name{content} \alias{content} \title{Content of blob} \usage{ content(blob = NULL, split = TRUE) } \arguments{ \item{blob}{The blob object.} \item{split}{Split blob content to text lines. Default TRUE.} } \value{ The content of the blob. NA_character_ if the blob is binary. } \description{ Content of blob } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Display content of blob. content(tree(commits(repo)[[1]])["example.txt"]) } } git2r/man/tree.Rd0000644000175000017500000000130714145546030013437 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{tree} \alias{tree} \title{Tree} \usage{ tree(object = NULL) } \arguments{ \item{object}{the \code{commit} or \code{stash} object} } \value{ A S3 class git_tree object } \description{ Get the tree pointed to by a commit or stash. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a first user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") tree(last_commit(repo)) } } git2r/man/commits.Rd0000644000175000017500000000657514145546030014167 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{commits} \alias{commits} \title{Commits} \usage{ commits( repo = ".", topological = TRUE, time = TRUE, reverse = FALSE, n = NULL, ref = NULL, path = NULL ) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{topological}{Sort the commits in topological order (parents before children); can be combined with time sorting. Default is TRUE.} \item{time}{Sort the commits by commit time; Can be combined with topological sorting. Default is TRUE.} \item{reverse}{Sort the commits in reverse order; can be combined with topological and/or time sorting. Default is FALSE.} \item{n}{The upper limit of the number of commits to output. The default is NULL for unlimited number of commits.} \item{ref}{The name of a reference to list commits from e.g. a tag or a branch. The default is NULL for the current branch.} \item{path}{The path to a file. If not NULL, only commits modifying this file will be returned. Note that modifying commits that occurred before the file was given its present name are not returned; that is, the output of \code{git log} with \code{--no-follow} is reproduced.} } \value{ list of commits in repository } \description{ Commits } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Second commit message") ## Create a tag tag(repo, "Tagname", "Tag message") ## Change file again and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Third commit message") ## Create a new file containing R code, and commit. writeLines(c("x <- seq(1,100)", "print(mean(x))"), file.path(path, "mean.R")) add(repo, "mean.R") commit(repo, "Fourth commit message") ## List the commits in the repository commits(repo) ## List the commits starting from the tag commits(repo, ref = "Tagname") ## List the commits modifying example.txt and mean.R. commits(repo, path = "example.txt") commits(repo, path = "mean.R") ## Create and checkout 'dev' branch in the repo checkout(repo, "dev", create = TRUE) ## Add changes to the 'dev' branch lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Commit message in dev branch") ## Checkout the 'master' branch again and list the commits ## starting from the 'dev' branch. checkout(repo, "master") commits(repo, ref = "dev") } } git2r/man/init.Rd0000644000175000017500000000200414145546030013436 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{init} \alias{init} \title{Init a repository} \usage{ init(path = ".", bare = FALSE, branch = NULL) } \arguments{ \item{path}{A path to where to init a git repository} \item{bare}{If TRUE, a Git repository without a working directory is created at the pointed path. If FALSE, provided path will be considered as the working directory into which the .git directory will be created.} \item{branch}{Use the specified name for the initial branch in the newly created repository. If \code{branch=NULL}, fall back to the default name.} } \value{ A \code{git_repository} object } \description{ Init a repository } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) is_bare(repo) ## Initialize a bare repository path_bare <- tempfile(pattern="git2r-") dir.create(path_bare) repo_bare <- init(path_bare, bare = TRUE) is_bare(repo_bare) } } \seealso{ \link{repository} } git2r/man/is_bare.Rd0000644000175000017500000000130514145546030014102 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{is_bare} \alias{is_bare} \title{Check if repository is bare} \usage{ is_bare(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ \code{TRUE} if bare repository, else \code{FALSE} } \description{ Check if repository is bare } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) is_bare(repo) ## Initialize a bare repository path_bare <- tempfile(pattern="git2r-") dir.create(path_bare) repo_bare <- init(path_bare, bare = TRUE) is_bare(repo_bare) } } \seealso{ \link{init} } git2r/man/note_create.Rd0000644000175000017500000000324614145546030014774 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/note.R \name{note_create} \alias{note_create} \title{Add note for a object} \usage{ note_create( object = NULL, message = NULL, ref = NULL, author = NULL, committer = NULL, force = FALSE ) } \arguments{ \item{object}{The object to annotate (git_blob, git_commit or git_tree).} \item{message}{Content of the note to add} \item{ref}{Canonical name of the reference to use. Default is \code{note_default_ref}.} \item{author}{Signature of the notes note author} \item{committer}{Signature of the notes note committer} \item{force}{Overwrite existing note. Default is FALSE} } \value{ git_note } \description{ Add note for a object } \examples{ \dontrun{ ## Create and initialize a repository in a temporary directory path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "Commit message 1") ## Create another commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "example.txt")) add(repo, "example.txt") commit_2 <- commit(repo, "Commit message 2") ## Check that notes is an empty list notes(repo) ## Create note in default namespace note_create(commit_1, "Note-1") ## Create note in named (review) namespace note_create(commit_1, "Note-2", ref="refs/notes/review") note_create(commit_2, "Note-3", ref="review") ## Create note on blob and tree note_create(tree(commit_1), "Note-4") note_create(tree(commit_1)["example.txt"], "Note-5") } } git2r/man/git2r.Rd0000644000175000017500000000035614145546030013532 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/git2r.R \docType{package} \name{git2r} \alias{git2r} \title{git2r: R bindings to the libgit2 library} \description{ git2r: R bindings to the libgit2 library. } git2r/man/checkout.Rd0000644000175000017500000000603114145546030014304 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/checkout.R \name{checkout} \alias{checkout} \title{Checkout} \usage{ checkout( object = NULL, branch = NULL, create = FALSE, force = FALSE, path = NULL, ... ) } \arguments{ \item{object}{A path to a repository, or a \code{git_repository} object, or a \code{git_commit} object, or a \code{git_tag} object, or a \code{git_tree} object.} \item{branch}{name of the branch to check out. Only used if object is a path to a repository or a \code{git_repository} object.} \item{create}{create branch if it doesn't exist. Only used if object is a path to a repository or a \code{git_repository} object.} \item{force}{If \code{TRUE}, then make working directory match target. This will throw away local changes. Default is \code{FALSE}.} \item{path}{Limit the checkout operation to only certain paths. This argument is only used if branch is NULL. Default is \code{NULL}.} \item{...}{Additional arguments. Not used.} } \value{ invisible NULL } \description{ Update files in the index and working tree to match the content of the tree pointed at by the treeish object (commit, tag or tree). The default checkout strategy (\code{force = FALSE}) will only make modifications that will not lose changes. Use \code{force = TRUE} to force working directory to look like index. } \examples{ \dontrun{ ## Create directories and initialize repositories path_bare <- tempfile(pattern="git2r-") path_repo_1 <- tempfile(pattern="git2r-") path_repo_2 <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) repo_bare <- init(path_bare, bare = TRUE) ## Clone to repo 1 and config user repo_1 <- clone(path_bare, path_repo_1) config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo 1 and push to bare lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo_1, "test.txt")) add(repo_1, "test.txt") commit(repo_1, "First commit message") push(repo_1, "origin", "refs/heads/master") ## Create and checkout 'dev' branch in repo 1 checkout(repo_1, "dev", create = TRUE) ## Add changes to 'dev' branch in repo 1 and push to bare lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path_repo_1, "test.txt")) add(repo_1, "test.txt") commit(repo_1, "Second commit message") push(repo_1, "origin", "refs/heads/dev") ## Clone to repo 2 repo_2 <- clone(path_bare, path_repo_2) config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Read content of 'test.txt' readLines(file.path(path_repo_2, "test.txt")) ## Checkout dev branch checkout(repo_2, "dev") ## Read content of 'test.txt' readLines(file.path(path_repo_2, "test.txt")) ## Edit "test.txt" in repo_2 writeLines("Hello world!", con = file.path(path_repo_2, "test.txt")) ## Check status status(repo_2) ## Checkout "test.txt" checkout(repo_2, path = "test.txt") ## Check status status(repo_2) } } git2r/man/summary.git_stash.Rd0000644000175000017500000000171414145546030016163 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stash.R \name{summary.git_stash} \alias{summary.git_stash} \title{Summary of a stash} \usage{ \method{summary}{git_stash}(object, ...) } \arguments{ \item{object}{The stash \code{object}} \item{...}{Additional arguments affecting the summary produced.} } \value{ None (invisible 'NULL'). } \description{ Summary of a stash } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") # Create a file, add and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, 'test.txt') commit(repo, "Commit message") # Change file writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) # Create stash in repository stash(repo, "Stash message") # View summary of stash summary(stash_list(repo)[[1]]) } } git2r/man/add.Rd0000644000175000017500000000416014145546030013230 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/index.R \name{add} \alias{add} \title{Add file(s) to index} \usage{ add(repo = ".", path = NULL, force = FALSE) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{path}{Character vector with file names or shell glob patterns that will matched against files in the repository's working directory. Each file that matches will be added to the index (either updating an existing entry or adding a new entry).} \item{force}{Add ignored files. Default is FALSE.} } \value{ invisible(NULL) } \description{ Add file(s) to index } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("a", file.path(path, "a.txt")) ## Add file to repository and view status add(repo, "a.txt") status(repo) ## Add file with a leading './' when the repository working ## directory is the current working directory setwd(path) writeLines("b", file.path(path, "b.txt")) add(repo, "./b.txt") status(repo) ## Add a file in a sub-folder with sub-folder as the working ## directory. Create a file in the root of the repository ## working directory that will remain untracked. dir.create(file.path(path, "sub_dir")) setwd("./sub_dir") writeLines("c", file.path(path, "c.txt")) writeLines("c", file.path(path, "sub_dir/c.txt")) add(repo, "c.txt") status(repo) ## Add files with glob expansion when the current working ## directory is outside the repository's working directory. setwd(tempdir()) dir.create(file.path(path, "glob_dir")) writeLines("d", file.path(path, "glob_dir/d.txt")) writeLines("e", file.path(path, "glob_dir/e.txt")) writeLines("f", file.path(path, "glob_dir/f.txt")) writeLines("g", file.path(path, "glob_dir/g.md")) add(repo, "glob_dir/*txt") status(repo) ## Add file with glob expansion with a relative path when ## the current working directory is inside the repository's ## working directory. setwd(path) add(repo, "./glob_dir/*md") status(repo) } } git2r/man/blob_create.Rd0000644000175000017500000000323314145546030014741 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/blob.R \name{blob_create} \alias{blob_create} \title{Create blob from file on disk} \usage{ blob_create(repo = ".", path = NULL, relative = TRUE) } \arguments{ \item{repo}{The repository where the blob(s) will be written. Can be a bare repository. A \code{git_repository} object, or a path to a repository, or \code{NULL}. If the \code{repo} argument is \code{NULL}, the repository is searched for with \code{\link{discover_repository}} in the current working directory.} \item{path}{The file(s) from which the blob will be created.} \item{relative}{TRUE if the file(s) from which the blob will be created is relative to the repository's working dir. Default is TRUE.} } \value{ list of S3 class git_blob \code{objects} } \description{ Read a file from the filesystem and write its content to the Object Database as a loose blob. The method is vectorized and accepts a vector of files to create blobs from. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create blobs from files relative to workdir writeLines("Hello, world!", file.path(path, "example-1.txt")) writeLines("test content", file.path(path, "example-2.txt")) blob_list_1 <- blob_create(repo, c("example-1.txt", "example-2.txt")) ## Create blobs from files not relative to workdir temp_file_1 <- tempfile() temp_file_2 <- tempfile() writeLines("Hello, world!", temp_file_1) writeLines("test content", temp_file_2) blob_list_2 <- blob_create(repo, c(temp_file_1, temp_file_2), relative = FALSE) } } git2r/man/descendant_of.Rd0000644000175000017500000000315414145546030015276 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{descendant_of} \alias{descendant_of} \title{Descendant} \usage{ descendant_of(commit = NULL, ancestor = NULL) } \arguments{ \item{commit}{a git_commit object. Can also be a tag or a branch, and in that case the commit will be the target of the tag or branch.} \item{ancestor}{a git_commit object to check if ancestor to \code{commit}. Can also be a tag or a branch, and in that case the commit will be the target of the tag or branch.} } \value{ TRUE if \code{commit} is descendant of \code{ancestor}, else FALSE } \description{ Determine if a commit is the descendant of another commit } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message 1") tag_1 <- tag(repo, "Tagname1", "Tag message 1") # Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Commit message 2") tag_2 <- tag(repo, "Tagname2", "Tag message 2") descendant_of(commit_1, commit_2) descendant_of(commit_2, commit_1) descendant_of(tag_1, tag_2) descendant_of(tag_2, tag_1) } } git2r/man/lookup.Rd0000644000175000017500000000301014145546030014002 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{lookup} \alias{lookup} \title{Lookup} \usage{ lookup(repo = ".", sha = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{sha}{The identity of the object to lookup. Must be 4 to 40 characters long.} } \value{ a \code{git_blob} or \code{git_commit} or \code{git_tag} or \code{git_tree} object } \description{ Lookup one object in a repository. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") ## Create tag tag(repo, "Tagname", "Tag message") ## First, get SHAs to lookup in the repository sha_commit <- sha(commit_1) sha_tree <- sha(tree(commit_1)) sha_blob <- sha(tree(commit_1)["example.txt"]) sha_tag <- sha(tags(repo)[[1]]) ## SHAs sha_commit sha_tree sha_blob sha_tag ## Lookup objects lookup(repo, sha_commit) lookup(repo, sha_tree) lookup(repo, sha_blob) lookup(repo, sha_tag) ## Lookup objects, using only the first seven characters lookup(repo, substr(sha_commit, 1, 7)) lookup(repo, substr(sha_tree, 1, 7)) lookup(repo, substr(sha_blob, 1, 7)) lookup(repo, substr(sha_tag, 1, 7)) } } git2r/man/length.git_diff.Rd0000644000175000017500000000053514145546030015535 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/diff.R \name{length.git_diff} \alias{length.git_diff} \title{Number of files in git_diff object} \usage{ \method{length}{git_diff}(x) } \arguments{ \item{x}{The git_diff \code{object}} } \value{ a non-negative integer } \description{ Number of files in git_diff object } git2r/man/stash_drop.Rd0000644000175000017500000000257014145546030014651 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stash.R \name{stash_drop} \alias{stash_drop} \title{Drop stash} \usage{ stash_drop(object = ".", index = 1) } \arguments{ \item{object}{path to a repository, or a \code{git_repository} object, or the stash \code{object} to drop. Default is a \code{path = '.'} to a reposiory.} \item{index}{The index to the stash to drop. Only used when \code{object} is a path to a repository or a \code{git_repository} object. Default is \code{index = 1}.} } \value{ invisible NULL } \description{ Drop stash } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") # Create a file, add and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, 'test.txt') commit(repo, "Commit message") # Change file writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) # Create stash in repository stash(repo) # Change file writeLines(c("Hello world!", "HeLlO wOrLd!"), file.path(path, "test.txt")) # Create stash in repository stash(repo) # View stashes stash_list(repo) # Drop git_stash object in repository stash_drop(stash_list(repo)[[1]]) ## Drop stash using an index to stash stash_drop(repo, 1) # View stashes stash_list(repo) } } git2r/man/parents.Rd0000644000175000017500000000171614145546030014160 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{parents} \alias{parents} \title{Parents} \usage{ parents(object = NULL) } \arguments{ \item{object}{a git_commit object.} } \value{ list of git_commit objects } \description{ Get parents of a commit. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("First line.", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") ## commit_1 has no parents parents(commit_1) ## Update 'example.txt' and commit writeLines(c("First line.", "Second line."), file.path(path, "example.txt")) add(repo, "example.txt") commit_2 <- commit(repo, "Second commit message") ## commit_2 has commit_1 as parent parents(commit_2) } } git2r/man/status.Rd0000644000175000017500000000314114145546030014021 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/status.R \name{status} \alias{status} \title{Status} \usage{ status( repo = ".", staged = TRUE, unstaged = TRUE, untracked = TRUE, ignored = FALSE, all_untracked = FALSE ) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{staged}{Include staged files. Default TRUE.} \item{unstaged}{Include unstaged files. Default TRUE.} \item{untracked}{Include untracked files and directories. Default TRUE.} \item{ignored}{Include ignored files. Default FALSE.} \item{all_untracked}{Shows individual files in untracked directories if \code{untracked} is \code{TRUE}.} } \value{ \code{git_status} with repository status } \description{ Display state of the repository working directory and the staging area. } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) ## Check status; untracked file status(repo) ## Add file add(repo, "test.txt") ## Check status; staged file status(repo) ## Commit commit(repo, "First commit message") ## Check status; clean status(repo) ## Change the file writeLines(c("Hello again!", "Here is a second line", "And a third"), file.path(path, "test.txt")) ## Check status; unstaged file status(repo) ## Add file and commit add(repo, "test.txt") commit(repo, "Second commit message") ## Check status; clean status(repo) } } git2r/man/is_shallow.Rd0000644000175000017500000000337514145546030014653 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{is_shallow} \alias{is_shallow} \title{Determine if the repository is a shallow clone} \usage{ is_shallow(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ \code{TRUE} if shallow clone, else \code{FALSE} } \description{ Determine if the repository is a shallow clone } \examples{ \dontrun{ ## Initialize repository path_repo_1 <- tempfile(pattern="git2r-") path_repo_2 <- tempfile(pattern="git2r-") dir.create(path_repo_1) dir.create(path_repo_2) repo_1 <- init(path_repo_1) ## Config user and commit a file config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "First commit message") ## Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "Second commit message") ## Change file again and commit. lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") writeLines(lines, file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "Third commit message") ## Clone to second repository repo_2 <- clone(path_repo_1, path_repo_2) ## Check if it's a shallow clone is_shallow(repo_2) } } git2r/man/cred_ssh_key.Rd0000644000175000017500000000304714145546030015145 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/credential.R \name{cred_ssh_key} \alias{cred_ssh_key} \title{Create a new passphrase-protected ssh key credential object} \usage{ cred_ssh_key( publickey = ssh_path("id_rsa.pub"), privatekey = ssh_path("id_rsa"), passphrase = character(0) ) } \arguments{ \item{publickey}{The path to the public key of the credential. Default is \code{ssh_path("id_rsa.pub")}} \item{privatekey}{The path to the private key of the credential. Default is \code{ssh_path("id_rsa")}} \item{passphrase}{The passphrase of the credential. Default is \code{character(0)}. If getPass is installed and private key is passphrase protected \code{getPass::getPass()} will be called to allow for interactive and obfuscated interactive input of the passphrase.} } \value{ A list of class \code{cred_ssh_key} with entries: \describe{ \item{publickey}{ The path to the public key of the credential } \item{privatekey}{ The path to the private key of the credential } \item{passphrase}{ The passphrase of the credential } } } \description{ Create a new passphrase-protected ssh key credential object } \examples{ \dontrun{ ## Create a ssh key credential object. It can optionally be ## passphrase-protected cred <- cred_ssh_key(ssh_path("id_rsa.pub"), ssh_path("id_rsa")) repo <- repository("git2r") push(repo, credentials = cred) } } \seealso{ Other git credential functions: \code{\link{cred_env}()}, \code{\link{cred_token}()}, \code{\link{cred_user_pass}()} } \concept{git credential functions} git2r/man/is_detached.Rd0000644000175000017500000000250014145546030014730 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{is_detached} \alias{is_detached} \title{Check if HEAD of repository is detached} \usage{ is_detached(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ \code{TRUE} if repository HEAD is detached, else \code{FALSE}. } \description{ Check if HEAD of repository is detached } \examples{ \dontrun{ ## Create and initialize a repository in a temporary directory path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "Commit message 1") ## Change file, add and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "Commit message 2") ## HEAD of repository is not detached is_detached(repo) ## Checkout first commit checkout(commit_1) ## HEAD of repository is detached is_detached(repo) } } git2r/man/summary.git_repository.Rd0000644000175000017500000000207314145546030017257 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{summary.git_repository} \alias{summary.git_repository} \title{Summary of repository} \usage{ \method{summary}{git_repository}(object, ...) } \arguments{ \item{object}{The repository \code{object}} \item{...}{Additional arguments affecting the summary produced.} } \value{ None (invisible 'NULL'). } \description{ Summary of repository } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) summary(repo) ## Add file add(repo, "test.txt") summary(repo) ## Commit commit(repo, "First commit message") summary(repo) ## Change the file writeLines(c("Hello again!", "Here is a second line", "And a third"), file.path(path, "test.txt")) summary(repo) ## Add file and commit add(repo, "test.txt") commit(repo, "Second commit message") summary(repo) } } git2r/man/repository.Rd0000644000175000017500000000327314145546030014723 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{repository} \alias{repository} \title{Open a repository} \usage{ repository(path = ".", discover = TRUE) } \arguments{ \item{path}{A path to an existing local git repository.} \item{discover}{Discover repository from path. Default is TRUE.} } \value{ A \code{git_repository} object with entries: \describe{ \item{path}{ Path to a git repository } } } \description{ Open a repository } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "test-1.txt")) add(repo, 'test-1.txt') commit_1 <- commit(repo, "Commit message") ## Make one more commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-1.txt")) add(repo, 'test-1.txt') commit(repo, "Next commit message") ## Create one more file writeLines("Hello world!", file.path(path, "test-2.txt")) ## Brief summary of repository repo ## Summary of repository summary(repo) ## Workdir of repository workdir(repo) ## Check if repository is bare is_bare(repo) ## Check if repository is empty is_empty(repo) ## Check if repository is a shallow clone is_shallow(repo) ## List all references in repository references(repo) ## List all branches in repository branches(repo) ## Get HEAD of repository repository_head(repo) ## Check if HEAD is head is_head(repository_head(repo)) ## Check if HEAD is local is_local(repository_head(repo)) ## List all tags in repository tags(repo) } } git2r/man/contributions.Rd0000644000175000017500000000457414145546030015413 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/contributions.R \name{contributions} \alias{contributions} \title{Contributions} \usage{ contributions( repo = ".", breaks = c("month", "year", "quarter", "week", "day"), by = c("commits", "author") ) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{breaks}{Default is \code{month}. Change to year, quarter, week or day as necessary.} \item{by}{Contributions by "commits" or "author". Default is "commits".} } \value{ A \code{data.frame} with contributions. } \description{ See contributions to a Git repo } \examples{ \dontrun{ ## Create directories and initialize repositories path_bare <- tempfile(pattern="git2r-") path_repo_1 <- tempfile(pattern="git2r-") path_repo_2 <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) repo_bare <- init(path_bare, bare = TRUE) ## Clone to repo 1 and config user repo_1 <- clone(path_bare, path_repo_1) config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo 1 and push to bare lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo_1, "test.txt")) add(repo_1, "test.txt") commit(repo_1, "First commit message") ## Add more changes to repo 1 lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path_repo_1, "test.txt")) add(repo_1, "test.txt") commit(repo_1, "Second commit message") ## Push to bare push(repo_1, "origin", "refs/heads/master") ## Clone to repo 2 repo_2 <- clone(path_bare, path_repo_2) config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Add changes to repo 2 lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") writeLines(lines, file.path(path_repo_2, "test.txt")) add(repo_2, "test.txt") commit(repo_2, "Third commit message") ## Push to bare push(repo_2, "origin", "refs/heads/master") ## Pull changes to repo 1 pull(repo_1) ## View contributions by day contributions(repo_1) ## View contributions by author and day contributions(repo_1, by = "author") } } git2r/man/cred_env.Rd0000644000175000017500000000252414145546030014267 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/credential.R \name{cred_env} \alias{cred_env} \title{Create a new environmental credential object} \usage{ cred_env(username = NULL, password = NULL) } \arguments{ \item{username}{The name of the environmental variable that holds the username for the authentication.} \item{password}{The name of the environmental variable that holds the password for the authentication.} } \value{ A list of class \code{cred_env} with entries: \describe{ \item{username}{ The name of the environmental variable that holds the username for the authentication. } \item{password}{ The name of the environmental variable that holds the password for the authentication. } } } \description{ Environmental variables can be written to the file \code{.Renviron}. This file is read by \emph{R} during startup, see \code{\link[base]{Startup}}. } \examples{ \dontrun{ ## Create an environmental credential object for the username and ## password. cred <- cred_env("NAME_OF_ENV_VARIABLE_WITH_USERNAME", "NAME_OF_ENV_VARIABLE_WITH_PASSWORD") repo <- repository("git2r") push(repo, credentials = cred) } } \seealso{ Other git credential functions: \code{\link{cred_ssh_key}()}, \code{\link{cred_token}()}, \code{\link{cred_user_pass}()} } \concept{git credential functions} git2r/man/remote_url.Rd0000644000175000017500000000247414145546030014663 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remote.R \name{remote_url} \alias{remote_url} \title{Get the remote url for remotes in a repo} \usage{ remote_url(repo = ".", remote = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{remote}{Character vector with the remotes to get the url from. Default is the remotes of the repository.} } \value{ Character vector with remote_url for each of the remote } \description{ Get the remote url for remotes in a repo } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name="Alice", user.email="alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Add a remote remote_add(repo, "playground", "https://example.org/git2r/playground") remotes(repo) remote_url(repo, "playground") ## Rename a remote remote_rename(repo, "playground", "foobar") remotes(repo) remote_url(repo, "foobar") ## Set remote url remote_set_url(repo, "foobar", "https://example.org/git2r/foobar") remotes(repo) remote_url(repo, "foobar") ## Remove a remote remote_remove(repo, "foobar") remotes(repo) } } git2r/man/print.git_reflog_entry.Rd0000644000175000017500000000137414145546030017201 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reflog.R \name{print.git_reflog_entry} \alias{print.git_reflog_entry} \title{Print a reflog entry} \usage{ \method{print}{git_reflog_entry}(x, ...) } \arguments{ \item{x}{The reflog entry} \item{...}{Unused} } \value{ None (invisible 'NULL'). } \description{ Print a reflog entry } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## View repository HEAD reflog reflog(repo) } } git2r/man/odb_blobs.Rd0000644000175000017500000000357314145546030014434 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/odb.R \name{odb_blobs} \alias{odb_blobs} \title{Blobs in the object database} \usage{ odb_blobs(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ A data.frame with the following columns: \describe{ \item{sha}{The sha of the blob} \item{path}{The path to the blob from the tree and sub-trees} \item{name}{The name of the blob from the tree that contains the blob} \item{len}{The length of the blob} \item{commit}{The sha of the commit} \item{author}{The author of the commit} \item{when}{The timestamp of the author signature in the commit} } } \description{ List all blobs reachable from the commits in the object database. For each commit, list blob's in the commit tree and sub-trees. } \note{ A blob sha can have several entries } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message 1") ## Change file and commit lines <- c( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua.") writeLines(lines, file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message 2") ## Commit same content under different name in a sub-directory dir.create(file.path(path, "sub-directory")) file.copy(file.path(path, "test.txt"), file.path(path, "sub-directory", "copy.txt")) add(repo, "sub-directory/copy.txt") commit(repo, "Commit message 3") ## List blobs odb_blobs(repo) } } git2r/man/fetch.Rd0000644000175000017500000000524314145546030013574 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/fetch.R \name{fetch} \alias{fetch} \title{Fetch new data and update tips} \usage{ fetch( repo = ".", name = NULL, credentials = NULL, verbose = TRUE, refspec = NULL ) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{name}{the remote's name} \item{credentials}{The credentials for remote repository access. Default is NULL. To use and query an ssh-agent for the ssh key credentials, let this parameter be NULL (the default).} \item{verbose}{Print information each time a reference is updated locally. Default is \code{TRUE}.} \item{refspec}{The refs to fetch and which local refs to update, see examples. Pass NULL to use the \code{remote..fetch} variable. Default is \code{NULL}.} } \value{ invisible list of class \code{git_transfer_progress} with statistics from the fetch operation: \describe{ \item{total_objects}{ Number of objects in the packfile being downloaded } \item{indexed_objects}{ Received objects that have been hashed } \item{received_objects}{ Objects which have been downloaded } \item{total_deltas}{ Total number of deltas in the pack } \item{indexed_deltas}{ Deltas which have been indexed } \item{local_objects}{ Locally-available objects that have been injected in order to fix a thin pack } \item{received_bytes}{ Size of the packfile received up to now } } } \description{ Fetch new data and update tips } \examples{ \dontrun{ ## Initialize three temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo_1 <- tempfile(pattern="git2r-") path_repo_2 <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) bare_repo <- init(path_bare, bare = TRUE) repo_1 <- clone(path_bare, path_repo_1) repo_2 <- clone(path_bare, path_repo_2) config(repo_1, user.name = "Alice", user.email = "alice@example.org") config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Add changes to repo 1 writeLines("Lorem ipsum dolor sit amet", con = file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "Commit message") ## Push changes from repo 1 to origin (bare_repo) push(repo_1, "origin", "refs/heads/master") ## Fetch changes from origin (bare_repo) to repo 2 fetch(repo_2, "origin") ## List updated heads fetch_heads(repo_2) ## Checking out GitHub pull requests locally path <- tempfile(pattern="ghit-") repo <- clone("https://github.com/leeper/ghit", path) fetch(repo, "origin", refspec = "pull/13/head:refs/heads/BRANCHNAME") checkout(repo, "BRANCHNAME") summary(repo) } } git2r/man/config.Rd0000644000175000017500000000271314145546030013747 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/config.R \name{config} \alias{config} \title{Config} \usage{ config(repo = NULL, global = FALSE, user.name, user.email, ...) } \arguments{ \item{repo}{The \code{repository}. Default is NULL.} \item{global}{Write option(s) to global configuration file. Default is FALSE.} \item{user.name}{The user name. Use NULL to delete the entry} \item{user.email}{The e-mail address. Use NULL to delete the entry} \item{...}{Additional options to write or delete from the configuration.} } \value{ S3 class \code{git_config}. When writing options, the configuration is returned invisible. } \description{ Config file management. To display the configuration variables, call method \code{config} without the \code{user.name}, \code{user.email} or \code{...} options. } \details{ There are two ways git2r can find the local repository when writing local options (1) Use the \code{repo} argument. (2) If the \code{repo} argument is \code{NULL} but the current working directory is inside the local repository, then \code{git2r} uses that repository. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern = "git2r-") dir.create(path) repo <- init(path) ## Set user name and email. config(repo, user.name = "Alice", user.email = "alice@example.org") ## Display configuration config(repo) ## Delete user email. config(repo, user.email = NULL) ## Display configuration config(repo) } } git2r/man/note_remove.Rd0000644000175000017500000000244114145546030015022 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/note.R \name{note_remove} \alias{note_remove} \title{Remove the note for an object} \usage{ note_remove(note = NULL, author = NULL, committer = NULL) } \arguments{ \item{note}{The note to remove} \item{author}{Signature of the notes commit author.} \item{committer}{Signature of the notes commit committer.} } \value{ invisible NULL } \description{ Remove the note for an object } \examples{ \dontrun{ ## Create and initialize a repository in a temporary directory path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "Commit message 1") ## Create note in default namespace note_1 <- note_create(commit_1, "Note-1") ## Create note in named (review) namespace note_2 <- note_create(commit_1, "Note-2", ref="refs/notes/review") ## List notes in default namespace notes(repo) ## List notes in 'review' namespace notes(repo, "review") ## Remove notes note_remove(note_1) note_remove(note_2) ## List notes in default namespace notes(repo) ## List notes in 'review' namespace notes(repo, "review") } } git2r/man/rm_file.Rd0000644000175000017500000000174414145546030014122 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/index.R \name{rm_file} \alias{rm_file} \title{Remove files from the working tree and from the index} \usage{ rm_file(repo = ".", path = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{path}{character vector with filenames to remove. Only files known to Git are removed.} } \value{ invisible(NULL) } \description{ Remove files from the working tree and from the index } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "file-to-remove.txt")) ## Add file to repository add(repo, "file-to-remove.txt") commit(repo, "First commit message") ## Remove file rm_file(repo, "file-to-remove.txt") ## View status of repository status(repo) } } git2r/man/is_head.Rd0000644000175000017500000000174214145546030014077 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{is_head} \alias{is_head} \title{Check if branch is head} \usage{ is_head(branch = NULL) } \arguments{ \item{branch}{The branch \code{object} to check if it's head.} } \value{ \code{TRUE} if branch is head, else \code{FALSE}. } \description{ Check if branch is head } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## List branches branches(repo) ## Check that 'master' is_head master <- branches(repo)[[1]] is_head(master) ## Create and checkout 'dev' branch checkout(repo, "dev", create = TRUE) ## List branches branches(repo) ## Check that 'master' is no longer head is_head(master) } } git2r/man/stash_list.Rd0000644000175000017500000000232514145546030014656 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stash.R \name{stash_list} \alias{stash_list} \title{List stashes in repository} \usage{ stash_list(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ list of stashes in repository } \description{ List stashes in repository } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) # Configure a user config(repo, user.name = "Alice", user.email = "alice@example.org") # Create a file, add and commit writeLines("Hello world!", file.path(path, "test-1.txt")) add(repo, 'test-1.txt') commit(repo, "Commit message") # Make one more commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-1.txt")) add(repo, 'test-1.txt') commit(repo, "Next commit message") # Create one more file writeLines("Hello world!", file.path(path, "test-2.txt")) # Check that there are no stashes stash_list(repo) # Stash stash(repo) # Only untracked changes, therefore no stashes stash_list(repo) # Stash and include untracked changes stash(repo, "Stash message", untracked=TRUE) # View stash stash_list(repo) } } git2r/man/ssh_path.Rd0000644000175000017500000000152614145546030014314 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/credential.R \name{ssh_path} \alias{ssh_path} \title{Compose usual path to ssh keys} \usage{ ssh_path(file = "") } \arguments{ \item{file}{basename of file for which path is requested} } \value{ Full path to the file } \description{ This function provides a consistent means across OS-types to access the \code{.ssh} directory. } \details{ On Windows-based systems, \code{path.expand("~")} returns \code{"C:/Users/username/Documents"}, whereas the usual path to the \code{.ssh} directory is \code{"C:/Users/username"}. On other operating systems, \code{path.expand("~")} returns the usual path to the \code{.ssh} directory. Calling \code{ssh_path()} with no arguments will return the usual path to the \code{.ssh} directory. } \examples{ ssh_path() ssh_path("is_rsa.pub") } git2r/man/as.data.frame.git_tree.Rd0000644000175000017500000000270214145546030016705 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tree.R \name{as.data.frame.git_tree} \alias{as.data.frame.git_tree} \title{Coerce entries in a git_tree to a \code{data.frame}} \usage{ \method{as.data.frame}{git_tree}(x, ...) } \arguments{ \item{x}{The tree \code{object}} \item{...}{Additional arguments. Not used.} } \value{ \code{data.frame} } \description{ The entries in a tree are coerced to a \code{data.frame} } \details{ The \code{data.frame} have the following columns: \describe{ \item{filemode}{ The UNIX file attributes of a tree entry } \item{type}{ String representation of the tree entry type } \item{sha}{ The sha of a tree entry } \item{name}{ The filename of a tree entry } } } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) dir.create(file.path(path, "subfolder")) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create three files and commit writeLines("First file", file.path(path, "example-1.txt")) writeLines("Second file", file.path(path, "subfolder/example-2.txt")) writeLines("Third file", file.path(path, "example-3.txt")) add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) commit(repo, "Commit message") ## Display tree tree(last_commit(repo)) ## Coerce tree to a data.frame df <- as.data.frame(tree(last_commit(repo))) df } } git2r/man/punch_card.Rd0000644000175000017500000000133114145546030014603 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/punch_card.R \name{punch_card} \alias{punch_card} \title{Punch card} \usage{ punch_card(repo = ".", main = NULL, ...) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{main}{Default title for the plot is "Punch card on repo:" and repository workdir basename. Supply a new title if you desire one.} \item{...}{Additional arguments affecting the plot} } \value{ invisible NULL } \description{ Punch card } \examples{ \dontrun{ ## Initialize repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- clone("https://github.com/ropensci/git2r.git", path) ## Plot punch_card(repo) } } git2r/man/branches.Rd0000644000175000017500000000235114145546030014265 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branches} \alias{branches} \title{Branches} \usage{ branches(repo = ".", flags = c("all", "local", "remote")) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{flags}{Filtering flags for the branch listing. Valid values are 'all', 'local' or 'remote'} } \value{ list of branches in repository } \description{ List branches in repository } \examples{ \dontrun{ ## Initialize repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Config first user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo, "origin", "refs/heads/master") ## List branches branches(repo) } } git2r/man/plot.git_repository.Rd0000644000175000017500000000152514145546030016541 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/plot.R \name{plot.git_repository} \alias{plot.git_repository} \title{Plot commits over time} \usage{ \method{plot}{git_repository}( x, breaks = c("month", "year", "quarter", "week", "day"), main = NULL, ... ) } \arguments{ \item{x}{The repository to plot} \item{breaks}{Default is \code{month}. Change to year, quarter, week or day as necessary.} \item{main}{Default title for the plot is "Commits on repo:" and repository workdir basename. Supply a new title if you desire one.} \item{...}{Additional arguments affecting the plot} } \description{ Plot commits over time } \examples{ \dontrun{ ## Initialize repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- clone("https://github.com/ropensci/git2r.git", path) ## Plot commits plot(repo) } } git2r/man/branch_target.Rd0000644000175000017500000000154614145546030015310 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_target} \alias{branch_target} \title{Get target (sha) pointed to by a branch} \usage{ branch_target(branch = NULL) } \arguments{ \item{branch}{The branch} } \value{ sha or NA if not a direct reference } \description{ Get target (sha) pointed to by a branch } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Get target (sha) pointed to by 'master' branch branch_target(repository_head(repo)) } } git2r/man/merge.Rd0000644000175000017500000000752314145546030013605 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/merge.R \name{merge.git_branch} \alias{merge.git_branch} \alias{merge.git_repository} \alias{merge.character} \title{Merge a branch into HEAD} \usage{ \method{merge}{git_branch}(x, y = NULL, commit_on_success = TRUE, merger = NULL, fail = FALSE, ...) \method{merge}{git_repository}(x, y = NULL, commit_on_success = TRUE, merger = NULL, fail = FALSE, ...) \method{merge}{character}( x = ".", y = NULL, commit_on_success = TRUE, merger = NULL, fail = FALSE, ... ) } \arguments{ \item{x}{A path (default '.') to a repository, or a \code{git_repository} object, or a \code{git_branch}.} \item{y}{If \code{x} is a \code{git_repository}, the name of the branch to merge into HEAD. Not used if \code{x} is a \code{git_branch}.} \item{commit_on_success}{If there are no conflicts written to the index, the merge commit will be committed. Default is TRUE.} \item{merger}{Who made the merge. The default (\code{NULL}) is to use \code{default_signature} for the repository.} \item{fail}{If a conflict occurs, exit immediately instead of attempting to continue resolving conflicts. Default is \code{FALSE}.} \item{...}{Additional arguments (unused).} } \value{ A list of class \code{git_merge_result} with entries: \describe{ \item{up_to_date}{ TRUE if the merge is already up-to-date, else FALSE. } \item{fast_forward}{ TRUE if a fast-forward merge, else FALSE. } \item{conflicts}{ TRUE if the index contain entries representing file conflicts, else FALSE. } \item{sha}{ If the merge created a merge commit, the sha of the merge commit. NA if no merge commit created. } } } \description{ Merge a branch into HEAD } \examples{ \dontrun{ ## Create a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) config(repo, user.name="Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message 1") ## Create first branch, checkout, add file and commit checkout(repo, "branch1", create = TRUE) writeLines("Branch 1", file.path(path, "branch-1.txt")) add(repo, "branch-1.txt") commit(repo, "Commit message branch 1") ## Create second branch, checkout, add file and commit b_2 <- branch_create(commit_1, "branch2") checkout(b_2) writeLines("Branch 2", file.path(path, "branch-2.txt")) add(repo, "branch-2.txt") commit(repo, "Commit message branch 2") ## Make a change to 'test.txt' writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Second commit message branch 2") ## Checkout master checkout(repo, "master", force = TRUE) ## Merge branch 1 merge(repo, "branch1") ## Merge branch 2 merge(repo, "branch2") ## Create third branch, checkout, change file and commit checkout(repo, "branch3", create=TRUE) writeLines(c("Lorem ipsum dolor amet sit, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message branch 3") ## Checkout master and create a change that creates a merge conflict checkout(repo, "master", force=TRUE) writeLines(c("Lorem ipsum dolor sit amet, adipisicing consectetur elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Some commit message branch 1") ## Merge branch 3 merge(repo, "branch3") ## Check status; Expect to have one unstaged unmerged conflict. status(repo) } } git2r/man/lookup_commit.Rd0000644000175000017500000000254214145546030015363 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/repository.R \name{lookup_commit} \alias{lookup_commit} \alias{lookup_commit.git_branch} \alias{lookup_commit.git_commit} \alias{lookup_commit.git_tag} \alias{lookup_commit.git_reference} \title{Lookup the commit related to a git object} \usage{ lookup_commit(object) \method{lookup_commit}{git_branch}(object) \method{lookup_commit}{git_commit}(object) \method{lookup_commit}{git_tag}(object) \method{lookup_commit}{git_reference}(object) } \arguments{ \item{object}{a git object to get the related commit from.} } \value{ A git commit object. } \description{ Lookup the commit related to a git_reference, git_tag or git_branch object. } \examples{ \dontrun{ ## Create a directory in tempdir path <- tempfile(pattern="git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message 1") ## Get the commit pointed to by the 'master' branch lookup_commit(repository_head(repo)) ## Create a tag a_tag <- tag(repo, "Tagname", "Tag message") ## Get the commit pointed to by 'a_tag' lookup_commit(a_tag) } } git2r/man/ssl_cert_locations.Rd0000644000175000017500000000116214145546030016370 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/libgit2.R \name{ssl_cert_locations} \alias{ssl_cert_locations} \title{Set the SSL certificate-authority locations} \usage{ ssl_cert_locations(filename = NULL, path = NULL) } \arguments{ \item{filename}{Location of a file containing several certificates concatenated together. Default NULL.} \item{path}{Location of a directory holding several certificates, one per file. Default NULL.} } \value{ invisible(NULL) } \description{ Set the SSL certificate-authority locations } \note{ Either parameter may be 'NULL', but not both. } \keyword{methods} git2r/man/branch_create.Rd0000644000175000017500000000262114145546030015260 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_create} \alias{branch_create} \title{Create a branch} \usage{ branch_create(commit = last_commit(), name = NULL, force = FALSE) } \arguments{ \item{commit}{Commit to which the branch should point. The default is to use the \code{last_commit()} function to determine the commit to which the branch should point.} \item{name}{Name for the branch} \item{force}{Overwrite existing branch. Default = FALSE} } \value{ invisible git_branch object } \description{ Create a branch } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") lines <- "Hello world!" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit_1 <- commit(repo, "First commit message") ## Create a branch branch_1 <- branch_create(commit_1, name = "test-branch") ## Add one more commit lines <- c("Hello world!", "HELLO WORLD!") writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit_2 <- commit(repo, "Another commit message") ## Create a branch with the same name should fail try(branch_create(commit_2, name = "test-branch"), TRUE) ## Force it branch_2 <- branch_create(commit_2, name = "test-branch", force = TRUE) } } git2r/man/index_remove_bypath.Rd0000644000175000017500000000237614145546030016542 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/index.R \name{index_remove_bypath} \alias{index_remove_bypath} \title{Remove an index entry corresponding to a file on disk} \usage{ index_remove_bypath(repo = ".", path = NULL) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{path}{character vector with filenames to remove. The path must be relative to the repository's working folder. It may exist. If this file currently is the result of a merge conflict, this file will no longer be marked as conflicting. The data about the conflict will be moved to the "resolve undo" (REUC) section.} } \value{ invisible(NULL) } \description{ Remove an index entry corresponding to a file on disk } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "file-to-remove.txt")) ## Add file to repository add(repo, "file-to-remove.txt") ## View status of repository status(repo) ## Remove file index_remove_bypath(repo, "file-to-remove.txt") ## View status of repository status(repo) } } git2r/man/bundle_r_package.Rd0000644000175000017500000000226114145546030015745 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bundle_r_package.R \name{bundle_r_package} \alias{bundle_r_package} \title{Bundle bare repo of package} \usage{ bundle_r_package(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ Invisible bundled \code{git_repository} object } \description{ Clone the package git repository as a bare repository to \code{pkg/inst/pkg.git} } \examples{ \dontrun{ ## Initialize repository path <- tempfile() dir.create(path) path <- file.path(path, "git2r") repo <- clone("https://github.com/ropensci/git2r.git", path) ## Bundle bare repository in package bundle_r_package(repo) ## Build and install bundled package wd <- setwd(dirname(path)) system(sprintf("R CMD build \%s", path)) pkg <- list.files(".", pattern = "[.]tar[.]gz$") system(sprintf("R CMD INSTALL \%s", pkg)) setwd(wd) ## Reload package detach("package:git2r", unload = TRUE) library(git2r) ## Summarize last five commits of bundled repo repo <- repository(system.file("git2r.git", package = "git2r")) invisible(lapply(commits(repo, n = 5), summary)) ## Plot content of bundled repo plot(repo) } } git2r/man/commit.Rd0000644000175000017500000000317614145546030013776 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commit.R \name{commit} \alias{commit} \title{Commit} \usage{ commit( repo = ".", message = NULL, all = FALSE, session = FALSE, author = NULL, committer = NULL ) } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} \item{message}{The commit message.} \item{all}{Stage modified and deleted files. Files not added to Git are not affected.} \item{session}{Add sessionInfo to commit message. Default is FALSE.} \item{author}{Signature with author and author time of commit.} \item{committer}{Signature with committer and commit time of commit.} } \value{ A list of class \code{git_commit} with entries: \describe{ \item{sha}{ The 40 character hexadecimal string of the SHA-1 } \item{author}{ An author signature } \item{committer}{ The committer signature } \item{summary}{ The short "summary" of a git commit message, comprising the first paragraph of the message with whitespace trimmed and squashed. } \item{message}{ The message of a commit } \item{repo}{ The \code{git_repository} object that contains the commit } } } \description{ Commit } \examples{ \dontrun{ ## Initialize a repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Config user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") } } git2r/man/fetch_heads.Rd0000644000175000017500000000254114145546030014736 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/fetch.R \name{fetch_heads} \alias{fetch_heads} \title{Get updated heads during the last fetch.} \usage{ fetch_heads(repo = ".") } \arguments{ \item{repo}{a path to a repository or a \code{git_repository} object. Default is '.'} } \value{ list with \code{git_fetch_head} entries. NULL if there is no FETCH_HEAD file. } \description{ Get updated heads during the last fetch. } \examples{ \dontrun{ ## Initialize three temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo_1 <- tempfile(pattern="git2r-") path_repo_2 <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) bare_repo <- init(path_bare, bare = TRUE) repo_1 <- clone(path_bare, path_repo_1) repo_2 <- clone(path_bare, path_repo_2) config(repo_1, user.name = "Alice", user.email = "alice@example.org") config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Add changes to repo 1 writeLines("Lorem ipsum dolor sit amet", con = file.path(path_repo_1, "example.txt")) add(repo_1, "example.txt") commit(repo_1, "Commit message") ## Push changes from repo 1 to origin (bare_repo) push(repo_1, "origin", "refs/heads/master") ## Fetch changes from origin (bare_repo) to repo 2 fetch(repo_2, "origin") ## List updated heads fetch_heads(repo_2) } } git2r/man/branch_remote_name.Rd0000644000175000017500000000223514145546030016311 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/branch.R \name{branch_remote_name} \alias{branch_remote_name} \title{Remote name of a branch} \usage{ branch_remote_name(branch = NULL) } \arguments{ \item{branch}{The branch} } \value{ character string with remote name } \description{ The name of remote that the remote tracking branch belongs to } \examples{ \dontrun{ ## Initialize two temporary repositories path_bare <- tempfile(pattern="git2r-") path_repo <- tempfile(pattern="git2r-") dir.create(path_bare) dir.create(path_repo) repo_bare <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Config user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") ## Write to a file and commit lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" writeLines(lines, file.path(path_repo, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Push commits from repository to bare repository ## Adds an upstream tracking branch to branch 'master' push(repo, "origin", "refs/heads/master") ## Get remote name branch_remote_name(branches(repo)[[2]]) } } git2r/man/git_time.Rd0000644000175000017500000000415014145546030014300 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time.R \name{git_time} \alias{git_time} \alias{as.character.git_time} \alias{format.git_time} \alias{as.POSIXct.git_time} \alias{print.git_time} \title{Time} \usage{ \method{as.character}{git_time}(x, tz = "GMT", origin = "1970-01-01", usetz = TRUE, ...) \method{format}{git_time}(x, tz = "GMT", origin = "1970-01-01", usetz = TRUE, ...) \method{as.POSIXct}{git_time}(x, tz = "GMT", origin = "1970-01-01", ...) \method{print}{git_time}(x, tz = "GMT", origin = "1970-01-01", usetz = TRUE, ...) } \arguments{ \item{x}{\R object to be converted.} \item{tz}{time zone specification to be used for the conversion, \emph{if one is required}. System-specific (see \link[base]{time zones}), but \code{""} is the current time zone, and \code{"GMT"} is UTC (Universal Time, Coordinated). Invalid values are most commonly treated as UTC, on some platforms with a warning.} \item{origin}{a date-time object, or something which can be coerced by \code{as.POSIXct(tz = "GMT")} to such an object.} \item{usetz}{logical. Should the time zone abbreviation be appended to the output? This is used in printing times, and more reliable than using \code{"\%Z"}.} \item{...}{further arguments to be passed to or from other methods.} } \description{ The class \code{git_time} stores the time a Git object was created. } \details{ The default is to use \code{tz = "GMT"} and \code{origin = "1970-01-01"}. To use your local timezone, set \code{tz = Sys.timezone()}. } \examples{ \dontrun{ ## Initialize a temporary repository path <- tempfile(pattern="git2r-") dir.create(path) repo <- init(path) ## Create a first user and commit a file config(repo, user.name = "Alice", user.email = "alice@example.org") writeLines("Hello world!", file.path(path, "example.txt")) add(repo, "example.txt") commit(repo, "First commit message") ## Create tag tag(repo, "Tagname", "Tag message") as.POSIXct(commits(repo)[[1]]$author$when) as.POSIXct(tags(repo)[[1]]$tagger$when) as.POSIXct(tags(repo)[[1]]$tagger$when, tz = Sys.timezone()) } } \seealso{ \code{\link{when}} } git2r/src/0000755000175000017500000000000014145550337012231 5ustar nileshnileshgit2r/src/git2r_revparse.h0000644000175000017500000000164613137360640015343 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_revparse_h #define INCLUDE_git2r_revparse_h #include #include SEXP git2r_revparse_single(SEXP repo, SEXP revision); #endif git2r/src/git2r_error.h0000644000175000017500000000512413273077667014657 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2018 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_error_h #define INCLUDE_git2r_error_h #include /** * Error messages */ extern const char git2r_err_alloc_memory_buffer[]; extern const char git2r_err_branch_not_local[]; extern const char git2r_err_branch_not_remote[]; extern const char git2r_err_checkout_tree[]; extern const char git2r_err_invalid_refname[]; extern const char git2r_err_invalid_remote[]; extern const char git2r_err_invalid_repository[]; extern const char git2r_err_nothing_added_to_commit[]; extern const char git2r_err_object_type[]; extern const char git2r_err_reference[]; extern const char git2r_err_repo_init[]; extern const char git2r_err_revparse_not_found[]; extern const char git2r_err_revparse_single[]; extern const char git2r_err_ssl_cert_locations[]; extern const char git2r_err_unexpected_config_level[]; extern const char git2r_err_unable_to_authenticate[]; /** * Error messages specific to argument checking */ extern const char git2r_err_blob_arg[]; extern const char git2r_err_branch_arg[]; extern const char git2r_err_commit_arg[]; extern const char git2r_err_commit_stash_arg[]; extern const char git2r_err_credentials_arg[]; extern const char git2r_err_diff_arg[]; extern const char git2r_err_fetch_heads_arg[]; extern const char git2r_err_filename_arg[]; extern const char git2r_err_sha_arg[]; extern const char git2r_err_integer_arg[]; extern const char git2r_err_integer_gte_zero_arg[]; extern const char git2r_err_list_arg[]; extern const char git2r_err_logical_arg[]; extern const char git2r_err_note_arg[]; extern const char git2r_err_signature_arg[]; extern const char git2r_err_string_arg[]; extern const char git2r_err_string_vec_arg[]; extern const char git2r_err_tag_arg[]; extern const char git2r_err_tree_arg[]; void git2r_error( const char *func_name, const git_error *err, const char *msg1, const char *msg2); #endif git2r/src/git2r_diff.h0000644000175000017500000000206613436723525014430 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2019 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_diff_h #define INCLUDE_git2r_diff_h SEXP git2r_diff( SEXP repo, SEXP tree1, SEXP tree2, SEXP index, SEXP filename, SEXP context_lines, SEXP interhunk_lines, SEXP old_prefix, SEXP new_prefix, SEXP id_abbrev, SEXP path, SEXP max_size); #endif git2r/src/git2r_repository.h0000644000175000017500000000277514075763574015756 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_repository_h #define INCLUDE_git2r_repository_h #include #include #include git_repository* git2r_repository_open(SEXP repo); SEXP git2r_repository_can_open(SEXP path); SEXP git2r_repository_discover(SEXP path, SEXP ceiling); SEXP git2r_repository_fetch_heads(SEXP repo); SEXP git2r_repository_head(SEXP repo); SEXP git2r_repository_head_detached(SEXP repo); SEXP git2r_repository_init(SEXP path, SEXP bare, SEXP branch); SEXP git2r_repository_is_bare(SEXP repo); SEXP git2r_repository_is_empty(SEXP repo); SEXP git2r_repository_is_shallow(SEXP repo); SEXP git2r_repository_set_head(SEXP repo, SEXP ref_name); SEXP git2r_repository_set_head_detached(SEXP commit); SEXP git2r_repository_workdir(SEXP repo); #endif git2r/src/git2r_note.c0000644000175000017500000002651213671131056014453 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_note.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" /** * Data structure to hold information when iterating over note * objects. */ typedef struct { size_t n; SEXP list; SEXP repo; git_repository *repository; const char *notes_ref; } git2r_note_foreach_cb_data; /** * Init slots in S3 class git_note * * @param blob_id Oid of the blob containing the message * @param annotated_object_id Oid of the git object being annotated * @param repository * @param repo S3 class git_repository that contains the stash * @param dest S3 class git_note to initialize * @return int 0 on success, or an error code. */ static int git2r_note_init( const git_oid *blob_id, const git_oid *annotated_object_id, git_repository *repository, const char *notes_ref, SEXP repo, SEXP dest) { int error; git_note *note = NULL; char sha[GIT_OID_HEXSZ + 1]; error = git_note_read(¬e, repository, notes_ref, annotated_object_id); if (error) return error; git_oid_fmt(sha, blob_id); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT( dest, git2r_S3_item__git_note__sha, Rf_mkString(sha)); git_oid_fmt(sha, annotated_object_id); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT(dest, git2r_S3_item__git_note__annotated, Rf_mkString(sha)); SET_VECTOR_ELT( dest, git2r_S3_item__git_note__message, Rf_mkString(git_note_message(note))); SET_VECTOR_ELT( dest, git2r_S3_item__git_note__refname, Rf_mkString(notes_ref)); SET_VECTOR_ELT( dest, git2r_S3_item__git_note__repo, Rf_duplicate(repo)); git_note_free(note); return 0; } /** * Add a note for an object * * @param repo S3 class git_repository * @param sha The sha string of object * @param commit S3 class git_commit * @param message Content of the note to add * @param ref Canonical name of the reference to use * @param author Signature of the notes note author * @param committer Signature of the notes note committer * @param force Overwrite existing note * @return S3 class git_note */ SEXP attribute_hidden git2r_note_create( SEXP repo, SEXP sha, SEXP message, SEXP ref, SEXP author, SEXP committer, SEXP force) { int error, nprotect = 0; SEXP result = R_NilValue; int overwrite = 0; git_oid note_oid; git_oid object_oid; git_signature *sig_author = NULL; git_signature *sig_committer = NULL; git_repository *repository = NULL; if (git2r_arg_check_sha(sha)) git2r_error(__func__, NULL, "'sha'", git2r_err_sha_arg); if (git2r_arg_check_string(message)) git2r_error(__func__, NULL, "'message'", git2r_err_string_arg); if (git2r_arg_check_string(ref)) git2r_error(__func__, NULL, "'ref'", git2r_err_string_arg); if (git2r_arg_check_signature(author)) git2r_error(__func__, NULL, "'author'", git2r_err_signature_arg); if (git2r_arg_check_signature(committer)) git2r_error(__func__, NULL, "'committer'", git2r_err_signature_arg); if (git2r_arg_check_logical(force)) git2r_error(__func__, NULL, "'force'", git2r_err_logical_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_signature_from_arg(&sig_author, author); if (error) goto cleanup; error = git2r_signature_from_arg(&sig_committer, committer); if (error) goto cleanup; error = git_oid_fromstr(&object_oid, CHAR(STRING_ELT(sha, 0))); if (error) goto cleanup; if (LOGICAL(force)[0]) overwrite = 1; error = git_note_create( ¬e_oid, repository, CHAR(STRING_ELT(ref, 0)), sig_author, sig_committer, &object_oid, CHAR(STRING_ELT(message, 0)), overwrite); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_note)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_note)); error = git2r_note_init(¬e_oid, &object_oid, repository, CHAR(STRING_ELT(ref, 0)), repo, result); cleanup: git_signature_free(sig_author); git_signature_free(sig_committer); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Default notes reference * * Get the default notes reference for a repository * @param repo S3 class git_repository * @return Character vector of length one with name of default * reference */ SEXP attribute_hidden git2r_note_default_ref( SEXP repo) { int error, nprotect = 0; SEXP result = R_NilValue; git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); git_repository *repository = NULL; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_note_default_ref(&buf, repository); if (error) goto cleanup; PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(buf.ptr)); cleanup: GIT2R_BUF_DISPOSE(&buf); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Callback when iterating over notes * * @param blob_id Oid of the blob containing the message * @param annotated_object_id Oid of the git object being annotated * @param payload Payload data passed to `git_note_foreach` * @return int 0 or error code */ static int git2r_note_foreach_cb( const git_oid *blob_id, const git_oid *annotated_object_id, void *payload) { int error = 0, nprotect = 0; SEXP note; git2r_note_foreach_cb_data *cb_data = (git2r_note_foreach_cb_data*)payload; /* Check if we have a list to populate */ if (!Rf_isNull(cb_data->list)) { PROTECT(note = Rf_mkNamed(VECSXP, git2r_S3_items__git_note)); nprotect++; Rf_setAttrib( note, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_note)); error = git2r_note_init( blob_id, annotated_object_id, cb_data->repository, cb_data->notes_ref, cb_data->repo, note); if (error) goto cleanup; SET_VECTOR_ELT(cb_data->list, cb_data->n, note); } cb_data->n += 1; cleanup: if (nprotect) UNPROTECT(nprotect); return error; } /** * List all the notes within a specified namespace. * * @param repo S3 class git_repository * @param ref Optional reference to read from. * @return VECXSP with S3 objects of class git_note */ SEXP attribute_hidden git2r_notes( SEXP repo, SEXP ref) { int error, nprotect = 0; SEXP result = R_NilValue; git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); const char *notes_ref; git2r_note_foreach_cb_data cb_data = {0, R_NilValue, R_NilValue, NULL, NULL}; git_repository *repository = NULL; if (!Rf_isNull(ref)) { if (git2r_arg_check_string(ref)) git2r_error(__func__, NULL, "'ref'", git2r_err_string_arg); } repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); if (!Rf_isNull(ref)) { notes_ref = CHAR(STRING_ELT(ref, 0)); } else { error = git_note_default_ref(&buf, repository); if (error) goto cleanup; notes_ref = buf.ptr; } /* Count number of notes before creating the list */ error = git_note_foreach( repository, notes_ref, &git2r_note_foreach_cb, &cb_data); if (error) { if (GIT_ENOTFOUND == error) { error = GIT_OK; PROTECT(result = Rf_allocVector(VECSXP, 0)); nprotect++; } goto cleanup; } PROTECT(result = Rf_allocVector(VECSXP, cb_data.n)); nprotect++; cb_data.n = 0; cb_data.list = result; cb_data.repo = repo; cb_data.repository = repository; cb_data.notes_ref = notes_ref; error = git_note_foreach(repository, notes_ref, &git2r_note_foreach_cb, &cb_data); cleanup: GIT2R_BUF_DISPOSE(&buf); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Remove the note for an object * * @param note S3 class git_note * @param author Signature of the notes commit author * @param committer Signature of the notes commit committer * @return R_NilValue */ SEXP attribute_hidden git2r_note_remove( SEXP note, SEXP author, SEXP committer) { int error; SEXP repo; SEXP annotated; git_oid note_oid; git_signature *sig_author = NULL; git_signature *sig_committer = NULL; git_repository *repository = NULL; if (git2r_arg_check_note(note)) git2r_error(__func__, NULL, "'note'", git2r_err_note_arg); if (git2r_arg_check_signature(author)) git2r_error(__func__, NULL, "'author'", git2r_err_signature_arg); if (git2r_arg_check_signature(committer)) git2r_error(__func__, NULL, "'committer'", git2r_err_signature_arg); repo = git2r_get_list_element(note, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_signature_from_arg(&sig_author, author); if (error) goto cleanup; error = git2r_signature_from_arg(&sig_committer, committer); if (error) goto cleanup; annotated = git2r_get_list_element(note, "annotated"); error = git_oid_fromstr(¬e_oid, CHAR(STRING_ELT(annotated, 0))); if (error) goto cleanup; error = git_note_remove( repository, CHAR(STRING_ELT(git2r_get_list_element(note, "refname"), 0)), sig_author, sig_committer, ¬e_oid); cleanup: git_signature_free(sig_author); git_signature_free(sig_committer); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } git2r/src/git2r_config.h0000644000175000017500000000212213254030154014741 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2018 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_config_h #define INCLUDE_git2r_config_h #include #include SEXP git2r_config_find_file(SEXP level); SEXP git2r_config_get(SEXP repo); SEXP git2r_config_get_logical(SEXP repo, SEXP name); SEXP git2r_config_get_string(SEXP repo, SEXP name); SEXP git2r_config_set(SEXP repo, SEXP variables); #endif git2r/src/git2r_clone.h0000644000175000017500000000177413137360640014616 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_clone_h #define INCLUDE_git2r_clone_h #include #include SEXP git2r_clone( SEXP url, SEXP local_path, SEXP bare, SEXP branch, SEXP checkout, SEXP credentials, SEXP progress); #endif git2r/src/git2r_odb.c0000644000175000017500000003470413671131056014254 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_odb.h" #include "git2r_repository.h" /** * Determine the sha of character vectors without writing to the * object data base. * * @param data STRSXP with character vectors to hash * @return A STRSXP with character vector of sha values */ SEXP attribute_hidden git2r_odb_hash( SEXP data) { SEXP result; int error = GIT_OK; size_t len, i; char sha[GIT_OID_HEXSZ + 1]; git_oid oid; if (git2r_arg_check_string_vec(data)) git2r_error(__func__, NULL, "'data'", git2r_err_string_vec_arg); len = Rf_length(data); PROTECT(result = Rf_allocVector(STRSXP, len)); for (i = 0; i < len; i++) { if (NA_STRING == STRING_ELT(data, i)) { SET_STRING_ELT(result, i, NA_STRING); } else { error = git_odb_hash(&oid, CHAR(STRING_ELT(data, i)), LENGTH(STRING_ELT(data, i)), GIT2R_OBJECT_BLOB); if (error) break; git_oid_fmt(sha, &oid); sha[GIT_OID_HEXSZ] = '\0'; SET_STRING_ELT(result, i, Rf_mkChar(sha)); } } UNPROTECT(1); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Determine the sha of files without writing to the object data * base. * * @param path STRSXP with file vectors to hash * @return A STRSXP with character vector of sha values */ SEXP attribute_hidden git2r_odb_hashfile( SEXP path) { SEXP result; int error = GIT_OK; size_t len, i; char sha[GIT_OID_HEXSZ + 1]; git_oid oid; if (git2r_arg_check_string_vec(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_vec_arg); len = Rf_length(path); PROTECT(result = Rf_allocVector(STRSXP, len)); for (i = 0; i < len; i++) { if (NA_STRING == STRING_ELT(path, i)) { SET_STRING_ELT(result, i, NA_STRING); } else { error = git_odb_hashfile(&oid, CHAR(STRING_ELT(path, i)), GIT2R_OBJECT_BLOB); if (error) break; git_oid_fmt(sha, &oid); sha[GIT_OID_HEXSZ] = '\0'; SET_STRING_ELT(result, i, Rf_mkChar(sha)); } } UNPROTECT(1); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Data structure to hold information when iterating over objects. */ typedef struct { size_t n; SEXP list; git_odb *odb; } git2r_odb_objects_cb_data; /** * Add object * * @param oid Oid of the object * @param list The list to hold the S3 class git_object * @param i The index to the list item * @param type The type of the object * @param len The length of the object * @param repo The S3 class that contains the object * @return void */ static void git2r_add_object( const git_oid *oid, SEXP list, size_t i, const char *type, size_t len) { int j = 0; char sha[GIT_OID_HEXSZ + 1]; /* Sha */ git_oid_fmt(sha, oid); sha[GIT_OID_HEXSZ] = '\0'; SET_STRING_ELT(VECTOR_ELT(list, j++), i, Rf_mkChar(sha)); /* Type */ SET_STRING_ELT(VECTOR_ELT(list, j++), i, Rf_mkChar(type)); /* Length */ INTEGER(VECTOR_ELT(list, j++))[i] = len; } /** * Callback when iterating over objects * * @param oid Oid of the object * @param payload Payload data * @return int 0 or error code */ static int git2r_odb_objects_cb( const git_oid *oid, void *payload) { int error; size_t len; GIT2R_OBJECT_T type; git2r_odb_objects_cb_data *p = (git2r_odb_objects_cb_data*)payload; error = git_odb_read_header(&len, &type, p->odb, oid); if (error) return error; switch(type) { case GIT2R_OBJECT_COMMIT: if (!Rf_isNull(p->list)) git2r_add_object(oid, p->list, p->n, "commit", len); break; case GIT2R_OBJECT_TREE: if (!Rf_isNull(p->list)) git2r_add_object(oid, p->list, p->n, "tree", len); break; case GIT2R_OBJECT_BLOB: if (!Rf_isNull(p->list)) git2r_add_object(oid, p->list, p->n, "blob", len); break; case GIT2R_OBJECT_TAG: if (!Rf_isNull(p->list)) git2r_add_object(oid, p->list, p->n, "tag", len); break; default: return 0; } p->n += 1; return 0; } /** * List all objects available in the database * * @param repo S3 class git_repository * @return list with sha's for commit's, tree's, blob's and tag's */ SEXP attribute_hidden git2r_odb_objects( SEXP repo) { const char *names[] = {"sha", "type", "len", ""}; int i, error, nprotect = 0; SEXP result = R_NilValue; git2r_odb_objects_cb_data cb_data = {0, R_NilValue, NULL}; git_odb *odb = NULL; git_repository *repository = NULL; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_repository_odb(&odb, repository); if (error) goto cleanup; cb_data.odb = odb; /* Count number of objects before creating the list */ error = git_odb_foreach(odb, &git2r_odb_objects_cb, &cb_data); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, names)); nprotect++; i = 0; SET_VECTOR_ELT(result, i++, Rf_allocVector(STRSXP, cb_data.n)); SET_VECTOR_ELT(result, i++, Rf_allocVector(STRSXP, cb_data.n)); SET_VECTOR_ELT(result, i, Rf_allocVector(INTSXP, cb_data.n)); cb_data.list = result; cb_data.n = 0; error = git_odb_foreach(odb, &git2r_odb_objects_cb, &cb_data); cleanup: git_repository_free(repository); git_odb_free(odb); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Data structure to hold information when iterating over blobs. */ typedef struct { size_t n; SEXP list; git_repository *repository; git_odb *odb; } git2r_odb_blobs_cb_data; /** * Add blob entry to list * * @param entry The tree entry (blob) to add * @param odb The object database * @param list The list to hold the blob information * @param i The vector index of the list items to use for the blob information * @param path The path to the tree relative to the repository workdir * @param commit The commit that contains the root tree of the iteration * @param author The author of the commit * @param when Time of the commit * @return 0 or error code */ static int git2r_odb_add_blob( const git_tree_entry *entry, git_odb *odb, SEXP list, size_t i, const char *path, const char *commit, const char *author, double when) { int error; int j = 0; size_t len; GIT2R_OBJECT_T type; char sha[GIT_OID_HEXSZ + 1]; /* Sha */ git_oid_fmt(sha, git_tree_entry_id(entry)); sha[GIT_OID_HEXSZ] = '\0'; SET_STRING_ELT(VECTOR_ELT(list, j++), i, Rf_mkChar(sha)); /* Path */ SET_STRING_ELT(VECTOR_ELT(list, j++), i, Rf_mkChar(path)); /* Name */ SET_STRING_ELT(VECTOR_ELT(list, j++), i, Rf_mkChar(git_tree_entry_name(entry))); /* Length */ error = git_odb_read_header(&len, &type, odb, git_tree_entry_id(entry)); if (error) return error; INTEGER(VECTOR_ELT(list, j++))[i] = len; /* Commit sha */ SET_STRING_ELT(VECTOR_ELT(list, j++), i, Rf_mkChar(commit)); /* Author */ SET_STRING_ELT(VECTOR_ELT(list, j++), i, Rf_mkChar(author)); /* When */ REAL(VECTOR_ELT(list, j++))[i] = when; return GIT_OK; } /** * Recursively iterate over all tree's * * @param tree The tree to iterate over * @param path The path to the tree relative to the repository workdir * @param commit The commit that contains the root tree of the iteration * @param author The author of the commit * @param when Time of the commit * @param data The callback data when iterating over odb objects * @return 0 or error code */ static int git2r_odb_tree_blobs( const git_tree *tree, const char *path, const char *commit, const char *author, double when, git2r_odb_blobs_cb_data *data) { int error; size_t i, n; n = git_tree_entrycount(tree); for (i = 0; i < n; ++i) { const git_tree_entry *entry; entry = git_tree_entry_byindex(tree, i); switch (git_tree_entry_type(entry)) { case GIT2R_OBJECT_TREE: { char *buf = NULL; size_t path_len, buf_len; const char *entry_name; const char *sep; git_tree *sub_tree = NULL; error = git_tree_lookup( &sub_tree, data->repository, git_tree_entry_id(entry)); if (error) return error; entry_name = git_tree_entry_name(entry); path_len = strlen(path); buf_len = path_len + strlen(entry_name) + 2; buf = malloc(buf_len); if (!buf) { git_tree_free(sub_tree); GIT2R_ERROR_SET_OOM(); return GIT2R_ERROR_NOMEMORY; } if (path_len) { sep = "/"; } else { sep = ""; } error = snprintf(buf, buf_len, "%s%s%s", path, sep, entry_name); if (0 <= error && (size_t)error < buf_len) { error = git2r_odb_tree_blobs( sub_tree, buf, commit, author, when, data); } else { GIT2R_ERROR_SET_STR(GIT2R_ERROR_OS, "Failed to snprintf tree path."); error = GIT2R_ERROR_OS; } free(buf); git_tree_free(sub_tree); if (error) return error; break; } case GIT2R_OBJECT_BLOB: if (!Rf_isNull(data->list)) { error = git2r_odb_add_blob( entry, data->odb, data->list, data->n, path, commit, author, when); if (error) return error; } data->n += 1; break; default: break; } } return GIT_OK; } /** * Callback when iterating over blobs * * @param oid Oid of the object * @param payload Payload data * @return int 0 or error code */ static int git2r_odb_blobs_cb( const git_oid *oid, void *payload) { int error = GIT_OK; size_t len; GIT2R_OBJECT_T type; git2r_odb_blobs_cb_data *p = (git2r_odb_blobs_cb_data*)payload; error = git_odb_read_header(&len, &type, p->odb, oid); if (error) return error; if (type == GIT2R_OBJECT_COMMIT) { const git_signature *author; git_commit *commit = NULL; git_tree *tree = NULL; char sha[GIT_OID_HEXSZ + 1]; error = git_commit_lookup(&commit, p->repository, oid); if (error) goto cleanup; error = git_commit_tree(&tree, commit); if (error) goto cleanup; git_oid_fmt(sha, oid); sha[GIT_OID_HEXSZ] = '\0'; author = git_commit_author(commit); /* Recursively iterate over all tree's */ error = git2r_odb_tree_blobs( tree, "", sha, author->name, (double)(author->when.time), p); cleanup: git_commit_free(commit); git_tree_free(tree); } return error; } /** * List all blobs available in the database * * List all blobs reachable from the commits in the object * database. First list all commits. Then iterate over each blob from * the tree and sub-trees of each commit. * @param repo S3 class git_repository * @return A list with blob entries */ SEXP attribute_hidden git2r_odb_blobs( SEXP repo) { const char *names[] = {"sha", "path", "name", "len", "commit", "author", "when", ""}; int i, error, nprotect = 0; SEXP result = R_NilValue; git2r_odb_blobs_cb_data cb_data = {0, R_NilValue, NULL, NULL}; git_odb *odb = NULL; git_repository *repository = NULL; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_repository_odb(&odb, repository); if (error) goto cleanup; cb_data.odb = odb; /* Count number of blobs before creating the list */ cb_data.repository = repository; error = git_odb_foreach(odb, &git2r_odb_blobs_cb, &cb_data); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, names)); nprotect++; i = 0; SET_VECTOR_ELT(result, i++, Rf_allocVector(STRSXP, cb_data.n)); SET_VECTOR_ELT(result, i++, Rf_allocVector(STRSXP, cb_data.n)); SET_VECTOR_ELT(result, i++, Rf_allocVector(STRSXP, cb_data.n)); SET_VECTOR_ELT(result, i++, Rf_allocVector(INTSXP, cb_data.n)); SET_VECTOR_ELT(result, i++, Rf_allocVector(STRSXP, cb_data.n)); SET_VECTOR_ELT(result, i++, Rf_allocVector(STRSXP, cb_data.n)); SET_VECTOR_ELT(result, i, Rf_allocVector(REALSXP, cb_data.n)); cb_data.list = result; cb_data.n = 0; error = git_odb_foreach(odb, &git2r_odb_blobs_cb, &cb_data); cleanup: git_repository_free(repository); git_odb_free(odb); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/Makevars_libgit2.in0000644000175000017500000001144714125111755015750 0ustar nileshnilesh# Generated by scripts/build_Makevars.R: do not edit by hand PKG_CFLAGS = @PKG_CFLAGS@ PKG_CPPFLAGS = @PKG_CPPFLAGS@ PKG_LIBS = -L. -lmygit @PKG_LIBS@ OBJECTS.libgit2 = libgit2/src/alloc.o libgit2/src/annotated_commit.o libgit2/src/apply.o \ libgit2/src/attr_file.o libgit2/src/attr.o libgit2/src/attrcache.o \ libgit2/src/blame_git.o libgit2/src/blame.o libgit2/src/blob.o \ libgit2/src/branch.o libgit2/src/buffer.o libgit2/src/cache.o \ libgit2/src/checkout.o libgit2/src/cherrypick.o libgit2/src/clone.o \ libgit2/src/commit_graph.o libgit2/src/commit_list.o libgit2/src/commit.o \ libgit2/src/config_cache.o libgit2/src/config_entries.o libgit2/src/config_file.o \ libgit2/src/config_mem.o libgit2/src/config_parse.o libgit2/src/config_snapshot.o \ libgit2/src/config.o libgit2/src/crlf.o libgit2/src/date.o \ libgit2/src/delta.o libgit2/src/describe.o libgit2/src/diff_driver.o \ libgit2/src/diff_file.o libgit2/src/diff_generate.o libgit2/src/diff_parse.o \ libgit2/src/diff_print.o libgit2/src/diff_stats.o libgit2/src/diff_tform.o \ libgit2/src/diff_xdiff.o libgit2/src/diff.o libgit2/src/email.o \ libgit2/src/errors.o libgit2/src/fetch.o libgit2/src/fetchhead.o \ libgit2/src/filebuf.o libgit2/src/filter.o libgit2/src/futils.o \ libgit2/src/graph.o libgit2/src/hash.o libgit2/src/hashsig.o \ libgit2/src/ident.o libgit2/src/idxmap.o libgit2/src/ignore.o \ libgit2/src/index.o libgit2/src/indexer.o libgit2/src/iterator.o \ libgit2/src/libgit2.o libgit2/src/mailmap.o libgit2/src/merge_driver.o \ libgit2/src/merge_file.o libgit2/src/merge.o libgit2/src/message.o \ libgit2/src/midx.o libgit2/src/mwindow.o libgit2/src/net.o \ libgit2/src/netops.o libgit2/src/notes.o libgit2/src/object_api.o \ libgit2/src/object.o libgit2/src/odb_loose.o libgit2/src/odb_mempack.o \ libgit2/src/odb_pack.o libgit2/src/odb.o libgit2/src/offmap.o \ libgit2/src/oid.o libgit2/src/oidarray.o libgit2/src/oidmap.o \ libgit2/src/pack-objects.o libgit2/src/pack.o libgit2/src/parse.o \ libgit2/src/patch_generate.o libgit2/src/patch_parse.o libgit2/src/patch.o \ libgit2/src/path.o libgit2/src/pathspec.o libgit2/src/pool.o \ libgit2/src/posix.o libgit2/src/pqueue.o libgit2/src/proxy.o \ libgit2/src/push.o libgit2/src/reader.o libgit2/src/rebase.o \ libgit2/src/refdb_fs.o libgit2/src/refdb.o libgit2/src/reflog.o \ libgit2/src/refs.o libgit2/src/refspec.o libgit2/src/regexp.o \ libgit2/src/remote.o libgit2/src/repository.o libgit2/src/reset.o \ libgit2/src/revert.o libgit2/src/revparse.o libgit2/src/revwalk.o \ libgit2/src/runtime.o libgit2/src/signature.o libgit2/src/sortedcache.o \ libgit2/src/stash.o libgit2/src/status.o libgit2/src/strarray.o \ libgit2/src/strmap.o libgit2/src/submodule.o libgit2/src/sysdir.o \ libgit2/src/tag.o libgit2/src/thread.o libgit2/src/threadstate.o \ libgit2/src/trace.o libgit2/src/trailer.o libgit2/src/transaction.o \ libgit2/src/transport.o libgit2/src/tree-cache.o libgit2/src/tree.o \ libgit2/src/tsort.o libgit2/src/utf8.o libgit2/src/util.o \ libgit2/src/varint.o libgit2/src/vector.o libgit2/src/wildmatch.o \ libgit2/src/worktree.o libgit2/src/zstream.o OBJECTS.libgit2.allocators = libgit2/src/allocators/failalloc.o libgit2/src/allocators/stdalloc.o libgit2/src/allocators/win32_leakcheck.o OBJECTS.libgit2.streams = libgit2/src/streams/mbedtls.o libgit2/src/streams/openssl_dynamic.o libgit2/src/streams/openssl_legacy.o \ libgit2/src/streams/openssl.o libgit2/src/streams/registry.o libgit2/src/streams/socket.o \ libgit2/src/streams/stransport.o libgit2/src/streams/tls.o OBJECTS.libgit2.transports = libgit2/src/transports/auth_ntlm.o libgit2/src/transports/auth.o libgit2/src/transports/credential_helpers.o \ libgit2/src/transports/credential.o libgit2/src/transports/git.o libgit2/src/transports/http.o \ libgit2/src/transports/httpclient.o libgit2/src/transports/local.o libgit2/src/transports/smart_pkt.o \ libgit2/src/transports/smart_protocol.o libgit2/src/transports/smart.o libgit2/src/transports/ssh.o OBJECTS.libgit2.unix = libgit2/src/unix/map.o libgit2/src/unix/realpath.o OBJECTS.libgit2.xdiff = libgit2/src/xdiff/xdiffi.o libgit2/src/xdiff/xemit.o libgit2/src/xdiff/xhistogram.o \ libgit2/src/xdiff/xmerge.o libgit2/src/xdiff/xpatience.o libgit2/src/xdiff/xprepare.o \ libgit2/src/xdiff/xutils.o OBJECTS.http_parser = libgit2/deps/http-parser/http_parser.o LIBGIT = $(OBJECTS.libgit2) $(OBJECTS.libgit2.allocators) $(OBJECTS.libgit2.streams) \ $(OBJECTS.libgit2.transports) $(OBJECTS.libgit2.unix) $(OBJECTS.libgit2.xdiff) \ $(OBJECTS.http_parser) @GIT2R_SRC_REGEX@ @GIT2R_SRC_SHA1@ $(SHLIB): libmygit.a libmygit.a: $(LIBGIT) $(AR) rcs libmygit.a $(LIBGIT) clean: rm -f *.o libmygit.a git2r.so $(LIBGIT) .PHONY: all clean git2r/src/git2r.c0000644000175000017500000001321614075763063013433 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2019 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** @file git2r.c * @brief R bindings to the libgit2 library * * These functions are called from R with .Call to interface the * libgit2 library from R. * */ #include "git2r_arg.h" #include "git2r_blame.h" #include "git2r_blob.h" #include "git2r_branch.h" #include "git2r_checkout.h" #include "git2r_clone.h" #include "git2r_config.h" #include "git2r_commit.h" #include "git2r_diff.h" #include "git2r_error.h" #include "git2r_graph.h" #include "git2r_index.h" #include "git2r_libgit2.h" #include "git2r_merge.h" #include "git2r_note.h" #include "git2r_object.h" #include "git2r_odb.h" #include "git2r_push.h" #include "git2r_reference.h" #include "git2r_reflog.h" #include "git2r_remote.h" #include "git2r_repository.h" #include "git2r_reset.h" #include "git2r_revparse.h" #include "git2r_revwalk.h" #include "git2r_signature.h" #include "git2r_stash.h" #include "git2r_status.h" #include "git2r_tag.h" #include "git2r_tree.h" #include #define CALLDEF(name, n) {#name, (DL_FUNC) &name, n} static const R_CallMethodDef callMethods[] = { CALLDEF(git2r_blame_file, 2), CALLDEF(git2r_blob_content, 1), CALLDEF(git2r_blob_create_fromdisk, 2), CALLDEF(git2r_blob_create_fromworkdir, 2), CALLDEF(git2r_blob_is_binary, 1), CALLDEF(git2r_blob_rawsize, 1), CALLDEF(git2r_branch_canonical_name, 1), CALLDEF(git2r_branch_create, 3), CALLDEF(git2r_branch_delete, 1), CALLDEF(git2r_branch_get_upstream, 1), CALLDEF(git2r_branch_is_head, 1), CALLDEF(git2r_branch_list, 2), CALLDEF(git2r_branch_remote_name, 1), CALLDEF(git2r_branch_remote_url, 1), CALLDEF(git2r_branch_rename, 3), CALLDEF(git2r_branch_set_upstream, 2), CALLDEF(git2r_branch_target, 1), CALLDEF(git2r_branch_upstream_canonical_name, 1), CALLDEF(git2r_checkout_path, 2), CALLDEF(git2r_checkout_tree, 3), CALLDEF(git2r_clone, 7), CALLDEF(git2r_commit, 4), CALLDEF(git2r_commit_parent_list, 1), CALLDEF(git2r_commit_tree, 1), CALLDEF(git2r_config_find_file, 1), CALLDEF(git2r_config_get, 1), CALLDEF(git2r_config_get_logical, 2), CALLDEF(git2r_config_get_string, 2), CALLDEF(git2r_config_set, 2), CALLDEF(git2r_diff, 12), CALLDEF(git2r_graph_ahead_behind, 2), CALLDEF(git2r_graph_descendant_of, 2), CALLDEF(git2r_index_add_all, 3), CALLDEF(git2r_index_remove_bypath, 2), CALLDEF(git2r_libgit2_features, 0), CALLDEF(git2r_libgit2_version, 0), CALLDEF(git2r_merge_base, 2), CALLDEF(git2r_merge_branch, 4), CALLDEF(git2r_merge_fetch_heads, 2), CALLDEF(git2r_note_create, 7), CALLDEF(git2r_note_default_ref, 1), CALLDEF(git2r_notes, 2), CALLDEF(git2r_note_remove, 3), CALLDEF(git2r_object_lookup, 2), CALLDEF(git2r_odb_blobs, 1), CALLDEF(git2r_odb_hash, 1), CALLDEF(git2r_odb_hashfile, 1), CALLDEF(git2r_odb_objects, 1), CALLDEF(git2r_push, 4), CALLDEF(git2r_reference_dwim, 2), CALLDEF(git2r_reference_list, 1), CALLDEF(git2r_reflog_list, 2), CALLDEF(git2r_remote_add, 3), CALLDEF(git2r_remote_fetch, 6), CALLDEF(git2r_remote_list, 1), CALLDEF(git2r_remote_remove, 2), CALLDEF(git2r_remote_rename, 3), CALLDEF(git2r_remote_set_url, 3), CALLDEF(git2r_remote_url, 2), CALLDEF(git2r_remote_ls, 3), CALLDEF(git2r_repository_can_open, 1), CALLDEF(git2r_repository_discover, 2), CALLDEF(git2r_repository_fetch_heads, 1), CALLDEF(git2r_repository_head, 1), CALLDEF(git2r_repository_head_detached, 1), CALLDEF(git2r_repository_init, 3), CALLDEF(git2r_repository_is_bare, 1), CALLDEF(git2r_repository_is_empty, 1), CALLDEF(git2r_repository_is_shallow, 1), CALLDEF(git2r_repository_set_head, 2), CALLDEF(git2r_repository_set_head_detached, 1), CALLDEF(git2r_repository_workdir, 1), CALLDEF(git2r_reset, 2), CALLDEF(git2r_reset_default, 2), CALLDEF(git2r_revparse_single, 2), CALLDEF(git2r_revwalk_contributions, 4), CALLDEF(git2r_revwalk_list, 6), CALLDEF(git2r_revwalk_list2, 7), CALLDEF(git2r_signature_default, 1), CALLDEF(git2r_ssl_cert_locations, 2), CALLDEF(git2r_stash_apply, 2), CALLDEF(git2r_stash_drop, 2), CALLDEF(git2r_stash_list, 1), CALLDEF(git2r_stash_pop, 2), CALLDEF(git2r_stash_save, 6), CALLDEF(git2r_status_list, 6), CALLDEF(git2r_tag_create, 5), CALLDEF(git2r_tag_delete, 2), CALLDEF(git2r_tag_list, 1), CALLDEF(git2r_tree_walk, 2), {NULL, NULL, 0} }; /** * Load 'git2r' * - Register routines to R. * - Initialize libgit2 * * @param info Information about the DLL being loaded */ void R_init_git2r(DllInfo *info) { R_registerRoutines(info, NULL, callMethods, NULL, NULL); R_useDynamicSymbols(info, FALSE); R_forceSymbols(info, TRUE); git_libgit2_init(); } /** * Unload 'git2r' * * @param info Information about the DLL being unloaded */ void R_unload_git2r(DllInfo *info) { GIT2R_UNUSED(info); git_libgit2_shutdown(); } git2r/src/git2r_merge.c0000644000175000017500000005032613671131056014605 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_merge.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" int git2r_commit_create( git_oid *out, git_repository *repository, git_index *index, const char *message, git_signature *author, git_signature *committer); /** * Find a merge base between two commits * * @param one One of the commits * @param two The other commit * @return The commit of a merge base between 'one' and 'two' * or NULL if not found */ SEXP attribute_hidden git2r_merge_base( SEXP one, SEXP two) { int error, nprotect = 0; SEXP result = R_NilValue; SEXP repo_one, repo_two; SEXP sha; git_oid oid, oid_one, oid_two; git_commit *commit = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(one)) git2r_error(__func__, NULL, "'one'", git2r_err_commit_arg); if (git2r_arg_check_commit(two)) git2r_error(__func__, NULL, "'two'", git2r_err_commit_arg); repo_one = git2r_get_list_element(one, "repo"); repo_two = git2r_get_list_element(two, "repo"); if (git2r_arg_check_same_repo(repo_one, repo_two)) git2r_error(__func__, NULL, "'one' and 'two' not from same repository", NULL); repository = git2r_repository_open(repo_one); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(one, "sha"); error = git_oid_fromstr(&oid_one, CHAR(STRING_ELT(sha, 0))); if (error) goto cleanup; sha = git2r_get_list_element(two, "sha"); error = git_oid_fromstr(&oid_two, CHAR(STRING_ELT(sha, 0))); if (error) goto cleanup; error = git_merge_base(&oid, repository, &oid_one, &oid_two); if (error) { if (GIT_ENOTFOUND == error) error = GIT_OK; goto cleanup; } error = git_commit_lookup(&commit, repository, &oid); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init(commit, repo_one, result); cleanup: git_commit_free(commit); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Perform a fast-forward merge * * @param merge_result S3 class git_merge_result * @param merge_head The merge head to fast-forward merge * @param repository The repository * @param log_message First part of the one line long message in the reflog * @return 0 on success, or error code */ static int git2r_fast_forward_merge( SEXP merge_result, const git_annotated_commit *merge_head, git_repository *repository, const char *log_message) { int error; const git_oid *oid; char *buf = NULL; size_t buf_len; git_commit *commit = NULL; git_tree *tree = NULL; git_reference *reference = NULL; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; oid = git_annotated_commit_id(merge_head); error = git_commit_lookup(&commit, repository, oid); if (error) goto cleanup; error = git_commit_tree(&tree, commit); if (error) goto cleanup; opts.checkout_strategy = GIT_CHECKOUT_SAFE; error = git_checkout_tree(repository, (git_object*)tree, &opts); if (error) goto cleanup; error = git_repository_head(&reference, repository); if (error) { if (GIT_ENOTFOUND != error) goto cleanup; } buf_len = strlen(log_message) + sizeof(": Fast-forward"); buf = malloc(buf_len); if (!buf) { GIT2R_ERROR_SET_OOM(); error = GIT2R_ERROR_NOMEMORY; goto cleanup; } error = snprintf(buf, buf_len, "%s: Fast-forward", log_message); if (error < 0 || (size_t)error >= buf_len) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_OS, "Failed to snprintf log message."); error = GIT2R_ERROR_OS; goto cleanup; } if (GIT_ENOTFOUND == error) { error = git_reference_create( &reference, repository, "HEAD", git_commit_id(commit), 0, /* force */ buf); } else { git_reference *target_ref = NULL; error = git_reference_set_target( &target_ref, reference, git_commit_id(commit), buf); if (target_ref) git_reference_free(target_ref); } SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__fast_forward, Rf_ScalarLogical(1)); SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__conflicts, Rf_ScalarLogical(0)); SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__sha, Rf_ScalarString(NA_STRING)); cleanup: if (buf) free(buf); git_commit_free(commit); git_reference_free(reference); git_tree_free(tree); return error; } /** * Perform a normal merge * * @param merge_result S3 class git_merge_result * @param merge_heads The merge heads to merge * @param n The number of merge heads * @param repository The repository * @param message The commit message of the merge * @param merger Who is performing the merge * @param commit_on_success Commit merge commit, if one was created * @param merge_opts Merge options * @return 0 on success, or error code */ static int git2r_normal_merge( SEXP merge_result, const git_annotated_commit **merge_heads, size_t n, git_repository *repository, const char *message, git_signature *merger, int commit_on_success, const git_checkout_options *checkout_opts, const git_merge_options *merge_opts) { int error; git_commit *commit = NULL; git_index *index = NULL; SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__fast_forward, Rf_ScalarLogical(0)); error = git_merge( repository, merge_heads, n, merge_opts, checkout_opts); if (error) { if (error == GIT_EMERGECONFLICT) { SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__conflicts, Rf_ScalarLogical(1)); SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__sha, Rf_ScalarString(NA_STRING)); error = 0; } goto cleanup; } error = git_repository_index(&index, repository); if (error) goto cleanup; if (git_index_has_conflicts(index)) { SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__conflicts, Rf_ScalarLogical(1)); SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__sha, Rf_ScalarString(NA_STRING)); } else { SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__conflicts, Rf_ScalarLogical(0)); if (commit_on_success) { char sha[GIT_OID_HEXSZ + 1]; git_oid oid; error = git2r_commit_create( &oid, repository, index, message, merger, merger); if (error) goto cleanup; git_oid_fmt(sha, &oid); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__sha, Rf_mkString(sha)); } } cleanup: git_commit_free(commit); git_index_free(index); return error; } /** * @param merge_result S3 class git_merge_result * @repository The repository * @param merge_head The merge head to merge * @param n The number of merge heads * @param preference The merge preference option (None [0], No * Fast-Forward [1] or Only Fast-Forward [2]) * @param name The name of the merge in the reflog * @param merger Who is performing the merge * @param commit_on_success Commit merge commit, if one was created * during a normal merge * @param fail If a conflict occurs, exit immediately instead of attempting to * continue resolving conflicts. * @return 0 on success, or error code */ static int git2r_merge( SEXP merge_result, git_repository *repository, const git_annotated_commit **merge_heads, size_t n, git_merge_preference_t preference, const char *name, git_signature *merger, int commit_on_success, int fail) { int error; git_merge_analysis_t merge_analysis; git_merge_preference_t merge_preference; git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; merge_opts.rename_threshold = 50; merge_opts.target_limit = 200; if (fail) merge_opts.flags |= GIT_MERGE_FAIL_ON_CONFLICT; checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; error = git_merge_analysis( &merge_analysis, &merge_preference, repository, merge_heads, n); if (error) return error; if (merge_analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) { SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__up_to_date, Rf_ScalarLogical(1)); SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__fast_forward, Rf_ScalarLogical(0)); SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__conflicts, Rf_ScalarLogical(0)); SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__sha, Rf_ScalarString(NA_STRING)); return 0; } else { SET_VECTOR_ELT( merge_result, git2r_S3_item__git_merge_result__up_to_date, Rf_ScalarLogical(0)); } if (GIT_MERGE_PREFERENCE_NONE == preference) preference = merge_preference; switch (preference) { case GIT_MERGE_PREFERENCE_NONE: if (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) { if (1 != n) { GIT2R_ERROR_SET_STR( GIT2R_ERROR_NONE, "Unable to perform Fast-Forward merge " "with mith multiple merge heads."); return GIT_ERROR; } error = git2r_fast_forward_merge( merge_result, merge_heads[0], repository, name); } else if (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL) { error = git2r_normal_merge( merge_result, merge_heads, n, repository, name, merger, commit_on_success, &checkout_opts, &merge_opts); } break; case GIT_MERGE_PREFERENCE_NO_FASTFORWARD: if (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL) { error = git2r_normal_merge( merge_result, merge_heads, n, repository, name, merger, commit_on_success, &checkout_opts, &merge_opts); } break; case GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY: if (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) { if (1 != n) { GIT2R_ERROR_SET_STR( GIT2R_ERROR_NONE, "Unable to perform Fast-Forward merge " "with mith multiple merge heads."); return GIT_ERROR; } error = git2r_fast_forward_merge( merge_result, merge_heads[0], repository, name); } else { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, "Unable to perform Fast-Forward merge."); return GIT_ERROR; } break; default: GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, "Unknown merge option"); return GIT_ERROR; } return GIT_OK; } /** * Free each git_annotated_commit in merge_heads and free memory of * merge_heads. * * @param merge_heads The vector of git_merge_head. * @param count The number of merge heads. * @return void */ static void git2r_merge_heads_free( git_annotated_commit **merge_heads, size_t n) { if (merge_heads) { size_t i = 0; for (; i < n; i++) { if (merge_heads[i]) git_annotated_commit_free(merge_heads[i]); } free(merge_heads); } } /** * Merge branch into HEAD * * @param branch S3 class git_branch to merge into HEAD. * @param merger Who is performing the merge * @param commit_on_success Commit merge commit, if one was created * during a normal merge * @param fail If a conflict occurs, exit immediately instead of attempting to * continue resolving conflicts. * @return S3 class git_merge_result */ SEXP attribute_hidden git2r_merge_branch( SEXP branch, SEXP merger, SEXP commit_on_success, SEXP fail) { int error, nprotect = 0; SEXP result = R_NilValue; const char *name; char *buf = NULL; size_t buf_len; git_branch_t type; git_annotated_commit **merge_heads = NULL; git_reference *reference = NULL; git_repository *repository = NULL; git_signature *who = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); if (git2r_arg_check_logical(commit_on_success)) git2r_error(__func__, NULL, "'commit_on_success'", git2r_err_logical_arg); if (git2r_arg_check_signature(merger)) git2r_error(__func__, NULL, "'merger'", git2r_err_signature_arg); error = git2r_signature_from_arg(&who, merger); if (error) goto cleanup; repository = git2r_repository_open(git2r_get_list_element(branch, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; merge_heads = calloc(1, sizeof(git_annotated_commit*)); if (NULL == merge_heads) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_alloc_memory_buffer); goto cleanup; } error = git_annotated_commit_from_ref( &(merge_heads[0]), repository, reference); if (error) goto cleanup; buf_len = strlen(name) + sizeof("merge "); buf = malloc(buf_len); if (!buf) { GIT2R_ERROR_SET_OOM(); error = GIT2R_ERROR_NOMEMORY; goto cleanup; } error = snprintf(buf, buf_len, "merge %s", name); if (error < 0 || (size_t)error >= buf_len) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_OS, "Failed to snprintf log message."); error = GIT2R_ERROR_OS; goto cleanup; } PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_merge_result)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_merge_result)); error = git2r_merge( result, repository, (const git_annotated_commit **)merge_heads, 1, GIT_MERGE_PREFERENCE_NONE, buf, who, LOGICAL(commit_on_success)[0], LOGICAL(fail)[0]); cleanup: if (buf) free(buf); git_signature_free(who); git2r_merge_heads_free(merge_heads, 1); git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Create and populate a vector of git_annotated_commit objects from * the given fetch head data. * * @param out Pointer the vector of git_annotated_commit objects. * @param repository The repository. * @param fetch_heads List of S3 class git_fetch_head objects. * @param n Length of fetch_heads list. * @return 0 on success, or error code */ static int git2r_merge_heads_from_fetch_heads( git_annotated_commit ***merge_heads, git_repository *repository, SEXP fetch_heads, size_t n) { int error = GIT_OK; size_t i; *merge_heads = calloc(n, sizeof(git_annotated_commit*)); if (!(*merge_heads)) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_alloc_memory_buffer); return GIT_ERROR; } for (i = 0; i < n; i++) { git_oid oid; SEXP fh = VECTOR_ELT(fetch_heads, i); error = git_oid_fromstr( &oid, CHAR(STRING_ELT(git2r_get_list_element(fh, "sha"), 0))); if (error) goto cleanup; error = git_annotated_commit_from_fetchhead( &((*merge_heads)[i]), repository, CHAR(STRING_ELT(git2r_get_list_element(fh, "ref_name"), 0)), CHAR(STRING_ELT(git2r_get_list_element(fh, "remote_url"), 0)), &oid); if (error) goto cleanup; } cleanup: if (error) { if (*merge_heads) git2r_merge_heads_free(*merge_heads, n); *merge_heads = NULL; } return error; } /** * Merge the given fetch head data into HEAD * * @param fetch_heads List of S3 class git_fetch_head objects. * @param merger Who made the merge, if the merge is non-fast forward * merge that creates a merge commit. * @return List of git_annotated_commit objects. */ SEXP attribute_hidden git2r_merge_fetch_heads( SEXP fetch_heads, SEXP merger) { int error, nprotect = 0; size_t n = 0; SEXP result = R_NilValue; git_annotated_commit **merge_heads = NULL; git_repository *repository = NULL; git_signature *who = NULL; if (git2r_arg_check_fetch_heads(fetch_heads)) git2r_error(__func__, NULL, "'fetch_heads'", git2r_err_fetch_heads_arg); if (git2r_arg_check_signature(merger)) git2r_error(__func__, NULL, "'merger'", git2r_err_signature_arg); error = git2r_signature_from_arg(&who, merger); if (error) goto cleanup; n = LENGTH(fetch_heads); if (n) { SEXP repo = git2r_get_list_element(VECTOR_ELT(fetch_heads, 0), "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); } error = git2r_merge_heads_from_fetch_heads( &merge_heads, repository, fetch_heads, n); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_merge_result)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_merge_result)); error = git2r_merge( result, repository, (const git_annotated_commit **)merge_heads, n, GIT_MERGE_PREFERENCE_NONE, "pull", who, 1, /* Commit on success */ 0); /* Don't fail on conflicts */ cleanup: git_signature_free(who); git2r_merge_heads_free(merge_heads, n); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_stash.h0000644000175000017500000000222413316243475014634 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2018 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_stash_h #define INCLUDE_git2r_stash_h #include #include SEXP git2r_stash_apply(SEXP repo, SEXP index); SEXP git2r_stash_drop(SEXP repo, SEXP index); SEXP git2r_stash_pop(SEXP repo, SEXP index); SEXP git2r_stash_save(SEXP repo, SEXP message, SEXP index, SEXP untracked, SEXP ignored, SEXP stasher); SEXP git2r_stash_list(SEXP repo); #endif git2r/src/git2r_S3.h0000644000175000017500000001554513453170501014000 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2018 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_S3_h #define INCLUDE_git2r_S3_h #include #include extern const char *git2r_S3_class__git_blame; extern const char *git2r_S3_items__git_blame[]; enum { git2r_S3_item__git_blame__path, git2r_S3_item__git_blame__hunks, git2r_S3_item__git_blame__repo}; extern const char *git2r_S3_class__git_blame_hunk; extern const char *git2r_S3_items__git_blame_hunk[]; enum { git2r_S3_item__git_blame_hunk__lines_in_hunk, git2r_S3_item__git_blame_hunk__final_commit_id, git2r_S3_item__git_blame_hunk__final_start_line_number, git2r_S3_item__git_blame_hunk__final_signature, git2r_S3_item__git_blame_hunk__orig_commit_id, git2r_S3_item__git_blame_hunk__orig_start_line_number, git2r_S3_item__git_blame_hunk__orig_signature, git2r_S3_item__git_blame_hunk__orig_path, git2r_S3_item__git_blame_hunk__boundary, git2r_S3_item__git_blame_hunk__repo}; extern const char *git2r_S3_class__git_blob; extern const char *git2r_S3_items__git_blob[]; enum { git2r_S3_item__git_blob__sha, git2r_S3_item__git_blob__repo}; extern const char *git2r_S3_class__git_branch; extern const char *git2r_S3_items__git_branch[]; enum { git2r_S3_item__git_branch__name, git2r_S3_item__git_branch__type, git2r_S3_item__git_branch__repo}; extern const char *git2r_S3_class__git_commit; extern const char *git2r_S3_items__git_commit[]; enum { git2r_S3_item__git_commit__sha, git2r_S3_item__git_commit__author, git2r_S3_item__git_commit__committer, git2r_S3_item__git_commit__summary, git2r_S3_item__git_commit__message, git2r_S3_item__git_commit__repo}; extern const char *git2r_S3_class__git_diff; extern const char *git2r_S3_items__git_diff[]; enum { git2r_S3_item__git_diff__old, git2r_S3_item__git_diff__new, git2r_S3_item__git_diff__files}; extern const char *git2r_S3_class__git_diff_file; extern const char *git2r_S3_items__git_diff_file[]; enum { git2r_S3_item__git_diff_file__old_file, git2r_S3_item__git_diff_file__new_file, git2r_S3_item__git_diff_file__hunks}; extern const char *git2r_S3_class__git_diff_hunk; extern const char *git2r_S3_items__git_diff_hunk[]; enum { git2r_S3_item__git_diff_hunk__old_start, git2r_S3_item__git_diff_hunk__old_lines, git2r_S3_item__git_diff_hunk__new_start, git2r_S3_item__git_diff_hunk__new_lines, git2r_S3_item__git_diff_hunk__header, git2r_S3_item__git_diff_hunk__lines}; extern const char *git2r_S3_class__git_diff_line; extern const char *git2r_S3_items__git_diff_line[]; enum { git2r_S3_item__git_diff_line__origin, git2r_S3_item__git_diff_line__old_lineno, git2r_S3_item__git_diff_line__new_lineno, git2r_S3_item__git_diff_line__num_lines, git2r_S3_item__git_diff_line__content}; extern const char *git2r_S3_class__git_fetch_head; extern const char *git2r_S3_items__git_fetch_head[]; enum { git2r_S3_item__git_fetch_head__ref_name, git2r_S3_item__git_fetch_head__remote_url, git2r_S3_item__git_fetch_head__sha, git2r_S3_item__git_fetch_head__is_merge, git2r_S3_item__git_fetch_head__repo}; extern const char *git2r_S3_class__git_merge_result; extern const char *git2r_S3_items__git_merge_result[]; enum { git2r_S3_item__git_merge_result__up_to_date, git2r_S3_item__git_merge_result__fast_forward, git2r_S3_item__git_merge_result__conflicts, git2r_S3_item__git_merge_result__sha}; extern const char *git2r_S3_class__git_note; extern const char *git2r_S3_items__git_note[]; enum { git2r_S3_item__git_note__sha, git2r_S3_item__git_note__annotated, git2r_S3_item__git_note__message, git2r_S3_item__git_note__refname, git2r_S3_item__git_note__repo}; extern const char *git2r_S3_class__git_reference; extern const char *git2r_S3_items__git_reference[]; enum { git2r_S3_item__git_reference__name, git2r_S3_item__git_reference__type, git2r_S3_item__git_reference__sha, git2r_S3_item__git_reference__target, git2r_S3_item__git_reference__shorthand, git2r_S3_item__git_reference__repo}; extern const char *git2r_S3_class__git_reflog_entry; extern const char *git2r_S3_items__git_reflog_entry[]; enum { git2r_S3_item__git_reflog_entry__sha, git2r_S3_item__git_reflog_entry__message, git2r_S3_item__git_reflog_entry__index, git2r_S3_item__git_reflog_entry__committer, git2r_S3_item__git_reflog_entry__refname, git2r_S3_item__git_reflog_entry__repo}; extern const char *git2r_S3_class__git_repository; extern const char *git2r_S3_items__git_repository[]; enum { git2r_S3_item__git_repository__path}; extern const char *git2r_S3_class__git_signature; extern const char *git2r_S3_items__git_signature[]; enum { git2r_S3_item__git_signature__name, git2r_S3_item__git_signature__email, git2r_S3_item__git_signature__when}; extern const char *git2r_S3_class__git_tag; extern const char *git2r_S3_items__git_tag[]; enum { git2r_S3_item__git_tag__sha, git2r_S3_item__git_tag__message, git2r_S3_item__git_tag__name, git2r_S3_item__git_tag__tagger, git2r_S3_item__git_tag__target, git2r_S3_item__git_tag__repo}; extern const char *git2r_S3_class__git_time; extern const char *git2r_S3_items__git_time[]; enum { git2r_S3_item__git_time__time, git2r_S3_item__git_time__offset}; extern const char *git2r_S3_class__git_transfer_progress; extern const char *git2r_S3_items__git_transfer_progress[]; enum { git2r_S3_item__git_transfer_progress__total_objects, git2r_S3_item__git_transfer_progress__indexed_objects, git2r_S3_item__git_transfer_progress__received_objects, git2r_S3_item__git_transfer_progress__local_objects, git2r_S3_item__git_transfer_progress__total_deltas, git2r_S3_item__git_transfer_progress__indexed_deltas, git2r_S3_item__git_transfer_progress__received_bytes}; extern const char *git2r_S3_class__git_tree; extern const char *git2r_S3_items__git_tree[]; enum { git2r_S3_item__git_tree__sha, git2r_S3_item__git_tree__filemode, git2r_S3_item__git_tree__type, git2r_S3_item__git_tree__id, git2r_S3_item__git_tree__name, git2r_S3_item__git_tree__repo}; SEXP git2r_get_list_element(SEXP list, const char *str); #endif git2r/src/git2r_graph.h0000644000175000017500000000174013137360640014610 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_graph_h #define INCLUDE_git2r_graph_h #include #include SEXP git2r_graph_ahead_behind(SEXP local, SEXP upstream); SEXP git2r_graph_descendant_of(SEXP commit, SEXP ancestor); #endif git2r/src/git2r_arg.c0000644000175000017500000003010313671131056014246 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_S3.h" /** * Check blob argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_blob( SEXP arg) { if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_blob")) return -1; if (git2r_arg_check_sha(git2r_get_list_element(arg, "sha"))) return -1; return 0; } /** * Check branch argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_branch( SEXP arg) { SEXP slot; if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_branch")) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "name"))) return -1; slot = git2r_get_list_element(arg, "type"); if (git2r_arg_check_integer(slot)) return -1; switch (INTEGER(slot)[0]) { case GIT_BRANCH_LOCAL: case GIT_BRANCH_REMOTE: break; default: return -1; } return 0; } /** * Check commit argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_commit( SEXP arg) { if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_commit")) return -1; if (git2r_arg_check_sha(git2r_get_list_element(arg, "sha"))) return -1; return 0; } /** * Check commit or stash argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_commit_stash( SEXP arg) { if (!Rf_isNewList(arg)) return -1; if (!Rf_inherits(arg, "git_commit") && !Rf_inherits(arg, "git_stash")) return -1; if (git2r_arg_check_sha(git2r_get_list_element(arg, "sha"))) return -1; return 0; } /** * Check credentials argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_credentials( SEXP arg) { /* It's ok if the credentials is R_NilValue */ if (Rf_isNull(arg)) return 0; if (!Rf_isNewList(arg)) return -1; if (Rf_inherits(arg, "cred_env")) { /* Check username and password */ if (git2r_arg_check_string(git2r_get_list_element(arg, "username"))) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "password"))) return -1; } else if (Rf_inherits(arg, "cred_token")) { /* Check token */ if (git2r_arg_check_string(git2r_get_list_element(arg, "token"))) return -1; } else if (Rf_inherits(arg, "cred_user_pass")) { /* Check username and password */ if (git2r_arg_check_string(git2r_get_list_element(arg, "username"))) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "password"))) return -1; } else if (Rf_inherits(arg, "cred_ssh_key")) { SEXP passphrase; /* Check public and private key */ if (git2r_arg_check_string(git2r_get_list_element(arg, "publickey"))) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "privatekey"))) return -1; /* Check that passphrase is a character vector */ passphrase = git2r_get_list_element(arg, "passphrase"); if (git2r_arg_check_string_vec(passphrase)) return -1; /* Check that length of passphrase < 2, i.e. it's either * character(0) or some "passphrase" */ switch (Rf_length(passphrase)) { case 0: break; case 1: if (NA_STRING == STRING_ELT(passphrase, 0)) return -1; break; default: return -1; } } else { return -1; } return 0; } /** * Check fetch_heads argument * * It's OK: * - A list with S3 class git_fetch_head objects * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_fetch_heads( SEXP arg) { const char *repo = NULL; size_t i,n; if (Rf_isNull(arg) || VECSXP != TYPEOF(arg)) return -1; /* Check that the repository paths are identical for each item */ n = Rf_length(arg); for (i = 0; i < n; i++) { SEXP path; SEXP item = VECTOR_ELT(arg, i); if (!Rf_isNewList(item) || !Rf_inherits(item, "git_fetch_head")) return -1; path = git2r_get_list_element(git2r_get_list_element(item, "repo"), "path"); if (git2r_arg_check_string(path)) return -1; if (0 == i) repo = CHAR(STRING_ELT(path, 0)); else if (0 != strcmp(repo, CHAR(STRING_ELT(path, 0)))) return -1; } return 0; } /** * Check filename argument * * It's OK: * - R_NilValue * - Zero length character vector * - character vector of length one with strlen(value) > 0 * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_filename( SEXP arg) { if (Rf_isNull(arg)) return 0; if (!Rf_isString(arg)) return -1; switch (Rf_length(arg)) { case 0: break; case 1: if (NA_STRING == STRING_ELT(arg, 0)) return -1; if (0 == strlen(CHAR(STRING_ELT(arg, 0)))) return -1; break; default: return -1; } return 0; } /** * Check sha argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_sha( SEXP arg) { size_t len; if (git2r_arg_check_string(arg)) return -1; len = LENGTH(STRING_ELT(arg, 0)); if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ) return -1; return 0; } /** * Check integer argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_integer( SEXP arg) { if (!Rf_isInteger(arg) || 1 != Rf_length(arg) || NA_INTEGER == INTEGER(arg)[0]) return -1; return 0; } /** * Check integer argument and that arg is greater than or equal to 0. * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_integer_gte_zero( SEXP arg) { if (git2r_arg_check_integer(arg)) return -1; if (0 > INTEGER(arg)[0]) return -1; return 0; } /** * Check list argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_list( SEXP arg) { if (!Rf_isNewList(arg)) return -1; return 0; } /** * Check logical argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_logical( SEXP arg) { if (!Rf_isLogical(arg) || 1 != Rf_length(arg) || NA_LOGICAL == LOGICAL(arg)[0]) return -1; return 0; } /** * Check note argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_note( SEXP arg) { if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_note")) return -1; if (git2r_arg_check_sha(git2r_get_list_element(arg, "sha"))) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "refname"))) return -1; return 0; } /** * Check real argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_real( SEXP arg) { if (!Rf_isReal(arg) || 1 != Rf_length(arg) || !R_finite(REAL(arg)[0])) return -1; return 0; } /** * Check repository argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_repository( SEXP arg) { if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_repository")) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "path"))) return -1; return 0; } /** * Check if the two repositories have the same path * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_same_repo( SEXP arg1, SEXP arg2) { SEXP path1, path2; if (git2r_arg_check_repository(arg1) || git2r_arg_check_repository(arg2)) return -1; path1 = git2r_get_list_element(arg1, "path"); path2 = git2r_get_list_element(arg2, "path"); if (strcmp(CHAR(STRING_ELT(path1, 0)), CHAR(STRING_ELT(path2, 0)))) return -1; return 0; } /** * Check signature argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_signature( SEXP arg) { SEXP when; if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_signature")) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "name"))) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "email"))) return -1; when = git2r_get_list_element(arg, "when"); if (git2r_arg_check_real(git2r_get_list_element(when, "time"))) return -1; if (git2r_arg_check_real(git2r_get_list_element(when, "offset"))) return -1; return 0; } /** * Check string argument * * Compared to git2r_arg_check_string_vec, also checks that length of vector * is one and non-NA. * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_string( SEXP arg) { if (git2r_arg_check_string_vec(arg) < 0) return -1; if (1 != Rf_length(arg) || NA_STRING == STRING_ELT(arg, 0)) return -1; return 0; } /** * Check string vector argument * * Compared to git2r_arg_check_string, only checks that argument is non-null * and string. Use git2r_arg_check_string to check scalar string. * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_string_vec( SEXP arg) { if (!Rf_isString(arg)) return -1; return 0; } /** * Copy SEXP character vector to git_strarray * * @param dst Destination of data * @src src Source of data. Skips NA strings. * @return 0 if OK, else error code */ int attribute_hidden git2r_copy_string_vec( git_strarray *dst, SEXP src) { size_t i, len; /* Count number of non NA values */ len = Rf_length(src); for (i = 0; i < len; i++) if (NA_STRING != STRING_ELT(src, i)) dst->count++; /* We are done if no non-NA values */ if (!dst->count) return 0; /* Allocate the strings in dst */ dst->strings = malloc(dst->count * sizeof(char*)); if (!dst->strings) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_alloc_memory_buffer); return GIT_ERROR; } /* Copy strings to dst */ for (i = 0; i < dst->count; i++) if (NA_STRING != STRING_ELT(src, i)) dst->strings[i] = (char *)CHAR(STRING_ELT(src, i)); return 0; } /** * Check tag argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_tag( SEXP arg) { if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_tag")) return -1; if (git2r_arg_check_string(git2r_get_list_element(arg, "target"))) return -1; return 0; } /** * Check tree argument * * @param arg the arg to check * @return 0 if OK, else -1 */ int attribute_hidden git2r_arg_check_tree( SEXP arg) { if (!Rf_isNewList(arg) || !Rf_inherits(arg, "git_tree")) return -1; if (git2r_arg_check_sha(git2r_get_list_element(arg, "sha"))) return -1; return 0; } git2r/src/git2r_error.c0000644000175000017500000001131113671131056014626 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include /** * Error messages */ const char git2r_err_alloc_memory_buffer[] = "Unable to allocate memory buffer"; const char git2r_err_branch_not_local[] = "'branch' is not local"; const char git2r_err_branch_not_remote[] = "'branch' is not remote"; const char git2r_err_checkout_tree[] = "Expected commit, tag or tree"; const char git2r_err_invalid_refname[] = "Invalid reference name"; const char git2r_err_invalid_remote[] = "Invalid remote name"; const char git2r_err_invalid_repository[] = "Invalid repository"; const char git2r_err_nothing_added_to_commit[] = "Nothing added to commit"; const char git2r_err_object_type[] = "Unexpected object type."; const char git2r_err_reference[] = "Unexpected reference type"; const char git2r_err_repo_init[] = "Unable to init repository"; const char git2r_err_revparse_not_found[] = "Requested object could not be found"; const char git2r_err_revparse_single[] = "Expected commit, tag or tree"; const char git2r_err_ssl_cert_locations[] = "Either 'filename' or 'path' may be 'NULL', but not both"; const char git2r_err_unexpected_config_level[] = "Unexpected config level"; const char git2r_err_unable_to_authenticate[] = "Unable to authenticate with supplied credentials"; /** * Error messages specific to argument checking */ const char git2r_err_blob_arg[] = "must be an S3 class git_blob"; const char git2r_err_branch_arg[] = "must be an S3 class git_branch"; const char git2r_err_commit_arg[] = "must be an S3 class git_commit"; const char git2r_err_commit_stash_arg[] = "must be an S3 class git_commit or an S3 class git_stash"; const char git2r_err_credentials_arg[] = "must be an S3 class with credentials"; const char git2r_err_diff_arg[] = "Invalid diff parameters"; const char git2r_err_fetch_heads_arg[] = "must be a list of S3 git_fetch_head objects"; const char git2r_err_filename_arg[] = "must be either 1) NULL, or 2) a character vector of length 0 or 3) a character vector of length 1 and nchar > 0"; const char git2r_err_sha_arg[] = "must be a sha value"; const char git2r_err_integer_arg[] = "must be an integer vector of length one with non NA value"; const char git2r_err_integer_gte_zero_arg[] = "must be an integer vector of length one with value greater than or equal to zero"; const char git2r_err_list_arg[] = "must be a list"; const char git2r_err_logical_arg[] = "must be logical vector of length one with non NA value"; const char git2r_err_note_arg[] = "must be an S3 class git_note"; const char git2r_err_signature_arg[] = "must be an S3 class git_signature"; const char git2r_err_string_arg[] = "must be a character vector of length one with non NA value"; const char git2r_err_string_vec_arg[] = "must be a character vector"; const char git2r_err_tag_arg[] = "must be an S3 class git_tag"; const char git2r_err_tree_arg[] = "must be an S3 class git_tree"; /** * Raise error * * @param func_name The name of the function that raise the error. * @param err Optional error argument from libgit2 with the git_error * object that was last generated. * @param msg1 Optional text argument with error message, used if * 'err' is NULL. * @param msg2 Optional text argument, e.g. used during argument * checking to pass the error message to the variable name in 'msg1'. */ void attribute_hidden git2r_error( const char *func_name, const git_error *err, const char *msg1, const char *msg2) { if (func_name && err && err->message) Rf_error("Error in '%s': %s\n", func_name, err->message); else if (func_name && msg1 && msg2) Rf_error("Error in '%s': %s %s\n", func_name, msg1, msg2); else if (func_name && msg1) Rf_error("Error in '%s': %s\n", func_name, msg1); else if (func_name) Rf_error("Error in '%s'\n", func_name); else Rf_error("Unexpected error. Please report at" " https://github.com/ropensci/git2r/issues\n"); } git2r/src/git2r_cred.h0000644000175000017500000000202413647041736014430 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_cred_h #define INCLUDE_git2r_cred_h #include #include "git2r_deprecated.h" int git2r_cred_acquire_cb( GIT2R_CREDENTIAL **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload); #endif git2r/src/git2r_object.c0000644000175000017500000000716713671131056014761 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_blob.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_tag.h" #include "git2r_tree.h" /** * Lookup an object in a repository * * @param repo S3 class git_repository * @param sha 4 to 40 char hexadecimal string * @return S3 object with lookup */ SEXP attribute_hidden git2r_object_lookup( SEXP repo, SEXP sha) { int error, nprotect = 0; size_t len; SEXP result = R_NilValue; git_object *object = NULL; git_oid oid; git_repository *repository = NULL; if (git2r_arg_check_sha(sha)) git2r_error(__func__, NULL, "'sha'", git2r_err_sha_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); len = LENGTH(STRING_ELT(sha, 0)); if (GIT_OID_HEXSZ == len) { git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); error = git_object_lookup(&object, repository, &oid, GIT2R_OBJECT_ANY); if (error) goto cleanup; } else { git_oid_fromstrn(&oid, CHAR(STRING_ELT(sha, 0)), len); error = git_object_lookup_prefix(&object, repository, &oid, len, GIT2R_OBJECT_ANY); if (error) goto cleanup; } switch (git_object_type(object)) { case GIT2R_OBJECT_COMMIT: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init((git_commit*)object, repo, result); break; case GIT2R_OBJECT_TREE: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_tree)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tree)); git2r_tree_init((git_tree*)object, repo, result); break; case GIT2R_OBJECT_BLOB: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_blob)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_blob)); git2r_blob_init((git_blob*)object, repo, result); break; case GIT2R_OBJECT_TAG: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_tag)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tag)); git2r_tag_init((git_tag*)object, repo, result); break; default: git2r_error(__func__, NULL, git2r_err_object_type, NULL); } cleanup: git_object_free(object); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/Makevars.win0000644000175000017500000000102014145513431014504 0ustar nileshnileshVERSION = 1.3.0 RWINLIB = ../windows/libgit2-${VERSION} PKG_CPPFLAGS = -I${RWINLIB}/include \ -DR_NO_REMAP -DSTRICT_R_HEADERS \ -DGIT2R_HAVE_BUF_DISPOSE \ -DGIT2R_HAVE_OBJECT_ANY \ -DGIT2R_LIBGIT2_V0_99_0_RENAMES PKG_LIBS = -L${RWINLIB}/lib${R_ARCH} \ -lgit2 -lssh2 -lz -lssl -lcrypto \ -lwinhttp -lws2_32 -lcrypt32 -lole32 -lrpcrt4 all: clean winlibs winlibs: "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" "../tools/winlibs.R" $(VERSION) clean: rm -f $(SHLIB) $(OBJECTS) .PHONY: all winlibs clean git2r/src/git2r_cred.c0000644000175000017500000003336513671131056014427 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #ifdef WIN32 #include #include # ifndef WC_ERR_INVALID_CHARS # define WC_ERR_INVALID_CHARS 0x80 # endif #endif #include #include #include #include "git2r_arg.h" #include "git2r_cred.h" #include "git2r_deprecated.h" #include "git2r_S3.h" #include "git2r_transfer.h" #define GIT2R_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) /** * Read an environtmental variable. * * @param out Pointer where to store the environmental variable. * @param obj The S3 object with name of the environmental * variable to read. * @param slot The slot in the S3 object with the name of the * environmental variable. * @return 0 on success, else -1. */ static int git2r_getenv( char **out, SEXP obj, const char *slot) { const char *buf; /* Read value of the environment variable */ buf = getenv(CHAR(STRING_ELT(git2r_get_list_element(obj, slot), 0))); if (!buf || !strlen(buf)) return -1; *out = malloc(strlen(buf)+1); if (!*out) return -1; strcpy(*out, buf); return 0; } /** * Create credential object from S3 class 'cred_ssh_key'. * * @param cred The newly created credential object. * @param user_from_url The username that was embedded in a "user@host" * @param allowed_types A bitmask stating which cred types are OK to return. * @param credentials The S3 class object with credentials. * @return 0 on success, else -1. */ static int git2r_cred_ssh_key( GIT2R_CREDENTIAL **cred, const char *username_from_url, unsigned int allowed_types, SEXP credentials) { if (GIT2R_CREDENTIAL_SSH_KEY & allowed_types) { SEXP elem; const char *publickey; const char *privatekey = NULL; const char *passphrase = NULL; publickey = CHAR(STRING_ELT(git2r_get_list_element(credentials, "publickey"), 0)); privatekey = CHAR(STRING_ELT(git2r_get_list_element(credentials, "privatekey"), 0)); elem = git2r_get_list_element(credentials, "passphrase"); if (Rf_length(elem) && (NA_STRING != STRING_ELT(elem, 0))) passphrase = CHAR(STRING_ELT(elem, 0)); if (GIT2R_CREDENTIAL_SSH_KEY_NEW( cred, username_from_url, publickey, privatekey, passphrase)) return -1; return 0; } return -1; } /** * Create credential object from S3 class 'cred_env'. * * @param cred The newly created credential object. * @param allowed_types A bitmask stating which cred types are OK to return. * @param credentials The S3 class object with credentials. * @return 0 on success, else -1. */ static int git2r_cred_env( GIT2R_CREDENTIAL **cred, unsigned int allowed_types, SEXP credentials) { if (GIT2R_CREDENTIAL_USERPASS_PLAINTEXT & allowed_types) { int error; char *username = NULL; char *password = NULL; /* Read value of the username environment variable */ error = git2r_getenv(&username, credentials, "username"); if (error) goto cleanup; /* Read value of the password environment variable */ error = git2r_getenv(&password, credentials, "password"); if (error) goto cleanup; error = GIT2R_CREDENTIAL_USERPASS_PLAINTEXT_NEW( cred, username, password); cleanup: free(username); free(password); if (error) return -1; return 0; } return -1; } /** * Create credential object from S3 class 'cred_token'. * * @param cred The newly created credential object. * @param allowed_types A bitmask stating which cred types are OK to return. * @param credentials The S3 class object with credentials. * @return 0 on success, else -1. */ static int git2r_cred_token( GIT2R_CREDENTIAL **cred, unsigned int allowed_types, SEXP credentials) { if (GIT2R_CREDENTIAL_USERPASS_PLAINTEXT & allowed_types) { int error; char *token = NULL; /* Read value of the personal access token from the * environment variable */ error = git2r_getenv(&token, credentials, "token"); if (error) goto cleanup; error = GIT2R_CREDENTIAL_USERPASS_PLAINTEXT_NEW(cred, " ", token); cleanup: free(token); if (error) return -1; return 0; } return -1; } /** * Create credential object from S3 class 'cred_user_pass'. * * @param cred The newly created credential object. * @param allowed_types A bitmask stating which cred types are OK to return. * @param credentials The S3 class object with credentials. * @return 0 on success, else -1. */ static int git2r_cred_user_pass( GIT2R_CREDENTIAL **cred, unsigned int allowed_types, SEXP credentials) { if (GIT2R_CREDENTIAL_USERPASS_PLAINTEXT & allowed_types) { const char *username; const char *password; username = CHAR(STRING_ELT(git2r_get_list_element(credentials, "username"), 0)); password = CHAR(STRING_ELT(git2r_get_list_element(credentials, "password"), 0)); if (GIT2R_CREDENTIAL_USERPASS_PLAINTEXT_NEW(cred, username, password)) return -1; return 0; } return -1; } /* static int git2r_join_str(char** out, const char *str_a, const char *str_b) */ /* { */ /* int len_a, len_b; */ /* if (!str_a || !str_b) */ /* return -1; */ /* len_a = strlen(str_a); */ /* len_b = strlen(str_b); */ /* *out = malloc(len_a + len_b + 1); */ /* if (!*out) */ /* return -1; */ /* if (len_a) */ /* memcpy(*out, str_a, len_a); */ /* if (len_b) */ /* memcpy(*out + len_a, str_b, len_b); */ /* (*out)[len_a + len_b] = '\0'; */ /* return 0; */ /* } */ /* static int git2r_file_exists(const char *path) */ /* { */ /* #ifdef WIN32 */ /* struct _stati64 sb; */ /* return _stati64(path, &sb) == 0; */ /* #else */ /* struct stat sb; */ /* return stat(path, &sb) == 0; */ /* #endif */ /* } */ /* #ifdef WIN32 */ /* static int git2r_expand_key(char** out, const wchar_t *key, const char *ext) */ /* { */ /* wchar_t wbuf[MAX_PATH]; */ /* char *buf_utf8 = NULL; */ /* DWORD len_wbuf; */ /* int len_utf8; */ /* *out = NULL; */ /* if (!key || !ext) */ /* goto on_error; */ /* /\* Expands environment-variable strings and replaces them with the */ /* * values defined for the current user. *\/ */ /* len_wbuf = ExpandEnvironmentStringsW(key, wbuf, GIT2R_ARRAY_SIZE(wbuf)); */ /* if (!len_wbuf || len_wbuf > GIT2R_ARRAY_SIZE(wbuf)) */ /* goto on_error; */ /* /\* Map wide character string to a new utf8 character string. *\/ */ /* len_utf8 = WideCharToMultiByte( */ /* CP_UTF8, WC_ERR_INVALID_CHARS, wbuf,-1, NULL, 0, NULL, NULL); */ /* if (!len_utf8) */ /* goto on_error; */ /* buf_utf8 = malloc(len_utf8); */ /* if (!buf_utf8) */ /* goto on_error; */ /* len_utf8 = WideCharToMultiByte( */ /* CP_UTF8, WC_ERR_INVALID_CHARS, wbuf, -1, buf_utf8, len_utf8, NULL, NULL); */ /* if (!len_utf8) */ /* goto on_error; */ /* if (git2r_join_str(out, buf_utf8, ext)) */ /* goto on_error; */ /* free(buf_utf8); */ /* if (git2r_file_exists(*out)) */ /* return 0; */ /* on_error: */ /* free(buf_utf8); */ /* free(*out); */ /* *out = NULL; */ /* return -1; */ /* } */ /* #else */ /* static int git2r_expand_key(char** out, const char *key, const char *ext) */ /* { */ /* const char *buf = R_ExpandFileName(key); */ /* *out = NULL; */ /* if (!key || !ext) */ /* return -1; */ /* if (git2r_join_str(out, buf, ext)) */ /* return -1; */ /* if (git2r_file_exists(*out)) */ /* return 0; */ /* free(*out); */ /* *out = NULL; */ /* return -1; */ /* } */ /* #endif */ /* static int git2r_ssh_key_needs_passphrase(const char *key) */ /* { */ /* size_t i; */ /* FILE* file = fopen(key, "r"); */ /* if (file == NULL) */ /* return 0; */ /* /\* Look for "ENCRYPTED" in the first three lines. *\/ */ /* for (i = 0; i < 3; i++) { */ /* char str[128] = {0}; */ /* if (fgets(str, GIT2R_ARRAY_SIZE(str), file) != NULL) { */ /* if (strstr(str, "ENCRYPTED") != NULL) { */ /* fclose(file); */ /* return 1; */ /* } */ /* } else { */ /* fclose(file); */ /* return 0; */ /* } */ /* } */ /* fclose(file); */ /* return 0; */ /* } */ /* static int git2r_cred_default_ssh_key( */ /* GIT2R_CREDENTIAL **cred, */ /* const char *username_from_url) */ /* { */ /* #ifdef WIN32 */ /* static const wchar_t *key_patterns[3] = */ /* {L"%HOME%\\.ssh\\id_rsa", */ /* L"%HOMEDRIVE%%HOMEPATH%\\.ssh\\id_rsa", */ /* L"%USERPROFILE%\\.ssh\\id_rsa"}; */ /* #else */ /* static const char *key_patterns[1] = {"~/.ssh/id_rsa"}; */ /* #endif */ /* size_t i; */ /* int error = 1; */ /* /\* Find key. *\/ */ /* for (i = 0; i < GIT2R_ARRAY_SIZE(key_patterns); i++) { */ /* char *private_key = NULL; */ /* char *public_key = NULL; */ /* const char *passphrase = NULL; */ /* SEXP pass, askpass, call; */ /* int nprotect = 0; */ /* /\* Expand key pattern and check if files exists. *\/ */ /* if (git2r_expand_key(&private_key, key_patterns[i], "") || */ /* git2r_expand_key(&public_key, key_patterns[i], ".pub")) */ /* { */ /* free(private_key); */ /* free(public_key); */ /* continue; */ /* } */ /* if (git2r_ssh_key_needs_passphrase(private_key)) { */ /* /\* Use the R package getPass to ask for the passphrase. *\/ */ /* PROTECT(pass = Rf_eval(Rf_lang2(Rf_install("getNamespace"), */ /* Rf_ScalarString(Rf_mkChar("getPass"))), */ /* R_GlobalEnv)); */ /* nprotect++; */ /* PROTECT(call = Rf_lcons( */ /* Rf_findFun(Rf_install("getPass"), pass), */ /* Rf_lcons(Rf_mkString("Enter passphrase: "), */ /* R_NilValue))); */ /* nprotect++; */ /* PROTECT(askpass = Rf_eval(call, pass)); */ /* nprotect++; */ /* if (git2r_arg_check_string(askpass) == 0) */ /* passphrase = CHAR(STRING_ELT(askpass, 0)); */ /* } */ /* error = git_cred_ssh_key_new( */ /* cred, */ /* username_from_url, */ /* public_key, */ /* private_key, */ /* passphrase); */ /* /\* Cleanup. *\/ */ /* free(private_key); */ /* free(public_key); */ /* if (nprotect) */ /* UNPROTECT(nprotect); */ /* break; */ /* } */ /* if (error) */ /* return -1; */ /* return 0; */ /* } */ /** * Callback if the remote host requires authentication in order to * connect to it * * @param cred The newly created credential object. * @param url The resource for which we are demanding a credential. * @param user_from_url The username that was embedded in a "user@host" * remote url, or NULL if not included. * @param allowed_types A bitmask stating which cred types are OK to return. * @param payload The payload provided when specifying this callback. * @return 0 on success, else -1. */ int attribute_hidden git2r_cred_acquire_cb( GIT2R_CREDENTIAL **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload) { git2r_transfer_data *td; SEXP credentials; GIT2R_UNUSED(url); if (!payload) return -1; td = (git2r_transfer_data*)payload; credentials = td->credentials; if (Rf_isNull(credentials)) { if (GIT2R_CREDENTIAL_SSH_KEY & allowed_types) { if (td->use_ssh_agent) { /* Try to get credentials from the ssh-agent. */ td->use_ssh_agent = 0; if (GIT2R_CREDENTIAL_SSH_KEY_FROM_AGENT(cred, username_from_url) == 0) return 0; } /* if (td->use_ssh_key) { */ /* /\* Try to get credentials from default ssh key. *\/ */ /* td->use_ssh_key = 0; */ /* if (git2r_cred_default_ssh_key(cred, username_from_url) == 0) */ /* return 0; */ /* } */ } return -1; } if (Rf_inherits(credentials, "cred_ssh_key")) { return git2r_cred_ssh_key( cred, username_from_url, allowed_types, credentials); } else if (Rf_inherits(credentials, "cred_env")) { return git2r_cred_env(cred, allowed_types, credentials); } else if (Rf_inherits(credentials, "cred_token")) { return git2r_cred_token(cred, allowed_types, credentials); } else if (Rf_inherits(credentials, "cred_user_pass")) { return git2r_cred_user_pass(cred, allowed_types, credentials); } return -1; } git2r/src/git2r_remote.c0000644000175000017500000003511313671131056014776 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_cred.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_remote.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" #include "git2r_transfer.h" /** * Add a remote with the default fetch refspec to the repository's * configuration. * * @param repo S3 class git_repository * @param name The name of the remote * @param url The url of the remote * @return R_NilValue */ SEXP attribute_hidden git2r_remote_add( SEXP repo, SEXP name, SEXP url) { int error; git_repository *repository = NULL; git_remote *remote = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_string(url)) git2r_error(__func__, NULL, "'url'", git2r_err_string_arg); if (!git_remote_is_valid_name(CHAR(STRING_ELT(name, 0)))) git2r_error(__func__, NULL, git2r_err_invalid_remote, NULL); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_remote_create( &remote, repository, CHAR(STRING_ELT(name, 0)), CHAR(STRING_ELT(url, 0))); git_remote_free(remote); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Each time a reference is updated locally, this function will be * called with information about it. * * Based on the libgit2 network/fetch.c example. * * @param refname The name of the remote * @param a The previous position of branch * @param b The new position of branch * @param payload Callback data. * @return 0 */ static int git2r_update_tips_cb( const char *refname, const git_oid *a, const git_oid *b, void *payload) { git2r_transfer_data *cb_data = (git2r_transfer_data*)payload; if (cb_data->verbose) { char b_str[GIT_OID_HEXSZ + 1]; git_oid_fmt(b_str, b); b_str[GIT_OID_HEXSZ] = '\0'; if (GIT2R_OID_IS_ZERO(a)) { Rprintf("[new] %.20s %s\n", b_str, refname); } else { char a_str[GIT_OID_HEXSZ + 1]; git_oid_fmt(a_str, a); a_str[GIT_OID_HEXSZ] = '\0'; Rprintf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname); } } return 0; } /** * Fetch new data and update tips * * @param repo S3 class git_repository * @param name The name of the remote to fetch from * @param credentials The credentials for remote repository access. * @param msg The one line long message to be appended to the reflog * @param verbose Print information each time a reference is updated locally. * @param refspecs The refspecs to use for this fetch. Pass R_NilValue * to use the base refspecs. * @return R_NilValue */ SEXP attribute_hidden git2r_remote_fetch( SEXP repo, SEXP name, SEXP credentials, SEXP msg, SEXP verbose, SEXP refspecs) { int error, nprotect = 0; SEXP result = R_NilValue; const GIT2R_INDEXER_PROGRESS *stats; git_remote *remote = NULL; git_repository *repository = NULL; git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT; git_strarray refs = {0}; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_credentials(credentials)) git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg); if (git2r_arg_check_string(msg)) git2r_error(__func__, NULL, "'msg'", git2r_err_string_arg); if (git2r_arg_check_logical(verbose)) git2r_error(__func__, NULL, "'verbose'", git2r_err_logical_arg); if ((!Rf_isNull(refspecs)) && git2r_arg_check_string_vec(refspecs)) git2r_error(__func__, NULL, "'refspecs'", git2r_err_string_vec_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_remote_lookup(&remote, repository, CHAR(STRING_ELT(name, 0))); if (error) goto cleanup; if (!Rf_isNull(refspecs)) { size_t i, len; /* Count number of non NA values */ len = Rf_length(refspecs); for (i = 0; i < len; i++) if (NA_STRING != STRING_ELT(refspecs, i)) refs.count++; if (refs.count) { /* Allocate the strings in refs */ refs.strings = malloc(refs.count * sizeof(char*)); if (!refs.strings) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_alloc_memory_buffer); error = GIT_ERROR; goto cleanup; } /* Populate the strings in refs */ for (i = 0; i < refs.count; i++) if (NA_STRING != STRING_ELT(refspecs, i)) refs.strings[i] = (char *)CHAR(STRING_ELT(refspecs, i)); } } if (LOGICAL(verbose)[0]) payload.verbose = 1; payload.credentials = credentials; fetch_opts.callbacks.payload = &payload; fetch_opts.callbacks.credentials = &git2r_cred_acquire_cb; fetch_opts.callbacks.update_tips = &git2r_update_tips_cb; error = git_remote_fetch(remote, &refs, &fetch_opts, CHAR(STRING_ELT(msg, 0))); if (error) goto cleanup; stats = git_remote_stats(remote); PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_transfer_progress)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_transfer_progress)); git2r_transfer_progress_init(stats, result); cleanup: free(refs.strings); if (remote && git_remote_connected(remote)) git_remote_disconnect(remote); git_remote_free(remote); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error( __func__, GIT2R_ERROR_LAST(), git2r_err_unable_to_authenticate, NULL); return result; } /** * Get the configured remotes for a repo * * @param repo S3 class git_repository * @return Character vector with name of the remotes */ SEXP attribute_hidden git2r_remote_list( SEXP repo) { int error, nprotect = 0; size_t i; git_strarray rem_list; SEXP list = R_NilValue; git_repository *repository; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_remote_list(&rem_list, repository); if (error) goto cleanup; PROTECT(list = Rf_allocVector(STRSXP, rem_list.count)); nprotect++; for (i = 0; i < rem_list.count; i++) SET_STRING_ELT(list, i, Rf_mkChar(rem_list.strings[i])); cleanup: git_strarray_free(&rem_list); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return list; } /** * Remove an existing remote * * All remote-tracking branches and configuration settings for the * remote will be removed. * @param repo S3 class git_repository * @param name The name of the remote to remove * @return R_NilValue */ SEXP attribute_hidden git2r_remote_remove( SEXP repo, SEXP name) { int error; git_repository *repository = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_remote_delete(repository, CHAR(STRING_ELT(name, 0))); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Give the remote a new name * * @param repo S3 class git_repository * @param oldname The old name of the remote * @param newname The new name of the remote * @return R_NilValue */ SEXP attribute_hidden git2r_remote_rename( SEXP repo, SEXP oldname, SEXP newname) { int error; git_strarray problems = {0}; git_repository *repository = NULL; if (git2r_arg_check_string(oldname)) git2r_error(__func__, NULL, "'oldname'", git2r_err_string_arg); if (git2r_arg_check_string(newname)) git2r_error(__func__, NULL, "'newname'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_remote_rename( &problems, repository, CHAR(STRING_ELT(oldname, 0)), CHAR(STRING_ELT(newname, 0))); if (error) goto cleanup; git_strarray_free(&problems); cleanup: git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Set the remote's url in the configuration * * This assumes the common case of a single-url remote and * will otherwise raise an error. * @param repo S3 class git_repository * @param name The name of the remote * @param url The url to set * @return R_NilValue */ SEXP attribute_hidden git2r_remote_set_url( SEXP repo, SEXP name, SEXP url) { int error; git_repository *repository = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_string(url)) git2r_error(__func__, NULL, "'url'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_remote_set_url( repository, CHAR(STRING_ELT(name, 0)), CHAR(STRING_ELT(url, 0))); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Get the remote's url * * @param repo S3 class git_repository * @param remote Character vector with name of remote. NA values are * ok and give NA values as result at corresponding index in url * vector * @return Character vector with url for each remote */ SEXP attribute_hidden git2r_remote_url( SEXP repo, SEXP remote) { int error = GIT_OK; SEXP url; size_t len; size_t i = 0; git_remote *tmp_remote; git_repository *repository = NULL; if (git2r_arg_check_string_vec(remote)) git2r_error(__func__, NULL, "'remote'", git2r_err_string_vec_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); len = LENGTH(remote); PROTECT(url = Rf_allocVector(STRSXP, len)); for (; i < len; i++) { if (NA_STRING == STRING_ELT(remote, i)) { SET_STRING_ELT(url, i, NA_STRING); } else { error = git_remote_lookup( &tmp_remote, repository, CHAR(STRING_ELT(remote, i))); if (error) goto cleanup; SET_STRING_ELT(url, i, Rf_mkChar(git_remote_url(tmp_remote))); git_remote_free(tmp_remote); } } cleanup: git_repository_free(repository); UNPROTECT(1); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return url; } /** * Get the remote's url * * Based on https://github.com/libgit2/libgit2/blob/babdc376c7/examples/network/ls-remote.c * @param repo S3 class git_repository * @param name Character vector with URL of remote. * @return Character vector for each reference with the associated commit IDs. */ SEXP attribute_hidden git2r_remote_ls( SEXP name, SEXP repo, SEXP credentials) { const char *name_ = NULL; SEXP result = R_NilValue; SEXP names = R_NilValue; git_remote *remote = NULL; int error, nprotect = 0; const git_remote_head **refs; size_t refs_len, i; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT; git_repository *repository = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_credentials(credentials)) git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg); if (!Rf_isNull(repo)) { repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); } name_ = CHAR(STRING_ELT(name, 0)); if (repository) { error = git_remote_lookup(&remote, repository, name_); if (error) { error = git_remote_create_anonymous(&remote, repository, name_); if (error) goto cleanup; } } else { error = git_remote_create_anonymous(&remote, repository, name_); if (error) goto cleanup; } payload.credentials = credentials; callbacks.payload = &payload; callbacks.credentials = &git2r_cred_acquire_cb; error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL, NULL); if (error) goto cleanup; error = git_remote_ls(&refs, &refs_len, remote); if (error) goto cleanup; PROTECT(result = Rf_allocVector(STRSXP, refs_len)); nprotect++; Rf_setAttrib(result, R_NamesSymbol, names = Rf_allocVector(STRSXP, refs_len)); for (i = 0; i < refs_len; i++) { char oid[GIT_OID_HEXSZ + 1] = {0}; git_oid_fmt(oid, &refs[i]->oid); SET_STRING_ELT(result, i, Rf_mkChar(oid)); SET_STRING_ELT(names, i, Rf_mkChar(refs[i]->name)); } cleanup: git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_blame.h0000644000175000017500000000162713137360640014573 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_blame_h #define INCLUDE_git2r_blame_h #include #include SEXP git2r_blame_file(SEXP repo, SEXP path); #endif git2r/src/git2r_oid.h0000644000175000017500000000165613273077667014307 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_oid_h #define INCLUDE_git2r_oid_h #include #include #include void git2r_oid_from_sha_sexp(SEXP sha, git_oid *oid); #endif git2r/src/git2r_libgit2.c0000644000175000017500000000626313671131056015043 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_libgit2.h" /** * Return compile time options for libgit2. * * @return A VECSXP with threads, https and ssh set to TRUE/FALSE */ SEXP attribute_hidden git2r_libgit2_features(void) { const char *names[] = {"threads", "https", "ssh", ""}; int value = git_libgit2_features(); SEXP features; PROTECT(features = Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(features, 0, Rf_ScalarLogical(value & GIT_FEATURE_THREADS)); SET_VECTOR_ELT(features, 1, Rf_ScalarLogical(value & GIT_FEATURE_HTTPS)); SET_VECTOR_ELT(features, 2, Rf_ScalarLogical(value & GIT_FEATURE_SSH)); UNPROTECT(1); return features; } /** * Return the version of the libgit2 library being currently used. * * @return A VECSXP with major, minor and rev. */ SEXP attribute_hidden git2r_libgit2_version(void) { const char *names[] = {"major", "minor", "rev", ""}; SEXP version; int major, minor, rev; git_libgit2_version(&major, &minor, &rev); PROTECT(version = Rf_mkNamed(VECSXP, names)); SET_VECTOR_ELT(version, 0, Rf_ScalarInteger(major)); SET_VECTOR_ELT(version, 1, Rf_ScalarInteger(minor)); SET_VECTOR_ELT(version, 2, Rf_ScalarInteger(rev)); UNPROTECT(1); return version; } /** * Set the SSL certificate-authority locations * * Either parameter may be 'NULL', but not both. * @param filename Location of a file containing several certificates * concatenated together. Default NULL. * @param path Location of a directory holding several certificates, * one per file. Default NULL. * @return NULL */ SEXP attribute_hidden git2r_ssl_cert_locations( SEXP filename, SEXP path) { const char *f = NULL; const char *p = NULL; if (!Rf_isNull(filename)) { if (git2r_arg_check_string(filename)) git2r_error(__func__, NULL, "'filename'", git2r_err_string_arg); f = CHAR(STRING_ELT(filename, 0)); } if (!Rf_isNull(path)) { if (git2r_arg_check_string(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_arg); p = CHAR(STRING_ELT(path, 0)); } if (f == NULL && p == NULL) git2r_error(__func__, NULL, git2r_err_ssl_cert_locations, NULL); if (git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, f, p)) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } git2r/src/git2r_oid.c0000644000175000017500000000242113671131056014252 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "git2r_oid.h" /** * Get oid from sha SEXP * * @param sha A character vector with sha's. The length * can be less than 40 bytes. * @param oid result is written into the oid * @return void */ void attribute_hidden git2r_oid_from_sha_sexp( SEXP sha, git_oid *oid) { size_t len; len = LENGTH(STRING_ELT(sha, 0)); if (GIT_OID_HEXSZ == len) git_oid_fromstr(oid, CHAR(STRING_ELT(sha, 0))); else git_oid_fromstrn(oid, CHAR(STRING_ELT(sha, 0)), len); } git2r/src/Makevars.in0000644000175000017500000000013713313727537014337 0ustar nileshnileshPKG_CPPFLAGS = -DR_NO_REMAP -DSTRICT_R_HEADERS PKG_CFLAGS = @PKG_CFLAGS@ PKG_LIBS = @PKG_LIBS@ git2r/src/git2r_blob.c0000644000175000017500000002126013671131056014417 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "git2r_arg.h" #include "git2r_blob.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" /** * Get content of a blob * * @param blob S3 class git_blob * @return content */ SEXP attribute_hidden git2r_blob_content( SEXP blob) { int error, nprotect = 0; SEXP result = R_NilValue; SEXP sha; git_blob *blob_obj = NULL; git_oid oid; git_repository *repository = NULL; if (git2r_arg_check_blob(blob)) git2r_error(__func__, NULL, "'blob'", git2r_err_blob_arg); repository = git2r_repository_open(git2r_get_list_element(blob, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(blob, "sha"); git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); error = git_blob_lookup(&blob_obj, repository, &oid); if (error) goto cleanup; PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(git_blob_rawcontent(blob_obj))); cleanup: git_blob_free(blob_obj); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Read a file from the filesystem and write its content to the * Object Database as a loose blob * @param repo The repository where the blob will be written. Can be * a bare repository. * @param path The file from which the blob will be created. * @return list of S3 class git_blob objects */ SEXP attribute_hidden git2r_blob_create_fromdisk( SEXP repo, SEXP path) { SEXP result = R_NilValue; int error = 0, nprotect = 0; size_t len, i; git_repository *repository = NULL; if (git2r_arg_check_string_vec(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_vec_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); len = Rf_length(path); PROTECT(result = Rf_allocVector(VECSXP, len)); nprotect++; for (i = 0; i < len; i++) { if (NA_STRING != STRING_ELT(path, i)) { git_oid oid; git_blob *blob = NULL; SEXP item; error = GIT2R_BLOB_CREATE_FROM_DISK( &oid, repository, CHAR(STRING_ELT(path, i))); if (error) goto cleanup; error = git_blob_lookup(&blob, repository, &oid); if (error) goto cleanup; SET_VECTOR_ELT(result, i, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_blob)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_blob)); git2r_blob_init(blob, repo, item); git_blob_free(blob); } } cleanup: git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Create blob from file in working directory * * Read a file from the working folder of a repository and write its * content to the Object Database as a loose blob. The method is * vectorized and accepts a vector of files to create blobs from. * @param repo The repository where the blob(s) will be * written. Cannot be a bare repository. * @param relative_path The file(s) from which the blob will be * created, relative to the repository's working dir. * @return list of S3 class git_blob objects */ SEXP attribute_hidden git2r_blob_create_fromworkdir( SEXP repo, SEXP relative_path) { SEXP result = R_NilValue; int error = 0, nprotect = 0; size_t len, i; git_repository *repository = NULL; if (git2r_arg_check_string_vec(relative_path)) git2r_error(__func__, NULL, "'relative_path'", git2r_err_string_vec_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); len = Rf_length(relative_path); PROTECT(result = Rf_allocVector(VECSXP, len)); nprotect++; for (i = 0; i < len; i++) { if (NA_STRING != STRING_ELT(relative_path, i)) { git_oid oid; git_blob *blob = NULL; SEXP item; error = GIT2R_BLOB_CREATE_FROM_WORKDIR( &oid, repository, CHAR(STRING_ELT(relative_path, i))); if (error) goto cleanup; error = git_blob_lookup(&blob, repository, &oid); if (error) goto cleanup; SET_VECTOR_ELT(result, i, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_blob)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_blob)); git2r_blob_init(blob, repo, item); git_blob_free(blob); } } cleanup: git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Init entries in a S3 class git_blob * * @param source a blob * @param repo S3 class git_repository that contains the blob * @param dest S3 class git_blob to initialize * @return void */ void attribute_hidden git2r_blob_init( const git_blob *source, SEXP repo, SEXP dest) { const git_oid *oid; char sha[GIT_OID_HEXSZ + 1]; oid = git_blob_id(source); git_oid_tostr(sha, sizeof(sha), oid); SET_VECTOR_ELT(dest, git2r_S3_item__git_blob__sha, Rf_mkString(sha)); SET_VECTOR_ELT(dest, git2r_S3_item__git_blob__repo, Rf_duplicate(repo)); } /** * Is blob binary * * @param blob S3 class git_blob * @return TRUE if binary data, FALSE if not */ SEXP attribute_hidden git2r_blob_is_binary( SEXP blob) { int error, nprotect = 0; SEXP result = R_NilValue; SEXP sha; git_blob *blob_obj = NULL; git_oid oid; git_repository *repository = NULL; if (git2r_arg_check_blob(blob)) git2r_error(__func__, NULL, "'blob'", git2r_err_blob_arg); repository = git2r_repository_open(git2r_get_list_element(blob, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(blob, "sha"); git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); error = git_blob_lookup(&blob_obj, repository, &oid); if (error) goto cleanup; PROTECT(result = Rf_allocVector(LGLSXP, 1)); nprotect++; if (git_blob_is_binary(blob_obj)) LOGICAL(result)[0] = 1; else LOGICAL(result)[0] = 0; cleanup: git_blob_free(blob_obj); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Size in bytes of contents of a blob * * @param blob S3 class git_blob * @return size */ SEXP attribute_hidden git2r_blob_rawsize( SEXP blob) { int error; SEXP sha; git_off_t size = 0; git_blob *blob_obj = NULL; git_oid oid; git_repository *repository = NULL; if (git2r_arg_check_blob(blob)) git2r_error(__func__, NULL, "'blob'", git2r_err_blob_arg); repository = git2r_repository_open(git2r_get_list_element(blob, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(blob, "sha"); git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); error = git_blob_lookup(&blob_obj, repository, &oid); if (error) goto cleanup; size = git_blob_rawsize(blob_obj); cleanup: git_blob_free(blob_obj); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return Rf_ScalarInteger(size); } git2r/src/git2r_transfer.c0000644000175000017500000000423013671131056015323 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "git2r_deprecated.h" #include "git2r_S3.h" #include "git2r_transfer.h" /** * Init slots in S3 class git_transfer_progress * * @param source A GIT2R_INDEXER_PROGRESS object * @param dest S3 class git_transfer_progress to initialize * @return void */ void attribute_hidden git2r_transfer_progress_init( const GIT2R_INDEXER_PROGRESS *source, SEXP dest) { SET_VECTOR_ELT( dest, git2r_S3_item__git_transfer_progress__total_objects, Rf_ScalarInteger(source->total_objects)); SET_VECTOR_ELT( dest, git2r_S3_item__git_transfer_progress__indexed_objects, Rf_ScalarInteger(source->indexed_objects)); SET_VECTOR_ELT( dest, git2r_S3_item__git_transfer_progress__received_objects, Rf_ScalarInteger(source->received_objects)); SET_VECTOR_ELT( dest, git2r_S3_item__git_transfer_progress__local_objects, Rf_ScalarInteger(source->local_objects)); SET_VECTOR_ELT( dest, git2r_S3_item__git_transfer_progress__total_deltas, Rf_ScalarInteger(source->total_deltas)); SET_VECTOR_ELT( dest, git2r_S3_item__git_transfer_progress__indexed_deltas, Rf_ScalarInteger(source->indexed_deltas)); SET_VECTOR_ELT( dest, git2r_S3_item__git_transfer_progress__received_bytes, Rf_ScalarInteger(source->received_bytes)); } git2r/src/git2r_push.h0000644000175000017500000000165713137360640014475 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_push_h #define INCLUDE_git2r_push_h #include #include SEXP git2r_push(SEXP repo, SEXP name, SEXP refspec, SEXP credentials); #endif git2r/src/git2r_libgit2.h0000644000175000017500000000175413137360640015050 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_libgit2_h #define INCLUDE_git2r_libgit2_h #include #include SEXP git2r_libgit2_features(void); SEXP git2r_libgit2_version(void); SEXP git2r_ssl_cert_locations(SEXP filename, SEXP path); #endif git2r/src/git2r_S3.c0000644000175000017500000001117413671131056013771 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_S3.h" const char *git2r_S3_class__git_blame = "git_blame"; const char *git2r_S3_items__git_blame[] = { "path", "hunks", "repo", ""}; const char *git2r_S3_class__git_blame_hunk = "git_blame_hunk"; const char *git2r_S3_items__git_blame_hunk[] = { "lines_in_hunk", "final_commit_id", "final_start_line_number", "final_signature", "orig_commit_id", "orig_start_line_number", "orig_signature", "orig_path", "boundary", "repo", ""}; const char *git2r_S3_class__git_blob = "git_blob"; const char *git2r_S3_items__git_blob[] = { "sha", "repo", ""}; const char *git2r_S3_class__git_branch = "git_branch"; const char *git2r_S3_items__git_branch[] = { "name", "type", "repo", ""}; const char *git2r_S3_class__git_commit = "git_commit"; const char *git2r_S3_items__git_commit[] = { "sha", "author", "committer", "summary", "message", "repo", ""}; const char *git2r_S3_class__git_diff = "git_diff"; const char *git2r_S3_items__git_diff[] = { "old", "new", "files", ""}; const char *git2r_S3_class__git_diff_file = "git_diff_file"; const char *git2r_S3_items__git_diff_file[] = { "old_file", "new_file", "hunks", ""}; const char *git2r_S3_class__git_diff_hunk = "git_diff_hunk"; const char *git2r_S3_items__git_diff_hunk[] = { "old_start", "old_lines", "new_start", "new_lines", "header", "lines", ""}; const char *git2r_S3_class__git_diff_line = "git_diff_line"; const char *git2r_S3_items__git_diff_line[] = { "origin", "old_lineno", "new_lineno", "num_lines", "content", ""}; const char *git2r_S3_class__git_fetch_head = "git_fetch_head"; const char *git2r_S3_items__git_fetch_head[] = { "ref_name", "remote_url", "sha", "is_merge", "repo", ""}; const char *git2r_S3_class__git_merge_result = "git_merge_result"; const char *git2r_S3_items__git_merge_result[] = { "up_to_date", "fast_forward", "conflicts", "sha", ""}; const char *git2r_S3_class__git_note = "git_note"; const char *git2r_S3_items__git_note[] = { "sha", "annotated", "message", "refname", "repo", ""}; const char *git2r_S3_class__git_reference = "git_reference"; const char *git2r_S3_items__git_reference[] = { "name", "type", "sha", "target", "shorthand", "repo", ""}; const char *git2r_S3_class__git_reflog_entry = "git_reflog_entry"; const char *git2r_S3_items__git_reflog_entry[] = { "sha", "message", "index", "committer", "refname", "repo", ""}; const char *git2r_S3_class__git_repository = "git_repository"; const char *git2r_S3_items__git_repository[] = { "path", ""}; const char *git2r_S3_class__git_signature = "git_signature"; const char *git2r_S3_items__git_signature[] = { "name", "email", "when", ""}; const char *git2r_S3_class__git_tag = "git_tag"; const char *git2r_S3_items__git_tag[] = { "sha", "message", "name", "tagger", "target", "repo", ""}; const char *git2r_S3_class__git_time = "git_time"; const char *git2r_S3_items__git_time[] = { "time", "offset", ""}; const char *git2r_S3_class__git_transfer_progress = "git_transfer_progress"; const char *git2r_S3_items__git_transfer_progress[] = { "total_objects", "indexed_objects", "received_objects", "local_objects", "total_deltas", "indexed_deltas", "received_bytes", ""}; const char *git2r_S3_class__git_tree = "git_tree"; const char *git2r_S3_items__git_tree[] = { "sha", "filemode", "type", "id", "name", "repo", ""}; /** * Get the list element named str, or return NULL. * * From the manual 'Writing R Extensions' * (https://cran.r-project.org/doc/manuals/r-release/R-exts.html) */ SEXP attribute_hidden git2r_get_list_element( SEXP list, const char *str) { int i = 0; SEXP elmt = R_NilValue, names = Rf_getAttrib(list, R_NamesSymbol); for (; i < Rf_length(list); i++) { if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { elmt = VECTOR_ELT(list, i); break; } } return elmt; } git2r/src/git2r_push.c0000644000175000017500000000652713671131056014471 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_cred.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_push.h" #include "git2r_repository.h" #include "git2r_signature.h" #include "git2r_transfer.h" /** * Check if any non NA refspec * * @param refspec The string vector of refspec to push * @return 1 if nothing to push else 0 */ static int git2r_nothing_to_push( SEXP refspec) { size_t i, n; n = Rf_length(refspec); if (0 == n) return 1; /* Nothing to push */ for (i = 0; i < n; i++) { if (NA_STRING != STRING_ELT(refspec, i)) return 0; } /* Nothing to push */ return 1; } /** * Push * * @param repo S3 class git_repository * @param name The remote to push to * @param refspec The string vector of refspec to push * @param credentials The credentials for remote repository access. * @return R_NilValue */ SEXP attribute_hidden git2r_push( SEXP repo, SEXP name, SEXP refspec, SEXP credentials) { int error; git_remote *remote = NULL; git_repository *repository = NULL; git_strarray c_refspecs = {0}; git_push_options opts = GIT_PUSH_OPTIONS_INIT; git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_string_vec(refspec)) git2r_error(__func__, NULL, "'refspec'", git2r_err_string_vec_arg); if (git2r_arg_check_credentials(credentials)) git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg); if (git2r_nothing_to_push(refspec)) return R_NilValue; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_remote_lookup(&remote, repository, CHAR(STRING_ELT(name, 0))); if (error) goto cleanup; payload.credentials = credentials; opts.callbacks.payload = &payload; opts.callbacks.credentials = &git2r_cred_acquire_cb; error = git2r_copy_string_vec(&c_refspecs, refspec); if (error) goto cleanup; error = git_remote_push(remote, &c_refspecs, &opts); cleanup: free(c_refspecs.strings); if (remote) { if (git_remote_connected(remote)) git_remote_disconnect(remote); git_remote_free(remote); } git_repository_free(repository); if (error) git2r_error( __func__, GIT2R_ERROR_LAST(), git2r_err_unable_to_authenticate, NULL); return R_NilValue; } git2r/src/git2r_signature.c0000644000175000017500000000720313671131056015503 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "git2r_error.h" #include "git2r_deprecated.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" /** * Get the configured signature for a repository * * @param repo S3 class git_repository * @return S3 class git_signature */ SEXP attribute_hidden git2r_signature_default( SEXP repo) { int error, nprotect = 0; git_repository *repository = NULL; git_signature *signature = NULL; SEXP result = R_NilValue; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_signature_default(&signature, repository); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_signature)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_signature)); git2r_signature_init(signature, result); cleanup: git_repository_free(repository); git_signature_free(signature); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Init signature from SEXP signature argument * * @param out new signature * @param signature SEXP argument with S3 class git_signature * @return 0 or an error code */ int attribute_hidden git2r_signature_from_arg( git_signature **out, SEXP signature) { SEXP when = git2r_get_list_element(signature, "when"); return git_signature_new( out, CHAR(STRING_ELT(git2r_get_list_element(signature, "name"), 0)), CHAR(STRING_ELT(git2r_get_list_element(signature, "email"), 0)), REAL(git2r_get_list_element(when, "time"))[0], REAL(git2r_get_list_element(when, "offset"))[0]); } /** * Init slots in S3 class git_signature. * * @param source A git signature * @param dest S3 class git_signature to initialize * @return void */ void attribute_hidden git2r_signature_init( const git_signature *source, SEXP dest) { SEXP when; SET_VECTOR_ELT( dest, git2r_S3_item__git_signature__name, Rf_mkString(source->name)); SET_VECTOR_ELT( dest, git2r_S3_item__git_signature__email, Rf_mkString(source->email)); when = VECTOR_ELT(dest, git2r_S3_item__git_signature__when); if (Rf_isNull(when)) { SET_VECTOR_ELT( dest, git2r_S3_item__git_signature__when, when = Rf_mkNamed(VECSXP, git2r_S3_items__git_time)); Rf_setAttrib(when, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_time)); } SET_VECTOR_ELT( when, git2r_S3_item__git_time__time, Rf_ScalarReal((double)source->when.time)); SET_VECTOR_ELT( when, git2r_S3_item__git_time__offset, Rf_ScalarReal((double)source->when.offset)); } git2r/src/git2r_object.h0000644000175000017500000000163313226504307014755 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_object_h #define INCLUDE_git2r_object_h #include #include SEXP git2r_object_lookup(SEXP repo, SEXP sha); #endif git2r/src/git2r_status.h0000644000175000017500000000176613137360640015042 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2016 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_status_h #define INCLUDE_git2r_status_h #include #include SEXP git2r_status_list( SEXP repo, SEXP staged, SEXP unstaged, SEXP untracked, SEXP all_untracked, SEXP ignored); #endif git2r/src/git2r_blob.h0000644000175000017500000000224613273077667014446 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_blob_h #define INCLUDE_git2r_blob_h #include #include #include SEXP git2r_blob_content(SEXP blob); SEXP git2r_blob_create_fromdisk(SEXP repo, SEXP path); SEXP git2r_blob_create_fromworkdir(SEXP repo, SEXP relative_path); void git2r_blob_init(const git_blob *source, SEXP repo, SEXP dest); SEXP git2r_blob_is_binary(SEXP blob); SEXP git2r_blob_rawsize(SEXP blob); #endif git2r/src/git2r_revwalk.h0000644000175000017500000000223413565053016015162 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2019 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_revwalk_h #define INCLUDE_git2r_revwalk_h #include #include SEXP git2r_revwalk_contributions(SEXP repo, SEXP topological, SEXP time, SEXP reverse); SEXP git2r_revwalk_list(SEXP repo, SEXP sha, SEXP topological, SEXP time, SEXP reverse, SEXP max_n); SEXP git2r_revwalk_list2(SEXP repo, SEXP sha, SEXP topological, SEXP time, SEXP reverse, SEXP max_n, SEXP path); #endif git2r/src/git2r_reflog.h0000644000175000017500000000163113137360640014764 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_reflog_h #define INCLUDE_git2r_reflog_h #include #include SEXP git2r_reflog_list(SEXP repo, SEXP ref); #endif git2r/src/git2r_config.c0000644000175000017500000003356213671131056014756 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #define GIT2R_N_CONFIG_LEVELS 7 /** * Count number of config variables by level * * @param cfg where to count the variables * @param n_level array to store the number of variables * @param 0 on succes, or error code */ static int git2r_config_count_variables( const git_config *cfg, size_t *n_level) { int error; git_config_iterator *iterator = NULL; error = git_config_iterator_new(&iterator, cfg); if (error) return error; for (;;) { git_config_entry *entry; error = git_config_next(&entry, iterator); if (error) { if (GIT_ITEROVER == error) error = GIT_OK; goto cleanup; } switch (entry->level) { case GIT_CONFIG_LEVEL_PROGRAMDATA: n_level[0]++; break; case GIT_CONFIG_LEVEL_SYSTEM: n_level[1]++; break; case GIT_CONFIG_LEVEL_XDG: n_level[2]++; break; case GIT_CONFIG_LEVEL_GLOBAL: n_level[3]++; break; case GIT_CONFIG_LEVEL_LOCAL: n_level[4]++; break; case GIT_CONFIG_LEVEL_APP: n_level[5]++; break; case GIT_CONFIG_HIGHEST_LEVEL: n_level[6]++; break; default: GIT2R_ERROR_SET_STR(GIT2R_ERROR_CONFIG, git2r_err_unexpected_config_level); error = GIT_ERROR; goto cleanup; } } cleanup: git_config_iterator_free(iterator); return error; } /** * Intialize a list for a config level. The list is only created if * there are any entries at that level. * * @param level the index of the level * @param n_level vector with number of entries per level * @param name name of the level to initialize * @return index of the config level list in the owning list */ static size_t git2r_config_list_init( SEXP list, size_t level, size_t *n_level, size_t *i_list, size_t i, const char *name) { if (n_level[level]) { SEXP item; SEXP names; i_list[level] = i++; SET_VECTOR_ELT( list, i_list[level], item = Rf_allocVector(VECSXP, n_level[level])); Rf_setAttrib(item, R_NamesSymbol, Rf_allocVector(STRSXP, n_level[level])); names = Rf_getAttrib(list, R_NamesSymbol); SET_STRING_ELT(names, i_list[level] , Rf_mkChar(name)); } return i; } /** * Add entry to result list. * * @param list the result list * @param level the level of the entry * @param i_level vector with the index where to add the entry within * the level * @param i_list vector with the index to the sub-list of the list at * level * @param entry the config entry to add * @return void */ static void git2r_config_list_add_entry( SEXP list, size_t level, size_t *i_level, size_t *i_list, git_config_entry *entry) { if (i_list[level] < (size_t)LENGTH(list)) { SEXP sub_list = VECTOR_ELT(list, i_list[level]); if (i_level[level] < (size_t)LENGTH(sub_list)) { SEXP names = Rf_getAttrib(sub_list, R_NamesSymbol); SET_STRING_ELT(names, i_level[level], Rf_mkChar(entry->name)); SET_VECTOR_ELT(sub_list, i_level[level], Rf_mkString(entry->value)); i_level[level]++; return; } } } /** * List config variables * * @param cfg Memory representation the configuration file for this * repository. * @param list The result list * @param n_level vector with number of entries per level * @return 0 if OK, else error code */ static int git2r_config_list_variables( git_config *cfg, SEXP list, size_t *n_level) { int error; size_t i_level[GIT2R_N_CONFIG_LEVELS] = {0}; /* Current index at level */ size_t i_list[GIT2R_N_CONFIG_LEVELS] = {0}; /* Index of level in target list */ git_config_iterator *iterator = NULL; size_t i = 0; error = git_config_iterator_new(&iterator, cfg); if (error) goto cleanup; i = git2r_config_list_init(list, 0, n_level, i_list, i, "programdata"); i = git2r_config_list_init(list, 1, n_level, i_list, i, "system"); i = git2r_config_list_init(list, 2, n_level, i_list, i, "xdg"); i = git2r_config_list_init(list, 3, n_level, i_list, i, "global"); i = git2r_config_list_init(list, 4, n_level, i_list, i, "local"); i = git2r_config_list_init(list, 5, n_level, i_list, i, "app"); i = git2r_config_list_init(list, 6, n_level, i_list, i, "highest"); for (;;) { git_config_entry *entry; error = git_config_next(&entry, iterator); if (error) { if (GIT_ITEROVER == error) error = GIT_OK; goto cleanup; } switch (entry->level) { case GIT_CONFIG_LEVEL_PROGRAMDATA: git2r_config_list_add_entry(list, 0, i_level, i_list, entry); break; case GIT_CONFIG_LEVEL_SYSTEM: git2r_config_list_add_entry(list, 1, i_level, i_list, entry); break; case GIT_CONFIG_LEVEL_XDG: git2r_config_list_add_entry(list, 2, i_level, i_list, entry); break; case GIT_CONFIG_LEVEL_GLOBAL: git2r_config_list_add_entry(list, 3, i_level, i_list, entry); break; case GIT_CONFIG_LEVEL_LOCAL: git2r_config_list_add_entry(list, 4, i_level, i_list, entry); break; case GIT_CONFIG_LEVEL_APP: git2r_config_list_add_entry(list, 5, i_level, i_list, entry); break; case GIT_CONFIG_HIGHEST_LEVEL: git2r_config_list_add_entry(list, 6, i_level, i_list, entry); break; default: GIT2R_ERROR_SET_STR(GIT2R_ERROR_CONFIG, git2r_err_unexpected_config_level); error = GIT_ERROR; goto cleanup; } } cleanup: git_config_iterator_free(iterator); return error; } /** * Open configuration file * * @param out Pointer to store the loaded configuration. * @param repo S3 class git_repository. If non-R_NilValue open the * configuration file for the repository. If R_NilValue open the * global, XDG and system configuration files. * @param snapshot Open a snapshot of the configuration. * @return 0 on success, or an error code. */ static int git2r_config_open( git_config **out, SEXP repo, int snapshot) { int error; if (!Rf_isNull(repo)) { git_repository *repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); if (snapshot) error = git_repository_config_snapshot(out, repository); else error = git_repository_config(out, repository); git_repository_free(repository); } else if (snapshot) { git_config *config = NULL; error = git_config_open_default(&config); if (error) { git_config_free(config); return error; } error = git_config_snapshot(out, config); git_config_free(config); } else { error = git_config_open_default(out); } return error; } /** * Get config variables * * @param repo S3 class git_repository * @return VECSXP list with variables by level */ SEXP attribute_hidden git2r_config_get( SEXP repo) { int error, nprotect = 0; SEXP result = R_NilValue; size_t i = 0, n = 0, n_level[GIT2R_N_CONFIG_LEVELS] = {0}; git_config *cfg = NULL; error = git2r_config_open(&cfg, repo, 0); if (error) goto cleanup; error = git2r_config_count_variables(cfg, n_level); if (error) goto cleanup; /* Count levels with entries */ for (; i < GIT2R_N_CONFIG_LEVELS; i++) { if (n_level[i]) n++; } PROTECT(result = Rf_allocVector(VECSXP, n)); nprotect++; Rf_setAttrib(result, R_NamesSymbol, Rf_allocVector(STRSXP, n)); if (git2r_config_list_variables(cfg, result, n_level)) goto cleanup; cleanup: git_config_free(cfg); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Set or delete config entries * * @param repo S3 class git_repository * @param variables list of variables. If variable is NULL, it's deleted. * @return R_NilValue */ SEXP attribute_hidden git2r_config_set( SEXP repo, SEXP variables) { int error = 0, nprotect = 0; SEXP names; size_t i, n; git_config *cfg = NULL; if (git2r_arg_check_list(variables)) git2r_error(__func__, NULL, "'variables'", git2r_err_list_arg); n = Rf_length(variables); if (n) { error = git2r_config_open(&cfg, repo, 0); if (error) goto cleanup; PROTECT(names = Rf_getAttrib(variables, R_NamesSymbol)); nprotect++; for (i = 0; i < n; i++) { const char *key = CHAR(STRING_ELT(names, i)); const char *value = NULL; if (!Rf_isNull(VECTOR_ELT(variables, i))) value = CHAR(STRING_ELT(VECTOR_ELT(variables, i), 0)); if (value) error = git_config_set_string(cfg, key, value); else error = git_config_delete_entry(cfg, key); if (error) { if (error == GIT_EINVALIDSPEC) { Rf_warning("Variable was not in a valid format: '%s'", key); error = 0; } else { goto cleanup; } } } } cleanup: git_config_free(cfg); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Get the value of a string config variable * * @param repo S3 class git_repository * @param name The name of the variable * @return If the variable exists, a character vector of length one * with the value, else R_NilValue. */ SEXP attribute_hidden git2r_config_get_string( SEXP repo, SEXP name) { int error, nprotect = 0; SEXP result = R_NilValue; const char *value; git_config *cfg = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); error = git2r_config_open(&cfg, repo, 1); if (error) goto cleanup; error = git_config_get_string(&value, cfg, CHAR(STRING_ELT(name, 0))); if (error) { if (error == GIT_ENOTFOUND) error = 0; goto cleanup; } PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(value)); cleanup: git_config_free(cfg); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get the value of a boolean config variable * * @param repo S3 class git_repository * @param name The name of the variable * @return If the variable exists, a logical vector of length one * with TRUE or FALSE, else R_NilValue. */ SEXP attribute_hidden git2r_config_get_logical( SEXP repo, SEXP name) { int error, nprotect = 0; SEXP result = R_NilValue; int value; git_config *cfg = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); error = git2r_config_open(&cfg, repo, 1); if (error) goto cleanup; error = git_config_get_bool(&value, cfg, CHAR(STRING_ELT(name, 0))); if (error) { if (error == GIT_ENOTFOUND) error = 0; goto cleanup; } PROTECT(result = Rf_allocVector(LGLSXP, 1)); nprotect++; if (value) LOGICAL(result)[0] = 1; else LOGICAL(result)[0] = 0; cleanup: git_config_free(cfg); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Locate the path to the configuration file * * @return path if a configuration file has been found, else NA. */ SEXP attribute_hidden git2r_config_find_file( SEXP level) { int not_found = 1; SEXP result; git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); if (git2r_arg_check_string(level)) git2r_error(__func__, NULL, "'level'", git2r_err_string_arg); if (strcmp(CHAR(STRING_ELT(level, 0)), "global") == 0) not_found = git_config_find_global(&buf); else if (strcmp(CHAR(STRING_ELT(level, 0)), "programdata") == 0) not_found = git_config_find_programdata(&buf); else if (strcmp(CHAR(STRING_ELT(level, 0)), "system") == 0) not_found = git_config_find_system(&buf); else if (strcmp(CHAR(STRING_ELT(level, 0)), "xdg") == 0) not_found = git_config_find_xdg(&buf); PROTECT(result = Rf_allocVector(STRSXP, 1)); if (not_found) SET_STRING_ELT(result, 0, NA_STRING); else SET_STRING_ELT(result, 0, Rf_mkChar(buf.ptr)); GIT2R_BUF_DISPOSE(&buf); UNPROTECT(1); return result; } git2r/src/git2r_remote.h0000644000175000017500000000254213137360640015003 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_remote_h #define INCLUDE_git2r_remote_h #include #include SEXP git2r_remote_add(SEXP repo, SEXP name, SEXP url); SEXP git2r_remote_fetch( SEXP repo, SEXP name, SEXP credentials, SEXP msg, SEXP verbose, SEXP refspecs); SEXP git2r_remote_list(SEXP repo); SEXP git2r_remote_remove(SEXP repo, SEXP remote); SEXP git2r_remote_rename(SEXP repo, SEXP oldname, SEXP newname); SEXP git2r_remote_set_url(SEXP repo, SEXP name, SEXP url); SEXP git2r_remote_url(SEXP repo, SEXP remote); SEXP git2r_remote_ls(SEXP name, SEXP repo, SEXP credentials); #endif git2r/src/git2r_status.c0000644000175000017500000003346513671131056015036 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_status.h" /** * Count number of ignored files * * @param status_list Representation of a status collection * @return The number of files */ static size_t git2r_status_count_ignored( git_status_list *status_list) { size_t i = 0; size_t ignored = 0; size_t count = git_status_list_entrycount(status_list); for (; i < count; ++i) { const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_IGNORED) ignored++; } return ignored; } /** * Count number of changes in index * * @param status_list Representation of a status collection * @return The number of changes */ static size_t git2r_status_count_staged( git_status_list *status_list) { size_t i = 0; size_t changes = 0; size_t count = git_status_list_entrycount(status_list); for (; i < count; ++i) { const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_CURRENT) continue; if (s->status & GIT_STATUS_INDEX_NEW) changes++; else if (s->status & GIT_STATUS_INDEX_MODIFIED) changes++; else if (s->status == GIT_STATUS_INDEX_DELETED) changes++; else if (s->status & GIT_STATUS_INDEX_RENAMED) changes++; else if (s->status & GIT_STATUS_INDEX_TYPECHANGE) changes++; } return changes; } /** * Count number of changes in workdir relative to index * * @param status_list Representation of a status collection * @return The number of changes */ static size_t git2r_status_count_unstaged( git_status_list *status_list) { size_t i = 0; size_t changes = 0; size_t count = git_status_list_entrycount(status_list); for (; i < count; ++i) { const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL) continue; if (s->status & GIT_STATUS_WT_MODIFIED) changes++; else if (s->status & GIT_STATUS_WT_DELETED) changes++; else if (s->status & GIT_STATUS_WT_RENAMED) changes++; else if (s->status & GIT_STATUS_WT_TYPECHANGE) changes++; else if (s->status == (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW)) changes++; else if (s->status & GIT_STATUS_CONFLICTED) changes++; } return changes; } /** * Count number of untracked files * * @param status_list Representation of a status collection * @return The number of files */ static size_t git2r_status_count_untracked( git_status_list *status_list) { size_t i = 0; size_t untracked = 0; size_t count = git_status_list_entrycount(status_list); for (; i < count; ++i) { const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_WT_NEW) untracked++; } return untracked; } /** * Add ignored files * * @param list The list to hold the result * @param list_index The index in list where to add the new sub list * with ignored files. * @param status_list Representation of a status collection * @return void */ static void git2r_status_list_ignored( SEXP list, size_t list_index, git_status_list *status_list) { size_t i = 0, j = 0, count; SEXP item, names; /* Create list with the correct number of entries */ count = git2r_status_count_ignored(status_list); SET_VECTOR_ELT(list, list_index, item = Rf_allocVector(VECSXP, count)); Rf_setAttrib(item, R_NamesSymbol, names = Rf_allocVector(STRSXP, count)); /* i index the entrycount. j index the added change in list */ count = git_status_list_entrycount(status_list); for (; i < count; i++) { const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_IGNORED) { SET_STRING_ELT(names, j, Rf_mkChar("ignored")); SET_VECTOR_ELT(item, j, Rf_mkString(s->index_to_workdir->old_file.path)); j++; } } } /** * Add changes in index * * @param list The list to hold the result * @param list_index The index in list where to add the new sub list * with changed files in index. * @param status_list Representation of a status collection * @return void */ static void git2r_status_list_staged( SEXP list, size_t list_index, git_status_list *status_list) { size_t i = 0, j = 0, n; SEXP sub_list, sub_list_names; /* Create list with the correct number of entries */ n = git2r_status_count_staged(status_list); SET_VECTOR_ELT(list, list_index, sub_list = Rf_allocVector(VECSXP, n)); Rf_setAttrib(sub_list, R_NamesSymbol, sub_list_names = Rf_allocVector(STRSXP, n)); /* i index the entrycount. j index the added change in list */ n = git_status_list_entrycount(status_list); for (; i < n; i++) { char *istatus = NULL; const char *old_path, *new_path; const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_CURRENT) continue; if (s->status & GIT_STATUS_INDEX_NEW) istatus = "new"; else if (s->status & GIT_STATUS_INDEX_MODIFIED) istatus = "modified"; else if (s->status == GIT_STATUS_INDEX_DELETED) istatus = "deleted"; else if (s->status & GIT_STATUS_INDEX_RENAMED) istatus = "renamed"; else if (s->status & GIT_STATUS_INDEX_TYPECHANGE) istatus = "typechange"; if (!istatus) continue; SET_STRING_ELT(sub_list_names, j, Rf_mkChar(istatus)); old_path = s->head_to_index->old_file.path; new_path = s->head_to_index->new_file.path; if (old_path && new_path && strcmp(old_path, new_path)) { SEXP item; SET_VECTOR_ELT(sub_list, j, item = Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(item, 0, Rf_mkChar(old_path)); SET_STRING_ELT(item, 1, Rf_mkChar(new_path)); } else { SEXP item; SET_VECTOR_ELT(sub_list, j, item = Rf_allocVector(STRSXP, 1)); SET_STRING_ELT(item, 0, Rf_mkChar(old_path ? old_path : new_path)); } j++; } } /** * Add changes in workdir relative to index * * @param list The list to hold the result * @param list_index The index in list where to add the new sub list * with unstaged files. * @param status_list Representation of a status collection * @return void */ static void git2r_status_list_unstaged( SEXP list, size_t list_index, git_status_list *status_list) { size_t i = 0, j = 0, n; SEXP sub_list, sub_list_names; /* Create list with the correct number of entries */ n = git2r_status_count_unstaged(status_list); SET_VECTOR_ELT(list, list_index, sub_list = Rf_allocVector(VECSXP, n)); Rf_setAttrib(sub_list, R_NamesSymbol, sub_list_names = Rf_allocVector(STRSXP, n)); /* i index the entrycount. j index the added change in list */ n = git_status_list_entrycount(status_list); for (; i < n; i++) { char *wstatus = NULL; const char *old_path, *new_path; const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL) continue; if (s->status & GIT_STATUS_WT_MODIFIED) wstatus = "modified"; else if (s->status & GIT_STATUS_WT_DELETED) wstatus = "deleted"; else if (s->status & GIT_STATUS_WT_RENAMED) wstatus = "renamed"; else if (s->status & GIT_STATUS_WT_TYPECHANGE) wstatus = "typechange"; else if (s->status == (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW)) wstatus = "unmerged"; else if (s->status & GIT_STATUS_CONFLICTED) wstatus = "conflicted"; if (!wstatus) continue; SET_STRING_ELT(sub_list_names, j, Rf_mkChar(wstatus)); old_path = s->index_to_workdir->old_file.path; new_path = s->index_to_workdir->new_file.path; if (old_path && new_path && strcmp(old_path, new_path)) { SEXP item; SET_VECTOR_ELT(sub_list, j, item = Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(item, 0, Rf_mkChar(old_path)); SET_STRING_ELT(item, 1, Rf_mkChar(new_path)); } else { SEXP item; SET_VECTOR_ELT(sub_list, j, item = Rf_allocVector(STRSXP, 1)); SET_STRING_ELT(item, 0, Rf_mkChar(old_path ? old_path : new_path)); } j++; } } /** * Add untracked files * * @param list The list to hold the result * @param list_index The index in list where to add the new sub list * with untracked files. * @param status_list Representation of a status collection * @return void */ static void git2r_status_list_untracked( SEXP list, size_t list_index, git_status_list *status_list) { size_t i = 0, j = 0, n; SEXP sub_list, sub_list_names; /* Create list with the correct number of entries */ n = git2r_status_count_untracked(status_list); SET_VECTOR_ELT(list, list_index, sub_list = Rf_allocVector(VECSXP, n)); Rf_setAttrib(sub_list, R_NamesSymbol, sub_list_names = Rf_allocVector(STRSXP, n)); /* i index the entrycount. j index the added change in list */ n = git_status_list_entrycount(status_list); for (; i < n; i++) { const git_status_entry *s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_WT_NEW) { SET_VECTOR_ELT( sub_list, j, Rf_mkString(s->index_to_workdir->old_file.path)); SET_STRING_ELT(sub_list_names, j, Rf_mkChar("untracked")); j++; } } } /** * Get state of the repository working directory and the staging area. * * @param repo S3 class git_repository * @param staged Include staged files. * @param unstaged Include unstaged files. * @param untracked Include untracked files and directories. * @param all_untracked Shows individual files in untracked * directories if 'untracked' is 'TRUE'. * @param ignored Include ignored files. * @return VECXSP with status */ SEXP attribute_hidden git2r_status_list( SEXP repo, SEXP staged, SEXP unstaged, SEXP untracked, SEXP all_untracked, SEXP ignored) { int error, nprotect = 0; size_t i=0, count; SEXP list = R_NilValue; SEXP list_names = R_NilValue; git_repository *repository; git_status_list *status_list = NULL; git_status_options opts = GIT_STATUS_OPTIONS_INIT; if (git2r_arg_check_logical(staged)) git2r_error(__func__, NULL, "'staged'", git2r_err_logical_arg); if (git2r_arg_check_logical(unstaged)) git2r_error(__func__, NULL, "'unstaged'", git2r_err_logical_arg); if (git2r_arg_check_logical(untracked)) git2r_error(__func__, NULL, "'untracked'", git2r_err_logical_arg); if (git2r_arg_check_logical(all_untracked)) git2r_error(__func__, NULL, "'all_untracked'", git2r_err_logical_arg); if (git2r_arg_check_logical(ignored)) git2r_error(__func__, NULL, "'ignored'", git2r_err_logical_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY; if (LOGICAL(untracked)[0]) { opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; if (LOGICAL(all_untracked)[0]) opts.flags |= GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; } if (LOGICAL(ignored)[0]) opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED; error = git_status_list_new(&status_list, repository, &opts); if (error) goto cleanup; count = LOGICAL(staged)[0] + LOGICAL(unstaged)[0] + LOGICAL(untracked)[0] + LOGICAL(ignored)[0]; PROTECT(list = Rf_allocVector(VECSXP, count)); nprotect++; Rf_setAttrib(list, R_NamesSymbol, list_names = Rf_allocVector(STRSXP, count)); if (LOGICAL(staged)[0]) { SET_STRING_ELT(list_names, i, Rf_mkChar("staged")); git2r_status_list_staged(list, i, status_list); i++; } if (LOGICAL(unstaged)[0]) { SET_STRING_ELT(list_names, i, Rf_mkChar("unstaged")); git2r_status_list_unstaged(list, i, status_list); i++; } if (LOGICAL(untracked)[0]) { SET_STRING_ELT(list_names, i, Rf_mkChar("untracked")); git2r_status_list_untracked(list, i, status_list); i++; } if (LOGICAL(ignored)[0]) { SET_STRING_ELT(list_names, i, Rf_mkChar("ignored")); git2r_status_list_ignored(list, i, status_list); } cleanup: git_status_list_free(status_list); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return list; } git2r/src/git2r_tag.c0000644000175000017500000002525113671131056014260 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_blob.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" #include "git2r_tag.h" #include "git2r_tree.h" /** * Init slots in S3 class git_tag * * @param source a tag * @param repo S3 class git_repository that contains the tag * @param dest S3 class git_tag to initialize * @return void */ void attribute_hidden git2r_tag_init( git_tag *source, SEXP repo, SEXP dest) { const git_signature *tagger; const git_oid *oid; char sha[GIT_OID_HEXSZ + 1]; char target[GIT_OID_HEXSZ + 1]; oid = git_tag_id(source); git_oid_tostr(sha, sizeof(sha), oid); SET_VECTOR_ELT(dest, git2r_S3_item__git_tag__sha, Rf_mkString(sha)); SET_VECTOR_ELT( dest, git2r_S3_item__git_tag__message, Rf_mkString(git_tag_message(source))); SET_VECTOR_ELT( dest, git2r_S3_item__git_tag__name, Rf_mkString(git_tag_name(source))); tagger = git_tag_tagger(source); if (tagger) { if (Rf_isNull(VECTOR_ELT(dest, git2r_S3_item__git_tag__tagger))) { SEXP item; SET_VECTOR_ELT( dest, git2r_S3_item__git_tag__tagger, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_signature)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_signature)); } git2r_signature_init( tagger, VECTOR_ELT(dest, git2r_S3_item__git_tag__tagger)); } oid = git_tag_target_id(source); git_oid_tostr(target, sizeof(target), oid);; SET_VECTOR_ELT(dest, git2r_S3_item__git_tag__target, Rf_mkString(target)); SET_VECTOR_ELT(dest, git2r_S3_item__git_tag__repo, Rf_duplicate(repo)); } /** * Create tag targeting HEAD commit in repository. * * @param repo S3 class git_repository * @param name Name for the tag. * @param message The tag message. * @param tagger The tagger (author) of the tag * @param force Overwrite existing tag. * @return S3 object of class git_tag or git_commit */ SEXP attribute_hidden git2r_tag_create( SEXP repo, SEXP name, SEXP message, SEXP tagger, SEXP force) { SEXP result = R_NilValue; int error, nprotect = 0, overwrite = 0; git_oid oid; git_repository *repository = NULL; git_signature *sig_tagger = NULL; git_tag *tag = NULL; git_object *target = NULL; git_commit *commit = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (!Rf_isNull(message)) { if (git2r_arg_check_string(message)) git2r_error(__func__, NULL, "'message'", git2r_err_string_arg); if (git2r_arg_check_signature(tagger)) git2r_error(__func__, NULL, "'tagger'", git2r_err_signature_arg); } if (git2r_arg_check_logical(force)) git2r_error(__func__, NULL, "'force'", git2r_err_logical_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_revparse_single(&target, repository, "HEAD^{commit}"); if (error) goto cleanup; if (LOGICAL(force)[0]) overwrite = 1; if (Rf_isNull(message)) { error = git_tag_create_lightweight( &oid, repository, CHAR(STRING_ELT(name, 0)), target, overwrite); if (error) goto cleanup; error = git_commit_lookup(&commit, repository, &oid); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init(commit, repo, result); } else { error = git2r_signature_from_arg(&sig_tagger, tagger); if (error) goto cleanup; error = git_tag_create( &oid, repository, CHAR(STRING_ELT(name, 0)), target, sig_tagger, CHAR(STRING_ELT(message, 0)), overwrite); if (error) goto cleanup; error = git_tag_lookup(&tag, repository, &oid); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_tag)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tag)); git2r_tag_init(tag, repo, result); } cleanup: git_commit_free(commit); git_tag_free(tag); git_signature_free(sig_tagger); git_object_free(target); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Delete an existing tag reference. * * @param repo S3 class git_repository * @param name Name of the tag to be deleted * @return R_NilValue */ SEXP attribute_hidden git2r_tag_delete( SEXP repo, SEXP name) { int error; git_repository *repository = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_tag_delete(repository, CHAR(STRING_ELT(name, 0))); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Data structure to hold information when iterating over tags. */ typedef struct { size_t n; git_repository *repository; SEXP repo; SEXP tags; } git2r_tag_foreach_cb_data; /** * Invoked 'callback' for each tag * * @param name The name of the tag * @param oid The id of the tag * @param payload Payload data passed to 'git_tag_foreach' * @return 0 on success, else error code */ static int git2r_tag_foreach_cb( const char *name, git_oid *oid, void *payload) { int error = 0; git_object *object = NULL; git2r_tag_foreach_cb_data *cb_data = (git2r_tag_foreach_cb_data*)payload; /* Check if we have a list to populate */ if (!Rf_isNull(cb_data->tags)) { int skip = 0; SEXP item = R_NilValue, tag; error = git_object_lookup(&object, cb_data->repository, oid, GIT2R_OBJECT_ANY); if (error) goto cleanup; switch (git_object_type(object)) { case GIT2R_OBJECT_COMMIT: PROTECT(item = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); Rf_setAttrib( item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init((git_commit*)object, cb_data->repo, item); break; case GIT2R_OBJECT_TREE: PROTECT(item = Rf_mkNamed(VECSXP, git2r_S3_items__git_tree)); Rf_setAttrib( item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tree)); git2r_tree_init((git_tree*)object, cb_data->repo, item); break; case GIT2R_OBJECT_BLOB: PROTECT(item = Rf_mkNamed(VECSXP, git2r_S3_items__git_blob)); Rf_setAttrib( item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_blob)); git2r_blob_init((git_blob*)object, cb_data->repo, item); break; case GIT2R_OBJECT_TAG: PROTECT(item = Rf_mkNamed(VECSXP, git2r_S3_items__git_tag)); Rf_setAttrib( item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tag)); git2r_tag_init((git_tag*)object, cb_data->repo, item); break; default: git2r_error(__func__, NULL, git2r_err_object_type, NULL); } SET_VECTOR_ELT(cb_data->tags, cb_data->n, item); if (strncmp(name, "refs/tags/", sizeof("refs/tags/") - 1) == 0) skip = strlen("refs/tags/"); PROTECT(tag = Rf_mkChar(name + skip)); SET_STRING_ELT( Rf_getAttrib(cb_data->tags, R_NamesSymbol), cb_data->n, tag); UNPROTECT(2); if (object) git_object_free(object); object = NULL; } cb_data->n += 1; cleanup: git_object_free(object); return error; } /** * Get all tags that can be found in a repository. * * @param repo S3 class git_repository * @return VECXSP with S3 objects of class git_tag */ SEXP attribute_hidden git2r_tag_list( SEXP repo) { int error, nprotect = 0; SEXP result = R_NilValue; git2r_tag_foreach_cb_data cb_data = {0, NULL, R_NilValue, R_NilValue}; git_repository *repository; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); /* Count number of tags before creating the list */ error = git_tag_foreach(repository, &git2r_tag_foreach_cb, &cb_data); if (error) { if (GIT_ENOTFOUND == error) { error = 0; PROTECT(result = Rf_allocVector(VECSXP, 0)); nprotect++; Rf_setAttrib(result, R_NamesSymbol, Rf_allocVector(STRSXP, 0)); } goto cleanup; } PROTECT(result = Rf_allocVector(VECSXP, cb_data.n)); nprotect++; Rf_setAttrib(result, R_NamesSymbol, Rf_allocVector(STRSXP, cb_data.n)); cb_data.n = 0; cb_data.tags = result; cb_data.repo = repo; cb_data.repository = repository; error = git_tag_foreach(repository, &git2r_tag_foreach_cb, &cb_data); cleanup: git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_signature.h0000644000175000017500000000206313273077667015526 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_signature_h #define INCLUDE_git2r_signature_h #include #include #include SEXP git2r_signature_default(SEXP repo); int git2r_signature_from_arg(git_signature **out, SEXP signature); void git2r_signature_init(const git_signature *source, SEXP dest); #endif git2r/src/git2r_checkout.c0000644000175000017500000001044413671131056015310 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_checkout.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" /** * Checkout path * * @param repo S3 class git_repository * @param path The paths to checkout * @return R_NilValue */ SEXP attribute_hidden git2r_checkout_path( SEXP repo, SEXP path) { int error = 0; size_t i, len; git_repository *repository = NULL; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; if (git2r_arg_check_string_vec(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_vec_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); /* Count number of non NA values */ len = Rf_length(path); for (i = 0; i < len; i++) if (NA_STRING != STRING_ELT(path, i)) opts.paths.count++; /* We are done if no non-NA values */ if (!opts.paths.count) goto cleanup; /* Allocate the strings in pathspec */ opts.paths.strings = malloc(opts.paths.count * sizeof(char*)); if (!opts.paths.strings) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_alloc_memory_buffer); error = GIT_ERROR; goto cleanup; } /* Populate the strings in opts.paths */ for (i = 0; i < opts.paths.count; i++) if (NA_STRING != STRING_ELT(path, i)) opts.paths.strings[i] = (char *)CHAR(STRING_ELT(path, i)); opts.checkout_strategy = GIT_CHECKOUT_FORCE; error = git_checkout_head(repository, &opts); cleanup: free(opts.paths.strings); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Checkout tree * * @param repo S3 class git_repository * @param revision The revision string, see * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions * @param force Using checkout strategy GIT_CHECKOUT_SAFE (force = * FALSE) or GIT_CHECKOUT_FORCE (force = TRUE). * @return R_NilValue */ SEXP attribute_hidden git2r_checkout_tree( SEXP repo, SEXP revision, SEXP force) { int error; git_repository *repository = NULL; git_object *treeish = NULL; git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; if (git2r_arg_check_string(revision)) git2r_error(__func__, NULL, "'revision'", git2r_err_string_arg); if (git2r_arg_check_logical(force)) git2r_error(__func__, NULL, "'force'", git2r_err_logical_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_revparse_single(&treeish, repository, CHAR(STRING_ELT(revision, 0))); if (error) goto cleanup; switch (git_object_type(treeish)) { case GIT2R_OBJECT_COMMIT: case GIT2R_OBJECT_TAG: case GIT2R_OBJECT_TREE: error = GIT_OK; break; default: GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_checkout_tree); error = GIT_ERROR; break; } if (error) goto cleanup; if (LOGICAL(force)[0]) checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; else checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; error = git_checkout_tree(repository, treeish, &checkout_opts); cleanup: git_object_free(treeish); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } git2r/src/git2r_tree.h0000644000175000017500000000176013273077667014467 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2018 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_tree_h #define INCLUDE_git2r_tree_h #include #include #include void git2r_tree_init(const git_tree *source, SEXP repo, SEXP dest); SEXP git2r_tree_walk(SEXP tree, SEXP recursive); #endif git2r/src/git2r_clone.c0000644000175000017500000001154613671131056014607 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_clone.h" #include "git2r_cred.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_transfer.h" /** * Show progress of clone * * @param progress The clone progress data * @param payload A pointer to a git2r_clone_data data * structure * @return 0 */ static int git2r_clone_progress( const GIT2R_INDEXER_PROGRESS *progress, void *payload) { int kbytes = progress->received_bytes / 1024; git2r_transfer_data *pd = (git2r_transfer_data*)payload; if (progress->received_objects < progress->total_objects) { int received_percent = (100 * progress->received_objects) / progress->total_objects; if (received_percent > pd->received_progress) { Rprintf("Receiving objects: % 3i%% (%i/%i), %4d kb\n", received_percent, progress->received_objects, progress->total_objects, kbytes); pd->received_progress += 10; } } else if (!pd->received_done) { Rprintf("Receiving objects: 100%% (%i/%i), %4d kb, done.\n", progress->received_objects, progress->total_objects, kbytes); pd->received_done = 1; } return 0; } /** * Clone a remote repository * * @param url the remote repository to clone * @param local_path local directory to clone to * @param bare Create a bare repository. * @param branch The name of the branch to checkout. Default is NULL * which means to use the remote's default branch. * @param checkout Checkout HEAD after the clone is complete. * @param credentials The credentials for remote repository access. * @param progress show progress * @return R_NilValue */ SEXP attribute_hidden git2r_clone( SEXP url, SEXP local_path, SEXP bare, SEXP branch, SEXP checkout, SEXP credentials, SEXP progress) { int error; git_repository *repository = NULL; git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT; if (git2r_arg_check_string(url)) git2r_error(__func__, NULL, "'url'", git2r_err_string_arg); if (git2r_arg_check_string(local_path)) git2r_error(__func__, NULL, "'local_path'", git2r_err_string_arg); if (git2r_arg_check_logical(bare)) git2r_error(__func__, NULL, "'bare'", git2r_err_logical_arg); if ((!Rf_isNull(branch)) && git2r_arg_check_string(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_string_arg); if (git2r_arg_check_logical(checkout)) git2r_error(__func__, NULL, "'checkout'", git2r_err_logical_arg); if (git2r_arg_check_credentials(credentials)) git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg); if (git2r_arg_check_logical(progress)) git2r_error(__func__, NULL, "'progress'", git2r_err_logical_arg); if (LOGICAL(checkout)[0]) checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; else checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; clone_opts.checkout_opts = checkout_opts; payload.credentials = credentials; clone_opts.fetch_opts.callbacks.payload = &payload; clone_opts.fetch_opts.callbacks.credentials = &git2r_cred_acquire_cb; if (LOGICAL(bare)[0]) clone_opts.bare = 1; if (!Rf_isNull(branch)) clone_opts.checkout_branch = CHAR(STRING_ELT(branch, 0)); if (LOGICAL(progress)[0]) { clone_opts.fetch_opts.callbacks.transfer_progress = &git2r_clone_progress; Rprintf("cloning into '%s'...\n", CHAR(STRING_ELT(local_path, 0))); } error = git_clone(&repository, CHAR(STRING_ELT(url, 0)), CHAR(STRING_ELT(local_path, 0)), &clone_opts); git_repository_free(repository); if (error) git2r_error( __func__, GIT2R_ERROR_LAST(), git2r_err_unable_to_authenticate, NULL); return R_NilValue; } git2r/src/git2r_repository.c0000644000175000017500000003770314076202707015733 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_blob.h" #include "git2r_branch.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" #include "git2r_tag.h" #include "git2r_tree.h" /** * Get repo from S3 class git_repository * * @param repo S3 class git_repository * @return a git_repository pointer on success else NULL */ attribute_hidden git_repository* git2r_repository_open( SEXP repo) { int error; SEXP path; git_repository *repository = NULL; if (git2r_arg_check_repository(repo)) { Rprintf("The repo argument is unexpectedly invalid\n"); return NULL; } path = git2r_get_list_element(repo, "path"); error = git_repository_open(&repository, CHAR(STRING_ELT(path, 0))); if (error) { if (error == GIT_ENOTFOUND) Rf_warning("Could not find repository at path '%s'", CHAR(STRING_ELT(path, 0))); else Rf_warning("Unable to open repository: %s", GIT2R_ERROR_LAST()->message); git_repository_free(repository); return NULL; } return repository; } /** * Data structure to hold information when iterating over FETCH_HEAD * entries. */ typedef struct { size_t n; SEXP list; SEXP repo; } git2r_fetch_head_cb_data; /** * Invoked 'callback' for each entry in the given FETCH_HEAD file. * * @param ref_name The name of the ref. * @param remote_url The url of the remote. * @param oid The id of the remote head that were updated during the * last fetch. * @param is_merge Is head for merge. * @return 0 */ static int git2r_repository_fetchhead_foreach_cb( const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload) { git2r_fetch_head_cb_data *cb_data = (git2r_fetch_head_cb_data*)payload; /* Check if we have a list to populate */ if (!Rf_isNull(cb_data->list)) { char sha[GIT_OID_HEXSZ + 1]; SEXP fetch_head; PROTECT(fetch_head = Rf_mkNamed(VECSXP, git2r_S3_items__git_fetch_head)); Rf_setAttrib( fetch_head, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_fetch_head)); SET_VECTOR_ELT( fetch_head, git2r_S3_item__git_fetch_head__ref_name, Rf_mkString(ref_name)); SET_VECTOR_ELT( fetch_head, git2r_S3_item__git_fetch_head__remote_url, Rf_mkString(remote_url)); git_oid_tostr(sha, sizeof(sha), oid); SET_VECTOR_ELT( fetch_head, git2r_S3_item__git_fetch_head__sha, Rf_mkString(sha)); SET_VECTOR_ELT( fetch_head, git2r_S3_item__git_fetch_head__is_merge, Rf_ScalarLogical(is_merge)); SET_VECTOR_ELT( fetch_head, git2r_S3_item__git_fetch_head__repo, Rf_duplicate(cb_data->repo)); SET_VECTOR_ELT(cb_data->list, cb_data->n, fetch_head); UNPROTECT(1); } cb_data->n += 1; return 0; } /** * Get entries in FETCH_HEAD file * * @param repo S3 class git_repository * @return list with the S3 class git_fetch_head entries. R_NilValue * if there is no FETCH_HEAD file. */ SEXP attribute_hidden git2r_repository_fetch_heads( SEXP repo) { int error, nprotect = 0; SEXP result = R_NilValue; git2r_fetch_head_cb_data cb_data = {0, R_NilValue, R_NilValue}; git_repository *repository = NULL; repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); /* Count number of fetch heads before creating the list */ error = git_repository_fetchhead_foreach( repository, git2r_repository_fetchhead_foreach_cb, &cb_data); if (error) { if (GIT_ENOTFOUND == error) error = GIT_OK; goto cleanup; } PROTECT(result = Rf_allocVector(VECSXP, cb_data.n)); nprotect++; cb_data.n = 0; cb_data.list = result; cb_data.repo = repo; error = git_repository_fetchhead_foreach( repository, git2r_repository_fetchhead_foreach_cb, &cb_data); cleanup: git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get head of repository * * @param repo S3 class git_repository * @return R_NilValue if unborn branch or not found. S3 class * git_branch if not a detached head. S3 class git_commit if detached * head */ SEXP attribute_hidden git2r_repository_head( SEXP repo) { int error, nprotect = 0; SEXP result = R_NilValue; git_commit *commit = NULL; git_reference *reference = NULL; git_repository *repository = NULL; repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_repository_head(&reference, repository); if (error) { if (GIT_EUNBORNBRANCH == error || GIT_ENOTFOUND == error) error = GIT_OK; goto cleanup; } if (git_reference_is_branch(reference)) { git_branch_t type = GIT_BRANCH_LOCAL; if (git_reference_is_remote(reference)) type = GIT_BRANCH_REMOTE; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_branch)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_branch)); error = git2r_branch_init(reference, type, repo, result); } else { error = git_commit_lookup( &commit, repository, git_reference_target(reference)); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init(commit, repo, result); } cleanup: git_commit_free(commit); git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Init a repository. * * @param path A path to where to init a git repository * @param bare If TRUE, a Git repository without a working directory * is created at the pointed path. If FALSE, provided path will be * considered as the working directory into which the .git directory * will be created. * @param branch Use the specified name for the initial branch in the * newly created repository. If NULL, fall back to the default name. * @return R_NilValue */ SEXP attribute_hidden git2r_repository_init( SEXP path, SEXP bare, SEXP branch) { int error; git_repository *repository = NULL; git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; if (git2r_arg_check_string(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_arg); if (git2r_arg_check_logical(bare)) git2r_error(__func__, NULL, "'bare'", git2r_err_logical_arg); if (!Rf_isNull(branch) && git2r_arg_check_string(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_string_arg); if (LOGICAL(bare)[0]) opts.flags |= GIT_REPOSITORY_INIT_BARE; if (!Rf_isNull(branch)) opts.initial_head = CHAR(STRING_ELT(branch, 0)); error = git_repository_init_ext(&repository, CHAR(STRING_ELT(path, 0)), &opts); if (error) git2r_error(__func__, NULL, git2r_err_repo_init, NULL); git_repository_free(repository); return R_NilValue; } /** * Check if repository is bare. * * @param repo S3 class git_repository * @return TRUE if bare else FALSE */ SEXP attribute_hidden git2r_repository_is_bare( SEXP repo) { int is_bare; git_repository *repository; repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); is_bare = git_repository_is_bare(repository); git_repository_free(repository); if (is_bare < 0) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return Rf_ScalarLogical(is_bare); } /** * Determine if the repository was a shallow clone. * * @param repo S3 class git_repository * @return TRUE if shallow else FALSE */ SEXP attribute_hidden git2r_repository_is_shallow( SEXP repo) { int is_shallow; git_repository *repository; repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); is_shallow = git_repository_is_shallow(repository); git_repository_free(repository); if (is_shallow < 0) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return Rf_ScalarLogical(is_shallow); } /** * Check if head of repository is detached * * @param repo S3 class git_repository * @return TRUE if detached else FALSE */ SEXP attribute_hidden git2r_repository_head_detached( SEXP repo) { int head_detached; git_repository *repository; repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); head_detached = git_repository_head_detached(repository); git_repository_free(repository); if (head_detached < 0) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return Rf_ScalarLogical(head_detached); } /** * Check if repository is empty. * * @param repo S3 class git_repository * @return TRUE if empty else FALSE */ SEXP attribute_hidden git2r_repository_is_empty( SEXP repo) { int is_empty; git_repository *repository; repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); is_empty = git_repository_is_empty(repository); git_repository_free(repository); if (is_empty < 0) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return Rf_ScalarLogical(is_empty); } /** * Check if valid repository. * * @param path The path to the potential repository * @return TRUE if the repository can be opened else FALSE */ SEXP attribute_hidden git2r_repository_can_open( SEXP path) { int error; git_repository *repository = NULL; if (git2r_arg_check_string(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_arg); error = git_repository_open(&repository, CHAR(STRING_ELT(path, 0))); git_repository_free(repository); if (error) return Rf_ScalarLogical(0); return Rf_ScalarLogical(1); } /** * Make the repository HEAD point to the specified reference. * * @param repo S3 class git_repository * @param ref_name Canonical name of the reference the HEAD should point at * @return R_NilValue */ SEXP attribute_hidden git2r_repository_set_head( SEXP repo, SEXP ref_name) { int error; git_repository *repository = NULL; if (git2r_arg_check_string(ref_name)) git2r_error(__func__, NULL, "'ref_name'", git2r_err_string_arg); if (!git_reference_is_valid_name(CHAR(STRING_ELT(ref_name, 0)))) git2r_error(__func__, NULL, git2r_err_invalid_refname, NULL); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_repository_set_head(repository, CHAR(STRING_ELT(ref_name, 0))); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Make the repository HEAD directly point to the commit. * * @param commit S3 class git_commit * @return R_NilValue */ SEXP attribute_hidden git2r_repository_set_head_detached( SEXP commit) { int error; SEXP sha; git_oid oid; git_commit *treeish = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg); repository = git2r_repository_open(git2r_get_list_element(commit, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(commit, "sha"); error = git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); if (error) goto cleanup; error = git_commit_lookup(&treeish, repository, &oid); if (error) goto cleanup; error = git_repository_set_head_detached( repository, git_commit_id(treeish)); cleanup: git_commit_free(treeish); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Get workdir of repository. * * @param repo S3 class git_repository * @return R_NilValue if bare repository, else character vector * of length one with path. */ SEXP attribute_hidden git2r_repository_workdir( SEXP repo) { int nprotect = 0; SEXP result = R_NilValue; git_repository *repository; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); if (!git_repository_is_bare(repository)) { const char *wd = git_repository_workdir(repository); PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(wd)); } git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); return result; } /** * Find repository base path for given path * * @param path A character vector specifying the path to a file or folder * @param ceiling The lookup will stop when this absolute path is reached. * @return R_NilValue if repository cannot be found or * a character vector of length one with path to repository's git dir * e.g. /path/to/my/repo/.git */ SEXP attribute_hidden git2r_repository_discover( SEXP path, SEXP ceiling) { int error, nprotect = 0; SEXP result = R_NilValue; git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); const char *ceiling_dirs = NULL; if (git2r_arg_check_string(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_arg); if (!Rf_isNull(ceiling)) { if (git2r_arg_check_string(ceiling)) git2r_error(__func__, NULL, "'ceiling'", git2r_err_string_arg); ceiling_dirs = CHAR(STRING_ELT(ceiling, 0)); } /* note that across_fs (arg #3) is set to 0 so this will stop when * a filesystem device change is detected while exploring parent * directories */ error = git_repository_discover( &buf, CHAR(STRING_ELT(path, 0)), 0, ceiling_dirs); if (error) { /* NB just return R_NilValue if we can't discover the repo */ if (GIT_ENOTFOUND == error) error = GIT_OK; goto cleanup; } PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(buf.ptr)); cleanup: GIT2R_BUF_DISPOSE(&buf); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_branch.h0000644000175000017500000000311313273077667014757 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_branch_h #define INCLUDE_git2r_branch_h #include #include #include SEXP git2r_branch_canonical_name(SEXP branch); SEXP git2r_branch_create(SEXP branch_name, SEXP commit, SEXP force); SEXP git2r_branch_delete(SEXP branch); int git2r_branch_init( const git_reference *source, git_branch_t type, SEXP repo, SEXP dest); SEXP git2r_branch_is_head(SEXP branch); SEXP git2r_branch_list(SEXP repo, SEXP flags); SEXP git2r_branch_remote_name(SEXP branch); SEXP git2r_branch_remote_url(SEXP branch); SEXP git2r_branch_rename(SEXP branch, SEXP new_branch_name, SEXP force); SEXP git2r_branch_target(SEXP branch); SEXP git2r_branch_get_upstream(SEXP branch); SEXP git2r_branch_set_upstream(SEXP branch, SEXP upstream_name); SEXP git2r_branch_upstream_canonical_name(SEXP branch); #endif git2r/src/git2r_revparse.c0000644000175000017500000000734013671131056015333 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_blob.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_tag.h" #include "git2r_tree.h" /** * Find object specified by revision * * @param repo S3 class git_repository * @param revision The revision string, see * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions * @return S3 object of class git_commit, git_tag or git_tree. */ SEXP attribute_hidden git2r_revparse_single( SEXP repo, SEXP revision) { int error, nprotect = 0; SEXP result = R_NilValue; git_repository *repository = NULL; git_object *treeish = NULL; if (git2r_arg_check_string(revision)) git2r_error(__func__, NULL, "'revision'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_revparse_single( &treeish, repository, CHAR(STRING_ELT(revision, 0))); if (error) goto cleanup; switch (git_object_type(treeish)) { case GIT2R_OBJECT_BLOB: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_blob)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_blob)); git2r_blob_init((git_blob*)treeish, repo, result); break; case GIT2R_OBJECT_COMMIT: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init((git_commit*)treeish, repo, result); break; case GIT2R_OBJECT_TAG: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_tag)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tag)); git2r_tag_init((git_tag*)treeish, repo, result); break; case GIT2R_OBJECT_TREE: PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_tree)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tree)); git2r_tree_init((git_tree*)treeish, repo, result); break; default: GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_revparse_single); error = GIT_ERROR; break; } cleanup: git_object_free(treeish); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) { if (GIT_ENOTFOUND == error) { git2r_error( __func__, NULL, git2r_err_revparse_not_found, NULL); } else { git2r_error( __func__, GIT2R_ERROR_LAST(), NULL, NULL); } } return result; } git2r/src/git2r_diff.c0000644000175000017500000010555413765411706014431 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_diff.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_tree.h" #include #include #include #include #include static int git2r_diff_count( git_diff *diff, size_t *num_files, size_t *max_hunks, size_t *max_lines); static int git2r_diff_format_to_r( git_diff *diff, SEXP dest); SEXP git2r_diff_index_to_wd( SEXP repo, SEXP filename, git_diff_options *opts); SEXP git2r_diff_head_to_index( SEXP repo, SEXP filename, git_diff_options *opts); SEXP git2r_diff_tree_to_wd( SEXP tree, SEXP filename, git_diff_options *opts); SEXP git2r_diff_tree_to_index( SEXP tree, SEXP filename, git_diff_options *opts); SEXP git2r_diff_tree_to_tree( SEXP tree1, SEXP tree2, SEXP filename, git_diff_options *opts); /** * Diff * * Setting index to TRUE is essentially like supplying the --cached * option to command line git. * * - If tree1 is NULL and index is FALSE, then we compare the working * directory to the index. (tree2 must be NULL in this case.) * - If tree1 is NULL and index is TRUE, then we compare the index * to HEAD. (tree2 must be NULL in this case.) * - If tree1 is not NULL and tree2 is NULL, and index is FALSE, * then we compare the working directory to the tree1. * (repo must be NULL in this case.) * - If tree1 is not NULL and tree2 is NULL, and index is TRUE, * then we compare the index to tree1. (repo must be NULL.) * - If tree1 is not NULL and tree2 is not NULL, then we compare * tree1 to tree2. (repo must be NULL, index is ignored in this case). * * @param repo Repository. * @param tree1 The first tree to compare. * @param tree2 The second tree to compare. * @param index Whether to compare to the index. * @param filename Determines where to write the diff. If filename is * R_NilValue, then the diff is written to a S3 class git_diff * object. If filename is a character vector of length 0, then the * diff is written to a character vector. If filename is a character * vector of length one with non-NA value, the diff is written to a * file with name filename (the file is overwritten if it exists). * @param context_lines The number of unchanged lines that define the * boundary of a hunk (and to display before and after). * @param interhunk_lines The maximum number of unchanged lines * between hunk boundaries before the hunks will be merged into one. * @param old_prefix The virtual "directory" prefix for old file * names in hunk headers. * @param new_prefix The virtual "directory" prefix for new file * names in hunk headers. * @param id_abbrev The abbreviation length to use when formatting * object ids. Defaults to the value of 'core.abbrev' from the * config, or 7 if NULL. * @param path A character vector of paths / fnmatch patterns to * constrain diff. Default is NULL which include all paths. * @param max_size A size (in bytes) above which a blob will be * marked as binary automatically; pass a negative value to * disable. Defaults to 512MB when max_size is NULL. * @return A S3 class git_diff object if filename equals R_NilValue. A * character vector with diff if filename has length 0. Oterwise NULL. */ SEXP attribute_hidden git2r_diff( SEXP repo, SEXP tree1, SEXP tree2, SEXP index, SEXP filename, SEXP context_lines, SEXP interhunk_lines, SEXP old_prefix, SEXP new_prefix, SEXP id_abbrev, SEXP path, SEXP max_size) { int c_index; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; if (git2r_arg_check_logical(index)) git2r_error(__func__, NULL, "'index'", git2r_err_logical_arg); c_index = LOGICAL(index)[0]; if (git2r_arg_check_integer_gte_zero(context_lines)) git2r_error(__func__, NULL, "'context_lines'", git2r_err_integer_gte_zero_arg); opts.context_lines = INTEGER(context_lines)[0]; if (git2r_arg_check_integer_gte_zero(interhunk_lines)) git2r_error(__func__, NULL, "'interhunk_lines'", git2r_err_integer_gte_zero_arg); opts.interhunk_lines = INTEGER(interhunk_lines)[0]; if (git2r_arg_check_string(old_prefix)) git2r_error(__func__, NULL, "'old_prefix'", git2r_err_string_arg); opts.old_prefix = CHAR(STRING_ELT(old_prefix, 0)); if (git2r_arg_check_string(new_prefix)) git2r_error(__func__, NULL, "'new_prefix'", git2r_err_string_arg); opts.new_prefix = CHAR(STRING_ELT(new_prefix, 0)); if (!Rf_isNull(id_abbrev)) { if (git2r_arg_check_integer_gte_zero(id_abbrev)) git2r_error(__func__, NULL, "'id_abbrev'", git2r_err_integer_gte_zero_arg); opts.id_abbrev = INTEGER(id_abbrev)[0]; } if (!Rf_isNull(path)) { int error; if (git2r_arg_check_string_vec(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_vec_arg); error = git2r_copy_string_vec(&(opts.pathspec), path); if (error || !opts.pathspec.count) { free(opts.pathspec.strings); git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); } } if (!Rf_isNull(max_size)) { if (git2r_arg_check_integer(max_size)) git2r_error(__func__, NULL, "'max_size'", git2r_err_integer_arg); opts.max_size = INTEGER(max_size)[0]; } if (Rf_isNull(tree1) && ! c_index) { if (!Rf_isNull(tree2)) git2r_error(__func__, NULL, git2r_err_diff_arg, NULL); return git2r_diff_index_to_wd(repo, filename, &opts); } if (Rf_isNull(tree1) && c_index) { if (!Rf_isNull(tree2)) git2r_error(__func__, NULL, git2r_err_diff_arg, NULL); return git2r_diff_head_to_index(repo, filename, &opts); } if (!Rf_isNull(tree1) && Rf_isNull(tree2) && !c_index) { if (!Rf_isNull(repo)) git2r_error(__func__, NULL, git2r_err_diff_arg, NULL); return git2r_diff_tree_to_wd(tree1, filename, &opts); } if (!Rf_isNull(tree1) && Rf_isNull(tree2) && c_index) { if (!Rf_isNull(repo)) git2r_error(__func__, NULL, git2r_err_diff_arg, NULL); return git2r_diff_tree_to_index(tree1, filename, &opts); } if (!Rf_isNull(repo)) git2r_error(__func__, NULL, git2r_err_diff_arg, NULL); return git2r_diff_tree_to_tree(tree1, tree2, filename, &opts); } static int git2r_diff_print_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { int error; GIT2R_UNUSED(delta); GIT2R_UNUSED(hunk); if (line->origin == GIT_DIFF_LINE_CONTEXT || line->origin == GIT_DIFF_LINE_ADDITION || line->origin == GIT_DIFF_LINE_DELETION) { while ((error = fputc(line->origin, (FILE *)payload)) == EINTR) continue; if (error == EOF) return -1; } if (fwrite(line->content, line->content_len, 1, (FILE *)payload) != 1) return -1; return 0; } /** * Create a diff between the repository index and the workdir * directory. * * @param repo S3 class git_repository * @param filename Determines where to write the diff. If filename is * R_NilValue, then the diff is written to a S3 class git_diff * object. If filename is a character vector of length 0, then the * diff is written to a character vector. If filename is a character * vector of length one with non-NA value, the diff is written to a * file with name filename (the file is overwritten if it exists). * @param opts Structure describing options about how the diff * should be executed. * @return A S3 class git_diff object if filename equals R_NilValue. A * character vector with diff if filename has length 0. Oterwise NULL. */ SEXP attribute_hidden git2r_diff_index_to_wd( SEXP repo, SEXP filename, git_diff_options *opts) { int error, nprotect = 0; git_repository *repository = NULL; git_diff *diff = NULL; SEXP result = R_NilValue; if (git2r_arg_check_filename(filename)) git2r_error(__func__, NULL, "'filename'", git2r_err_filename_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_diff_index_to_workdir(&diff, repository, /*index=*/ NULL, opts); if (error) goto cleanup; if (Rf_isNull(filename)) { PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff)); SET_VECTOR_ELT(result, git2r_S3_item__git_diff__old, Rf_mkString("index")); SET_VECTOR_ELT(result, git2r_S3_item__git_diff__new, Rf_mkString("workdir")); error = git2r_diff_format_to_r(diff, result); } else if (0 == Rf_length(filename)) { git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); error = git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH); if (!error) { PROTECT(result = Rf_mkString(buf.ptr)); nprotect++; } GIT2R_BUF_DISPOSE(&buf); } else { FILE *fp = fopen(CHAR(STRING_ELT(filename, 0)), "w+"); error = git_diff_print( diff, GIT_DIFF_FORMAT_PATCH, git2r_diff_print_cb, fp); if (fp) fclose(fp); } cleanup: free(opts->pathspec.strings); git_diff_free(diff); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Create a diff between head and repository index * * @param repo S3 class git_repository * @param filename Determines where to write the diff. If filename is * R_NilValue, then the diff is written to a S3 class git_diff * object. If filename is a character vector of length 0, then the * diff is written to a character vector. If filename is a character * vector of length one with non-NA value, the diff is written to a * file with name filename (the file is overwritten if it exists). * @param opts Structure describing options about how the diff * should be executed. * @return A S3 class git_diff object if filename equals R_NilValue. A * character vector with diff if filename has length 0. Oterwise NULL. */ SEXP attribute_hidden git2r_diff_head_to_index( SEXP repo, SEXP filename, git_diff_options *opts) { int error, nprotect = 0; git_repository *repository = NULL; git_diff *diff = NULL; git_object *obj = NULL; git_tree *head = NULL; SEXP result = R_NilValue; if (git2r_arg_check_filename(filename)) git2r_error(__func__, NULL, "'filename'", git2r_err_filename_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_revparse_single(&obj, repository, "HEAD^{tree}"); if (error) goto cleanup; error = git_tree_lookup(&head, repository, git_object_id(obj)); if (error) goto cleanup; error = git_diff_tree_to_index( &diff, repository, head, /* index= */ NULL, opts); if (error) goto cleanup; if (Rf_isNull(filename)) { /* TODO: object instead of HEAD string */ PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff)); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__old, Rf_mkString("HEAD")); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__new, Rf_mkString("index")); error = git2r_diff_format_to_r(diff, result); } else if (0 == Rf_length(filename)) { git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); error = git_diff_to_buf( &buf, diff, GIT_DIFF_FORMAT_PATCH); if (!error) { PROTECT(result = Rf_mkString(buf.ptr)); nprotect++; } GIT2R_BUF_DISPOSE(&buf); } else { FILE *fp = fopen(CHAR(STRING_ELT(filename, 0)), "w+"); error = git_diff_print( diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_file_handle, fp); if (fp) fclose(fp); } cleanup: free(opts->pathspec.strings); git_tree_free(head); git_object_free(obj); git_diff_free(diff); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Create a diff between a tree and the working directory * * @param tree S3 class git_tree * @param filename Determines where to write the diff. If filename is * R_NilValue, then the diff is written to a S3 class git_diff * object. If filename is a character vector of length 0, then the * diff is written to a character vector. If filename is a character * vector of length one with non-NA value, the diff is written to a * file with name filename (the file is overwritten if it exists). * @param opts Structure describing options about how the diff * should be executed. * @return A S3 class git_diff object if filename equals R_NilValue. A * character vector with diff if filename has length 0. Oterwise NULL. */ SEXP attribute_hidden git2r_diff_tree_to_wd( SEXP tree, SEXP filename, git_diff_options *opts) { int error, nprotect = 0; git_repository *repository = NULL; git_diff *diff = NULL; git_object *obj = NULL; git_tree *c_tree = NULL; SEXP sha; SEXP result = R_NilValue; SEXP repo; if (git2r_arg_check_tree(tree)) git2r_error(__func__, NULL, "'tree'", git2r_err_tree_arg); if (git2r_arg_check_filename(filename)) git2r_error(__func__, NULL, "'filename'", git2r_err_filename_arg); repo = git2r_get_list_element(tree, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(tree, "sha"); error = git_revparse_single(&obj, repository, CHAR(STRING_ELT(sha, 0))); if (error) goto cleanup; error = git_tree_lookup(&c_tree, repository, git_object_id(obj)); if (error) goto cleanup; error = git_diff_tree_to_workdir(&diff, repository, c_tree, opts); if (error) goto cleanup; if (Rf_isNull(filename)) { PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff)); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__old, tree); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__new, Rf_mkString("workdir")); error = git2r_diff_format_to_r(diff, result); } else if (0 == Rf_length(filename)) { git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); error = git_diff_to_buf( &buf, diff, GIT_DIFF_FORMAT_PATCH); if (!error) { PROTECT(result = Rf_mkString(buf.ptr)); nprotect++; } GIT2R_BUF_DISPOSE(&buf); } else { FILE *fp = fopen(CHAR(STRING_ELT(filename, 0)), "w+"); error = git_diff_print( diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_file_handle, fp); if (fp) fclose(fp); } cleanup: free(opts->pathspec.strings); git_diff_free(diff); git_tree_free(c_tree); git_object_free(obj); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Create a diff between a tree and repository index * * @param tree S3 class git_tree * @param filename Determines where to write the diff. If filename is * R_NilValue, then the diff is written to a S3 class git_diff * object. If filename is a character vector of length 0, then the * diff is written to a character vector. If filename is a character * vector of length one with non-NA value, the diff is written to a * file with name filename (the file is overwritten if it exists). * @param opts Structure describing options about how the diff * should be executed. * @return A S3 class git_diff object if filename equals R_NilValue. A * character vector with diff if filename has length 0. Oterwise NULL. */ SEXP attribute_hidden git2r_diff_tree_to_index( SEXP tree, SEXP filename, git_diff_options *opts) { int error, nprotect = 0; git_repository *repository = NULL; git_diff *diff = NULL; git_object *obj = NULL; git_tree *c_tree = NULL; SEXP sha; SEXP result = R_NilValue; SEXP repo; if (git2r_arg_check_tree(tree)) git2r_error(__func__, NULL, "'tree'", git2r_err_tree_arg); if (git2r_arg_check_filename(filename)) git2r_error(__func__, NULL, "'filename'", git2r_err_filename_arg); repo = git2r_get_list_element(tree, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(tree, "sha"); error = git_revparse_single(&obj, repository, CHAR(STRING_ELT(sha, 0))); if (error) goto cleanup; error = git_tree_lookup(&c_tree, repository, git_object_id(obj)); if (error) goto cleanup; error = git_diff_tree_to_index( &diff, repository, c_tree, /* index= */ NULL, opts); if (error) goto cleanup; if (Rf_isNull(filename)) { PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff)); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__old, tree); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__new, Rf_mkString("index")); error = git2r_diff_format_to_r(diff, result); } else if (0 == Rf_length(filename)) { git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); error = git_diff_to_buf( &buf, diff, GIT_DIFF_FORMAT_PATCH); if (!error) { PROTECT(result = Rf_mkString(buf.ptr)); nprotect++; } GIT2R_BUF_DISPOSE(&buf); } else { FILE *fp = fopen(CHAR(STRING_ELT(filename, 0)), "w+"); error = git_diff_print( diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_file_handle, fp); if (fp) fclose(fp); } cleanup: free(opts->pathspec.strings); git_diff_free(diff); git_tree_free(c_tree); git_object_free(obj); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Create a diff with the difference between two tree objects * * @param tree1 S3 class git_tree * @param tree2 S3 class git_tree * @param filename Determines where to write the diff. If filename is * R_NilValue, then the diff is written to a S3 class git_diff * object. If filename is a character vector of length 0, then the * diff is written to a character vector. If filename is a character * vector of length one with non-NA value, the diff is written to a * file with name filename (the file is overwritten if it exists). * @param opts Structure describing options about how the diff * should be executed. * @return A S3 class git_diff object if filename equals R_NilValue. A * character vector with diff if filename has length 0. Oterwise NULL. */ SEXP attribute_hidden git2r_diff_tree_to_tree( SEXP tree1, SEXP tree2, SEXP filename, git_diff_options *opts) { int error, nprotect = 0; git_repository *repository = NULL; git_diff *diff = NULL; git_object *obj1 = NULL, *obj2 = NULL; git_tree *c_tree1 = NULL, *c_tree2 = NULL; SEXP result = R_NilValue; SEXP tree1_repo, tree2_repo; SEXP sha1, sha2; if (git2r_arg_check_tree(tree1)) git2r_error(__func__, NULL, "'tree1'", git2r_err_tree_arg); if (git2r_arg_check_tree(tree2)) git2r_error(__func__, NULL, "'tree2'", git2r_err_tree_arg); if (git2r_arg_check_filename(filename)) git2r_error(__func__, NULL, "'filename'", git2r_err_filename_arg); tree1_repo = git2r_get_list_element(tree1, "repo"); tree2_repo = git2r_get_list_element(tree2, "repo"); if (git2r_arg_check_same_repo(tree1_repo, tree2_repo)) git2r_error(__func__, NULL, "'tree1' and 'tree2' not from same repository", NULL); repository = git2r_repository_open(tree1_repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha1 = git2r_get_list_element(tree1, "sha"); error = git_revparse_single(&obj1, repository, CHAR(STRING_ELT(sha1, 0))); if (error) goto cleanup; sha2 = git2r_get_list_element(tree2, "sha"); error = git_revparse_single(&obj2, repository, CHAR(STRING_ELT(sha2, 0))); if (error) goto cleanup; error = git_tree_lookup(&c_tree1, repository, git_object_id(obj1)); if (error) goto cleanup; error = git_tree_lookup(&c_tree2, repository, git_object_id(obj2)); if (error) goto cleanup; error = git_diff_tree_to_tree( &diff, repository, c_tree1, c_tree2, opts); if (error) goto cleanup; if (Rf_isNull(filename)) { PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff)); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__old, tree1); SET_VECTOR_ELT( result, git2r_S3_item__git_diff__new, tree2); error = git2r_diff_format_to_r(diff, result); } else if (0 == Rf_length(filename)) { git_buf buf = GIT_BUF_INIT_CONST(NULL, 0); error = git_diff_to_buf( &buf, diff, GIT_DIFF_FORMAT_PATCH); if (!error) { PROTECT(result = Rf_mkString(buf.ptr)); nprotect++; } GIT2R_BUF_DISPOSE(&buf); } else { FILE *fp = fopen(CHAR(STRING_ELT(filename, 0)), "w+"); error = git_diff_print( diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_file_handle, fp); if (fp) fclose(fp); } cleanup: free(opts->pathspec.strings); git_diff_free(diff); git_tree_free(c_tree1); git_tree_free(c_tree2); git_object_free(obj1); git_object_free(obj2); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Data structure to hold the information when counting diff objects. */ typedef struct { size_t num_files; size_t max_hunks; size_t max_lines; size_t num_hunks; size_t num_lines; } git2r_diff_count_payload; /** * Callback per file in the diff * * @param delta A pointer to the delta data for the file * @param progress Goes from 0 to 1 over the diff * @param payload A pointer to the git2r_diff_count_payload data structure * @return 0 */ static int git2r_diff_count_file_cb( const git_diff_delta *delta, float progress, void *payload) { git2r_diff_count_payload *n = payload; GIT2R_UNUSED(delta); GIT2R_UNUSED(progress); n->num_files += 1; n->num_hunks = n->num_lines = 0; return 0; } /** * Callback per hunk in the diff * * @param delta A pointer to the delta data for the file * @param hunk A pointer to the structure describing a hunk of a diff * @param payload A pointer to the git2r_diff_count_payload data structure * @return 0 */ static int git2r_diff_count_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) { git2r_diff_count_payload *n = payload; GIT2R_UNUSED(delta); GIT2R_UNUSED(hunk); n->num_hunks += 1; if (n->num_hunks > n->max_hunks) { n->max_hunks = n->num_hunks; } n->num_lines = 0; return 0; } /** * Callback per text diff line * * @param delta A pointer to the delta data for the file * @param hunk A pointer to the structure describing a hunk of a diff * @param line A pointer to the structure describing a line (or data * span) of a diff. * @param payload A pointer to the git2r_diff_count_payload data structure * @return 0 */ static int git2r_diff_count_line_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { git2r_diff_count_payload *n = payload; GIT2R_UNUSED(delta); GIT2R_UNUSED(hunk); GIT2R_UNUSED(line); n->num_lines += 1; if (n->num_lines > n->max_lines) n->max_lines = n->num_lines; return 0; } /** * Count diff objects * * @param diff Pointer to the diff * @param num_files Pointer where to save the number of files * @param max_hunks Pointer where to save the maximum number of hunks * @param max_lines Pointer where to save the maximum number of lines * @return 0 if OK, else -1 */ static int git2r_diff_count( git_diff *diff, size_t *num_files, size_t *max_hunks, size_t *max_lines) { int error; git2r_diff_count_payload n = { 0, 0, 0 }; error = git_diff_foreach(diff, git2r_diff_count_file_cb, /* binary_cb */ NULL, git2r_diff_count_hunk_cb, git2r_diff_count_line_cb, /* payload= */ (void*) &n); if (error) return -1; *num_files = n.num_files; *max_hunks = n.max_hunks; *max_lines = n.max_lines; return 0; } /** * Data structure to hold the callback information when generating * diff objects. */ typedef struct { SEXP result; SEXP hunk_tmp; SEXP line_tmp; size_t file_ptr, hunk_ptr, line_ptr; } git2r_diff_payload; static int git2r_diff_get_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload); static int git2r_diff_get_line_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload); /** * Callback per file in the diff * * @param delta A pointer to the delta data for the file * @param progress Goes from 0 to 1 over the diff * @param payload A pointer to the git2r_diff_payload data structure * @return 0 */ static int git2r_diff_get_file_cb( const git_diff_delta *delta, float progress, void *payload) { git2r_diff_payload *p = (git2r_diff_payload *) payload; GIT2R_UNUSED(progress); /* Save previous hunk's lines in hunk_tmp, we just call the hunk callback, with a NULL hunk */ git2r_diff_get_hunk_cb(delta, /* hunk= */ 0, payload); /* Save the previous file's hunks from the hunk_tmp temporary storage. */ if (p->file_ptr != 0) { SEXP hunks; size_t len=p->hunk_ptr, i; SET_VECTOR_ELT( VECTOR_ELT(p->result, p->file_ptr - 1), git2r_S3_item__git_diff_file__hunks, hunks = Rf_allocVector(VECSXP, p->hunk_ptr)); for (i = 0; i < len ; i++) SET_VECTOR_ELT(hunks, i, VECTOR_ELT(p->hunk_tmp, i)); } /* OK, ready for next file, if any */ if (delta) { SEXP file_obj; PROTECT(file_obj = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff_file)); Rf_setAttrib( file_obj, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff_file)); SET_VECTOR_ELT(p->result, p->file_ptr, file_obj); SET_VECTOR_ELT( file_obj, git2r_S3_item__git_diff_file__old_file, Rf_mkString(delta->old_file.path)); SET_VECTOR_ELT( file_obj, git2r_S3_item__git_diff_file__new_file, Rf_mkString(delta->new_file.path)); p->file_ptr++; p->hunk_ptr = 0; p->line_ptr = 0; UNPROTECT(1); } return 0; } /** * Process a hunk * * First we save the previous hunk, if there was one. Then create an * empty hunk (i.e. without any lines) and put it in the result. * * @param delta A pointer to the delta data for the file * @param hunk A pointer to the structure describing a hunk of a diff * @param payload Pointer to a git2r_diff_payload data structure * @return 0 */ static int git2r_diff_get_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) { git2r_diff_payload *p = (git2r_diff_payload *) payload; GIT2R_UNUSED(delta); /* Save previous hunk's lines in hunk_tmp, from the line_tmp temporary storage. */ if (p->hunk_ptr != 0) { SEXP lines; size_t len=p->line_ptr, i; SET_VECTOR_ELT( VECTOR_ELT(p->hunk_tmp, p->hunk_ptr-1), git2r_S3_item__git_diff_hunk__lines, lines = Rf_allocVector(VECSXP, p->line_ptr)); for (i = 0; i < len; i++) SET_VECTOR_ELT(lines, i, VECTOR_ELT(p->line_tmp, i)); } /* OK, ready for the next hunk, if any */ if (hunk) { SEXP hunk_obj; PROTECT(hunk_obj = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff_hunk)); Rf_setAttrib( hunk_obj, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff_hunk)); SET_VECTOR_ELT( hunk_obj, git2r_S3_item__git_diff_hunk__old_start, Rf_ScalarInteger(hunk->old_start)); SET_VECTOR_ELT( hunk_obj, git2r_S3_item__git_diff_hunk__old_lines, Rf_ScalarInteger(hunk->old_lines)); SET_VECTOR_ELT( hunk_obj, git2r_S3_item__git_diff_hunk__new_start, Rf_ScalarInteger(hunk->new_start)); SET_VECTOR_ELT( hunk_obj, git2r_S3_item__git_diff_hunk__new_lines, Rf_ScalarInteger(hunk->new_lines)); SET_VECTOR_ELT( hunk_obj, git2r_S3_item__git_diff_hunk__header, Rf_mkString(hunk->header)); SET_VECTOR_ELT(p->hunk_tmp, p->hunk_ptr, hunk_obj); UNPROTECT(1); p->hunk_ptr += 1; p->line_ptr = 0; } return 0; } /** * Process a line * * This is easy, just populate a git_diff_line object and * put it in the temporary hunk. * * @param delta A pointer to the delta data for the file * @param hunk A pointer to the structure describing a hunk of a diff * @param line A pointer to the structure describing a line (or data * span) of a diff. * @param payload Pointer to a git2r_diff_payload data structure * @return 0 */ static int git2r_diff_get_line_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { git2r_diff_payload *p = (git2r_diff_payload *) payload; static char short_buffer[200]; char *buffer = short_buffer; SEXP line_obj; GIT2R_UNUSED(delta); GIT2R_UNUSED(hunk); PROTECT(line_obj = Rf_mkNamed(VECSXP, git2r_S3_items__git_diff_line)); Rf_setAttrib( line_obj, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_diff_line)); SET_VECTOR_ELT( line_obj, git2r_S3_item__git_diff_line__origin, Rf_ScalarInteger(line->origin)); SET_VECTOR_ELT( line_obj, git2r_S3_item__git_diff_line__old_lineno, Rf_ScalarInteger(line->old_lineno)); SET_VECTOR_ELT( line_obj, git2r_S3_item__git_diff_line__new_lineno, Rf_ScalarInteger(line->new_lineno)); SET_VECTOR_ELT( line_obj, git2r_S3_item__git_diff_line__num_lines, Rf_ScalarInteger(line->num_lines)); if (line->content_len > sizeof(buffer)) buffer = malloc(line->content_len+1); memcpy(buffer, line->content, line->content_len); buffer[line->content_len] = 0; SET_VECTOR_ELT( line_obj, git2r_S3_item__git_diff_line__content, Rf_mkString(buffer)); if (buffer != short_buffer) free(buffer); SET_VECTOR_ELT(p->line_tmp, p->line_ptr++, line_obj); UNPROTECT(1); return 0; } /** * Format a diff as an R object * * libgit2 has callbacks to walk over the files, hunks and line * of a diff. This means that we need to walk over the diff twice, * if we don't want to reallocate our lists over and over again (or write a * smart list that preallocates memory). * * So we walk over it first and calculate the maximum number of * hunks in a file, and the maximum number of lines in a hunk. * * Then in the second walk, we have a correspondingly allocated * list that we use for temporary storage. * * @param diff Pointer to the diff * @param dest The S3 class git_diff to hold the formated diff * @return 0 if OK, else error code */ static int git2r_diff_format_to_r( git_diff *diff, SEXP dest) { int error, nprotect = 0; git2r_diff_payload payload = { /* result= */ R_NilValue, /* hunk_tmp= */ R_NilValue, /* line_tmp= */ R_NilValue, /* file_ptr= */ 0, /* hunk_ptr= */ 0, /* line_ptr= */ 0 }; size_t num_files, max_hunks, max_lines; error = git2r_diff_count(diff, &num_files, &max_hunks, &max_lines); if (error) return error; SET_VECTOR_ELT( dest, git2r_S3_item__git_diff__files, payload.result = Rf_allocVector(VECSXP, num_files)); PROTECT(payload.hunk_tmp = Rf_allocVector(VECSXP, max_hunks)); nprotect++; PROTECT(payload.line_tmp = Rf_allocVector(VECSXP, max_lines)); nprotect++; error = git_diff_foreach( diff, git2r_diff_get_file_cb, /* binary_cb */ NULL, git2r_diff_get_hunk_cb, git2r_diff_get_line_cb, &payload); if (!error) { /* Need to call them once more, to put in the last lines/hunks/files. */ error = git2r_diff_get_file_cb( /* delta= */ NULL, /* progress= */ 100, &payload); } if (nprotect) UNPROTECT(nprotect); return error; } git2r/src/git2r_arg.h0000644000175000017500000000336713525341463014272 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2019 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_arg_h #define INCLUDE_git2r_arg_h #include #include #include #define GIT2R_UNUSED(x) ((void)(x)) int git2r_arg_check_blob(SEXP arg); int git2r_arg_check_branch(SEXP arg); int git2r_arg_check_commit(SEXP arg); int git2r_arg_check_commit_stash(SEXP arg); int git2r_arg_check_credentials(SEXP arg); int git2r_arg_check_fetch_heads(SEXP arg); int git2r_arg_check_filename(SEXP arg); int git2r_arg_check_sha(SEXP arg); int git2r_arg_check_integer(SEXP arg); int git2r_arg_check_integer_gte_zero(SEXP arg); int git2r_arg_check_list(SEXP arg); int git2r_arg_check_logical(SEXP arg); int git2r_arg_check_note(SEXP arg); int git2r_arg_check_repository(SEXP arg); int git2r_arg_check_same_repo(SEXP arg1, SEXP arg2); int git2r_arg_check_signature(SEXP arg); int git2r_arg_check_string(SEXP arg); int git2r_arg_check_string_vec(SEXP arg); int git2r_arg_check_tag(SEXP arg); int git2r_arg_check_tree(SEXP arg); int git2r_copy_string_vec(git_strarray *dst, SEXP src); #endif git2r/src/git2r_checkout.h0000644000175000017500000000174013137360640015314 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_checkout_h #define INCLUDE_git2r_checkout_h #include #include SEXP git2r_checkout_path(SEXP repo, SEXP path); SEXP git2r_checkout_tree(SEXP repo, SEXP revision, SEXP force); #endif git2r/src/git2r_reset.h0000644000175000017500000000171213137360640014630 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2016 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_reset_h #define INCLUDE_git2r_reset_h #include #include SEXP git2r_reset(SEXP commit, SEXP reset_type); SEXP git2r_reset_default(SEXP repo, SEXP path); #endif git2r/src/git2r_merge.h0000644000175000017500000000205013254030154014573 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2018 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_merge_h #define INCLUDE_git2r_merge_h #include #include SEXP git2r_merge_base(SEXP one, SEXP two); SEXP git2r_merge_branch(SEXP branch, SEXP merger, SEXP commit_on_success, SEXP fail); SEXP git2r_merge_fetch_heads(SEXP fetch_heads, SEXP merger); #endif git2r/src/git2r_index.h0000644000175000017500000000173413137360640014621 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_index_h #define INCLUDE_git2r_index_h #include #include SEXP git2r_index_add_all(SEXP repo, SEXP path, SEXP force); SEXP git2r_index_remove_bypath(SEXP repo, SEXP path); #endif git2r/src/git2r_graph.c0000644000175000017500000001071313671131056014603 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_oid.h" #include "git2r_repository.h" #include "git2r_S3.h" /** * Count the number of unique commits between two commit objects * * @param local The commit for local * @param upstream The commit for upstream * @return Integer vector of length two with the values ahead and * behind. */ SEXP attribute_hidden git2r_graph_ahead_behind( SEXP local, SEXP upstream) { size_t ahead, behind; int error, nprotect = 0; SEXP result = R_NilValue; SEXP local_repo, local_sha; SEXP upstream_repo, upstream_sha; git_oid local_oid, upstream_oid; git_repository *repository = NULL; if (git2r_arg_check_commit(local)) git2r_error(__func__, NULL, "'local'", git2r_err_commit_arg); if (git2r_arg_check_commit(upstream)) git2r_error(__func__, NULL, "'upstream'", git2r_err_commit_arg); local_repo = git2r_get_list_element(local, "repo"); upstream_repo = git2r_get_list_element(upstream, "repo"); if (git2r_arg_check_same_repo(local_repo, upstream_repo)) git2r_error(__func__, NULL, "'local' and 'upstream' not from same repository", NULL); repository = git2r_repository_open(local_repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); local_sha = git2r_get_list_element(local, "sha"); git2r_oid_from_sha_sexp(local_sha, &local_oid); upstream_sha = git2r_get_list_element(upstream, "sha"); git2r_oid_from_sha_sexp(upstream_sha, &upstream_oid); error = git_graph_ahead_behind(&ahead, &behind, repository, &local_oid, &upstream_oid); if (error) goto cleanup; PROTECT(result = Rf_allocVector(INTSXP, 2)); nprotect++; INTEGER(result)[0] = ahead; INTEGER(result)[1] = behind; cleanup: git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Determine if a commit is the descendant of another commit. * * @param commit A commit. * @param ancestor A potential ancestor commit. * @return TRUE or FALSE */ SEXP attribute_hidden git2r_graph_descendant_of( SEXP commit, SEXP ancestor) { int error, descendant_of = 0; SEXP commit_repo, commit_sha; SEXP ancestor_repo, ancestor_sha; git_oid commit_oid, ancestor_oid; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg); if (git2r_arg_check_commit(ancestor)) git2r_error(__func__, NULL, "'ancestor'", git2r_err_commit_arg); commit_repo = git2r_get_list_element(commit, "repo"); ancestor_repo = git2r_get_list_element(ancestor, "repo"); if (git2r_arg_check_same_repo(commit_repo, ancestor_repo)) git2r_error(__func__, NULL, "'commit' and 'ancestor' not from same repository", NULL); repository = git2r_repository_open(commit_repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); commit_sha = git2r_get_list_element(commit, "sha"); git2r_oid_from_sha_sexp(commit_sha, &commit_oid); ancestor_sha = git2r_get_list_element(ancestor, "sha"); git2r_oid_from_sha_sexp(ancestor_sha, &ancestor_oid); error = git_graph_descendant_of(repository, &commit_oid, &ancestor_oid); if (0 > error || 1 < error) goto cleanup; descendant_of = error; error = 0; cleanup: git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return Rf_ScalarLogical(descendant_of); } git2r/src/libgit2/0000755000175000017500000000000014125111754013557 5ustar nileshnileshgit2r/src/libgit2/src/0000755000175000017500000000000014145550337014354 5ustar nileshnileshgit2r/src/libgit2/src/commit_graph.c0000644000175000017500000010132514125111754017165 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "commit_graph.h" #include "array.h" #include "filebuf.h" #include "futils.h" #include "hash.h" #include "oidarray.h" #include "oidmap.h" #include "pack.h" #include "repository.h" #include "revwalk.h" #define GIT_COMMIT_GRAPH_MISSING_PARENT 0x70000000 #define GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX 0x3FFFFFFF #define GIT_COMMIT_GRAPH_GENERATION_NUMBER_INFINITY 0xFFFFFFFF #define COMMIT_GRAPH_SIGNATURE 0x43475048 /* "CGPH" */ #define COMMIT_GRAPH_VERSION 1 #define COMMIT_GRAPH_OBJECT_ID_VERSION 1 struct git_commit_graph_header { uint32_t signature; uint8_t version; uint8_t object_id_version; uint8_t chunks; uint8_t base_graph_files; }; #define COMMIT_GRAPH_OID_FANOUT_ID 0x4f494446 /* "OIDF" */ #define COMMIT_GRAPH_OID_LOOKUP_ID 0x4f49444c /* "OIDL" */ #define COMMIT_GRAPH_COMMIT_DATA_ID 0x43444154 /* "CDAT" */ #define COMMIT_GRAPH_EXTRA_EDGE_LIST_ID 0x45444745 /* "EDGE" */ #define COMMIT_GRAPH_BLOOM_FILTER_INDEX_ID 0x42494458 /* "BIDX" */ #define COMMIT_GRAPH_BLOOM_FILTER_DATA_ID 0x42444154 /* "BDAT" */ struct git_commit_graph_chunk { off64_t offset; size_t length; }; typedef git_array_t(size_t) parent_index_array_t; struct packed_commit { size_t index; git_oid sha1; git_oid tree_oid; uint32_t generation; git_time_t commit_time; git_array_oid_t parents; parent_index_array_t parent_indices; }; static void packed_commit_free(struct packed_commit *p) { if (!p) return; git_array_clear(p->parents); git_array_clear(p->parent_indices); git__free(p); } static struct packed_commit *packed_commit_new(git_commit *commit) { unsigned int i, parentcount = git_commit_parentcount(commit); struct packed_commit *p = git__calloc(1, sizeof(struct packed_commit)); if (!p) goto cleanup; git_array_init_to_size(p->parents, parentcount); if (parentcount && !p->parents.ptr) goto cleanup; if (git_oid_cpy(&p->sha1, git_commit_id(commit)) < 0) goto cleanup; if (git_oid_cpy(&p->tree_oid, git_commit_tree_id(commit)) < 0) goto cleanup; p->commit_time = git_commit_time(commit); for (i = 0; i < parentcount; ++i) { git_oid *parent_id = git_array_alloc(p->parents); if (!parent_id) goto cleanup; if (git_oid_cpy(parent_id, git_commit_parent_id(commit, i)) < 0) goto cleanup; } return p; cleanup: packed_commit_free(p); return NULL; } typedef int (*commit_graph_write_cb)(const char *buf, size_t size, void *cb_data); static int commit_graph_error(const char *message) { git_error_set(GIT_ERROR_ODB, "invalid commit-graph file - %s", message); return -1; } static int commit_graph_parse_oid_fanout( git_commit_graph_file *file, const unsigned char *data, struct git_commit_graph_chunk *chunk_oid_fanout) { uint32_t i, nr; if (chunk_oid_fanout->offset == 0) return commit_graph_error("missing OID Fanout chunk"); if (chunk_oid_fanout->length == 0) return commit_graph_error("empty OID Fanout chunk"); if (chunk_oid_fanout->length != 256 * 4) return commit_graph_error("OID Fanout chunk has wrong length"); file->oid_fanout = (const uint32_t *)(data + chunk_oid_fanout->offset); nr = 0; for (i = 0; i < 256; ++i) { uint32_t n = ntohl(file->oid_fanout[i]); if (n < nr) return commit_graph_error("index is non-monotonic"); nr = n; } file->num_commits = nr; return 0; } static int commit_graph_parse_oid_lookup( git_commit_graph_file *file, const unsigned char *data, struct git_commit_graph_chunk *chunk_oid_lookup) { uint32_t i; git_oid *oid, *prev_oid, zero_oid = {{0}}; if (chunk_oid_lookup->offset == 0) return commit_graph_error("missing OID Lookup chunk"); if (chunk_oid_lookup->length == 0) return commit_graph_error("empty OID Lookup chunk"); if (chunk_oid_lookup->length != file->num_commits * GIT_OID_RAWSZ) return commit_graph_error("OID Lookup chunk has wrong length"); file->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset); prev_oid = &zero_oid; for (i = 0; i < file->num_commits; ++i, ++oid) { if (git_oid_cmp(prev_oid, oid) >= 0) return commit_graph_error("OID Lookup index is non-monotonic"); prev_oid = oid; } return 0; } static int commit_graph_parse_commit_data( git_commit_graph_file *file, const unsigned char *data, struct git_commit_graph_chunk *chunk_commit_data) { if (chunk_commit_data->offset == 0) return commit_graph_error("missing Commit Data chunk"); if (chunk_commit_data->length == 0) return commit_graph_error("empty Commit Data chunk"); if (chunk_commit_data->length != file->num_commits * (GIT_OID_RAWSZ + 16)) return commit_graph_error("Commit Data chunk has wrong length"); file->commit_data = data + chunk_commit_data->offset; return 0; } static int commit_graph_parse_extra_edge_list( git_commit_graph_file *file, const unsigned char *data, struct git_commit_graph_chunk *chunk_extra_edge_list) { if (chunk_extra_edge_list->length == 0) return 0; if (chunk_extra_edge_list->length % 4 != 0) return commit_graph_error("malformed Extra Edge List chunk"); file->extra_edge_list = data + chunk_extra_edge_list->offset; file->num_extra_edge_list = chunk_extra_edge_list->length / 4; return 0; } int git_commit_graph_file_parse( git_commit_graph_file *file, const unsigned char *data, size_t size) { struct git_commit_graph_header *hdr; const unsigned char *chunk_hdr; struct git_commit_graph_chunk *last_chunk; uint32_t i; off64_t last_chunk_offset, chunk_offset, trailer_offset; git_oid cgraph_checksum = {{0}}; int error; struct git_commit_graph_chunk chunk_oid_fanout = {0}, chunk_oid_lookup = {0}, chunk_commit_data = {0}, chunk_extra_edge_list = {0}, chunk_unsupported = {0}; GIT_ASSERT_ARG(file); if (size < sizeof(struct git_commit_graph_header) + GIT_OID_RAWSZ) return commit_graph_error("commit-graph is too short"); hdr = ((struct git_commit_graph_header *)data); if (hdr->signature != htonl(COMMIT_GRAPH_SIGNATURE) || hdr->version != COMMIT_GRAPH_VERSION || hdr->object_id_version != COMMIT_GRAPH_OBJECT_ID_VERSION) { return commit_graph_error("unsupported commit-graph version"); } if (hdr->chunks == 0) return commit_graph_error("no chunks in commit-graph"); /* * The very first chunk's offset should be after the header, all the chunk * headers, and a special zero chunk. */ last_chunk_offset = sizeof(struct git_commit_graph_header) + (1 + hdr->chunks) * 12; trailer_offset = size - GIT_OID_RAWSZ; if (trailer_offset < last_chunk_offset) return commit_graph_error("wrong commit-graph size"); git_oid_cpy(&file->checksum, (git_oid *)(data + trailer_offset)); if (git_hash_buf(&cgraph_checksum, data, (size_t)trailer_offset) < 0) return commit_graph_error("could not calculate signature"); if (!git_oid_equal(&cgraph_checksum, &file->checksum)) return commit_graph_error("index signature mismatch"); chunk_hdr = data + sizeof(struct git_commit_graph_header); last_chunk = NULL; for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) { chunk_offset = ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) << 32 | ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 8)))); if (chunk_offset < last_chunk_offset) return commit_graph_error("chunks are non-monotonic"); if (chunk_offset >= trailer_offset) return commit_graph_error("chunks extend beyond the trailer"); if (last_chunk != NULL) last_chunk->length = (size_t)(chunk_offset - last_chunk_offset); last_chunk_offset = chunk_offset; switch (ntohl(*((uint32_t *)(chunk_hdr + 0)))) { case COMMIT_GRAPH_OID_FANOUT_ID: chunk_oid_fanout.offset = last_chunk_offset; last_chunk = &chunk_oid_fanout; break; case COMMIT_GRAPH_OID_LOOKUP_ID: chunk_oid_lookup.offset = last_chunk_offset; last_chunk = &chunk_oid_lookup; break; case COMMIT_GRAPH_COMMIT_DATA_ID: chunk_commit_data.offset = last_chunk_offset; last_chunk = &chunk_commit_data; break; case COMMIT_GRAPH_EXTRA_EDGE_LIST_ID: chunk_extra_edge_list.offset = last_chunk_offset; last_chunk = &chunk_extra_edge_list; break; case COMMIT_GRAPH_BLOOM_FILTER_INDEX_ID: case COMMIT_GRAPH_BLOOM_FILTER_DATA_ID: chunk_unsupported.offset = last_chunk_offset; last_chunk = &chunk_unsupported; break; default: return commit_graph_error("unrecognized chunk ID"); } } last_chunk->length = (size_t)(trailer_offset - last_chunk_offset); error = commit_graph_parse_oid_fanout(file, data, &chunk_oid_fanout); if (error < 0) return error; error = commit_graph_parse_oid_lookup(file, data, &chunk_oid_lookup); if (error < 0) return error; error = commit_graph_parse_commit_data(file, data, &chunk_commit_data); if (error < 0) return error; error = commit_graph_parse_extra_edge_list(file, data, &chunk_extra_edge_list); if (error < 0) return error; return 0; } int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file) { git_commit_graph *cgraph = NULL; int error = 0; GIT_ASSERT_ARG(cgraph_out); GIT_ASSERT_ARG(objects_dir); cgraph = git__calloc(1, sizeof(git_commit_graph)); GIT_ERROR_CHECK_ALLOC(cgraph); error = git_buf_joinpath(&cgraph->filename, objects_dir, "info/commit-graph"); if (error < 0) goto error; if (open_file) { error = git_commit_graph_file_open(&cgraph->file, git_buf_cstr(&cgraph->filename)); if (error < 0) goto error; cgraph->checked = 1; } *cgraph_out = cgraph; return 0; error: git_commit_graph_free(cgraph); return error; } int git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir) { return git_commit_graph_new(cgraph_out, objects_dir, true); } int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path) { git_commit_graph_file *file; git_file fd = -1; size_t cgraph_size; struct stat st; int error; /* TODO: properly open the file without access time using O_NOATIME */ fd = git_futils_open_ro(path); if (fd < 0) return fd; if (p_fstat(fd, &st) < 0) { p_close(fd); git_error_set(GIT_ERROR_ODB, "commit-graph file not found - '%s'", path); return GIT_ENOTFOUND; } if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size)) { p_close(fd); git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path); return GIT_ENOTFOUND; } cgraph_size = (size_t)st.st_size; file = git__calloc(1, sizeof(git_commit_graph_file)); GIT_ERROR_CHECK_ALLOC(file); error = git_futils_mmap_ro(&file->graph_map, fd, 0, cgraph_size); p_close(fd); if (error < 0) { git_commit_graph_file_free(file); return error; } if ((error = git_commit_graph_file_parse(file, file->graph_map.data, cgraph_size)) < 0) { git_commit_graph_file_free(file); return error; } *file_out = file; return 0; } int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph *cgraph) { if (!cgraph->checked) { int error = 0; git_commit_graph_file *result = NULL; /* We only check once, no matter the result. */ cgraph->checked = 1; /* Best effort */ error = git_commit_graph_file_open(&result, git_buf_cstr(&cgraph->filename)); if (error < 0) return error; cgraph->file = result; } if (!cgraph->file) return GIT_ENOTFOUND; *file_out = cgraph->file; return 0; } void git_commit_graph_refresh(git_commit_graph *cgraph) { if (!cgraph->checked) return; if (cgraph->file && git_commit_graph_file_needs_refresh(cgraph->file, git_buf_cstr(&cgraph->filename))) { /* We just free the commit graph. The next time it is requested, it will be * re-loaded. */ git_commit_graph_file_free(cgraph->file); cgraph->file = NULL; } /* Force a lazy re-check next time it is needed. */ cgraph->checked = 0; } static int git_commit_graph_entry_get_byindex( git_commit_graph_entry *e, const git_commit_graph_file *file, size_t pos) { const unsigned char *commit_data; GIT_ASSERT_ARG(e); GIT_ASSERT_ARG(file); if (pos >= file->num_commits) { git_error_set(GIT_ERROR_INVALID, "commit index %zu does not exist", pos); return GIT_ENOTFOUND; } commit_data = file->commit_data + pos * (GIT_OID_RAWSZ + 4 * sizeof(uint32_t)); git_oid_cpy(&e->tree_oid, (const git_oid *)commit_data); e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ))); e->parent_indices[1] = ntohl( *((uint32_t *)(commit_data + GIT_OID_RAWSZ + sizeof(uint32_t)))); e->parent_count = (e->parent_indices[0] != GIT_COMMIT_GRAPH_MISSING_PARENT) + (e->parent_indices[1] != GIT_COMMIT_GRAPH_MISSING_PARENT); e->generation = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ + 2 * sizeof(uint32_t)))); e->commit_time = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ + 3 * sizeof(uint32_t)))); e->commit_time |= (e->generation & UINT64_C(0x3)) << UINT64_C(32); e->generation >>= 2u; if (e->parent_indices[1] & 0x80000000u) { uint32_t extra_edge_list_pos = e->parent_indices[1] & 0x7fffffff; /* Make sure we're not being sent out of bounds */ if (extra_edge_list_pos >= file->num_extra_edge_list) { git_error_set(GIT_ERROR_INVALID, "commit %u does not exist", extra_edge_list_pos); return GIT_ENOTFOUND; } e->extra_parents_index = extra_edge_list_pos; while (extra_edge_list_pos < file->num_extra_edge_list && (ntohl(*( (uint32_t *)(file->extra_edge_list + extra_edge_list_pos * sizeof(uint32_t)))) & 0x80000000u) == 0) { extra_edge_list_pos++; e->parent_count++; } } git_oid_cpy(&e->sha1, &file->oid_lookup[pos]); return 0; } bool git_commit_graph_file_needs_refresh(const git_commit_graph_file *file, const char *path) { git_file fd = -1; struct stat st; ssize_t bytes_read; git_oid cgraph_checksum = {{0}}; /* TODO: properly open the file without access time using O_NOATIME */ fd = git_futils_open_ro(path); if (fd < 0) return true; if (p_fstat(fd, &st) < 0) { p_close(fd); return true; } if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size) || (size_t)st.st_size != file->graph_map.len) { p_close(fd); return true; } bytes_read = p_pread(fd, cgraph_checksum.id, GIT_OID_RAWSZ, st.st_size - GIT_OID_RAWSZ); p_close(fd); if (bytes_read != GIT_OID_RAWSZ) return true; return !git_oid_equal(&cgraph_checksum, &file->checksum); } int git_commit_graph_entry_find( git_commit_graph_entry *e, const git_commit_graph_file *file, const git_oid *short_oid, size_t len) { int pos, found = 0; uint32_t hi, lo; const git_oid *current = NULL; GIT_ASSERT_ARG(e); GIT_ASSERT_ARG(file); GIT_ASSERT_ARG(short_oid); hi = ntohl(file->oid_fanout[(int)short_oid->id[0]]); lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(file->oid_fanout[(int)short_oid->id[0] - 1])); pos = git_pack__lookup_sha1(file->oid_lookup, GIT_OID_RAWSZ, lo, hi, short_oid->id); if (pos >= 0) { /* An object matching exactly the oid was found */ found = 1; current = file->oid_lookup + pos; } else { /* No object was found */ /* pos refers to the object with the "closest" oid to short_oid */ pos = -1 - pos; if (pos < (int)file->num_commits) { current = file->oid_lookup + pos; if (!git_oid_ncmp(short_oid, current, len)) found = 1; } } if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)file->num_commits) { /* Check for ambiguousity */ const git_oid *next = current + 1; if (!git_oid_ncmp(short_oid, next, len)) { found = 2; } } if (!found) return git_odb__error_notfound( "failed to find offset for commit-graph index entry", short_oid, len); if (found > 1) return git_odb__error_ambiguous( "found multiple offsets for commit-graph index entry"); return git_commit_graph_entry_get_byindex(e, file, pos); } int git_commit_graph_entry_parent( git_commit_graph_entry *parent, const git_commit_graph_file *file, const git_commit_graph_entry *entry, size_t n) { GIT_ASSERT_ARG(parent); GIT_ASSERT_ARG(file); if (n >= entry->parent_count) { git_error_set(GIT_ERROR_INVALID, "parent index %zu does not exist", n); return GIT_ENOTFOUND; } if (n == 0 || (n == 1 && entry->parent_count == 2)) return git_commit_graph_entry_get_byindex(parent, file, entry->parent_indices[n]); return git_commit_graph_entry_get_byindex( parent, file, ntohl( *(uint32_t *)(file->extra_edge_list + (entry->extra_parents_index + n - 1) * sizeof(uint32_t))) & 0x7fffffff); } int git_commit_graph_file_close(git_commit_graph_file *file) { GIT_ASSERT_ARG(file); if (file->graph_map.data) git_futils_mmap_free(&file->graph_map); return 0; } void git_commit_graph_free(git_commit_graph *cgraph) { if (!cgraph) return; git_buf_dispose(&cgraph->filename); git_commit_graph_file_free(cgraph->file); git__free(cgraph); } void git_commit_graph_file_free(git_commit_graph_file *file) { if (!file) return; git_commit_graph_file_close(file); git__free(file); } static int packed_commit__cmp(const void *a_, const void *b_) { const struct packed_commit *a = a_; const struct packed_commit *b = b_; return git_oid_cmp(&a->sha1, &b->sha1); } int git_commit_graph_writer_new(git_commit_graph_writer **out, const char *objects_info_dir) { git_commit_graph_writer *w = git__calloc(1, sizeof(git_commit_graph_writer)); GIT_ERROR_CHECK_ALLOC(w); if (git_buf_sets(&w->objects_info_dir, objects_info_dir) < 0) { git__free(w); return -1; } if (git_vector_init(&w->commits, 0, packed_commit__cmp) < 0) { git_buf_dispose(&w->objects_info_dir); git__free(w); return -1; } *out = w; return 0; } void git_commit_graph_writer_free(git_commit_graph_writer *w) { struct packed_commit *packed_commit; size_t i; if (!w) return; git_vector_foreach (&w->commits, i, packed_commit) packed_commit_free(packed_commit); git_vector_free(&w->commits); git_buf_dispose(&w->objects_info_dir); git__free(w); } struct object_entry_cb_state { git_repository *repo; git_odb *db; git_vector *commits; }; static int object_entry__cb(const git_oid *id, void *data) { struct object_entry_cb_state *state = (struct object_entry_cb_state *)data; git_commit *commit = NULL; struct packed_commit *packed_commit = NULL; size_t header_len; git_object_t header_type; int error = 0; error = git_odb_read_header(&header_len, &header_type, state->db, id); if (error < 0) return error; if (header_type != GIT_OBJECT_COMMIT) return 0; error = git_commit_lookup(&commit, state->repo, id); if (error < 0) return error; packed_commit = packed_commit_new(commit); git_commit_free(commit); GIT_ERROR_CHECK_ALLOC(packed_commit); error = git_vector_insert(state->commits, packed_commit); if (error < 0) { packed_commit_free(packed_commit); return error; } return 0; } int git_commit_graph_writer_add_index_file( git_commit_graph_writer *w, git_repository *repo, const char *idx_path) { int error; struct git_pack_file *p = NULL; struct object_entry_cb_state state = {0}; state.repo = repo; state.commits = &w->commits; error = git_repository_odb(&state.db, repo); if (error < 0) goto cleanup; error = git_mwindow_get_pack(&p, idx_path); if (error < 0) goto cleanup; error = git_pack_foreach_entry(p, object_entry__cb, &state); if (error < 0) goto cleanup; cleanup: if (p) git_mwindow_put_pack(p); git_odb_free(state.db); return error; } int git_commit_graph_writer_add_revwalk(git_commit_graph_writer *w, git_revwalk *walk) { int error; git_oid id; git_repository *repo = git_revwalk_repository(walk); git_commit *commit; struct packed_commit *packed_commit; while ((git_revwalk_next(&id, walk)) == 0) { error = git_commit_lookup(&commit, repo, &id); if (error < 0) return error; packed_commit = packed_commit_new(commit); git_commit_free(commit); GIT_ERROR_CHECK_ALLOC(packed_commit); error = git_vector_insert(&w->commits, packed_commit); if (error < 0) { packed_commit_free(packed_commit); return error; } } return 0; } enum generation_number_commit_state { GENERATION_NUMBER_COMMIT_STATE_UNVISITED = 0, GENERATION_NUMBER_COMMIT_STATE_ADDED = 1, GENERATION_NUMBER_COMMIT_STATE_EXPANDED = 2, GENERATION_NUMBER_COMMIT_STATE_VISITED = 3, }; static int compute_generation_numbers(git_vector *commits) { git_array_t(size_t) index_stack = GIT_ARRAY_INIT; size_t i, j; size_t *parent_idx; enum generation_number_commit_state *commit_states = NULL; struct packed_commit *child_packed_commit; git_oidmap *packed_commit_map = NULL; int error = 0; /* First populate the parent indices fields */ error = git_oidmap_new(&packed_commit_map); if (error < 0) goto cleanup; git_vector_foreach (commits, i, child_packed_commit) { child_packed_commit->index = i; error = git_oidmap_set( packed_commit_map, &child_packed_commit->sha1, child_packed_commit); if (error < 0) goto cleanup; } git_vector_foreach (commits, i, child_packed_commit) { size_t parent_i, *parent_idx_ptr; struct packed_commit *parent_packed_commit; git_oid *parent_id; git_array_init_to_size( child_packed_commit->parent_indices, git_array_size(child_packed_commit->parents)); if (git_array_size(child_packed_commit->parents) && !child_packed_commit->parent_indices.ptr) { error = -1; goto cleanup; } git_array_foreach (child_packed_commit->parents, parent_i, parent_id) { parent_packed_commit = git_oidmap_get(packed_commit_map, parent_id); if (!parent_packed_commit) { git_error_set(GIT_ERROR_ODB, "parent commit %s not found in commit graph", git_oid_tostr_s(parent_id)); error = GIT_ENOTFOUND; goto cleanup; } parent_idx_ptr = git_array_alloc(child_packed_commit->parent_indices); if (!parent_idx_ptr) { error = -1; goto cleanup; } *parent_idx_ptr = parent_packed_commit->index; } } /* * We copy all the commits to the stack and then during visitation, * each node can be added up to two times to the stack. */ git_array_init_to_size(index_stack, 3 * git_vector_length(commits)); if (!index_stack.ptr) { error = -1; goto cleanup; } commit_states = (enum generation_number_commit_state *)git__calloc( git_vector_length(commits), sizeof(enum generation_number_commit_state)); if (!commit_states) { error = -1; goto cleanup; } /* * Perform a Post-Order traversal so that all parent nodes are fully * visited before the child node. */ git_vector_foreach (commits, i, child_packed_commit) *(size_t *)git_array_alloc(index_stack) = i; while (git_array_size(index_stack)) { size_t *index_ptr = git_array_pop(index_stack); i = *index_ptr; child_packed_commit = git_vector_get(commits, i); if (commit_states[i] == GENERATION_NUMBER_COMMIT_STATE_VISITED) { /* This commit has already been fully visited. */ continue; } if (commit_states[i] == GENERATION_NUMBER_COMMIT_STATE_EXPANDED) { /* All of the commits parents have been visited. */ child_packed_commit->generation = 0; git_array_foreach (child_packed_commit->parent_indices, j, parent_idx) { struct packed_commit *parent = git_vector_get(commits, *parent_idx); if (child_packed_commit->generation < parent->generation) child_packed_commit->generation = parent->generation; } if (child_packed_commit->generation < GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX) { ++child_packed_commit->generation; } commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_VISITED; continue; } /* * This is the first time we see this commit. We need * to visit all its parents before we can fully visit * it. */ if (git_array_size(child_packed_commit->parent_indices) == 0) { /* * Special case: if the commit has no parents, there's * no need to add it to the stack just to immediately * remove it. */ commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_VISITED; child_packed_commit->generation = 1; continue; } /* * Add this current commit again so that it is visited * again once all its children have been visited. */ *(size_t *)git_array_alloc(index_stack) = i; git_array_foreach (child_packed_commit->parent_indices, j, parent_idx) { if (commit_states[*parent_idx] != GENERATION_NUMBER_COMMIT_STATE_UNVISITED) { /* This commit has already been considered. */ continue; } commit_states[*parent_idx] = GENERATION_NUMBER_COMMIT_STATE_ADDED; *(size_t *)git_array_alloc(index_stack) = *parent_idx; } commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_EXPANDED; } cleanup: git_oidmap_free(packed_commit_map); git__free(commit_states); git_array_clear(index_stack); return error; } static int write_offset(off64_t offset, commit_graph_write_cb write_cb, void *cb_data) { int error; uint32_t word; word = htonl((uint32_t)((offset >> 32) & 0xffffffffu)); error = write_cb((const char *)&word, sizeof(word), cb_data); if (error < 0) return error; word = htonl((uint32_t)((offset >> 0) & 0xffffffffu)); error = write_cb((const char *)&word, sizeof(word), cb_data); if (error < 0) return error; return 0; } static int write_chunk_header( int chunk_id, off64_t offset, commit_graph_write_cb write_cb, void *cb_data) { uint32_t word = htonl(chunk_id); int error = write_cb((const char *)&word, sizeof(word), cb_data); if (error < 0) return error; return write_offset(offset, write_cb, cb_data); } static int commit_graph_write_buf(const char *buf, size_t size, void *data) { git_buf *b = (git_buf *)data; return git_buf_put(b, buf, size); } struct commit_graph_write_hash_context { commit_graph_write_cb write_cb; void *cb_data; git_hash_ctx *ctx; }; static int commit_graph_write_hash(const char *buf, size_t size, void *data) { struct commit_graph_write_hash_context *ctx = data; int error; error = git_hash_update(ctx->ctx, buf, size); if (error < 0) return error; return ctx->write_cb(buf, size, ctx->cb_data); } static void packed_commit_free_dup(void *packed_commit) { packed_commit_free(packed_commit); } static int commit_graph_write( git_commit_graph_writer *w, commit_graph_write_cb write_cb, void *cb_data) { int error = 0; size_t i; struct packed_commit *packed_commit; struct git_commit_graph_header hdr = {0}; uint32_t oid_fanout_count; uint32_t extra_edge_list_count; uint32_t oid_fanout[256]; off64_t offset; git_buf oid_lookup = GIT_BUF_INIT, commit_data = GIT_BUF_INIT, extra_edge_list = GIT_BUF_INIT; git_oid cgraph_checksum = {{0}}; git_hash_ctx ctx; struct commit_graph_write_hash_context hash_cb_data = {0}; hdr.signature = htonl(COMMIT_GRAPH_SIGNATURE); hdr.version = COMMIT_GRAPH_VERSION; hdr.object_id_version = COMMIT_GRAPH_OBJECT_ID_VERSION; hdr.chunks = 0; hdr.base_graph_files = 0; hash_cb_data.write_cb = write_cb; hash_cb_data.cb_data = cb_data; hash_cb_data.ctx = &ctx; error = git_hash_ctx_init(&ctx); if (error < 0) return error; cb_data = &hash_cb_data; write_cb = commit_graph_write_hash; /* Sort the commits. */ git_vector_sort(&w->commits); git_vector_uniq(&w->commits, packed_commit_free_dup); error = compute_generation_numbers(&w->commits); if (error < 0) goto cleanup; /* Fill the OID Fanout table. */ oid_fanout_count = 0; for (i = 0; i < 256; i++) { while (oid_fanout_count < git_vector_length(&w->commits) && (packed_commit = (struct packed_commit *)git_vector_get(&w->commits, oid_fanout_count)) && packed_commit->sha1.id[0] <= i) ++oid_fanout_count; oid_fanout[i] = htonl(oid_fanout_count); } /* Fill the OID Lookup table. */ git_vector_foreach (&w->commits, i, packed_commit) { error = git_buf_put(&oid_lookup, (const char *)&packed_commit->sha1, sizeof(git_oid)); if (error < 0) goto cleanup; } /* Fill the Commit Data and Extra Edge List tables. */ extra_edge_list_count = 0; git_vector_foreach (&w->commits, i, packed_commit) { uint64_t commit_time; uint32_t generation; uint32_t word; size_t *packed_index; unsigned int parentcount = (unsigned int)git_array_size(packed_commit->parents); error = git_buf_put(&commit_data, (const char *)&packed_commit->tree_oid, sizeof(git_oid)); if (error < 0) goto cleanup; if (parentcount == 0) { word = htonl(GIT_COMMIT_GRAPH_MISSING_PARENT); } else { packed_index = git_array_get(packed_commit->parent_indices, 0); word = htonl((uint32_t)*packed_index); } error = git_buf_put(&commit_data, (const char *)&word, sizeof(word)); if (error < 0) goto cleanup; if (parentcount < 2) { word = htonl(GIT_COMMIT_GRAPH_MISSING_PARENT); } else if (parentcount == 2) { packed_index = git_array_get(packed_commit->parent_indices, 1); word = htonl((uint32_t)*packed_index); } else { word = htonl(0x80000000u | extra_edge_list_count); } error = git_buf_put(&commit_data, (const char *)&word, sizeof(word)); if (error < 0) goto cleanup; if (parentcount > 2) { unsigned int parent_i; for (parent_i = 1; parent_i < parentcount; ++parent_i) { packed_index = git_array_get( packed_commit->parent_indices, parent_i); word = htonl((uint32_t)(*packed_index | (parent_i + 1 == parentcount ? 0x80000000u : 0))); error = git_buf_put(&extra_edge_list, (const char *)&word, sizeof(word)); if (error < 0) goto cleanup; } extra_edge_list_count += parentcount - 1; } generation = packed_commit->generation; commit_time = (uint64_t)packed_commit->commit_time; if (generation > GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX) generation = GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX; word = ntohl((uint32_t)((generation << 2) | ((commit_time >> 32ull) & 0x3ull))); error = git_buf_put(&commit_data, (const char *)&word, sizeof(word)); if (error < 0) goto cleanup; word = ntohl((uint32_t)(commit_time & 0xffffffffull)); error = git_buf_put(&commit_data, (const char *)&word, sizeof(word)); if (error < 0) goto cleanup; } /* Write the header. */ hdr.chunks = 3; if (git_buf_len(&extra_edge_list) > 0) hdr.chunks++; error = write_cb((const char *)&hdr, sizeof(hdr), cb_data); if (error < 0) goto cleanup; /* Write the chunk headers. */ offset = sizeof(hdr) + (hdr.chunks + 1) * 12; error = write_chunk_header(COMMIT_GRAPH_OID_FANOUT_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += sizeof(oid_fanout); error = write_chunk_header(COMMIT_GRAPH_OID_LOOKUP_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += git_buf_len(&oid_lookup); error = write_chunk_header(COMMIT_GRAPH_COMMIT_DATA_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += git_buf_len(&commit_data); if (git_buf_len(&extra_edge_list) > 0) { error = write_chunk_header( COMMIT_GRAPH_EXTRA_EDGE_LIST_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += git_buf_len(&extra_edge_list); } error = write_chunk_header(0, offset, write_cb, cb_data); if (error < 0) goto cleanup; /* Write all the chunks. */ error = write_cb((const char *)oid_fanout, sizeof(oid_fanout), cb_data); if (error < 0) goto cleanup; error = write_cb(git_buf_cstr(&oid_lookup), git_buf_len(&oid_lookup), cb_data); if (error < 0) goto cleanup; error = write_cb(git_buf_cstr(&commit_data), git_buf_len(&commit_data), cb_data); if (error < 0) goto cleanup; error = write_cb(git_buf_cstr(&extra_edge_list), git_buf_len(&extra_edge_list), cb_data); if (error < 0) goto cleanup; /* Finalize the checksum and write the trailer. */ error = git_hash_final(&cgraph_checksum, &ctx); if (error < 0) goto cleanup; error = write_cb((const char *)&cgraph_checksum, sizeof(cgraph_checksum), cb_data); if (error < 0) goto cleanup; cleanup: git_buf_dispose(&oid_lookup); git_buf_dispose(&commit_data); git_buf_dispose(&extra_edge_list); git_hash_ctx_cleanup(&ctx); return error; } static int commit_graph_write_filebuf(const char *buf, size_t size, void *data) { git_filebuf *f = (git_filebuf *)data; return git_filebuf_write(f, buf, size); } int git_commit_graph_writer_options_init( git_commit_graph_writer_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_commit_graph_writer_options, GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT); return 0; } int git_commit_graph_writer_commit( git_commit_graph_writer *w, git_commit_graph_writer_options *opts) { int error; int filebuf_flags = GIT_FILEBUF_DO_NOT_BUFFER; git_buf commit_graph_path = GIT_BUF_INIT; git_filebuf output = GIT_FILEBUF_INIT; /* TODO: support options and fill in defaults. */ GIT_UNUSED(opts); error = git_buf_joinpath( &commit_graph_path, git_buf_cstr(&w->objects_info_dir), "commit-graph"); if (error < 0) return error; if (git_repository__fsync_gitdir) filebuf_flags |= GIT_FILEBUF_FSYNC; error = git_filebuf_open(&output, git_buf_cstr(&commit_graph_path), filebuf_flags, 0644); git_buf_dispose(&commit_graph_path); if (error < 0) return error; error = commit_graph_write(w, commit_graph_write_filebuf, &output); if (error < 0) { git_filebuf_cleanup(&output); return error; } return git_filebuf_commit(&output); } int git_commit_graph_writer_dump( git_buf *cgraph, git_commit_graph_writer *w, git_commit_graph_writer_options *opts) { /* TODO: support options. */ GIT_UNUSED(opts); return commit_graph_write(w, commit_graph_write_buf, cgraph); } git2r/src/libgit2/src/commit.h0000644000175000017500000000201214125111754016002 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_commit_h__ #define INCLUDE_commit_h__ #include "common.h" #include "git2/commit.h" #include "tree.h" #include "repository.h" #include "array.h" #include struct git_commit { git_object object; git_array_t(git_oid) parent_ids; git_oid tree_id; git_signature *author; git_signature *committer; char *message_encoding; char *raw_message; char *raw_header; char *summary; char *body; }; void git_commit__free(void *commit); int git_commit__parse(void *commit, git_odb_object *obj); int git_commit__parse_raw(void *commit, const char *data, size_t size); typedef enum { GIT_COMMIT_PARSE_QUICK = (1 << 0), /**< Only parse parents and committer info */ } git_commit__parse_flags; int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags); #endif git2r/src/libgit2/src/delta.h0000644000175000017500000000751414125111754015617 0ustar nileshnilesh/* * diff-delta code taken from git.git. See diff-delta.c for details. * */ #ifndef INCLUDE_git_delta_h__ #define INCLUDE_git_delta_h__ #include "common.h" #include "pack.h" typedef struct git_delta_index git_delta_index; /* * git_delta_index_init: compute index data from given buffer * * This returns a pointer to a struct delta_index that should be passed to * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer * is returned on failure. The given buffer must not be freed nor altered * before free_delta_index() is called. The returned pointer must be freed * using free_delta_index(). */ extern int git_delta_index_init( git_delta_index **out, const void *buf, size_t bufsize); /* * Free the index created by git_delta_index_init() */ extern void git_delta_index_free(git_delta_index *index); /* * Returns memory usage of delta index. */ extern size_t git_delta_index_size(git_delta_index *index); /* * create_delta: create a delta from given index for the given buffer * * This function may be called multiple times with different buffers using * the same delta_index pointer. If max_delta_size is non-zero and the * resulting delta is to be larger than max_delta_size then NULL is returned. * On success, a non-NULL pointer to the buffer with the delta data is * returned and *delta_size is updated with its size. The returned buffer * must be freed by the caller. */ extern int git_delta_create_from_index( void **out, size_t *out_size, const struct git_delta_index *index, const void *buf, size_t bufsize, size_t max_delta_size); /* * diff_delta: create a delta from source buffer to target buffer * * If max_delta_size is non-zero and the resulting delta is to be larger * than max_delta_size then GIT_EBUFS is returned. On success, a non-NULL * pointer to the buffer with the delta data is returned and *delta_size is * updated with its size. The returned buffer must be freed by the caller. */ GIT_INLINE(int) git_delta( void **out, size_t *out_len, const void *src_buf, size_t src_bufsize, const void *trg_buf, size_t trg_bufsize, size_t max_delta_size) { git_delta_index *index; int error = 0; *out = NULL; *out_len = 0; if ((error = git_delta_index_init(&index, src_buf, src_bufsize)) < 0) return error; if (index) { error = git_delta_create_from_index(out, out_len, index, trg_buf, trg_bufsize, max_delta_size); git_delta_index_free(index); } return error; } /* the smallest possible delta size is 4 bytes */ #define GIT_DELTA_SIZE_MIN 4 /** * Apply a git binary delta to recover the original content. * The caller is responsible for freeing the returned buffer. * * @param out the output buffer * @param out_len the length of the output buffer * @param base the base to copy from during copy instructions. * @param base_len number of bytes available at base. * @param delta the delta to execute copy/insert instructions from. * @param delta_len total number of bytes in the delta. * @return 0 on success or an error code */ extern int git_delta_apply( void **out, size_t *out_len, const unsigned char *base, size_t base_len, const unsigned char *delta, size_t delta_len); /** * Read the header of a git binary delta. * * @param base_out pointer to store the base size field. * @param result_out pointer to store the result size field. * @param delta the delta to execute copy/insert instructions from. * @param delta_len total number of bytes in the delta. * @return 0 on success or an error code */ extern int git_delta_read_header( size_t *base_out, size_t *result_out, const unsigned char *delta, size_t delta_len); /** * Read the header of a git binary delta * * This variant reads just enough from the packfile stream to read the * delta header. */ extern int git_delta_read_header_fromstream( size_t *base_out, size_t *result_out, git_packfile_stream *stream); #endif git2r/src/libgit2/src/map.h0000644000175000017500000000221114125111754015270 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_map_h__ #define INCLUDE_map_h__ #include "common.h" /* p_mmap() prot values */ #define GIT_PROT_NONE 0x0 #define GIT_PROT_READ 0x1 #define GIT_PROT_WRITE 0x2 #define GIT_PROT_EXEC 0x4 /* git__mmmap() flags values */ #define GIT_MAP_FILE 0 #define GIT_MAP_SHARED 1 #define GIT_MAP_PRIVATE 2 #define GIT_MAP_TYPE 0xf #define GIT_MAP_FIXED 0x10 #ifdef __amigaos4__ #define MAP_FAILED 0 #endif typedef struct { /* memory mapped buffer */ void *data; /* data bytes */ size_t len; /* data length */ #ifdef GIT_WIN32 HANDLE fmh; /* file mapping handle */ #endif } git_map; #define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \ GIT_ASSERT(out != NULL && len > 0); \ GIT_ASSERT((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \ GIT_ASSERT((flags & GIT_MAP_FIXED) == 0); } while (0) extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset); extern int p_munmap(git_map *map); #endif git2r/src/libgit2/src/tree.h0000644000175000017500000000234514125111754015462 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_tree_h__ #define INCLUDE_tree_h__ #include "common.h" #include "git2/tree.h" #include "repository.h" #include "odb.h" #include "vector.h" #include "strmap.h" #include "pool.h" struct git_tree_entry { uint16_t attr; uint16_t filename_len; const git_oid *oid; const char *filename; }; struct git_tree { git_object object; git_odb_object *odb_obj; git_array_t(git_tree_entry) entries; }; struct git_treebuilder { git_repository *repo; git_strmap *map; git_buf write_cache; }; GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e) { return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr)); } void git_tree__free(void *tree); int git_tree__parse(void *tree, git_odb_object *obj); int git_tree__parse_raw(void *_tree, const char *data, size_t size); /** * Write a tree to the given repository */ int git_tree__write_index( git_oid *oid, git_index *index, git_repository *repo); /** * Obsolete mode kept for compatibility reasons */ #define GIT_FILEMODE_BLOB_GROUP_WRITABLE 0100664 #endif git2r/src/libgit2/src/idxmap.h0000644000175000017500000001236614125111754016011 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_idxmap_h__ #define INCLUDE_idxmap_h__ #include "common.h" #include "git2/index.h" /** A map with `git_index_entry`s as key. */ typedef struct kh_idx_s git_idxmap; /** A map with case-insensitive `git_index_entry`s as key */ typedef struct kh_idxicase_s git_idxmap_icase; /** * Allocate a new index entry map. * * @param out Pointer to the map that shall be allocated. * @return 0 on success, an error code if allocation has failed. */ int git_idxmap_new(git_idxmap **out); /** * Allocate a new case-insensitive index entry map. * * @param out Pointer to the map that shall be allocated. * @return 0 on success, an error code if allocation has failed. */ int git_idxmap_icase_new(git_idxmap_icase **out); /** * Free memory associated with the map. * * Note that this function will _not_ free values added to this * map. * * @param map Pointer to the map that is to be free'd. May be * `NULL`. */ void git_idxmap_free(git_idxmap *map); /** * Free memory associated with the map. * * Note that this function will _not_ free values added to this * map. * * @param map Pointer to the map that is to be free'd. May be * `NULL`. */ void git_idxmap_icase_free(git_idxmap_icase *map); /** * Clear all entries from the map. * * This function will remove all entries from the associated map. * Memory associated with it will not be released, though. * * @param map Pointer to the map that shall be cleared. May be * `NULL`. */ void git_idxmap_clear(git_idxmap *map); /** * Clear all entries from the map. * * This function will remove all entries from the associated map. * Memory associated with it will not be released, though. * * @param map Pointer to the map that shall be cleared. May be * `NULL`. */ void git_idxmap_icase_clear(git_idxmap_icase *map); /** * Resize the map by allocating more memory. * * @param map map that shall be resized * @param size count of entries that the map shall hold * @return `0` if the map was successfully resized, a negative * error code otherwise */ int git_idxmap_resize(git_idxmap *map, size_t size); /** * Resize the map by allocating more memory. * * @param map map that shall be resized * @param size count of entries that the map shall hold * @return `0` if the map was successfully resized, a negative * error code otherwise */ int git_idxmap_icase_resize(git_idxmap_icase *map, size_t size); /** * Return value associated with the given key. * * @param map map to search key in * @param key key to search for; the index entry will be searched * for by its case-sensitive path * @return value associated with the given key or NULL if the key was not found */ void *git_idxmap_get(git_idxmap *map, const git_index_entry *key); /** * Return value associated with the given key. * * @param map map to search key in * @param key key to search for; the index entry will be searched * for by its case-insensitive path * @return value associated with the given key or NULL if the key was not found */ void *git_idxmap_icase_get(git_idxmap_icase *map, const git_index_entry *key); /** * Set the entry for key to value. * * If the map has no corresponding entry for the given key, a new * entry will be created with the given value. If an entry exists * already, its value will be updated to match the given value. * * @param map map to create new entry in * @param key key to set * @param value value to associate the key with; may be NULL * @return zero if the key was successfully set, a negative error * code otherwise */ int git_idxmap_set(git_idxmap *map, const git_index_entry *key, void *value); /** * Set the entry for key to value. * * If the map has no corresponding entry for the given key, a new * entry will be created with the given value. If an entry exists * already, its value will be updated to match the given value. * * @param map map to create new entry in * @param key key to set * @param value value to associate the key with; may be NULL * @return zero if the key was successfully set, a negative error * code otherwise */ int git_idxmap_icase_set(git_idxmap_icase *map, const git_index_entry *key, void *value); /** * Delete an entry from the map. * * Delete the given key and its value from the map. If no such * key exists, this will do nothing. * * @param map map to delete key in * @param key key to delete * @return `0` if the key has been deleted, GIT_ENOTFOUND if no * such key was found, a negative code in case of an * error */ int git_idxmap_delete(git_idxmap *map, const git_index_entry *key); /** * Delete an entry from the map. * * Delete the given key and its value from the map. If no such * key exists, this will do nothing. * * @param map map to delete key in * @param key key to delete * @return `0` if the key has been deleted, GIT_ENOTFOUND if no * such key was found, a negative code in case of an * error */ int git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key); #endif git2r/src/libgit2/src/regexp.c0000644000175000017500000001251214125111754016005 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "regexp.h" #if defined(GIT_REGEX_BUILTIN) || defined(GIT_REGEX_PCRE) int git_regexp_compile(git_regexp *r, const char *pattern, int flags) { int erroffset, cflags = 0; const char *error = NULL; if (flags & GIT_REGEXP_ICASE) cflags |= PCRE_CASELESS; if ((*r = pcre_compile(pattern, cflags, &error, &erroffset, NULL)) == NULL) { git_error_set_str(GIT_ERROR_REGEX, error); return GIT_EINVALIDSPEC; } return 0; } void git_regexp_dispose(git_regexp *r) { pcre_free(*r); *r = NULL; } int git_regexp_match(const git_regexp *r, const char *string) { int error; if ((error = pcre_exec(*r, NULL, string, (int) strlen(string), 0, 0, NULL, 0)) < 0) return (error == PCRE_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; return 0; } int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches) { int static_ovec[9] = {0}, *ovec; int error; size_t i; /* The ovec array always needs to be a mutiple of three */ if (nmatches <= ARRAY_SIZE(static_ovec) / 3) ovec = static_ovec; else ovec = git__calloc(nmatches * 3, sizeof(*ovec)); GIT_ERROR_CHECK_ALLOC(ovec); if ((error = pcre_exec(*r, NULL, string, (int) strlen(string), 0, 0, ovec, (int) nmatches * 3)) < 0) goto out; if (error == 0) error = (int) nmatches; for (i = 0; i < (unsigned int) error; i++) { matches[i].start = (ovec[i * 2] < 0) ? -1 : ovec[i * 2]; matches[i].end = (ovec[i * 2 + 1] < 0) ? -1 : ovec[i * 2 + 1]; } for (i = (unsigned int) error; i < nmatches; i++) matches[i].start = matches[i].end = -1; out: if (nmatches > ARRAY_SIZE(static_ovec) / 3) git__free(ovec); if (error < 0) return (error == PCRE_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; return 0; } #elif defined(GIT_REGEX_PCRE2) int git_regexp_compile(git_regexp *r, const char *pattern, int flags) { unsigned char errmsg[1024]; PCRE2_SIZE erroff; int error, cflags = 0; if (flags & GIT_REGEXP_ICASE) cflags |= PCRE2_CASELESS; if ((*r = pcre2_compile((const unsigned char *) pattern, PCRE2_ZERO_TERMINATED, cflags, &error, &erroff, NULL)) == NULL) { pcre2_get_error_message(error, errmsg, sizeof(errmsg)); git_error_set_str(GIT_ERROR_REGEX, (char *) errmsg); return GIT_EINVALIDSPEC; } return 0; } void git_regexp_dispose(git_regexp *r) { pcre2_code_free(*r); *r = NULL; } int git_regexp_match(const git_regexp *r, const char *string) { pcre2_match_data *data; int error; data = pcre2_match_data_create(1, NULL); GIT_ERROR_CHECK_ALLOC(data); if ((error = pcre2_match(*r, (const unsigned char *) string, strlen(string), 0, 0, data, NULL)) < 0) return (error == PCRE2_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; pcre2_match_data_free(data); return 0; } int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches) { pcre2_match_data *data = NULL; PCRE2_SIZE *ovec; int error; size_t i; if ((data = pcre2_match_data_create(nmatches, NULL)) == NULL) { git_error_set_oom(); goto out; } if ((error = pcre2_match(*r, (const unsigned char *) string, strlen(string), 0, 0, data, NULL)) < 0) goto out; if (error == 0 || (unsigned int) error > nmatches) error = nmatches; ovec = pcre2_get_ovector_pointer(data); for (i = 0; i < (unsigned int) error; i++) { matches[i].start = (ovec[i * 2] == PCRE2_UNSET) ? -1 : (ssize_t) ovec[i * 2]; matches[i].end = (ovec[i * 2 + 1] == PCRE2_UNSET) ? -1 : (ssize_t) ovec[i * 2 + 1]; } for (i = (unsigned int) error; i < nmatches; i++) matches[i].start = matches[i].end = -1; out: pcre2_match_data_free(data); if (error < 0) return (error == PCRE2_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; return 0; } #elif defined(GIT_REGEX_REGCOMP) || defined(GIT_REGEX_REGCOMP_L) #if defined(GIT_REGEX_REGCOMP_L) # include #endif int git_regexp_compile(git_regexp *r, const char *pattern, int flags) { int cflags = REG_EXTENDED, error; char errmsg[1024]; if (flags & GIT_REGEXP_ICASE) cflags |= REG_ICASE; # if defined(GIT_REGEX_REGCOMP) if ((error = regcomp(r, pattern, cflags)) != 0) # else if ((error = regcomp_l(r, pattern, cflags, (locale_t) 0)) != 0) # endif { regerror(error, r, errmsg, sizeof(errmsg)); git_error_set_str(GIT_ERROR_REGEX, errmsg); return GIT_EINVALIDSPEC; } return 0; } void git_regexp_dispose(git_regexp *r) { regfree(r); } int git_regexp_match(const git_regexp *r, const char *string) { int error; if ((error = regexec(r, string, 0, NULL, 0)) != 0) return (error == REG_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; return 0; } int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches) { regmatch_t static_m[3], *m; int error; size_t i; if (nmatches <= ARRAY_SIZE(static_m)) m = static_m; else m = git__calloc(nmatches, sizeof(*m)); if ((error = regexec(r, string, nmatches, m, 0)) != 0) goto out; for (i = 0; i < nmatches; i++) { matches[i].start = (m[i].rm_so < 0) ? -1 : m[i].rm_so; matches[i].end = (m[i].rm_eo < 0) ? -1 : m[i].rm_eo; } out: if (nmatches > ARRAY_SIZE(static_m)) git__free(m); if (error) return (error == REG_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; return 0; } #endif git2r/src/libgit2/src/cache.c0000644000175000017500000001340214125111754015555 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "cache.h" #include "repository.h" #include "commit.h" #include "thread.h" #include "util.h" #include "odb.h" #include "object.h" #include "git2/oid.h" bool git_cache__enabled = true; ssize_t git_cache__max_storage = (256 * 1024 * 1024); git_atomic_ssize git_cache__current_storage = {0}; static size_t git_cache__max_object_size[8] = { 0, /* GIT_OBJECT__EXT1 */ 4096, /* GIT_OBJECT_COMMIT */ 4096, /* GIT_OBJECT_TREE */ 0, /* GIT_OBJECT_BLOB */ 4096, /* GIT_OBJECT_TAG */ 0, /* GIT_OBJECT__EXT2 */ 0, /* GIT_OBJECT_OFS_DELTA */ 0 /* GIT_OBJECT_REF_DELTA */ }; int git_cache_set_max_object_size(git_object_t type, size_t size) { if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) { git_error_set(GIT_ERROR_INVALID, "type out of range"); return -1; } git_cache__max_object_size[type] = size; return 0; } int git_cache_init(git_cache *cache) { memset(cache, 0, sizeof(*cache)); if ((git_oidmap_new(&cache->map)) < 0) return -1; if (git_rwlock_init(&cache->lock)) { git_error_set(GIT_ERROR_OS, "failed to initialize cache rwlock"); return -1; } return 0; } /* called with lock */ static void clear_cache(git_cache *cache) { git_cached_obj *evict = NULL; if (git_cache_size(cache) == 0) return; git_oidmap_foreach_value(cache->map, evict, { git_cached_obj_decref(evict); }); git_oidmap_clear(cache->map); git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory); cache->used_memory = 0; } void git_cache_clear(git_cache *cache) { if (git_rwlock_wrlock(&cache->lock) < 0) return; clear_cache(cache); git_rwlock_wrunlock(&cache->lock); } void git_cache_dispose(git_cache *cache) { git_cache_clear(cache); git_oidmap_free(cache->map); git_rwlock_free(&cache->lock); git__memzero(cache, sizeof(*cache)); } /* Called with lock */ static void cache_evict_entries(git_cache *cache) { size_t evict_count = git_cache_size(cache) / 2048, i; ssize_t evicted_memory = 0; if (evict_count < 8) evict_count = 8; /* do not infinite loop if there's not enough entries to evict */ if (evict_count > git_cache_size(cache)) { clear_cache(cache); return; } i = 0; while (evict_count > 0) { git_cached_obj *evict; const git_oid *key; if (git_oidmap_iterate((void **) &evict, cache->map, &i, &key) == GIT_ITEROVER) break; evict_count--; evicted_memory += evict->size; git_oidmap_delete(cache->map, key); git_cached_obj_decref(evict); } cache->used_memory -= evicted_memory; git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory); } static bool cache_should_store(git_object_t object_type, size_t object_size) { size_t max_size = git_cache__max_object_size[object_type]; return git_cache__enabled && object_size < max_size; } static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) { git_cached_obj *entry; if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0) return NULL; if ((entry = git_oidmap_get(cache->map, oid)) != NULL) { if (flags && entry->flags != flags) { entry = NULL; } else { git_cached_obj_incref(entry); } } git_rwlock_rdunlock(&cache->lock); return entry; } static void *cache_store(git_cache *cache, git_cached_obj *entry) { git_cached_obj *stored_entry; git_cached_obj_incref(entry); if (!git_cache__enabled && cache->used_memory > 0) { git_cache_clear(cache); return entry; } if (!cache_should_store(entry->type, entry->size)) return entry; if (git_rwlock_wrlock(&cache->lock) < 0) return entry; /* soften the load on the cache */ if (git_atomic_ssize_get(&git_cache__current_storage) > git_cache__max_storage) cache_evict_entries(cache); /* not found */ if ((stored_entry = git_oidmap_get(cache->map, &entry->oid)) == NULL) { if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) { git_cached_obj_incref(entry); cache->used_memory += entry->size; git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size); } } /* found */ else { if (stored_entry->flags == entry->flags) { git_cached_obj_decref(entry); git_cached_obj_incref(stored_entry); entry = stored_entry; } else if (stored_entry->flags == GIT_CACHE_STORE_RAW && entry->flags == GIT_CACHE_STORE_PARSED) { if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) { git_cached_obj_decref(stored_entry); git_cached_obj_incref(entry); } else { git_cached_obj_decref(entry); git_cached_obj_incref(stored_entry); entry = stored_entry; } } else { /* NO OP */ } } git_rwlock_wrunlock(&cache->lock); return entry; } void *git_cache_store_raw(git_cache *cache, git_odb_object *entry) { entry->cached.flags = GIT_CACHE_STORE_RAW; return cache_store(cache, (git_cached_obj *)entry); } void *git_cache_store_parsed(git_cache *cache, git_object *entry) { entry->cached.flags = GIT_CACHE_STORE_PARSED; return cache_store(cache, (git_cached_obj *)entry); } git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid) { return cache_get(cache, oid, GIT_CACHE_STORE_RAW); } git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid) { return cache_get(cache, oid, GIT_CACHE_STORE_PARSED); } void *git_cache_get_any(git_cache *cache, const git_oid *oid) { return cache_get(cache, oid, GIT_CACHE_STORE_ANY); } void git_cached_obj_decref(void *_obj) { git_cached_obj *obj = _obj; if (git_atomic32_dec(&obj->refcount) == 0) { switch (obj->flags) { case GIT_CACHE_STORE_RAW: git_odb_object__free(_obj); break; case GIT_CACHE_STORE_PARSED: git_object__free(_obj); break; default: git__free(_obj); break; } } } git2r/src/libgit2/src/trace.h0000644000175000017500000000243414125111754015620 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_trace_h__ #define INCLUDE_trace_h__ #include "common.h" #include #include "buffer.h" #ifdef GIT_TRACE struct git_trace_data { git_trace_level_t level; git_trace_cb callback; }; extern struct git_trace_data git_trace__data; GIT_INLINE(void) git_trace__write_fmt( git_trace_level_t level, const char *fmt, va_list ap) { git_trace_cb callback = git_trace__data.callback; git_buf message = GIT_BUF_INIT; git_buf_vprintf(&message, fmt, ap); callback(level, git_buf_cstr(&message)); git_buf_dispose(&message); } #define git_trace_level() (git_trace__data.level) GIT_INLINE(void) git_trace(git_trace_level_t level, const char *fmt, ...) { if (git_trace__data.level >= level && git_trace__data.callback != NULL) { va_list ap; va_start(ap, fmt); git_trace__write_fmt(level, fmt, ap); va_end(ap); } } #else GIT_INLINE(void) git_trace__null( git_trace_level_t level, const char *fmt, ...) { GIT_UNUSED(level); GIT_UNUSED(fmt); } #define git_trace_level() ((git_trace_level_t)0) #define git_trace git_trace__null #endif #endif git2r/src/libgit2/src/pack.h0000644000175000017500000001260414125111754015440 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_pack_h__ #define INCLUDE_pack_h__ #include "common.h" #include "git2/oid.h" #include "array.h" #include "map.h" #include "mwindow.h" #include "odb.h" #include "offmap.h" #include "oidmap.h" #include "zstream.h" /** * Function type for callbacks from git_pack_foreach_entry_offset. */ typedef int git_pack_foreach_entry_offset_cb( const git_oid *id, off64_t offset, void *payload); #define GIT_PACK_FILE_MODE 0444 #define PACK_SIGNATURE 0x5041434b /* "PACK" */ #define PACK_VERSION 2 #define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) struct git_pack_header { uint32_t hdr_signature; uint32_t hdr_version; uint32_t hdr_entries; }; /* * The first four bytes of index formats later than version 1 should * start with this signature, as all older git binaries would find this * value illegal and abort reading the file. * * This is the case because the number of objects in a packfile * cannot exceed 1,431,660,000 as every object would need at least * 3 bytes of data and the overall packfile cannot exceed 4 GiB with * version 1 of the index file due to the offsets limited to 32 bits. * Clearly the signature exceeds this maximum. * * Very old git binaries will also compare the first 4 bytes to the * next 4 bytes in the index and abort with a "non-monotonic index" * error if the second 4 byte word is smaller than the first 4 * byte word. This would be true in the proposed future index * format as idx_signature would be greater than idx_version. */ #define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */ struct git_pack_idx_header { uint32_t idx_signature; uint32_t idx_version; }; typedef struct git_pack_cache_entry { size_t last_usage; /* enough? */ git_atomic32 refcount; git_rawobj raw; } git_pack_cache_entry; struct pack_chain_elem { off64_t base_key; off64_t offset; size_t size; git_object_t type; }; typedef git_array_t(struct pack_chain_elem) git_dependency_chain; #define GIT_PACK_CACHE_MEMORY_LIMIT 16 * 1024 * 1024 #define GIT_PACK_CACHE_SIZE_LIMIT 1024 * 1024 /* don't bother caching anything over 1MB */ typedef struct { size_t memory_used; size_t memory_limit; size_t use_ctr; git_mutex lock; git_offmap *entries; } git_pack_cache; struct git_pack_file { git_mwindow_file mwf; git_map index_map; git_mutex lock; /* protect updates to index_map */ git_atomic32 refcount; uint32_t num_objects; uint32_t num_bad_objects; git_oid *bad_object_sha1; /* array of git_oid */ int index_version; git_time_t mtime; unsigned pack_local:1, pack_keep:1, has_cache:1; git_oidmap *idx_cache; git_oid **oids; git_pack_cache bases; /* delta base cache */ time_t last_freshen; /* last time the packfile was freshened */ /* something like ".git/objects/pack/xxxxx.pack" */ char pack_name[GIT_FLEX_ARRAY]; /* more */ }; /** * Return the position where an OID (or a prefix) would be inserted within the * OID Lookup Table of an .idx file. This performs binary search between the lo * and hi indices. * * The stride parameter is provided because .idx files version 1 store the OIDs * interleaved with the 4-byte file offsets of the objects within the .pack * file (stride = 24), whereas files with version 2 store them in a contiguous * flat array (stride = 20). */ int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo, unsigned hi, const unsigned char *oid_prefix); struct git_pack_entry { off64_t offset; git_oid sha1; struct git_pack_file *p; }; typedef struct git_packfile_stream { off64_t curpos; int done; git_zstream zstream; struct git_pack_file *p; git_mwindow *mw; } git_packfile_stream; int git_packfile__object_header(size_t *out, unsigned char *hdr, size_t size, git_object_t type); int git_packfile__name(char **out, const char *path); int git_packfile_unpack_header( size_t *size_p, git_object_t *type_p, struct git_pack_file *p, git_mwindow **w_curs, off64_t *curpos); int git_packfile_resolve_header( size_t *size_p, git_object_t *type_p, struct git_pack_file *p, off64_t offset); int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, off64_t *obj_offset); int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, off64_t curpos); ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len); void git_packfile_stream_dispose(git_packfile_stream *obj); int get_delta_base( off64_t *delta_base_out, struct git_pack_file *p, git_mwindow **w_curs, off64_t *curpos, git_object_t type, off64_t delta_obj_offset); void git_packfile_free(struct git_pack_file *p, bool unlink_packfile); int git_packfile_alloc(struct git_pack_file **pack_out, const char *path); int git_pack_entry_find( struct git_pack_entry *e, struct git_pack_file *p, const git_oid *short_oid, size_t len); int git_pack_foreach_entry( struct git_pack_file *p, git_odb_foreach_cb cb, void *data); /** * Similar to git_pack_foreach_entry, but: * - It also provides the offset of the object within the * packfile. * - It does not sort the objects in any order. * - It retains the lock while invoking the callback. */ int git_pack_foreach_entry_offset( struct git_pack_file *p, git_pack_foreach_entry_offset_cb cb, void *data); #endif git2r/src/libgit2/src/utf8.c0000644000175000017500000001004714125111754015402 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "utf8.h" #include "common.h" /* * git_utf8_iterate is taken from the utf8proc project, * http://www.public-software-group.org/utf8proc * * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the ""Software""), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ static const uint8_t utf8proc_utf8class[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0 }; static int utf8_charlen(const uint8_t *str, size_t str_len) { uint8_t length; size_t i; length = utf8proc_utf8class[str[0]]; if (!length) return -1; if (str_len > 0 && length > str_len) return -1; for (i = 1; i < length; i++) { if ((str[i] & 0xC0) != 0x80) return -1; } return (int)length; } int git_utf8_iterate(uint32_t *out, const char *_str, size_t str_len) { const uint8_t *str = (const uint8_t *)_str; uint32_t uc = 0; int length; *out = 0; if ((length = utf8_charlen(str, str_len)) < 0) return -1; switch (length) { case 1: uc = str[0]; break; case 2: uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F); if (uc < 0x80) uc = -1; break; case 3: uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6) + (str[2] & 0x3F); if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) || (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1; break; case 4: uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12) + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F); if (uc < 0x10000 || uc >= 0x110000) uc = -1; break; default: return -1; } if ((uc & 0xFFFF) >= 0xFFFE) return -1; *out = uc; return length; } size_t git_utf8_char_length(const char *_str, size_t str_len) { const uint8_t *str = (const uint8_t *)_str; size_t offset = 0, count = 0; while (offset < str_len) { int length = utf8_charlen(str + offset, str_len - offset); if (length < 0) length = 1; offset += length; count++; } return count; } size_t git_utf8_valid_buf_length(const char *_str, size_t str_len) { const uint8_t *str = (const uint8_t *)_str; size_t offset = 0; while (offset < str_len) { int length = utf8_charlen(str + offset, str_len - offset); if (length < 0) break; offset += length; } return offset; } git2r/src/libgit2/src/iterator.c0000644000175000017500000016531214125111754016353 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "iterator.h" #include "tree.h" #include "index.h" #define GIT_ITERATOR_FIRST_ACCESS (1 << 15) #define GIT_ITERATOR_HONOR_IGNORES (1 << 16) #define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17) #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0) #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE) #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES) #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND) #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND) #define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS) #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS) #define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES) #define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT) #define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS) static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case) { if (ignore_case) iter->flags |= GIT_ITERATOR_IGNORE_CASE; else iter->flags &= ~GIT_ITERATOR_IGNORE_CASE; iter->strcomp = ignore_case ? git__strcasecmp : git__strcmp; iter->strncomp = ignore_case ? git__strncasecmp : git__strncmp; iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp; iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch; git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp); } static int iterator_range_init( git_iterator *iter, const char *start, const char *end) { if (start && *start) { iter->start = git__strdup(start); GIT_ERROR_CHECK_ALLOC(iter->start); iter->start_len = strlen(iter->start); } if (end && *end) { iter->end = git__strdup(end); GIT_ERROR_CHECK_ALLOC(iter->end); iter->end_len = strlen(iter->end); } iter->started = (iter->start == NULL); iter->ended = false; return 0; } static void iterator_range_free(git_iterator *iter) { if (iter->start) { git__free(iter->start); iter->start = NULL; iter->start_len = 0; } if (iter->end) { git__free(iter->end); iter->end = NULL; iter->end_len = 0; } } static int iterator_reset_range( git_iterator *iter, const char *start, const char *end) { iterator_range_free(iter); return iterator_range_init(iter, start, end); } static int iterator_pathlist_init(git_iterator *iter, git_strarray *pathlist) { size_t i; if (git_vector_init(&iter->pathlist, pathlist->count, NULL) < 0) return -1; for (i = 0; i < pathlist->count; i++) { if (!pathlist->strings[i]) continue; if (git_vector_insert(&iter->pathlist, pathlist->strings[i]) < 0) return -1; } return 0; } static int iterator_init_common( git_iterator *iter, git_repository *repo, git_index *index, git_iterator_options *given_opts) { static git_iterator_options default_opts = GIT_ITERATOR_OPTIONS_INIT; git_iterator_options *options = given_opts ? given_opts : &default_opts; bool ignore_case; int precompose; int error; iter->repo = repo; iter->index = index; iter->flags = options->flags; if ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) { ignore_case = true; } else if ((iter->flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) { ignore_case = false; } else if (repo) { git_index *index; if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0) return error; ignore_case = !!index->ignore_case; if (ignore_case == 1) iter->flags |= GIT_ITERATOR_IGNORE_CASE; else iter->flags |= GIT_ITERATOR_DONT_IGNORE_CASE; } else { ignore_case = false; } /* try to look up precompose and set flag if appropriate */ if (repo && (iter->flags & GIT_ITERATOR_PRECOMPOSE_UNICODE) == 0 && (iter->flags & GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE) == 0) { if (git_repository__configmap_lookup(&precompose, repo, GIT_CONFIGMAP_PRECOMPOSE) < 0) git_error_clear(); else if (precompose) iter->flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE; } if ((iter->flags & GIT_ITERATOR_DONT_AUTOEXPAND)) iter->flags |= GIT_ITERATOR_INCLUDE_TREES; if ((error = iterator_range_init(iter, options->start, options->end)) < 0 || (error = iterator_pathlist_init(iter, &options->pathlist)) < 0) return error; iterator_set_ignore_case(iter, ignore_case); return 0; } static void iterator_clear(git_iterator *iter) { iter->started = false; iter->ended = false; iter->stat_calls = 0; iter->pathlist_walk_idx = 0; iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS; } GIT_INLINE(bool) iterator_has_started( git_iterator *iter, const char *path, bool is_submodule) { size_t path_len; if (iter->start == NULL || iter->started == true) return true; /* the starting path is generally a prefix - we have started once we * are prefixed by this path */ iter->started = (iter->prefixcomp(path, iter->start) >= 0); if (iter->started) return true; path_len = strlen(path); /* if, however, we are a submodule, then we support `start` being * suffixed with a `/` for crazy legacy reasons. match `submod` * with a start path of `submod/`. */ if (is_submodule && iter->start_len && path_len == iter->start_len - 1 && iter->start[iter->start_len-1] == '/') return true; /* if, however, our current path is a directory, and our starting path * is _beneath_ that directory, then recurse into the directory (even * though we have not yet "started") */ if (path_len > 0 && path[path_len-1] == '/' && iter->strncomp(path, iter->start, path_len) == 0) return true; return false; } GIT_INLINE(bool) iterator_has_ended(git_iterator *iter, const char *path) { if (iter->end == NULL) return false; else if (iter->ended) return true; iter->ended = (iter->prefixcomp(path, iter->end) > 0); return iter->ended; } /* walker for the index and tree iterator that allows it to walk the sorted * pathlist entries alongside sorted iterator entries. */ static bool iterator_pathlist_next_is(git_iterator *iter, const char *path) { char *p; size_t path_len, p_len, cmp_len, i; int cmp; if (iter->pathlist.length == 0) return true; git_vector_sort(&iter->pathlist); path_len = strlen(path); /* for comparison, drop the trailing slash on the current '/' */ if (path_len && path[path_len-1] == '/') path_len--; for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) { p = iter->pathlist.contents[i]; p_len = strlen(p); if (p_len && p[p_len-1] == '/') p_len--; cmp_len = min(path_len, p_len); /* see if the pathlist entry is a prefix of this path */ cmp = iter->strncomp(p, path, cmp_len); /* prefix match - see if there's an exact match, or if we were * given a path that matches the directory */ if (cmp == 0) { /* if this pathlist entry is not suffixed with a '/' then * it matches a path that is a file or a directory. * (eg, pathlist = "foo" and path is "foo" or "foo/" or * "foo/something") */ if (p[cmp_len] == '\0' && (path[cmp_len] == '\0' || path[cmp_len] == '/')) return true; /* if this pathlist entry _is_ suffixed with a '/' then * it matches only paths that are directories. * (eg, pathlist = "foo/" and path is "foo/" or "foo/something") */ if (p[cmp_len] == '/' && path[cmp_len] == '/') return true; } /* this pathlist entry sorts before the given path, try the next */ else if (cmp < 0) { iter->pathlist_walk_idx++; continue; } /* this pathlist sorts after the given path, no match. */ else if (cmp > 0) { break; } } return false; } typedef enum { ITERATOR_PATHLIST_NONE = 0, ITERATOR_PATHLIST_IS_FILE = 1, ITERATOR_PATHLIST_IS_DIR = 2, ITERATOR_PATHLIST_IS_PARENT = 3, ITERATOR_PATHLIST_FULL = 4, } iterator_pathlist_search_t; static iterator_pathlist_search_t iterator_pathlist_search( git_iterator *iter, const char *path, size_t path_len) { const char *p; size_t idx; int error; if (iter->pathlist.length == 0) return ITERATOR_PATHLIST_FULL; git_vector_sort(&iter->pathlist); error = git_vector_bsearch2(&idx, &iter->pathlist, (git_vector_cmp)iter->strcomp, path); /* the given path was found in the pathlist. since the pathlist only * matches directories when they're suffixed with a '/', analyze the * path string to determine whether it's a directory or not. */ if (error == 0) { if (path_len && path[path_len-1] == '/') return ITERATOR_PATHLIST_IS_DIR; return ITERATOR_PATHLIST_IS_FILE; } /* at this point, the path we're examining may be a directory (though we * don't know that yet, since we're avoiding a stat unless it's necessary) * so walk the pathlist looking for the given path with a '/' after it, */ while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) { if (iter->prefixcomp(p, path) != 0) break; /* an exact match would have been matched by the bsearch above */ GIT_ASSERT_WITH_RETVAL(p[path_len], ITERATOR_PATHLIST_NONE); /* is this a literal directory entry (eg `foo/`) or a file beneath */ if (p[path_len] == '/') { return (p[path_len+1] == '\0') ? ITERATOR_PATHLIST_IS_DIR : ITERATOR_PATHLIST_IS_PARENT; } if (p[path_len] > '/') break; idx++; } return ITERATOR_PATHLIST_NONE; } /* Empty iterator */ static int empty_iterator_noop(const git_index_entry **e, git_iterator *i) { GIT_UNUSED(i); if (e) *e = NULL; return GIT_ITEROVER; } static int empty_iterator_advance_over( const git_index_entry **e, git_iterator_status_t *s, git_iterator *i) { *s = GIT_ITERATOR_STATUS_EMPTY; return empty_iterator_noop(e, i); } static int empty_iterator_reset(git_iterator *i) { GIT_UNUSED(i); return 0; } static void empty_iterator_free(git_iterator *i) { GIT_UNUSED(i); } typedef struct { git_iterator base; git_iterator_callbacks cb; } empty_iterator; int git_iterator_for_nothing( git_iterator **out, git_iterator_options *options) { empty_iterator *iter; static git_iterator_callbacks callbacks = { empty_iterator_noop, empty_iterator_noop, empty_iterator_noop, empty_iterator_advance_over, empty_iterator_reset, empty_iterator_free }; *out = NULL; iter = git__calloc(1, sizeof(empty_iterator)); GIT_ERROR_CHECK_ALLOC(iter); iter->base.type = GIT_ITERATOR_EMPTY; iter->base.cb = &callbacks; iter->base.flags = options->flags; *out = &iter->base; return 0; } /* Tree iterator */ typedef struct { git_tree_entry *tree_entry; const char *parent_path; } tree_iterator_entry; typedef struct { git_tree *tree; /* path to this particular frame (folder) */ git_buf path; /* a sorted list of the entries for this frame (folder), these are * actually pointers to the iterator's entry pool. */ git_vector entries; tree_iterator_entry *current; size_t next_idx; /* on case insensitive iterations, we also have an array of other * paths that were case insensitively equal to this one, and their * tree objects. we have coalesced the tree entries into this frame. * a child `tree_iterator_entry` will contain a pointer to its actual * parent path. */ git_vector similar_trees; git_array_t(git_buf) similar_paths; } tree_iterator_frame; typedef struct { git_iterator base; git_tree *root; git_array_t(tree_iterator_frame) frames; git_index_entry entry; git_buf entry_path; /* a pool of entries to reduce the number of allocations */ git_pool entry_pool; } tree_iterator; GIT_INLINE(tree_iterator_frame *) tree_iterator_parent_frame( tree_iterator *iter) { return iter->frames.size > 1 ? &iter->frames.ptr[iter->frames.size-2] : NULL; } GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame( tree_iterator *iter) { return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL; } GIT_INLINE(int) tree_entry_cmp( const git_tree_entry *a, const git_tree_entry *b, bool icase) { return git_path_cmp( a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE, b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE, icase ? git__strncasecmp : git__strncmp); } GIT_INLINE(int) tree_iterator_entry_cmp_icase( const void *ptr_a, const void *ptr_b) { const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a; const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b; return tree_entry_cmp(a->tree_entry, b->tree_entry, true); } static int tree_iterator_entry_sort_icase(const void *ptr_a, const void *ptr_b) { const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a; const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b; int c = tree_entry_cmp(a->tree_entry, b->tree_entry, true); /* stabilize the sort order for filenames that are (case insensitively) * the same by examining the parent path (case sensitively) before * falling back to a case sensitive sort of the filename. */ if (!c && a->parent_path != b->parent_path) c = git__strcmp(a->parent_path, b->parent_path); if (!c) c = tree_entry_cmp(a->tree_entry, b->tree_entry, false); return c; } static int tree_iterator_compute_path( git_buf *out, tree_iterator_entry *entry) { git_buf_clear(out); if (entry->parent_path) git_buf_joinpath(out, entry->parent_path, entry->tree_entry->filename); else git_buf_puts(out, entry->tree_entry->filename); if (git_tree_entry__is_tree(entry->tree_entry)) git_buf_putc(out, '/'); if (git_buf_oom(out)) return -1; return 0; } static int tree_iterator_frame_init( tree_iterator *iter, git_tree *tree, tree_iterator_entry *frame_entry) { tree_iterator_frame *new_frame = NULL; tree_iterator_entry *new_entry; git_tree *dup = NULL; git_tree_entry *tree_entry; git_vector_cmp cmp; size_t i; int error = 0; new_frame = git_array_alloc(iter->frames); GIT_ERROR_CHECK_ALLOC(new_frame); if ((error = git_tree_dup(&dup, tree)) < 0) goto done; memset(new_frame, 0x0, sizeof(tree_iterator_frame)); new_frame->tree = dup; if (frame_entry && (error = tree_iterator_compute_path(&new_frame->path, frame_entry)) < 0) goto done; cmp = iterator__ignore_case(&iter->base) ? tree_iterator_entry_sort_icase : NULL; if ((error = git_vector_init(&new_frame->entries, dup->entries.size, cmp)) < 0) goto done; git_array_foreach(dup->entries, i, tree_entry) { if ((new_entry = git_pool_malloc(&iter->entry_pool, 1)) == NULL) { git_error_set_oom(); error = -1; goto done; } new_entry->tree_entry = tree_entry; new_entry->parent_path = new_frame->path.ptr; if ((error = git_vector_insert(&new_frame->entries, new_entry)) < 0) goto done; } git_vector_set_sorted(&new_frame->entries, !iterator__ignore_case(&iter->base)); done: if (error < 0) { git_tree_free(dup); git_array_pop(iter->frames); } return error; } GIT_INLINE(tree_iterator_entry *) tree_iterator_current_entry( tree_iterator_frame *frame) { return frame->current; } GIT_INLINE(int) tree_iterator_frame_push_neighbors( tree_iterator *iter, tree_iterator_frame *parent_frame, tree_iterator_frame *frame, const char *filename) { tree_iterator_entry *entry, *new_entry; git_tree *tree = NULL; git_tree_entry *tree_entry; git_buf *path; size_t new_size, i; int error = 0; while (parent_frame->next_idx < parent_frame->entries.length) { entry = parent_frame->entries.contents[parent_frame->next_idx]; if (strcasecmp(filename, entry->tree_entry->filename) != 0) break; if ((error = git_tree_lookup(&tree, iter->base.repo, entry->tree_entry->oid)) < 0) break; if (git_vector_insert(&parent_frame->similar_trees, tree) < 0) break; path = git_array_alloc(parent_frame->similar_paths); GIT_ERROR_CHECK_ALLOC(path); memset(path, 0, sizeof(git_buf)); if ((error = tree_iterator_compute_path(path, entry)) < 0) break; GIT_ERROR_CHECK_ALLOC_ADD(&new_size, frame->entries.length, tree->entries.size); git_vector_size_hint(&frame->entries, new_size); git_array_foreach(tree->entries, i, tree_entry) { new_entry = git_pool_malloc(&iter->entry_pool, 1); GIT_ERROR_CHECK_ALLOC(new_entry); new_entry->tree_entry = tree_entry; new_entry->parent_path = path->ptr; if ((error = git_vector_insert(&frame->entries, new_entry)) < 0) break; } if (error) break; parent_frame->next_idx++; } return error; } GIT_INLINE(int) tree_iterator_frame_push( tree_iterator *iter, tree_iterator_entry *entry) { tree_iterator_frame *parent_frame, *frame; git_tree *tree = NULL; int error; if ((error = git_tree_lookup(&tree, iter->base.repo, entry->tree_entry->oid)) < 0 || (error = tree_iterator_frame_init(iter, tree, entry)) < 0) goto done; parent_frame = tree_iterator_parent_frame(iter); frame = tree_iterator_current_frame(iter); /* if we're case insensitive, then we may have another directory that * is (case insensitively) equal to this one. coalesce those children * into this tree. */ if (iterator__ignore_case(&iter->base)) error = tree_iterator_frame_push_neighbors(iter, parent_frame, frame, entry->tree_entry->filename); done: git_tree_free(tree); return error; } static int tree_iterator_frame_pop(tree_iterator *iter) { tree_iterator_frame *frame; git_buf *buf = NULL; git_tree *tree; size_t i; GIT_ASSERT(iter->frames.size); frame = git_array_pop(iter->frames); git_vector_free(&frame->entries); git_tree_free(frame->tree); do { buf = git_array_pop(frame->similar_paths); git_buf_dispose(buf); } while (buf != NULL); git_array_clear(frame->similar_paths); git_vector_foreach(&frame->similar_trees, i, tree) git_tree_free(tree); git_vector_free(&frame->similar_trees); git_buf_dispose(&frame->path); return 0; } static int tree_iterator_current( const git_index_entry **out, git_iterator *i) { tree_iterator *iter = (tree_iterator *)i; if (!iterator__has_been_accessed(i)) return iter->base.cb->advance(out, i); if (!iter->frames.size) { *out = NULL; return GIT_ITEROVER; } *out = &iter->entry; return 0; } static void tree_iterator_set_current( tree_iterator *iter, tree_iterator_frame *frame, tree_iterator_entry *entry) { git_tree_entry *tree_entry = entry->tree_entry; frame->current = entry; memset(&iter->entry, 0x0, sizeof(git_index_entry)); iter->entry.mode = tree_entry->attr; iter->entry.path = iter->entry_path.ptr; git_oid_cpy(&iter->entry.id, tree_entry->oid); } static int tree_iterator_advance(const git_index_entry **out, git_iterator *i) { tree_iterator *iter = (tree_iterator *)i; int error = 0; iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; /* examine tree entries until we find the next one to return */ while (true) { tree_iterator_entry *prev_entry, *entry; tree_iterator_frame *frame; bool is_tree; if ((frame = tree_iterator_current_frame(iter)) == NULL) { error = GIT_ITEROVER; break; } /* no more entries in this frame. pop the frame out */ if (frame->next_idx == frame->entries.length) { if ((error = tree_iterator_frame_pop(iter)) < 0) break; continue; } /* we may have coalesced the contents of case-insensitively same-named * directories, so do the sort now. */ if (frame->next_idx == 0 && !git_vector_is_sorted(&frame->entries)) git_vector_sort(&frame->entries); /* we have more entries in the current frame, that's our next entry */ prev_entry = tree_iterator_current_entry(frame); entry = frame->entries.contents[frame->next_idx]; frame->next_idx++; /* we can have collisions when iterating case insensitively. (eg, * 'A/a' and 'a/A'). squash this one if it's already been seen. */ if (iterator__ignore_case(&iter->base) && prev_entry && tree_iterator_entry_cmp_icase(prev_entry, entry) == 0) continue; if ((error = tree_iterator_compute_path(&iter->entry_path, entry)) < 0) break; /* if this path is before our start, advance over this entry */ if (!iterator_has_started(&iter->base, iter->entry_path.ptr, false)) continue; /* if this path is after our end, stop */ if (iterator_has_ended(&iter->base, iter->entry_path.ptr)) { error = GIT_ITEROVER; break; } /* if we have a list of paths we're interested in, examine it */ if (!iterator_pathlist_next_is(&iter->base, iter->entry_path.ptr)) continue; is_tree = git_tree_entry__is_tree(entry->tree_entry); /* if we are *not* including trees then advance over this entry */ if (is_tree && !iterator__include_trees(iter)) { /* if we've found a tree (and are not returning it to the caller) * and we are autoexpanding, then we want to return the first * child. push the new directory and advance. */ if (iterator__do_autoexpand(iter)) { if ((error = tree_iterator_frame_push(iter, entry)) < 0) break; } continue; } tree_iterator_set_current(iter, frame, entry); /* if we are autoexpanding, then push this as a new frame, so that * the next call to `advance` will dive into this directory. */ if (is_tree && iterator__do_autoexpand(iter)) error = tree_iterator_frame_push(iter, entry); break; } if (out) *out = (error == 0) ? &iter->entry : NULL; return error; } static int tree_iterator_advance_into( const git_index_entry **out, git_iterator *i) { tree_iterator *iter = (tree_iterator *)i; tree_iterator_frame *frame; tree_iterator_entry *prev_entry; int error; if (out) *out = NULL; if ((frame = tree_iterator_current_frame(iter)) == NULL) return GIT_ITEROVER; /* get the last seen entry */ prev_entry = tree_iterator_current_entry(frame); /* it's legal to call advance_into when auto-expand is on. in this case, * we will have pushed a new (empty) frame on to the stack for this * new directory. since it's empty, its current_entry should be null. */ GIT_ASSERT(iterator__do_autoexpand(i) ^ (prev_entry != NULL)); if (prev_entry) { if (!git_tree_entry__is_tree(prev_entry->tree_entry)) return 0; if ((error = tree_iterator_frame_push(iter, prev_entry)) < 0) return error; } /* we've advanced into the directory in question, let advance * find the first entry */ return tree_iterator_advance(out, i); } static int tree_iterator_advance_over( const git_index_entry **out, git_iterator_status_t *status, git_iterator *i) { *status = GIT_ITERATOR_STATUS_NORMAL; return git_iterator_advance(out, i); } static void tree_iterator_clear(tree_iterator *iter) { while (iter->frames.size) tree_iterator_frame_pop(iter); git_array_clear(iter->frames); git_pool_clear(&iter->entry_pool); git_buf_clear(&iter->entry_path); iterator_clear(&iter->base); } static int tree_iterator_init(tree_iterator *iter) { int error; if ((error = git_pool_init(&iter->entry_pool, sizeof(tree_iterator_entry))) < 0 || (error = tree_iterator_frame_init(iter, iter->root, NULL)) < 0) return error; iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; return 0; } static int tree_iterator_reset(git_iterator *i) { tree_iterator *iter = (tree_iterator *)i; tree_iterator_clear(iter); return tree_iterator_init(iter); } static void tree_iterator_free(git_iterator *i) { tree_iterator *iter = (tree_iterator *)i; tree_iterator_clear(iter); git_tree_free(iter->root); git_buf_dispose(&iter->entry_path); } int git_iterator_for_tree( git_iterator **out, git_tree *tree, git_iterator_options *options) { tree_iterator *iter; int error; static git_iterator_callbacks callbacks = { tree_iterator_current, tree_iterator_advance, tree_iterator_advance_into, tree_iterator_advance_over, tree_iterator_reset, tree_iterator_free }; *out = NULL; if (tree == NULL) return git_iterator_for_nothing(out, options); iter = git__calloc(1, sizeof(tree_iterator)); GIT_ERROR_CHECK_ALLOC(iter); iter->base.type = GIT_ITERATOR_TREE; iter->base.cb = &callbacks; if ((error = iterator_init_common(&iter->base, git_tree_owner(tree), NULL, options)) < 0 || (error = git_tree_dup(&iter->root, tree)) < 0 || (error = tree_iterator_init(iter)) < 0) goto on_error; *out = &iter->base; return 0; on_error: git_iterator_free(&iter->base); return error; } int git_iterator_current_tree_entry( const git_tree_entry **tree_entry, git_iterator *i) { tree_iterator *iter; tree_iterator_frame *frame; tree_iterator_entry *entry; GIT_ASSERT(i->type == GIT_ITERATOR_TREE); iter = (tree_iterator *)i; frame = tree_iterator_current_frame(iter); entry = tree_iterator_current_entry(frame); *tree_entry = entry->tree_entry; return 0; } int git_iterator_current_parent_tree( const git_tree **parent_tree, git_iterator *i, size_t depth) { tree_iterator *iter; tree_iterator_frame *frame; GIT_ASSERT(i->type == GIT_ITERATOR_TREE); iter = (tree_iterator *)i; GIT_ASSERT(depth < iter->frames.size); frame = &iter->frames.ptr[iter->frames.size-depth-1]; *parent_tree = frame->tree; return 0; } /* Filesystem iterator */ typedef struct { struct stat st; size_t path_len; iterator_pathlist_search_t match; git_oid id; char path[GIT_FLEX_ARRAY]; } filesystem_iterator_entry; typedef struct { git_vector entries; git_pool entry_pool; size_t next_idx; size_t path_len; int is_ignored; } filesystem_iterator_frame; typedef struct { git_iterator base; char *root; size_t root_len; unsigned int dirload_flags; git_tree *tree; git_index *index; git_vector index_snapshot; git_array_t(filesystem_iterator_frame) frames; git_ignores ignores; /* info about the current entry */ git_index_entry entry; git_buf current_path; int current_is_ignored; /* temporary buffer for advance_over */ git_buf tmp_buf; } filesystem_iterator; GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_parent_frame( filesystem_iterator *iter) { return iter->frames.size > 1 ? &iter->frames.ptr[iter->frames.size-2] : NULL; } GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_current_frame( filesystem_iterator *iter) { return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL; } GIT_INLINE(filesystem_iterator_entry *) filesystem_iterator_current_entry( filesystem_iterator_frame *frame) { return frame->next_idx == 0 ? NULL : frame->entries.contents[frame->next_idx-1]; } static int filesystem_iterator_entry_cmp(const void *_a, const void *_b) { const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a; const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b; return git__strcmp(a->path, b->path); } static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b) { const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a; const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b; return git__strcasecmp(a->path, b->path); } #define FILESYSTEM_MAX_DEPTH 100 /** * Figure out if an entry is a submodule. * * We consider it a submodule if the path is listed as a submodule in * either the tree or the index. */ static int filesystem_iterator_is_submodule( bool *out, filesystem_iterator *iter, const char *path, size_t path_len) { bool is_submodule = false; int error; *out = false; /* first see if this path is a submodule in HEAD */ if (iter->tree) { git_tree_entry *entry; error = git_tree_entry_bypath(&entry, iter->tree, path); if (error < 0 && error != GIT_ENOTFOUND) return error; if (!error) { is_submodule = (entry->attr == GIT_FILEMODE_COMMIT); git_tree_entry_free(entry); } } if (!is_submodule && iter->base.index) { size_t pos; error = git_index_snapshot_find(&pos, &iter->index_snapshot, iter->base.entry_srch, path, path_len, 0); if (error < 0 && error != GIT_ENOTFOUND) return error; if (!error) { git_index_entry *e = git_vector_get(&iter->index_snapshot, pos); is_submodule = (e->mode == GIT_FILEMODE_COMMIT); } } *out = is_submodule; return 0; } static void filesystem_iterator_frame_push_ignores( filesystem_iterator *iter, filesystem_iterator_entry *frame_entry, filesystem_iterator_frame *new_frame) { filesystem_iterator_frame *previous_frame; const char *path = frame_entry ? frame_entry->path : ""; if (!iterator__honor_ignores(&iter->base)) return; if (git_ignore__lookup(&new_frame->is_ignored, &iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) { git_error_clear(); new_frame->is_ignored = GIT_IGNORE_NOTFOUND; } /* if this is not the top level directory... */ if (frame_entry) { const char *relative_path; previous_frame = filesystem_iterator_parent_frame(iter); /* push new ignores for files in this directory */ relative_path = frame_entry->path + previous_frame->path_len; /* inherit ignored from parent if no rule specified */ if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND) new_frame->is_ignored = previous_frame->is_ignored; git_ignore__push_dir(&iter->ignores, relative_path); } } static void filesystem_iterator_frame_pop_ignores( filesystem_iterator *iter) { if (iterator__honor_ignores(&iter->base)) git_ignore__pop_dir(&iter->ignores); } GIT_INLINE(bool) filesystem_iterator_examine_path( bool *is_dir_out, iterator_pathlist_search_t *match_out, filesystem_iterator *iter, filesystem_iterator_entry *frame_entry, const char *path, size_t path_len) { bool is_dir = 0; iterator_pathlist_search_t match = ITERATOR_PATHLIST_FULL; *is_dir_out = false; *match_out = ITERATOR_PATHLIST_NONE; if (iter->base.start_len) { int cmp = iter->base.strncomp(path, iter->base.start, path_len); /* we haven't stat'ed `path` yet, so we don't yet know if it's a * directory or not. special case if the current path may be a * directory that matches the start prefix. */ if (cmp == 0) { if (iter->base.start[path_len] == '/') is_dir = true; else if (iter->base.start[path_len] != '\0') cmp = -1; } if (cmp < 0) return false; } if (iter->base.end_len) { int cmp = iter->base.strncomp(path, iter->base.end, iter->base.end_len); if (cmp > 0) return false; } /* if we have a pathlist that we're limiting to, examine this path now * to avoid a `stat` if we're not interested in the path. */ if (iter->base.pathlist.length) { /* if our parent was explicitly included, so too are we */ if (frame_entry && frame_entry->match != ITERATOR_PATHLIST_IS_PARENT) match = ITERATOR_PATHLIST_FULL; else match = iterator_pathlist_search(&iter->base, path, path_len); if (match == ITERATOR_PATHLIST_NONE) return false; /* Ensure that the pathlist entry lines up with what we expected */ if (match == ITERATOR_PATHLIST_IS_DIR || match == ITERATOR_PATHLIST_IS_PARENT) is_dir = true; } *is_dir_out = is_dir; *match_out = match; return true; } GIT_INLINE(bool) filesystem_iterator_is_dot_git( filesystem_iterator *iter, const char *path, size_t path_len) { size_t len; if (!iterator__ignore_dot_git(&iter->base)) return false; if ((len = path_len) < 4) return false; if (path[len - 1] == '/') len--; if (git__tolower(path[len - 1]) != 't' || git__tolower(path[len - 2]) != 'i' || git__tolower(path[len - 3]) != 'g' || git__tolower(path[len - 4]) != '.') return false; return (len == 4 || path[len - 5] == '/'); } static int filesystem_iterator_entry_hash( filesystem_iterator *iter, filesystem_iterator_entry *entry) { git_buf fullpath = GIT_BUF_INIT; int error; if (S_ISDIR(entry->st.st_mode)) { memset(&entry->id, 0, GIT_OID_RAWSZ); return 0; } if (iter->base.type == GIT_ITERATOR_WORKDIR) return git_repository_hashfile(&entry->id, iter->base.repo, entry->path, GIT_OBJECT_BLOB, NULL); if (!(error = git_buf_joinpath(&fullpath, iter->root, entry->path)) && !(error = git_path_validate_workdir_buf(iter->base.repo, &fullpath))) error = git_odb_hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB); git_buf_dispose(&fullpath); return error; } static int filesystem_iterator_entry_init( filesystem_iterator_entry **out, filesystem_iterator *iter, filesystem_iterator_frame *frame, const char *path, size_t path_len, struct stat *statbuf, iterator_pathlist_search_t pathlist_match) { filesystem_iterator_entry *entry; size_t entry_size; int error = 0; *out = NULL; /* Make sure to append two bytes, one for the path's null * termination, one for a possible trailing '/' for folders. */ GIT_ERROR_CHECK_ALLOC_ADD(&entry_size, sizeof(filesystem_iterator_entry), path_len); GIT_ERROR_CHECK_ALLOC_ADD(&entry_size, entry_size, 2); entry = git_pool_malloc(&frame->entry_pool, entry_size); GIT_ERROR_CHECK_ALLOC(entry); entry->path_len = path_len; entry->match = pathlist_match; memcpy(entry->path, path, path_len); memcpy(&entry->st, statbuf, sizeof(struct stat)); /* Suffix directory paths with a '/' */ if (S_ISDIR(entry->st.st_mode)) entry->path[entry->path_len++] = '/'; entry->path[entry->path_len] = '\0'; if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH) error = filesystem_iterator_entry_hash(iter, entry); if (!error) *out = entry; return error; } static int filesystem_iterator_frame_push( filesystem_iterator *iter, filesystem_iterator_entry *frame_entry) { filesystem_iterator_frame *new_frame = NULL; git_path_diriter diriter = GIT_PATH_DIRITER_INIT; git_buf root = GIT_BUF_INIT; const char *path; filesystem_iterator_entry *entry; struct stat statbuf; size_t path_len; int error; if (iter->frames.size == FILESYSTEM_MAX_DEPTH) { git_error_set(GIT_ERROR_REPOSITORY, "directory nesting too deep (%"PRIuZ")", iter->frames.size); return -1; } new_frame = git_array_alloc(iter->frames); GIT_ERROR_CHECK_ALLOC(new_frame); memset(new_frame, 0, sizeof(filesystem_iterator_frame)); if (frame_entry) git_buf_joinpath(&root, iter->root, frame_entry->path); else git_buf_puts(&root, iter->root); if (git_buf_oom(&root) || git_path_validate_workdir_buf(iter->base.repo, &root) < 0) { error = -1; goto done; } new_frame->path_len = frame_entry ? frame_entry->path_len : 0; /* Any error here is equivalent to the dir not existing, skip over it */ if ((error = git_path_diriter_init( &diriter, root.ptr, iter->dirload_flags)) < 0) { error = GIT_ENOTFOUND; goto done; } if ((error = git_vector_init(&new_frame->entries, 64, iterator__ignore_case(&iter->base) ? filesystem_iterator_entry_cmp_icase : filesystem_iterator_entry_cmp)) < 0) goto done; if ((error = git_pool_init(&new_frame->entry_pool, 1)) < 0) goto done; /* check if this directory is ignored */ filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame); while ((error = git_path_diriter_next(&diriter)) == 0) { iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL; bool dir_expected = false; if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0 || (error = git_path_validate_workdir_with_len(iter->base.repo, path, path_len)) < 0) goto done; GIT_ASSERT(path_len > iter->root_len); /* remove the prefix if requested */ path += iter->root_len; path_len -= iter->root_len; /* examine start / end and the pathlist to see if this path is in it. * note that since we haven't yet stat'ed the path, we cannot know * whether it's a directory yet or not, so this can give us an * expected type (S_IFDIR or S_IFREG) that we should examine) */ if (!filesystem_iterator_examine_path(&dir_expected, &pathlist_match, iter, frame_entry, path, path_len)) continue; /* TODO: don't need to stat if assume unchanged for this path and * we have an index, we can just copy the data out of it. */ if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) { /* file was removed between readdir and lstat */ if (error == GIT_ENOTFOUND) continue; /* treat the file as unreadable */ memset(&statbuf, 0, sizeof(statbuf)); statbuf.st_mode = GIT_FILEMODE_UNREADABLE; error = 0; } iter->base.stat_calls++; /* Ignore wacky things in the filesystem */ if (!S_ISDIR(statbuf.st_mode) && !S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode) && statbuf.st_mode != GIT_FILEMODE_UNREADABLE) continue; if (filesystem_iterator_is_dot_git(iter, path, path_len)) continue; /* convert submodules to GITLINK and remove trailing slashes */ if (S_ISDIR(statbuf.st_mode)) { bool submodule = false; if ((error = filesystem_iterator_is_submodule(&submodule, iter, path, path_len)) < 0) goto done; if (submodule) statbuf.st_mode = GIT_FILEMODE_COMMIT; } /* Ensure that the pathlist entry lines up with what we expected */ else if (dir_expected) continue; if ((error = filesystem_iterator_entry_init(&entry, iter, new_frame, path, path_len, &statbuf, pathlist_match)) < 0) goto done; git_vector_insert(&new_frame->entries, entry); } if (error == GIT_ITEROVER) error = 0; /* sort now that directory suffix is added */ git_vector_sort(&new_frame->entries); done: if (error < 0) git_array_pop(iter->frames); git_buf_dispose(&root); git_path_diriter_free(&diriter); return error; } GIT_INLINE(int) filesystem_iterator_frame_pop(filesystem_iterator *iter) { filesystem_iterator_frame *frame; GIT_ASSERT(iter->frames.size); frame = git_array_pop(iter->frames); filesystem_iterator_frame_pop_ignores(iter); git_pool_clear(&frame->entry_pool); git_vector_free(&frame->entries); return 0; } static void filesystem_iterator_set_current( filesystem_iterator *iter, filesystem_iterator_entry *entry) { /* * Index entries are limited to 32 bit timestamps. We can safely * cast this since workdir times are only used in the cache; any * mismatch will cause a hash recomputation which is unfortunate * but affects only people who set their filetimes to 2038. * (Same with the file size.) */ iter->entry.ctime.seconds = (int32_t)entry->st.st_ctime; iter->entry.mtime.seconds = (int32_t)entry->st.st_mtime; #if defined(GIT_USE_NSEC) iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec; iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec; #else iter->entry.ctime.nanoseconds = 0; iter->entry.mtime.nanoseconds = 0; #endif iter->entry.dev = entry->st.st_dev; iter->entry.ino = entry->st.st_ino; iter->entry.mode = git_futils_canonical_mode(entry->st.st_mode); iter->entry.uid = entry->st.st_uid; iter->entry.gid = entry->st.st_gid; iter->entry.file_size = (uint32_t)entry->st.st_size; if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH) git_oid_cpy(&iter->entry.id, &entry->id); iter->entry.path = entry->path; iter->current_is_ignored = GIT_IGNORE_UNCHECKED; } static int filesystem_iterator_current( const git_index_entry **out, git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); if (!iterator__has_been_accessed(i)) return iter->base.cb->advance(out, i); if (!iter->frames.size) { *out = NULL; return GIT_ITEROVER; } *out = &iter->entry; return 0; } static int filesystem_iterator_is_dir( bool *is_dir, const filesystem_iterator *iter, const filesystem_iterator_entry *entry) { struct stat st; git_buf fullpath = GIT_BUF_INIT; int error = 0; if (S_ISDIR(entry->st.st_mode)) { *is_dir = 1; goto done; } if (!iterator__descend_symlinks(iter) || !S_ISLNK(entry->st.st_mode)) { *is_dir = 0; goto done; } if ((error = git_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 || (error = git_path_validate_workdir_buf(iter->base.repo, &fullpath)) < 0 || (error = p_stat(fullpath.ptr, &st)) < 0) goto done; *is_dir = S_ISDIR(st.st_mode); done: git_buf_dispose(&fullpath); return error; } static int filesystem_iterator_advance( const git_index_entry **out, git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); bool is_dir; int error = 0; iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; /* examine filesystem entries until we find the next one to return */ while (true) { filesystem_iterator_frame *frame; filesystem_iterator_entry *entry; if ((frame = filesystem_iterator_current_frame(iter)) == NULL) { error = GIT_ITEROVER; break; } /* no more entries in this frame. pop the frame out */ if (frame->next_idx == frame->entries.length) { filesystem_iterator_frame_pop(iter); continue; } /* we have more entries in the current frame, that's our next entry */ entry = frame->entries.contents[frame->next_idx]; frame->next_idx++; if ((error = filesystem_iterator_is_dir(&is_dir, iter, entry)) < 0) break; if (is_dir) { if (iterator__do_autoexpand(iter)) { error = filesystem_iterator_frame_push(iter, entry); /* may get GIT_ENOTFOUND due to races or permission problems * that we want to quietly swallow */ if (error == GIT_ENOTFOUND) continue; else if (error < 0) break; } if (!iterator__include_trees(iter)) continue; } filesystem_iterator_set_current(iter, entry); break; } if (out) *out = (error == 0) ? &iter->entry : NULL; return error; } static int filesystem_iterator_advance_into( const git_index_entry **out, git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); filesystem_iterator_frame *frame; filesystem_iterator_entry *prev_entry; int error; if (out) *out = NULL; if ((frame = filesystem_iterator_current_frame(iter)) == NULL) return GIT_ITEROVER; /* get the last seen entry */ prev_entry = filesystem_iterator_current_entry(frame); /* it's legal to call advance_into when auto-expand is on. in this case, * we will have pushed a new (empty) frame on to the stack for this * new directory. since it's empty, its current_entry should be null. */ GIT_ASSERT(iterator__do_autoexpand(i) ^ (prev_entry != NULL)); if (prev_entry) { if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT && !S_ISDIR(prev_entry->st.st_mode)) return 0; if ((error = filesystem_iterator_frame_push(iter, prev_entry)) < 0) return error; } /* we've advanced into the directory in question, let advance * find the first entry */ return filesystem_iterator_advance(out, i); } int git_iterator_current_workdir_path(git_buf **out, git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); const git_index_entry *entry; if (i->type != GIT_ITERATOR_FS && i->type != GIT_ITERATOR_WORKDIR) { *out = NULL; return 0; } git_buf_truncate(&iter->current_path, iter->root_len); if (git_iterator_current(&entry, i) < 0 || git_buf_puts(&iter->current_path, entry->path) < 0) return -1; *out = &iter->current_path; return 0; } GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry) { #if defined(GIT_WIN32) && !defined(__MINGW32__) return (entry && entry->mode) ? (S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE) : GIT_DIR_FLAG_UNKNOWN; #else GIT_UNUSED(entry); return GIT_DIR_FLAG_UNKNOWN; #endif } static void filesystem_iterator_update_ignored(filesystem_iterator *iter) { filesystem_iterator_frame *frame; git_dir_flag dir_flag = entry_dir_flag(&iter->entry); if (git_ignore__lookup(&iter->current_is_ignored, &iter->ignores, iter->entry.path, dir_flag) < 0) { git_error_clear(); iter->current_is_ignored = GIT_IGNORE_NOTFOUND; } /* use ignore from containing frame stack */ if (iter->current_is_ignored <= GIT_IGNORE_NOTFOUND) { frame = filesystem_iterator_current_frame(iter); iter->current_is_ignored = frame->is_ignored; } } GIT_INLINE(bool) filesystem_iterator_current_is_ignored( filesystem_iterator *iter) { if (iter->current_is_ignored == GIT_IGNORE_UNCHECKED) filesystem_iterator_update_ignored(iter); return (iter->current_is_ignored == GIT_IGNORE_TRUE); } bool git_iterator_current_is_ignored(git_iterator *i) { filesystem_iterator *iter = NULL; if (i->type != GIT_ITERATOR_WORKDIR) return false; iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); return filesystem_iterator_current_is_ignored(iter); } bool git_iterator_current_tree_is_ignored(git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); filesystem_iterator_frame *frame; if (i->type != GIT_ITERATOR_WORKDIR) return false; frame = filesystem_iterator_current_frame(iter); return (frame->is_ignored == GIT_IGNORE_TRUE); } static int filesystem_iterator_advance_over( const git_index_entry **out, git_iterator_status_t *status, git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); filesystem_iterator_frame *current_frame; filesystem_iterator_entry *current_entry; const git_index_entry *entry = NULL; const char *base; int error = 0; *out = NULL; *status = GIT_ITERATOR_STATUS_NORMAL; GIT_ASSERT(iterator__has_been_accessed(i)); current_frame = filesystem_iterator_current_frame(iter); GIT_ASSERT(current_frame); current_entry = filesystem_iterator_current_entry(current_frame); GIT_ASSERT(current_entry); if ((error = git_iterator_current(&entry, i)) < 0) return error; if (!S_ISDIR(entry->mode)) { if (filesystem_iterator_current_is_ignored(iter)) *status = GIT_ITERATOR_STATUS_IGNORED; return filesystem_iterator_advance(out, i); } git_buf_clear(&iter->tmp_buf); if ((error = git_buf_puts(&iter->tmp_buf, entry->path)) < 0) return error; base = iter->tmp_buf.ptr; /* scan inside the directory looking for files. if we find nothing, * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to * IGNORED. if we find a real actual item, upgrade all the way to NORMAL * and then stop. * * however, if we're here looking for a pathlist item (but are not * actually in the pathlist ourselves) then start at FILTERED instead of * EMPTY. callers then know that this path was not something they asked * about. */ *status = current_entry->match == ITERATOR_PATHLIST_IS_PARENT ? GIT_ITERATOR_STATUS_FILTERED : GIT_ITERATOR_STATUS_EMPTY; while (entry && !iter->base.prefixcomp(entry->path, base)) { if (filesystem_iterator_current_is_ignored(iter)) { /* if we found an explicitly ignored item, then update from * EMPTY to IGNORED */ *status = GIT_ITERATOR_STATUS_IGNORED; } else if (S_ISDIR(entry->mode)) { error = filesystem_iterator_advance_into(&entry, i); if (!error) continue; /* this directory disappeared, ignore it */ else if (error == GIT_ENOTFOUND) error = 0; /* a real error occurred */ else break; } else { /* we found a non-ignored item, treat parent as untracked */ *status = GIT_ITERATOR_STATUS_NORMAL; break; } if ((error = git_iterator_advance(&entry, i)) < 0) break; } /* wrap up scan back to base directory */ while (entry && !iter->base.prefixcomp(entry->path, base)) { if ((error = git_iterator_advance(&entry, i)) < 0) break; } if (!error) *out = entry; return error; } static void filesystem_iterator_clear(filesystem_iterator *iter) { while (iter->frames.size) filesystem_iterator_frame_pop(iter); git_array_clear(iter->frames); git_ignore__free(&iter->ignores); git_buf_dispose(&iter->tmp_buf); iterator_clear(&iter->base); } static int filesystem_iterator_init(filesystem_iterator *iter) { int error; if (iterator__honor_ignores(&iter->base) && (error = git_ignore__for_path(iter->base.repo, ".gitignore", &iter->ignores)) < 0) return error; if ((error = filesystem_iterator_frame_push(iter, NULL)) < 0) return error; iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; return 0; } static int filesystem_iterator_reset(git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); filesystem_iterator_clear(iter); return filesystem_iterator_init(iter); } static void filesystem_iterator_free(git_iterator *i) { filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); git__free(iter->root); git_buf_dispose(&iter->current_path); git_tree_free(iter->tree); if (iter->index) git_index_snapshot_release(&iter->index_snapshot, iter->index); filesystem_iterator_clear(iter); } static int iterator_for_filesystem( git_iterator **out, git_repository *repo, const char *root, git_index *index, git_tree *tree, git_iterator_t type, git_iterator_options *options) { filesystem_iterator *iter; size_t root_len; int error; static git_iterator_callbacks callbacks = { filesystem_iterator_current, filesystem_iterator_advance, filesystem_iterator_advance_into, filesystem_iterator_advance_over, filesystem_iterator_reset, filesystem_iterator_free }; *out = NULL; if (root == NULL) return git_iterator_for_nothing(out, options); iter = git__calloc(1, sizeof(filesystem_iterator)); GIT_ERROR_CHECK_ALLOC(iter); iter->base.type = type; iter->base.cb = &callbacks; root_len = strlen(root); iter->root = git__malloc(root_len+2); GIT_ERROR_CHECK_ALLOC(iter->root); memcpy(iter->root, root, root_len); if (root_len == 0 || root[root_len-1] != '/') { iter->root[root_len] = '/'; root_len++; } iter->root[root_len] = '\0'; iter->root_len = root_len; if ((error = git_buf_puts(&iter->current_path, iter->root)) < 0) goto on_error; if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0) goto on_error; if (tree && (error = git_tree_dup(&iter->tree, tree)) < 0) goto on_error; if (index && (error = git_index_snapshot_new(&iter->index_snapshot, index)) < 0) goto on_error; iter->index = index; iter->dirload_flags = (iterator__ignore_case(&iter->base) ? GIT_PATH_DIR_IGNORE_CASE : 0) | (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ? GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0); if ((error = filesystem_iterator_init(iter)) < 0) goto on_error; *out = &iter->base; return 0; on_error: git_iterator_free(&iter->base); return error; } int git_iterator_for_filesystem( git_iterator **out, const char *root, git_iterator_options *options) { return iterator_for_filesystem(out, NULL, root, NULL, NULL, GIT_ITERATOR_FS, options); } int git_iterator_for_workdir_ext( git_iterator **out, git_repository *repo, const char *repo_workdir, git_index *index, git_tree *tree, git_iterator_options *given_opts) { git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT; if (!repo_workdir) { if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) return GIT_EBAREREPO; repo_workdir = git_repository_workdir(repo); } /* upgrade to a workdir iterator, adding necessary internal flags */ if (given_opts) memcpy(&options, given_opts, sizeof(git_iterator_options)); options.flags |= GIT_ITERATOR_HONOR_IGNORES | GIT_ITERATOR_IGNORE_DOT_GIT; return iterator_for_filesystem(out, repo, repo_workdir, index, tree, GIT_ITERATOR_WORKDIR, &options); } /* Index iterator */ typedef struct { git_iterator base; git_vector entries; size_t next_idx; /* the pseudotree entry */ git_index_entry tree_entry; git_buf tree_buf; bool skip_tree; const git_index_entry *entry; } index_iterator; static int index_iterator_current( const git_index_entry **out, git_iterator *i) { index_iterator *iter = (index_iterator *)i; if (!iterator__has_been_accessed(i)) return iter->base.cb->advance(out, i); if (iter->entry == NULL) { *out = NULL; return GIT_ITEROVER; } *out = iter->entry; return 0; } static bool index_iterator_create_pseudotree( const git_index_entry **out, index_iterator *iter, const char *path) { const char *prev_path, *relative_path, *dirsep; size_t common_len; prev_path = iter->entry ? iter->entry->path : ""; /* determine if the new path is in a different directory from the old */ common_len = git_path_common_dirlen(prev_path, path); relative_path = path + common_len; if ((dirsep = strchr(relative_path, '/')) == NULL) return false; git_buf_clear(&iter->tree_buf); git_buf_put(&iter->tree_buf, path, (dirsep - path) + 1); iter->tree_entry.mode = GIT_FILEMODE_TREE; iter->tree_entry.path = iter->tree_buf.ptr; *out = &iter->tree_entry; return true; } static int index_iterator_skip_pseudotree(index_iterator *iter) { GIT_ASSERT(iterator__has_been_accessed(&iter->base)); GIT_ASSERT(S_ISDIR(iter->entry->mode)); while (true) { const git_index_entry *next_entry = NULL; if (++iter->next_idx >= iter->entries.length) return GIT_ITEROVER; next_entry = iter->entries.contents[iter->next_idx]; if (iter->base.strncomp(iter->tree_buf.ptr, next_entry->path, iter->tree_buf.size) != 0) break; } iter->skip_tree = false; return 0; } static int index_iterator_advance( const git_index_entry **out, git_iterator *i) { index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); const git_index_entry *entry = NULL; bool is_submodule; int error = 0; iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; while (true) { if (iter->next_idx >= iter->entries.length) { error = GIT_ITEROVER; break; } /* we were not asked to expand this pseudotree. advance over it. */ if (iter->skip_tree) { index_iterator_skip_pseudotree(iter); continue; } entry = iter->entries.contents[iter->next_idx]; is_submodule = S_ISGITLINK(entry->mode); if (!iterator_has_started(&iter->base, entry->path, is_submodule)) { iter->next_idx++; continue; } if (iterator_has_ended(&iter->base, entry->path)) { error = GIT_ITEROVER; break; } /* if we have a list of paths we're interested in, examine it */ if (!iterator_pathlist_next_is(&iter->base, entry->path)) { iter->next_idx++; continue; } /* if this is a conflict, skip it unless we're including conflicts */ if (git_index_entry_is_conflict(entry) && !iterator__include_conflicts(&iter->base)) { iter->next_idx++; continue; } /* we've found what will be our next _file_ entry. but if we are * returning trees entries, we may need to return a pseudotree * entry that will contain this. don't advance over this entry, * though, we still need to return it on the next `advance`. */ if (iterator__include_trees(&iter->base) && index_iterator_create_pseudotree(&entry, iter, entry->path)) { /* Note whether this pseudo tree should be expanded or not */ iter->skip_tree = iterator__dont_autoexpand(&iter->base); break; } iter->next_idx++; break; } iter->entry = (error == 0) ? entry : NULL; if (out) *out = iter->entry; return error; } static int index_iterator_advance_into( const git_index_entry **out, git_iterator *i) { index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); if (! S_ISDIR(iter->tree_entry.mode)) { if (out) *out = NULL; return 0; } iter->skip_tree = false; return index_iterator_advance(out, i); } static int index_iterator_advance_over( const git_index_entry **out, git_iterator_status_t *status, git_iterator *i) { index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); const git_index_entry *entry; int error; if ((error = index_iterator_current(&entry, i)) < 0) return error; if (S_ISDIR(entry->mode)) index_iterator_skip_pseudotree(iter); *status = GIT_ITERATOR_STATUS_NORMAL; return index_iterator_advance(out, i); } static void index_iterator_clear(index_iterator *iter) { iterator_clear(&iter->base); } static int index_iterator_init(index_iterator *iter) { iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; iter->next_idx = 0; iter->skip_tree = false; return 0; } static int index_iterator_reset(git_iterator *i) { index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); index_iterator_clear(iter); return index_iterator_init(iter); } static void index_iterator_free(git_iterator *i) { index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); git_index_snapshot_release(&iter->entries, iter->base.index); git_buf_dispose(&iter->tree_buf); } int git_iterator_for_index( git_iterator **out, git_repository *repo, git_index *index, git_iterator_options *options) { index_iterator *iter; int error; static git_iterator_callbacks callbacks = { index_iterator_current, index_iterator_advance, index_iterator_advance_into, index_iterator_advance_over, index_iterator_reset, index_iterator_free }; *out = NULL; if (index == NULL) return git_iterator_for_nothing(out, options); iter = git__calloc(1, sizeof(index_iterator)); GIT_ERROR_CHECK_ALLOC(iter); iter->base.type = GIT_ITERATOR_INDEX; iter->base.cb = &callbacks; if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0 || (error = git_index_snapshot_new(&iter->entries, index)) < 0 || (error = index_iterator_init(iter)) < 0) goto on_error; git_vector_set_cmp(&iter->entries, iterator__ignore_case(&iter->base) ? git_index_entry_icmp : git_index_entry_cmp); git_vector_sort(&iter->entries); *out = &iter->base; return 0; on_error: git_iterator_free(&iter->base); return error; } /* Iterator API */ int git_iterator_reset_range( git_iterator *i, const char *start, const char *end) { if (iterator_reset_range(i, start, end) < 0) return -1; return i->cb->reset(i); } int git_iterator_set_ignore_case(git_iterator *i, bool ignore_case) { GIT_ASSERT(!iterator__has_been_accessed(i)); iterator_set_ignore_case(i, ignore_case); return 0; } void git_iterator_free(git_iterator *iter) { if (iter == NULL) return; iter->cb->free(iter); git_vector_free(&iter->pathlist); git__free(iter->start); git__free(iter->end); memset(iter, 0, sizeof(*iter)); git__free(iter); } int git_iterator_foreach( git_iterator *iterator, git_iterator_foreach_cb cb, void *data) { const git_index_entry *iterator_item; int error = 0; if ((error = git_iterator_current(&iterator_item, iterator)) < 0) goto done; if ((error = cb(iterator_item, data)) != 0) goto done; while (true) { if ((error = git_iterator_advance(&iterator_item, iterator)) < 0) goto done; if ((error = cb(iterator_item, data)) != 0) goto done; } done: if (error == GIT_ITEROVER) error = 0; return error; } int git_iterator_walk( git_iterator **iterators, size_t cnt, git_iterator_walk_cb cb, void *data) { const git_index_entry **iterator_item; /* next in each iterator */ const git_index_entry **cur_items; /* current path in each iter */ const git_index_entry *first_match; size_t i, j; int error = 0; iterator_item = git__calloc(cnt, sizeof(git_index_entry *)); cur_items = git__calloc(cnt, sizeof(git_index_entry *)); GIT_ERROR_CHECK_ALLOC(iterator_item); GIT_ERROR_CHECK_ALLOC(cur_items); /* Set up the iterators */ for (i = 0; i < cnt; i++) { error = git_iterator_current(&iterator_item[i], iterators[i]); if (error < 0 && error != GIT_ITEROVER) goto done; } while (true) { for (i = 0; i < cnt; i++) cur_items[i] = NULL; first_match = NULL; /* Find the next path(s) to consume from each iterator */ for (i = 0; i < cnt; i++) { if (iterator_item[i] == NULL) continue; if (first_match == NULL) { first_match = iterator_item[i]; cur_items[i] = iterator_item[i]; } else { int path_diff = git_index_entry_cmp(iterator_item[i], first_match); if (path_diff < 0) { /* Found an index entry that sorts before the one we're * looking at. Forget that we've seen the other and * look at the other iterators for this path. */ for (j = 0; j < i; j++) cur_items[j] = NULL; first_match = iterator_item[i]; cur_items[i] = iterator_item[i]; } else if (path_diff == 0) { cur_items[i] = iterator_item[i]; } } } if (first_match == NULL) break; if ((error = cb(cur_items, data)) != 0) goto done; /* Advance each iterator that participated */ for (i = 0; i < cnt; i++) { if (cur_items[i] == NULL) continue; error = git_iterator_advance(&iterator_item[i], iterators[i]); if (error < 0 && error != GIT_ITEROVER) goto done; } } done: git__free((git_index_entry **)iterator_item); git__free((git_index_entry **)cur_items); if (error == GIT_ITEROVER) error = 0; return error; } git2r/src/libgit2/src/oidmap.c0000644000175000017500000000375214125111754015772 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "oidmap.h" #define kmalloc git__malloc #define kcalloc git__calloc #define krealloc git__realloc #define kreallocarray git__reallocarray #define kfree git__free #include "khash.h" __KHASH_TYPE(oid, const git_oid *, void *) GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) { khint_t h; memcpy(&h, oid, sizeof(khint_t)); return h; } __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal) int git_oidmap_new(git_oidmap **out) { *out = kh_init(oid); GIT_ERROR_CHECK_ALLOC(*out); return 0; } void git_oidmap_free(git_oidmap *map) { kh_destroy(oid, map); } void git_oidmap_clear(git_oidmap *map) { kh_clear(oid, map); } size_t git_oidmap_size(git_oidmap *map) { return kh_size(map); } void *git_oidmap_get(git_oidmap *map, const git_oid *key) { size_t idx = kh_get(oid, map, key); if (idx == kh_end(map) || !kh_exist(map, idx)) return NULL; return kh_val(map, idx); } int git_oidmap_set(git_oidmap *map, const git_oid *key, void *value) { size_t idx; int rval; idx = kh_put(oid, map, key, &rval); if (rval < 0) return -1; if (rval == 0) kh_key(map, idx) = key; kh_val(map, idx) = value; return 0; } int git_oidmap_delete(git_oidmap *map, const git_oid *key) { khiter_t idx = kh_get(oid, map, key); if (idx == kh_end(map)) return GIT_ENOTFOUND; kh_del(oid, map, idx); return 0; } int git_oidmap_exists(git_oidmap *map, const git_oid *key) { return kh_get(oid, map, key) != kh_end(map); } int git_oidmap_iterate(void **value, git_oidmap *map, size_t *iter, const git_oid **key) { size_t i = *iter; while (i < map->n_buckets && !kh_exist(map, i)) i++; if (i >= map->n_buckets) return GIT_ITEROVER; if (key) *key = kh_key(map, i); if (value) *value = kh_value(map, i); *iter = ++i; return 0; } git2r/src/libgit2/src/diff_tform.h0000644000175000017500000000136514125111754016643 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_diff_tform_h__ #define INCLUDE_diff_tform_h__ #include "common.h" #include "diff_file.h" extern int git_diff_find_similar__hashsig_for_file( void **out, const git_diff_file *f, const char *path, void *p); extern int git_diff_find_similar__hashsig_for_buf( void **out, const git_diff_file *f, const char *buf, size_t len, void *p); extern void git_diff_find_similar__hashsig_free(void *sig, void *payload); extern int git_diff_find_similar__calc_similarity( int *score, void *siga, void *sigb, void *payload); #endif git2r/src/libgit2/src/trace.c0000644000175000017500000000140714125111754015612 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "trace.h" #include "buffer.h" #include "runtime.h" #include "git2/trace.h" #ifdef GIT_TRACE struct git_trace_data git_trace__data = {0}; #endif int git_trace_set(git_trace_level_t level, git_trace_cb callback) { #ifdef GIT_TRACE GIT_ASSERT_ARG(level == 0 || callback != NULL); git_trace__data.level = level; git_trace__data.callback = callback; GIT_MEMORY_BARRIER; return 0; #else GIT_UNUSED(level); GIT_UNUSED(callback); git_error_set(GIT_ERROR_INVALID, "this version of libgit2 was not built with tracing."); return -1; #endif } git2r/src/libgit2/src/object_api.c0000644000175000017500000000653014125111754016615 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/object.h" #include "repository.h" #include "commit.h" #include "tree.h" #include "blob.h" #include "tag.h" /** * Commit */ int git_commit_lookup(git_commit **out, git_repository *repo, const git_oid *id) { return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_COMMIT); } int git_commit_lookup_prefix(git_commit **out, git_repository *repo, const git_oid *id, size_t len) { return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_COMMIT); } void git_commit_free(git_commit *obj) { git_object_free((git_object *)obj); } const git_oid *git_commit_id(const git_commit *obj) { return git_object_id((const git_object *)obj); } git_repository *git_commit_owner(const git_commit *obj) { return git_object_owner((const git_object *)obj); } int git_commit_dup(git_commit **out, git_commit *obj) { return git_object_dup((git_object **)out, (git_object *)obj); } /** * Tree */ int git_tree_lookup(git_tree **out, git_repository *repo, const git_oid *id) { return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_TREE); } int git_tree_lookup_prefix(git_tree **out, git_repository *repo, const git_oid *id, size_t len) { return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_TREE); } void git_tree_free(git_tree *obj) { git_object_free((git_object *)obj); } const git_oid *git_tree_id(const git_tree *obj) { return git_object_id((const git_object *)obj); } git_repository *git_tree_owner(const git_tree *obj) { return git_object_owner((const git_object *)obj); } int git_tree_dup(git_tree **out, git_tree *obj) { return git_object_dup((git_object **)out, (git_object *)obj); } /** * Tag */ int git_tag_lookup(git_tag **out, git_repository *repo, const git_oid *id) { return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_TAG); } int git_tag_lookup_prefix(git_tag **out, git_repository *repo, const git_oid *id, size_t len) { return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_TAG); } void git_tag_free(git_tag *obj) { git_object_free((git_object *)obj); } const git_oid *git_tag_id(const git_tag *obj) { return git_object_id((const git_object *)obj); } git_repository *git_tag_owner(const git_tag *obj) { return git_object_owner((const git_object *)obj); } int git_tag_dup(git_tag **out, git_tag *obj) { return git_object_dup((git_object **)out, (git_object *)obj); } /** * Blob */ int git_blob_lookup(git_blob **out, git_repository *repo, const git_oid *id) { return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_BLOB); } int git_blob_lookup_prefix(git_blob **out, git_repository *repo, const git_oid *id, size_t len) { return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_BLOB); } void git_blob_free(git_blob *obj) { git_object_free((git_object *)obj); } const git_oid *git_blob_id(const git_blob *obj) { return git_object_id((const git_object *)obj); } git_repository *git_blob_owner(const git_blob *obj) { return git_object_owner((const git_object *)obj); } int git_blob_dup(git_blob **out, git_blob *obj) { return git_object_dup((git_object **)out, (git_object *)obj); } git2r/src/libgit2/src/ignore.c0000644000175000017500000003737514125111754016014 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "ignore.h" #include "git2/ignore.h" #include "common.h" #include "attrcache.h" #include "path.h" #include "config.h" #include "wildmatch.h" #define GIT_IGNORE_INTERNAL "[internal]exclude" #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n" /** * A negative ignore pattern can negate a positive one without * wildcards if it is a basename only and equals the basename of * the positive pattern. Thus * * foo/bar * !bar * * would result in foo/bar being unignored again while * * moo/foo/bar * !foo/bar * * would do nothing. The reverse also holds true: a positive * basename pattern can be negated by unignoring the basename in * subdirectories. Thus * * bar * !foo/bar * * would result in foo/bar being unignored again. As with the * first case, * * foo/bar * !moo/foo/bar * * would do nothing, again. */ static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg) { int (*cmp)(const char *, const char *, size_t); git_attr_fnmatch *longer, *shorter; char *p; if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 || (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) return false; if (neg->flags & GIT_ATTR_FNMATCH_ICASE) cmp = git__strncasecmp; else cmp = git__strncmp; /* If lengths match we need to have an exact match */ if (rule->length == neg->length) { return cmp(rule->pattern, neg->pattern, rule->length) == 0; } else if (rule->length < neg->length) { shorter = rule; longer = neg; } else { shorter = neg; longer = rule; } /* Otherwise, we need to check if the shorter * rule is a basename only (that is, it contains * no path separator) and, if so, if it * matches the tail of the longer rule */ p = longer->pattern + longer->length - shorter->length; if (p[-1] != '/') return false; if (memchr(shorter->pattern, '/', shorter->length) != NULL) return false; return cmp(p, shorter->pattern, shorter->length) == 0; } /** * A negative ignore can only unignore a file which is given explicitly before, thus * * foo * !foo/bar * * does not unignore 'foo/bar' as it's not in the list. However * * foo/ * !foo/bar * * does unignore 'foo/bar', as it is contained within the 'foo/' rule. */ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match) { int error = 0, wildmatch_flags, effective_flags; size_t i; git_attr_fnmatch *rule; char *path; git_buf buf = GIT_BUF_INIT; *out = 0; wildmatch_flags = WM_PATHNAME; if (match->flags & GIT_ATTR_FNMATCH_ICASE) wildmatch_flags |= WM_CASEFOLD; /* path of the file relative to the workdir, so we match the rules in subdirs */ if (match->containing_dir) { git_buf_puts(&buf, match->containing_dir); } if (git_buf_puts(&buf, match->pattern) < 0) return -1; path = git_buf_detach(&buf); git_vector_foreach(rules, i, rule) { if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) { if (does_negate_pattern(rule, match)) { error = 0; *out = 1; goto out; } else continue; } git_buf_clear(&buf); if (rule->containing_dir) git_buf_puts(&buf, rule->containing_dir); git_buf_puts(&buf, rule->pattern); if (git_buf_oom(&buf)) goto out; /* * if rule isn't for full path we match without PATHNAME flag * as lines like *.txt should match something like dir/test.txt * requiring * to also match / */ effective_flags = wildmatch_flags; if (!(rule->flags & GIT_ATTR_FNMATCH_FULLPATH)) effective_flags &= ~WM_PATHNAME; /* if we found a match, we want to keep this rule */ if ((wildmatch(git_buf_cstr(&buf), path, effective_flags)) == WM_MATCH) { *out = 1; error = 0; goto out; } } error = 0; out: git__free(path); git_buf_dispose(&buf); return error; } static int parse_ignore_file( git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros) { int error = 0; int ignore_case = false; const char *scan = data, *context = NULL; git_attr_fnmatch *match = NULL; GIT_UNUSED(allow_macros); if (git_repository__configmap_lookup(&ignore_case, repo, GIT_CONFIGMAP_IGNORECASE) < 0) git_error_clear(); /* if subdir file path, convert context for file paths */ if (attrs->entry && git_path_root(attrs->entry->path) < 0 && !git__suffixcmp(attrs->entry->path, "/" GIT_IGNORE_FILE)) context = attrs->entry->path; if (git_mutex_lock(&attrs->lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock ignore file"); return -1; } while (!error && *scan) { int valid_rule = 1; if (!match && !(match = git__calloc(1, sizeof(*match)))) { error = -1; break; } match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; if (!(error = git_attr_fnmatch__parse( match, &attrs->pool, context, &scan))) { match->flags |= GIT_ATTR_FNMATCH_IGNORE; if (ignore_case) match->flags |= GIT_ATTR_FNMATCH_ICASE; scan = git__next_line(scan); /* * If a negative match doesn't actually do anything, * throw it away. As we cannot always verify whether a * rule containing wildcards negates another rule, we * do not optimize away these rules, though. * */ if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE && !(match->flags & GIT_ATTR_FNMATCH_HASWILD)) error = does_negate_rule(&valid_rule, &attrs->rules, match); if (!error && valid_rule) error = git_vector_insert(&attrs->rules, match); } if (error != 0 || !valid_rule) { match->pattern = NULL; if (error == GIT_ENOTFOUND) error = 0; } else { match = NULL; /* vector now "owns" the match */ } } git_mutex_unlock(&attrs->lock); git__free(match); return error; } static int push_ignore_file( git_ignores *ignores, git_vector *which_list, const char *base, const char *filename) { git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename }; git_attr_file *file = NULL; int error = 0; error = git_attr_cache__get(&file, ignores->repo, NULL, &source, parse_ignore_file, false); if (error < 0) return error; if (file != NULL) { if ((error = git_vector_insert(which_list, file)) < 0) git_attr_file__free(file); } return error; } static int push_one_ignore(void *payload, const char *path) { git_ignores *ign = payload; ign->depth++; return push_ignore_file(ign, &ign->ign_path, path, GIT_IGNORE_FILE); } static int get_internal_ignores(git_attr_file **out, git_repository *repo) { git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_MEMORY, NULL, GIT_IGNORE_INTERNAL }; int error; if ((error = git_attr_cache__init(repo)) < 0) return error; error = git_attr_cache__get(out, repo, NULL, &source, NULL, false); /* if internal rules list is empty, insert default rules */ if (!error && !(*out)->rules.length) error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES, false); return error; } int git_ignore__for_path( git_repository *repo, const char *path, git_ignores *ignores) { int error = 0; const char *workdir = git_repository_workdir(repo); git_buf infopath = GIT_BUF_INIT; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(ignores); GIT_ASSERT_ARG(path); memset(ignores, 0, sizeof(*ignores)); ignores->repo = repo; /* Read the ignore_case flag */ if ((error = git_repository__configmap_lookup( &ignores->ignore_case, repo, GIT_CONFIGMAP_IGNORECASE)) < 0) goto cleanup; if ((error = git_attr_cache__init(repo)) < 0) goto cleanup; /* given a unrooted path in a non-bare repo, resolve it */ if (workdir && git_path_root(path) < 0) { git_buf local = GIT_BUF_INIT; if ((error = git_path_dirname_r(&local, path)) < 0 || (error = git_path_resolve_relative(&local, 0)) < 0 || (error = git_path_to_dir(&local)) < 0 || (error = git_buf_joinpath(&ignores->dir, workdir, local.ptr)) < 0 || (error = git_path_validate_workdir_buf(repo, &ignores->dir)) < 0) { /* Nothing, we just want to stop on the first error */ } git_buf_dispose(&local); } else { if (!(error = git_buf_joinpath(&ignores->dir, path, ""))) error = git_path_validate_filesystem(ignores->dir.ptr, ignores->dir.size); } if (error < 0) goto cleanup; if (workdir && !git__prefixcmp(ignores->dir.ptr, workdir)) ignores->dir_root = strlen(workdir); /* set up internals */ if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0) goto cleanup; /* load .gitignore up the path */ if (workdir != NULL) { error = git_path_walk_up( &ignores->dir, workdir, push_one_ignore, ignores); if (error < 0) goto cleanup; } /* load .git/info/exclude if possible */ if ((error = git_repository_item_path(&infopath, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 || (error = push_ignore_file(ignores, &ignores->ign_global, infopath.ptr, GIT_IGNORE_FILE_INREPO)) < 0) { if (error != GIT_ENOTFOUND) goto cleanup; error = 0; } /* load core.excludesfile */ if (git_repository_attr_cache(repo)->cfg_excl_file != NULL) error = push_ignore_file( ignores, &ignores->ign_global, NULL, git_repository_attr_cache(repo)->cfg_excl_file); cleanup: git_buf_dispose(&infopath); if (error < 0) git_ignore__free(ignores); return error; } int git_ignore__push_dir(git_ignores *ign, const char *dir) { if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0) return -1; ign->depth++; return push_ignore_file( ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); } int git_ignore__pop_dir(git_ignores *ign) { if (ign->ign_path.length > 0) { git_attr_file *file = git_vector_last(&ign->ign_path); const char *start = file->entry->path, *end; /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/") * - file->path looks something like "a/b/.gitignore * * We are popping the last directory off ign->dir. We also want * to remove the file from the vector if the popped directory * matches the ignore path. We need to test if the "a/b" part of * the file key matches the path we are about to pop. */ if ((end = strrchr(start, '/')) != NULL) { size_t dirlen = (end - start) + 1; const char *relpath = ign->dir.ptr + ign->dir_root; size_t pathlen = ign->dir.size - ign->dir_root; if (pathlen == dirlen && !memcmp(relpath, start, dirlen)) { git_vector_pop(&ign->ign_path); git_attr_file__free(file); } } } if (--ign->depth > 0) { git_buf_rtruncate_at_char(&ign->dir, '/'); git_path_to_dir(&ign->dir); } return 0; } void git_ignore__free(git_ignores *ignores) { unsigned int i; git_attr_file *file; git_attr_file__free(ignores->ign_internal); git_vector_foreach(&ignores->ign_path, i, file) { git_attr_file__free(file); ignores->ign_path.contents[i] = NULL; } git_vector_free(&ignores->ign_path); git_vector_foreach(&ignores->ign_global, i, file) { git_attr_file__free(file); ignores->ign_global.contents[i] = NULL; } git_vector_free(&ignores->ign_global); git_buf_dispose(&ignores->dir); } static bool ignore_lookup_in_rules( int *ignored, git_attr_file *file, git_attr_path *path) { size_t j; git_attr_fnmatch *match; git_vector_rforeach(&file->rules, j, match) { if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && path->is_dir == GIT_DIR_FLAG_FALSE) continue; if (git_attr_fnmatch__match(match, path)) { *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) ? GIT_IGNORE_TRUE : GIT_IGNORE_FALSE; return true; } } return false; } int git_ignore__lookup( int *out, git_ignores *ignores, const char *pathname, git_dir_flag dir_flag) { size_t i; git_attr_file *file; git_attr_path path; *out = GIT_IGNORE_NOTFOUND; if (git_attr_path__init( &path, pathname, git_repository_workdir(ignores->repo), dir_flag) < 0) return -1; /* first process builtins - success means path was found */ if (ignore_lookup_in_rules(out, ignores->ign_internal, &path)) goto cleanup; /* next process files in the path. * this process has to process ignores in reverse order * to ensure correct prioritization of rules */ git_vector_rforeach(&ignores->ign_path, i, file) { if (ignore_lookup_in_rules(out, file, &path)) goto cleanup; } /* last process global ignores */ git_vector_foreach(&ignores->ign_global, i, file) { if (ignore_lookup_in_rules(out, file, &path)) goto cleanup; } cleanup: git_attr_path__free(&path); return 0; } int git_ignore_add_rule(git_repository *repo, const char *rules) { int error; git_attr_file *ign_internal = NULL; if ((error = get_internal_ignores(&ign_internal, repo)) < 0) return error; error = parse_ignore_file(repo, ign_internal, rules, false); git_attr_file__free(ign_internal); return error; } int git_ignore_clear_internal_rules(git_repository *repo) { int error; git_attr_file *ign_internal; if ((error = get_internal_ignores(&ign_internal, repo)) < 0) return error; if (!(error = git_attr_file__clear_rules(ign_internal, true))) error = parse_ignore_file( repo, ign_internal, GIT_IGNORE_DEFAULT_RULES, false); git_attr_file__free(ign_internal); return error; } int git_ignore_path_is_ignored( int *ignored, git_repository *repo, const char *pathname) { int error; const char *workdir; git_attr_path path; git_ignores ignores; unsigned int i; git_attr_file *file; git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(ignored); GIT_ASSERT_ARG(pathname); workdir = git_repository_workdir(repo); memset(&path, 0, sizeof(path)); memset(&ignores, 0, sizeof(ignores)); if (!git__suffixcmp(pathname, "/")) dir_flag = GIT_DIR_FLAG_TRUE; else if (git_repository_is_bare(repo)) dir_flag = GIT_DIR_FLAG_FALSE; if ((error = git_attr_path__init(&path, pathname, workdir, dir_flag)) < 0 || (error = git_ignore__for_path(repo, path.path, &ignores)) < 0) goto cleanup; while (1) { /* first process builtins - success means path was found */ if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path)) goto cleanup; /* next process files in the path */ git_vector_foreach(&ignores.ign_path, i, file) { if (ignore_lookup_in_rules(ignored, file, &path)) goto cleanup; } /* last process global ignores */ git_vector_foreach(&ignores.ign_global, i, file) { if (ignore_lookup_in_rules(ignored, file, &path)) goto cleanup; } /* move up one directory */ if (path.basename == path.path) break; path.basename[-1] = '\0'; while (path.basename > path.path && *path.basename != '/') path.basename--; if (path.basename > path.path) path.basename++; path.is_dir = 1; if ((error = git_ignore__pop_dir(&ignores)) < 0) break; } *ignored = 0; cleanup: git_attr_path__free(&path); git_ignore__free(&ignores); return error; } int git_ignore__check_pathspec_for_exact_ignores( git_repository *repo, git_vector *vspec, bool no_fnmatch) { int error = 0; size_t i; git_attr_fnmatch *match; int ignored; git_buf path = GIT_BUF_INIT; const char *filename; git_index *idx; if ((error = git_repository__ensure_not_bare( repo, "validate pathspec")) < 0 || (error = git_repository_index(&idx, repo)) < 0) return error; git_vector_foreach(vspec, i, match) { /* skip wildcard matches (if they are being used) */ if ((match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0 && !no_fnmatch) continue; filename = match->pattern; /* if file is already in the index, it's fine */ if (git_index_get_bypath(idx, filename, 0) != NULL) continue; if ((error = git_repository_workdir_path(&path, repo, filename)) < 0) break; /* is there a file on disk that matches this exactly? */ if (!git_path_isfile(path.ptr)) continue; /* is that file ignored? */ if ((error = git_ignore_path_is_ignored(&ignored, repo, filename)) < 0) break; if (ignored) { git_error_set(GIT_ERROR_INVALID, "pathspec contains ignored file '%s'", filename); error = GIT_EINVALIDSPEC; break; } } git_index_free(idx); git_buf_dispose(&path); return error; } git2r/src/libgit2/src/diff_parse.c0000644000175000017500000000452314125111754016620 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "diff_parse.h" #include "diff.h" #include "patch.h" #include "patch_parse.h" static void diff_parsed_free(git_diff *d) { git_diff_parsed *diff = (git_diff_parsed *)d; git_patch *patch; size_t i; git_vector_foreach(&diff->patches, i, patch) git_patch_free(patch); git_vector_free(&diff->patches); git_vector_free(&diff->base.deltas); git_pool_clear(&diff->base.pool); git__memzero(diff, sizeof(*diff)); git__free(diff); } static git_diff_parsed *diff_parsed_alloc(void) { git_diff_parsed *diff; if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL) return NULL; GIT_REFCOUNT_INC(&diff->base); diff->base.type = GIT_DIFF_TYPE_PARSED; diff->base.strcomp = git__strcmp; diff->base.strncomp = git__strncmp; diff->base.pfxcomp = git__prefixcmp; diff->base.entrycomp = git_diff__entry_cmp; diff->base.patch_fn = git_patch_parsed_from_diff; diff->base.free_fn = diff_parsed_free; if (git_diff_options_init(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) { git__free(diff); return NULL; } diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE; if (git_pool_init(&diff->base.pool, 1) < 0 || git_vector_init(&diff->patches, 0, NULL) < 0 || git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) { git_diff_free(&diff->base); return NULL; } git_vector_set_cmp(&diff->base.deltas, git_diff_delta__cmp); return diff; } int git_diff_from_buffer( git_diff **out, const char *content, size_t content_len) { git_diff_parsed *diff; git_patch *patch; git_patch_parse_ctx *ctx = NULL; int error = 0; *out = NULL; diff = diff_parsed_alloc(); GIT_ERROR_CHECK_ALLOC(diff); ctx = git_patch_parse_ctx_init(content, content_len, NULL); GIT_ERROR_CHECK_ALLOC(ctx); while (ctx->parse_ctx.remain_len) { if ((error = git_patch_parse(&patch, ctx)) < 0) break; git_vector_insert(&diff->patches, patch); git_vector_insert(&diff->base.deltas, patch->delta); } if (error == GIT_ENOTFOUND && git_vector_length(&diff->patches) > 0) { git_error_clear(); error = 0; } git_patch_parse_ctx_free(ctx); if (error < 0) git_diff_free(&diff->base); else *out = &diff->base; return error; } git2r/src/libgit2/src/config_snapshot.c0000644000175000017500000001230114125111754017673 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "config_backend.h" #include "config.h" #include "config_entries.h" typedef struct { git_config_backend parent; git_mutex values_mutex; git_config_entries *entries; git_config_backend *source; } config_snapshot_backend; static int config_error_readonly(void) { git_error_set(GIT_ERROR_CONFIG, "this backend is read-only"); return -1; } static int config_snapshot_iterator( git_config_iterator **iter, struct git_config_backend *backend) { config_snapshot_backend *b = GIT_CONTAINER_OF(backend, config_snapshot_backend, parent); git_config_entries *entries = NULL; int error; if ((error = git_config_entries_dup(&entries, b->entries)) < 0 || (error = git_config_entries_iterator_new(iter, entries)) < 0) goto out; out: /* Let iterator delete duplicated entries when it's done */ git_config_entries_free(entries); return error; } /* release the map containing the entry as an equivalent to freeing it */ static void config_snapshot_entry_free(git_config_entry *entry) { git_config_entries *entries = (git_config_entries *) entry->payload; git_config_entries_free(entries); } static int config_snapshot_get(git_config_backend *cfg, const char *key, git_config_entry **out) { config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent); git_config_entries *entries = NULL; git_config_entry *entry; int error = 0; if (git_mutex_lock(&b->values_mutex) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock config backend"); return -1; } entries = b->entries; git_config_entries_incref(entries); git_mutex_unlock(&b->values_mutex); if ((error = (git_config_entries_get(&entry, entries, key))) < 0) { git_config_entries_free(entries); return error; } entry->free = config_snapshot_entry_free; entry->payload = entries; *out = entry; return 0; } static int config_snapshot_set(git_config_backend *cfg, const char *name, const char *value) { GIT_UNUSED(cfg); GIT_UNUSED(name); GIT_UNUSED(value); return config_error_readonly(); } static int config_snapshot_set_multivar( git_config_backend *cfg, const char *name, const char *regexp, const char *value) { GIT_UNUSED(cfg); GIT_UNUSED(name); GIT_UNUSED(regexp); GIT_UNUSED(value); return config_error_readonly(); } static int config_snapshot_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp) { GIT_UNUSED(cfg); GIT_UNUSED(name); GIT_UNUSED(regexp); return config_error_readonly(); } static int config_snapshot_delete(git_config_backend *cfg, const char *name) { GIT_UNUSED(cfg); GIT_UNUSED(name); return config_error_readonly(); } static int config_snapshot_lock(git_config_backend *_cfg) { GIT_UNUSED(_cfg); return config_error_readonly(); } static int config_snapshot_unlock(git_config_backend *_cfg, int success) { GIT_UNUSED(_cfg); GIT_UNUSED(success); return config_error_readonly(); } static void config_snapshot_free(git_config_backend *_backend) { config_snapshot_backend *backend = GIT_CONTAINER_OF(_backend, config_snapshot_backend, parent); if (backend == NULL) return; git_config_entries_free(backend->entries); git_mutex_free(&backend->values_mutex); git__free(backend); } static int config_snapshot_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo) { config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent); git_config_entries *entries = NULL; git_config_iterator *it = NULL; git_config_entry *entry; int error; /* We're just copying data, don't care about the level or repo*/ GIT_UNUSED(level); GIT_UNUSED(repo); if ((error = git_config_entries_new(&entries)) < 0 || (error = b->source->iterator(&it, b->source)) < 0) goto out; while ((error = git_config_next(&entry, it)) == 0) if ((error = git_config_entries_dup_entry(entries, entry)) < 0) goto out; if (error < 0) { if (error != GIT_ITEROVER) goto out; error = 0; } b->entries = entries; out: git_config_iterator_free(it); if (error) git_config_entries_free(entries); return error; } int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source) { config_snapshot_backend *backend; backend = git__calloc(1, sizeof(config_snapshot_backend)); GIT_ERROR_CHECK_ALLOC(backend); backend->parent.version = GIT_CONFIG_BACKEND_VERSION; git_mutex_init(&backend->values_mutex); backend->source = source; backend->parent.readonly = 1; backend->parent.version = GIT_CONFIG_BACKEND_VERSION; backend->parent.open = config_snapshot_open; backend->parent.get = config_snapshot_get; backend->parent.set = config_snapshot_set; backend->parent.set_multivar = config_snapshot_set_multivar; backend->parent.snapshot = git_config_backend_snapshot; backend->parent.del = config_snapshot_delete; backend->parent.del_multivar = config_snapshot_delete_multivar; backend->parent.iterator = config_snapshot_iterator; backend->parent.lock = config_snapshot_lock; backend->parent.unlock = config_snapshot_unlock; backend->parent.free = config_snapshot_free; *out = &backend->parent; return 0; } git2r/src/libgit2/src/odb.c0000644000175000017500000012106714125111754015265 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "odb.h" #include #include "git2/object.h" #include "git2/sys/odb_backend.h" #include "futils.h" #include "hash.h" #include "delta.h" #include "filter.h" #include "repository.h" #include "blob.h" #include "git2/odb_backend.h" #include "git2/oid.h" #include "git2/oidarray.h" #define GIT_ALTERNATES_FILE "info/alternates" #define GIT_ALTERNATES_MAX_DEPTH 5 /* * We work under the assumption that most objects for long-running * operations will be packed */ int git_odb__loose_priority = GIT_ODB_DEFAULT_LOOSE_PRIORITY; int git_odb__packed_priority = GIT_ODB_DEFAULT_PACKED_PRIORITY; bool git_odb__strict_hash_verification = true; typedef struct { git_odb_backend *backend; int priority; bool is_alternate; ino_t disk_inode; } backend_internal; static git_cache *odb_cache(git_odb *odb) { git_repository *owner = GIT_REFCOUNT_OWNER(odb); if (owner != NULL) { return &owner->objects; } return &odb->own_cache; } static int odb_otype_fast(git_object_t *type_p, git_odb *db, const git_oid *id); static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth); static int error_null_oid(int error, const char *message); static git_object_t odb_hardcoded_type(const git_oid *id) { static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }}; if (!git_oid_cmp(id, &empty_tree)) return GIT_OBJECT_TREE; return GIT_OBJECT_INVALID; } static int odb_read_hardcoded(bool *found, git_rawobj *raw, const git_oid *id) { git_object_t type; *found = false; if ((type = odb_hardcoded_type(id)) == GIT_OBJECT_INVALID) return 0; raw->type = type; raw->len = 0; raw->data = git__calloc(1, sizeof(uint8_t)); GIT_ERROR_CHECK_ALLOC(raw->data); *found = true; return 0; } int git_odb__format_object_header( size_t *written, char *hdr, size_t hdr_size, git_object_size_t obj_len, git_object_t obj_type) { const char *type_str = git_object_type2string(obj_type); int hdr_max = (hdr_size > INT_MAX-2) ? (INT_MAX-2) : (int)hdr_size; int len; len = p_snprintf(hdr, hdr_max, "%s %"PRId64, type_str, (int64_t)obj_len); if (len < 0 || len >= hdr_max) { git_error_set(GIT_ERROR_OS, "object header creation failed"); return -1; } *written = (size_t)(len + 1); return 0; } int git_odb__hashobj(git_oid *id, git_rawobj *obj) { git_buf_vec vec[2]; char header[64]; size_t hdrlen; int error; GIT_ASSERT_ARG(id); GIT_ASSERT_ARG(obj); if (!git_object_typeisloose(obj->type)) { git_error_set(GIT_ERROR_INVALID, "invalid object type"); return -1; } if (!obj->data && obj->len != 0) { git_error_set(GIT_ERROR_INVALID, "invalid object"); return -1; } if ((error = git_odb__format_object_header(&hdrlen, header, sizeof(header), obj->len, obj->type)) < 0) return error; vec[0].data = header; vec[0].len = hdrlen; vec[1].data = obj->data; vec[1].len = obj->len; return git_hash_vec(id, vec, 2); } static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source) { git_odb_object *object = git__calloc(1, sizeof(git_odb_object)); if (object != NULL) { git_oid_cpy(&object->cached.oid, oid); object->cached.type = source->type; object->cached.size = source->len; object->buffer = source->data; } return object; } void git_odb_object__free(void *object) { if (object != NULL) { git__free(((git_odb_object *)object)->buffer); git__free(object); } } const git_oid *git_odb_object_id(git_odb_object *object) { return &object->cached.oid; } const void *git_odb_object_data(git_odb_object *object) { return object->buffer; } size_t git_odb_object_size(git_odb_object *object) { return object->cached.size; } git_object_t git_odb_object_type(git_odb_object *object) { return object->cached.type; } int git_odb_object_dup(git_odb_object **dest, git_odb_object *source) { git_cached_obj_incref(source); *dest = source; return 0; } void git_odb_object_free(git_odb_object *object) { if (object == NULL) return; git_cached_obj_decref(object); } int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_object_t type) { size_t hdr_len; char hdr[64], buffer[FILEIO_BUFSIZE]; git_hash_ctx ctx; ssize_t read_len = 0; int error = 0; if (!git_object_typeisloose(type)) { git_error_set(GIT_ERROR_INVALID, "invalid object type for hash"); return -1; } if ((error = git_hash_ctx_init(&ctx)) < 0) return error; if ((error = git_odb__format_object_header(&hdr_len, hdr, sizeof(hdr), size, type)) < 0) goto done; if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0) goto done; while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) { if ((error = git_hash_update(&ctx, buffer, read_len)) < 0) goto done; size -= read_len; } /* If p_read returned an error code, the read obviously failed. * If size is not zero, the file was truncated after we originally * stat'd it, so we consider this a read failure too */ if (read_len < 0 || size > 0) { git_error_set(GIT_ERROR_OS, "error reading file for hashing"); error = -1; goto done; } error = git_hash_final(out, &ctx); done: git_hash_ctx_cleanup(&ctx); return error; } int git_odb__hashfd_filtered( git_oid *out, git_file fd, size_t size, git_object_t type, git_filter_list *fl) { int error; git_buf raw = GIT_BUF_INIT; if (!fl) return git_odb__hashfd(out, fd, size, type); /* size of data is used in header, so we have to read the whole file * into memory to apply filters before beginning to calculate the hash */ if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) { git_buf post = GIT_BUF_INIT; error = git_filter_list__convert_buf(&post, fl, &raw); if (!error) error = git_odb_hash(out, post.ptr, post.size, type); git_buf_dispose(&post); } return error; } int git_odb__hashlink(git_oid *out, const char *path) { struct stat st; int size; int result; if (git_path_lstat(path, &st) < 0) return -1; if (!git__is_int(st.st_size) || (int)st.st_size < 0) { git_error_set(GIT_ERROR_FILESYSTEM, "file size overflow for 32-bit systems"); return -1; } size = (int)st.st_size; if (S_ISLNK(st.st_mode)) { char *link_data; int read_len; size_t alloc_size; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, size, 1); link_data = git__malloc(alloc_size); GIT_ERROR_CHECK_ALLOC(link_data); read_len = p_readlink(path, link_data, size); if (read_len == -1) { git_error_set(GIT_ERROR_OS, "failed to read symlink data for '%s'", path); git__free(link_data); return -1; } GIT_ASSERT(read_len <= size); link_data[read_len] = '\0'; result = git_odb_hash(out, link_data, read_len, GIT_OBJECT_BLOB); git__free(link_data); } else { int fd = git_futils_open_ro(path); if (fd < 0) return -1; result = git_odb__hashfd(out, fd, size, GIT_OBJECT_BLOB); p_close(fd); } return result; } int git_odb_hashfile(git_oid *out, const char *path, git_object_t type) { uint64_t size; int fd, error = 0; if ((fd = git_futils_open_ro(path)) < 0) return fd; if ((error = git_futils_filesize(&size, fd)) < 0) goto done; if (!git__is_sizet(size)) { git_error_set(GIT_ERROR_OS, "file size overflow for 32-bit systems"); error = -1; goto done; } error = git_odb__hashfd(out, fd, (size_t)size, type); done: p_close(fd); return error; } int git_odb_hash(git_oid *id, const void *data, size_t len, git_object_t type) { git_rawobj raw; GIT_ASSERT_ARG(id); raw.data = (void *)data; raw.len = len; raw.type = type; return git_odb__hashobj(id, &raw); } /** * FAKE WSTREAM */ typedef struct { git_odb_stream stream; char *buffer; size_t size, written; git_object_t type; } fake_wstream; static int fake_wstream__fwrite(git_odb_stream *_stream, const git_oid *oid) { fake_wstream *stream = (fake_wstream *)_stream; return _stream->backend->write(_stream->backend, oid, stream->buffer, stream->size, stream->type); } static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len) { fake_wstream *stream = (fake_wstream *)_stream; GIT_ASSERT(stream->written + len <= stream->size); memcpy(stream->buffer + stream->written, data, len); stream->written += len; return 0; } static void fake_wstream__free(git_odb_stream *_stream) { fake_wstream *stream = (fake_wstream *)_stream; git__free(stream->buffer); git__free(stream); } static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, git_object_size_t size, git_object_t type) { fake_wstream *stream; size_t blobsize; GIT_ERROR_CHECK_BLOBSIZE(size); blobsize = (size_t)size; stream = git__calloc(1, sizeof(fake_wstream)); GIT_ERROR_CHECK_ALLOC(stream); stream->size = blobsize; stream->type = type; stream->buffer = git__malloc(blobsize); if (stream->buffer == NULL) { git__free(stream); return -1; } stream->stream.backend = backend; stream->stream.read = NULL; /* read only */ stream->stream.write = &fake_wstream__write; stream->stream.finalize_write = &fake_wstream__fwrite; stream->stream.free = &fake_wstream__free; stream->stream.mode = GIT_STREAM_WRONLY; *stream_p = (git_odb_stream *)stream; return 0; } /*********************************************************** * * OBJECT DATABASE PUBLIC API * * Public calls for the ODB functionality * ***********************************************************/ static int backend_sort_cmp(const void *a, const void *b) { const backend_internal *backend_a = (const backend_internal *)(a); const backend_internal *backend_b = (const backend_internal *)(b); if (backend_b->priority == backend_a->priority) { if (backend_a->is_alternate) return -1; if (backend_b->is_alternate) return 1; return 0; } return (backend_b->priority - backend_a->priority); } int git_odb_new(git_odb **out) { git_odb *db = git__calloc(1, sizeof(*db)); GIT_ERROR_CHECK_ALLOC(db); if (git_mutex_init(&db->lock) < 0) { git__free(db); return -1; } if (git_cache_init(&db->own_cache) < 0) { git_mutex_free(&db->lock); git__free(db); return -1; } if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { git_cache_dispose(&db->own_cache); git_mutex_free(&db->lock); git__free(db); return -1; } *out = db; GIT_REFCOUNT_INC(db); return 0; } static int add_backend_internal( git_odb *odb, git_odb_backend *backend, int priority, bool is_alternate, ino_t disk_inode) { backend_internal *internal; GIT_ASSERT_ARG(odb); GIT_ASSERT_ARG(backend); GIT_ERROR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend"); /* Check if the backend is already owned by another ODB */ GIT_ASSERT(!backend->odb || backend->odb == odb); internal = git__malloc(sizeof(backend_internal)); GIT_ERROR_CHECK_ALLOC(internal); internal->backend = backend; internal->priority = priority; internal->is_alternate = is_alternate; internal->disk_inode = disk_inode; if (git_mutex_lock(&odb->lock) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return -1; } if (git_vector_insert(&odb->backends, internal) < 0) { git_mutex_unlock(&odb->lock); git__free(internal); return -1; } git_vector_sort(&odb->backends); internal->backend->odb = odb; git_mutex_unlock(&odb->lock); return 0; } int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority) { return add_backend_internal(odb, backend, priority, false, 0); } int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority) { return add_backend_internal(odb, backend, priority, true, 0); } size_t git_odb_num_backends(git_odb *odb) { size_t length; bool locked = true; GIT_ASSERT_ARG(odb); if (git_mutex_lock(&odb->lock) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); locked = false; } length = odb->backends.length; if (locked) git_mutex_unlock(&odb->lock); return length; } static int git_odb__error_unsupported_in_backend(const char *action) { git_error_set(GIT_ERROR_ODB, "cannot %s - unsupported in the loaded odb backends", action); return -1; } int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) { backend_internal *internal; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(odb); if ((error = git_mutex_lock(&odb->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } internal = git_vector_get(&odb->backends, pos); if (!internal || !internal->backend) { git_mutex_unlock(&odb->lock); git_error_set(GIT_ERROR_ODB, "no ODB backend loaded at index %" PRIuZ, pos); return GIT_ENOTFOUND; } *out = internal->backend; git_mutex_unlock(&odb->lock); return 0; } int git_odb__add_default_backends( git_odb *db, const char *objects_dir, bool as_alternates, int alternate_depth) { size_t i = 0; struct stat st; ino_t inode; git_odb_backend *loose, *packed; /* TODO: inodes are not really relevant on Win32, so we need to find * a cross-platform workaround for this */ #ifdef GIT_WIN32 GIT_UNUSED(i); GIT_UNUSED(&st); inode = 0; #else if (p_stat(objects_dir, &st) < 0) { if (as_alternates) /* this should warn */ return 0; git_error_set(GIT_ERROR_ODB, "failed to load object database in '%s'", objects_dir); return -1; } inode = st.st_ino; if (git_mutex_lock(&db->lock) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return -1; } for (i = 0; i < db->backends.length; ++i) { backend_internal *backend = git_vector_get(&db->backends, i); if (backend->disk_inode == inode) { git_mutex_unlock(&db->lock); return 0; } } git_mutex_unlock(&db->lock); #endif /* add the loose object backend */ if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 || add_backend_internal(db, loose, git_odb__loose_priority, as_alternates, inode) < 0) return -1; /* add the packed file backend */ if (git_odb_backend_pack(&packed, objects_dir) < 0 || add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0) return -1; if (git_mutex_lock(&db->lock) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return -1; } if (!db->cgraph && git_commit_graph_new(&db->cgraph, objects_dir, false) < 0) { git_mutex_unlock(&db->lock); return -1; } git_mutex_unlock(&db->lock); return load_alternates(db, objects_dir, alternate_depth); } static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth) { git_buf alternates_path = GIT_BUF_INIT; git_buf alternates_buf = GIT_BUF_INIT; char *buffer; const char *alternate; int result = 0; /* Git reports an error, we just ignore anything deeper */ if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) return 0; if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0) return -1; if (git_path_exists(alternates_path.ptr) == false) { git_buf_dispose(&alternates_path); return 0; } if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) { git_buf_dispose(&alternates_path); return -1; } buffer = (char *)alternates_buf.ptr; /* add each alternate as a new backend; one alternate per line */ while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) { if (*alternate == '\0' || *alternate == '#') continue; /* * Relative path: build based on the current `objects` * folder. However, relative paths are only allowed in * the current repository. */ if (*alternate == '.' && !alternate_depth) { if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0) break; alternate = git_buf_cstr(&alternates_path); } if ((result = git_odb__add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) break; } git_buf_dispose(&alternates_path); git_buf_dispose(&alternates_buf); return result; } int git_odb_add_disk_alternate(git_odb *odb, const char *path) { return git_odb__add_default_backends(odb, path, true, 0); } int git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph) { int error = 0; GIT_ASSERT_ARG(odb); if ((error = git_mutex_lock(&odb->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock"); return error; } git_commit_graph_free(odb->cgraph); odb->cgraph = cgraph; git_mutex_unlock(&odb->lock); return error; } int git_odb_open(git_odb **out, const char *objects_dir) { git_odb *db; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(objects_dir); *out = NULL; if (git_odb_new(&db) < 0) return -1; if (git_odb__add_default_backends(db, objects_dir, 0, 0) < 0) { git_odb_free(db); return -1; } *out = db; return 0; } int git_odb__set_caps(git_odb *odb, int caps) { if (caps == GIT_ODB_CAP_FROM_OWNER) { git_repository *repo = GIT_REFCOUNT_OWNER(odb); int val; if (!repo) { git_error_set(GIT_ERROR_ODB, "cannot access repository to set odb caps"); return -1; } if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_FSYNCOBJECTFILES)) odb->do_fsync = !!val; } return 0; } static void odb_free(git_odb *db) { size_t i; bool locked = true; if (git_mutex_lock(&db->lock) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); locked = false; } for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *backend = internal->backend; backend->free(backend); git__free(internal); } if (locked) git_mutex_unlock(&db->lock); git_commit_graph_free(db->cgraph); git_vector_free(&db->backends); git_cache_dispose(&db->own_cache); git_mutex_free(&db->lock); git__memzero(db, sizeof(*db)); git__free(db); } void git_odb_free(git_odb *db) { if (db == NULL) return; GIT_REFCOUNT_DEC(db, odb_free); } static int odb_exists_1( git_odb *db, const git_oid *id, bool only_refreshed) { size_t i; bool found = false; int error; if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (only_refreshed && !b->refresh) continue; if (b->exists != NULL) found = (bool)b->exists(b, id); } git_mutex_unlock(&db->lock); return (int)found; } int git_odb__get_commit_graph_file(git_commit_graph_file **out, git_odb *db) { int error = 0; git_commit_graph_file *result = NULL; if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock"); return error; } if (!db->cgraph) { error = GIT_ENOTFOUND; goto done; } error = git_commit_graph_get_file(&result, db->cgraph); if (error) goto done; *out = result; done: git_mutex_unlock(&db->lock); return error; } static int odb_freshen_1( git_odb *db, const git_oid *id, bool only_refreshed) { size_t i; bool found = false; int error; if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (only_refreshed && !b->refresh) continue; if (b->freshen != NULL) found = !b->freshen(b, id); else if (b->exists != NULL) found = b->exists(b, id); } git_mutex_unlock(&db->lock); return (int)found; } int git_odb__freshen(git_odb *db, const git_oid *id) { GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(id); if (odb_freshen_1(db, id, false)) return 1; if (!git_odb_refresh(db)) return odb_freshen_1(db, id, true); /* Failed to refresh, hence not found */ return 0; } int git_odb_exists(git_odb *db, const git_oid *id) { git_odb_object *object; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(id); if (git_oid_is_zero(id)) return 0; if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { git_odb_object_free(object); return 1; } if (odb_exists_1(db, id, false)) return 1; if (!git_odb_refresh(db)) return odb_exists_1(db, id, true); /* Failed to refresh, hence not found */ return 0; } static int odb_exists_prefix_1(git_oid *out, git_odb *db, const git_oid *key, size_t len, bool only_refreshed) { size_t i; int error = GIT_ENOTFOUND, num_found = 0; git_oid last_found = {{0}}, found; if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } error = GIT_ENOTFOUND; for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (only_refreshed && !b->refresh) continue; if (!b->exists_prefix) continue; error = b->exists_prefix(&found, b, key, len); if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) continue; if (error) { git_mutex_unlock(&db->lock); return error; } /* make sure found item doesn't introduce ambiguity */ if (num_found) { if (git_oid__cmp(&last_found, &found)) { git_mutex_unlock(&db->lock); return git_odb__error_ambiguous("multiple matches for prefix"); } } else { git_oid_cpy(&last_found, &found); num_found++; } } git_mutex_unlock(&db->lock); if (!num_found) return GIT_ENOTFOUND; if (out) git_oid_cpy(out, &last_found); return 0; } int git_odb_exists_prefix( git_oid *out, git_odb *db, const git_oid *short_id, size_t len) { int error; git_oid key = {{0}}; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(short_id); if (len < GIT_OID_MINPREFIXLEN) return git_odb__error_ambiguous("prefix length too short"); if (len >= GIT_OID_HEXSZ) { if (git_odb_exists(db, short_id)) { if (out) git_oid_cpy(out, short_id); return 0; } else { return git_odb__error_notfound( "no match for id prefix", short_id, len); } } git_oid__cpy_prefix(&key, short_id, len); error = odb_exists_prefix_1(out, db, &key, len, false); if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) error = odb_exists_prefix_1(out, db, &key, len, true); if (error == GIT_ENOTFOUND) return git_odb__error_notfound("no match for id prefix", &key, len); return error; } int git_odb_expand_ids( git_odb *db, git_odb_expand_id *ids, size_t count) { size_t i; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(ids); for (i = 0; i < count; i++) { git_odb_expand_id *query = &ids[i]; int error = GIT_EAMBIGUOUS; if (!query->type) query->type = GIT_OBJECT_ANY; /* if we have a short OID, expand it first */ if (query->length >= GIT_OID_MINPREFIXLEN && query->length < GIT_OID_HEXSZ) { git_oid actual_id; error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false); if (!error) { git_oid_cpy(&query->id, &actual_id); query->length = GIT_OID_HEXSZ; } } /* * now we ought to have a 40-char OID, either because we've expanded it * or because the user passed a full OID. Ensure its type is right. */ if (query->length >= GIT_OID_HEXSZ) { git_object_t actual_type; error = odb_otype_fast(&actual_type, db, &query->id); if (!error) { if (query->type != GIT_OBJECT_ANY && query->type != actual_type) error = GIT_ENOTFOUND; else query->type = actual_type; } } switch (error) { /* no errors, so we've successfully expanded the OID */ case 0: continue; /* the object is missing or ambiguous */ case GIT_ENOTFOUND: case GIT_EAMBIGUOUS: memset(&query->id, 0, sizeof(git_oid)); query->length = 0; query->type = 0; break; /* something went very wrong with the ODB; bail hard */ default: return error; } } git_error_clear(); return 0; } int git_odb_read_header(size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id) { int error; git_odb_object *object; error = git_odb__read_header_or_object(&object, len_p, type_p, db, id); if (object) git_odb_object_free(object); return error; } static int odb_read_header_1( size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id, bool only_refreshed) { size_t i; git_object_t ht; bool passthrough = false; int error; if (!only_refreshed && (ht = odb_hardcoded_type(id)) != GIT_OBJECT_INVALID) { *type_p = ht; *len_p = 0; return 0; } if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (only_refreshed && !b->refresh) continue; if (!b->read_header) { passthrough = true; continue; } error = b->read_header(len_p, type_p, b, id); switch (error) { case GIT_PASSTHROUGH: passthrough = true; break; case GIT_ENOTFOUND: break; default: git_mutex_unlock(&db->lock); return error; } } git_mutex_unlock(&db->lock); return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND; } int git_odb__read_header_or_object( git_odb_object **out, size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id) { int error = GIT_ENOTFOUND; git_odb_object *object; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(id); GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(len_p); GIT_ASSERT_ARG(type_p); *out = NULL; if (git_oid_is_zero(id)) return error_null_oid(GIT_ENOTFOUND, "cannot read object"); if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { *len_p = object->cached.size; *type_p = object->cached.type; *out = object; return 0; } error = odb_read_header_1(len_p, type_p, db, id, false); if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) error = odb_read_header_1(len_p, type_p, db, id, true); if (error == GIT_ENOTFOUND) return git_odb__error_notfound("cannot read header for", id, GIT_OID_HEXSZ); /* we found the header; return early */ if (!error) return 0; if (error == GIT_PASSTHROUGH) { /* * no backend has header-reading functionality * so try using `git_odb_read` instead */ error = git_odb_read(&object, db, id); if (!error) { *len_p = object->cached.size; *type_p = object->cached.type; *out = object; } } return error; } static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, bool only_refreshed) { size_t i; git_rawobj raw; git_odb_object *object; git_oid hashed; bool found = false; int error = 0; if (!only_refreshed) { if ((error = odb_read_hardcoded(&found, &raw, id)) < 0) return error; } if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (only_refreshed && !b->refresh) continue; if (b->read != NULL) { error = b->read(&raw.data, &raw.len, &raw.type, b, id); if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) continue; if (error < 0) { git_mutex_unlock(&db->lock); return error; } found = true; } } git_mutex_unlock(&db->lock); if (!found) return GIT_ENOTFOUND; if (git_odb__strict_hash_verification) { if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) goto out; if (!git_oid_equal(id, &hashed)) { error = git_odb__error_mismatch(id, &hashed); goto out; } } git_error_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) { error = -1; goto out; } *out = git_cache_store_raw(odb_cache(db), object); out: if (error) git__free(raw.data); return error; } int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(id); if (git_oid_is_zero(id)) return error_null_oid(GIT_ENOTFOUND, "cannot read object"); *out = git_cache_get_raw(odb_cache(db), id); if (*out != NULL) return 0; error = odb_read_1(out, db, id, false); if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) error = odb_read_1(out, db, id, true); if (error == GIT_ENOTFOUND) return git_odb__error_notfound("no match for id", id, GIT_OID_HEXSZ); return error; } static int odb_otype_fast(git_object_t *type_p, git_odb *db, const git_oid *id) { git_odb_object *object; size_t _unused; int error; if (git_oid_is_zero(id)) return error_null_oid(GIT_ENOTFOUND, "cannot get object type"); if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { *type_p = object->cached.type; git_odb_object_free(object); return 0; } error = odb_read_header_1(&_unused, type_p, db, id, false); if (error == GIT_PASSTHROUGH) { error = odb_read_1(&object, db, id, false); if (!error) *type_p = object->cached.type; git_odb_object_free(object); } return error; } static int read_prefix_1(git_odb_object **out, git_odb *db, const git_oid *key, size_t len, bool only_refreshed) { size_t i; int error = 0; git_oid found_full_oid = {{0}}; git_rawobj raw = {0}; void *data = NULL; bool found = false; git_odb_object *object; if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (only_refreshed && !b->refresh) continue; if (b->read_prefix != NULL) { git_oid full_oid; error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len); if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) { error = 0; continue; } if (error) { git_mutex_unlock(&db->lock); goto out; } git__free(data); data = raw.data; if (found && git_oid__cmp(&full_oid, &found_full_oid)) { git_buf buf = GIT_BUF_INIT; git_buf_printf(&buf, "multiple matches for prefix: %s", git_oid_tostr_s(&full_oid)); git_buf_printf(&buf, " %s", git_oid_tostr_s(&found_full_oid)); error = git_odb__error_ambiguous(buf.ptr); git_buf_dispose(&buf); git_mutex_unlock(&db->lock); goto out; } found_full_oid = full_oid; found = true; } } git_mutex_unlock(&db->lock); if (!found) return GIT_ENOTFOUND; if (git_odb__strict_hash_verification) { git_oid hash; if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0) goto out; if (!git_oid_equal(&found_full_oid, &hash)) { error = git_odb__error_mismatch(&found_full_oid, &hash); goto out; } } if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) { error = -1; goto out; } *out = git_cache_store_raw(odb_cache(db), object); out: if (error) git__free(raw.data); return error; } int git_odb_read_prefix( git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) { git_oid key = {{0}}; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(db); if (len < GIT_OID_MINPREFIXLEN) return git_odb__error_ambiguous("prefix length too short"); if (len > GIT_OID_HEXSZ) len = GIT_OID_HEXSZ; if (len == GIT_OID_HEXSZ) { *out = git_cache_get_raw(odb_cache(db), short_id); if (*out != NULL) return 0; } git_oid__cpy_prefix(&key, short_id, len); error = read_prefix_1(out, db, &key, len, false); if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) error = read_prefix_1(out, db, &key, len, true); if (error == GIT_ENOTFOUND) return git_odb__error_notfound("no match for prefix", &key, len); return error; } int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload) { unsigned int i; git_vector backends = GIT_VECTOR_INIT; backend_internal *internal; int error = 0; /* Make a copy of the backends vector to invoke the callback without holding the lock. */ if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); goto cleanup; } error = git_vector_dup(&backends, &db->backends, NULL); git_mutex_unlock(&db->lock); if (error < 0) goto cleanup; git_vector_foreach(&backends, i, internal) { git_odb_backend *b = internal->backend; error = b->foreach(b, cb, payload); if (error != 0) goto cleanup; } cleanup: git_vector_free(&backends); return error; } int git_odb_write( git_oid *oid, git_odb *db, const void *data, size_t len, git_object_t type) { size_t i; int error; git_odb_stream *stream; GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(db); if ((error = git_odb_hash(oid, data, len, type)) < 0) return error; if (git_oid_is_zero(oid)) return error_null_oid(GIT_EINVALID, "cannot write object"); if (git_odb__freshen(db, oid)) return 0; if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } for (i = 0, error = GIT_ERROR; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; /* we don't write in alternates! */ if (internal->is_alternate) continue; if (b->write != NULL) error = b->write(b, oid, data, len, type); } git_mutex_unlock(&db->lock); if (!error || error == GIT_PASSTHROUGH) return 0; /* if no backends were able to write the object directly, we try a * streaming write to the backends; just write the whole object into the * stream in one push */ if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0) return error; stream->write(stream, data, len); error = stream->finalize_write(stream, oid); git_odb_stream_free(stream); return error; } static int hash_header(git_hash_ctx *ctx, git_object_size_t size, git_object_t type) { char header[64]; size_t hdrlen; int error; if ((error = git_odb__format_object_header(&hdrlen, header, sizeof(header), size, type)) < 0) return error; return git_hash_update(ctx, header, hdrlen); } int git_odb_open_wstream( git_odb_stream **stream, git_odb *db, git_object_size_t size, git_object_t type) { size_t i, writes = 0; int error = GIT_ERROR; git_hash_ctx *ctx = NULL; GIT_ASSERT_ARG(stream); GIT_ASSERT_ARG(db); if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } error = GIT_ERROR; for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; /* we don't write in alternates! */ if (internal->is_alternate) continue; if (b->writestream != NULL) { ++writes; error = b->writestream(stream, b, size, type); } else if (b->write != NULL) { ++writes; error = init_fake_wstream(stream, b, size, type); } } git_mutex_unlock(&db->lock); if (error < 0) { if (error == GIT_PASSTHROUGH) error = 0; else if (!writes) error = git_odb__error_unsupported_in_backend("write object"); goto done; } ctx = git__malloc(sizeof(git_hash_ctx)); GIT_ERROR_CHECK_ALLOC(ctx); if ((error = git_hash_ctx_init(ctx)) < 0 || (error = hash_header(ctx, size, type)) < 0) goto done; (*stream)->hash_ctx = ctx; (*stream)->declared_size = size; (*stream)->received_bytes = 0; done: if (error) git__free(ctx); return error; } static int git_odb_stream__invalid_length( const git_odb_stream *stream, const char *action) { git_error_set(GIT_ERROR_ODB, "cannot %s - " "Invalid length. %"PRId64" was expected. The " "total size of the received chunks amounts to %"PRId64".", action, stream->declared_size, stream->received_bytes); return -1; } int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len) { git_hash_update(stream->hash_ctx, buffer, len); stream->received_bytes += len; if (stream->received_bytes > stream->declared_size) return git_odb_stream__invalid_length(stream, "stream_write()"); return stream->write(stream, buffer, len); } int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream) { if (stream->received_bytes != stream->declared_size) return git_odb_stream__invalid_length(stream, "stream_finalize_write()"); git_hash_final(out, stream->hash_ctx); if (git_odb__freshen(stream->backend->odb, out)) return 0; return stream->finalize_write(stream, out); } int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len) { return stream->read(stream, buffer, len); } void git_odb_stream_free(git_odb_stream *stream) { if (stream == NULL) return; git_hash_ctx_cleanup(stream->hash_ctx); git__free(stream->hash_ctx); stream->free(stream); } int git_odb_open_rstream( git_odb_stream **stream, size_t *len, git_object_t *type, git_odb *db, const git_oid *oid) { size_t i, reads = 0; int error = GIT_ERROR; GIT_ASSERT_ARG(stream); GIT_ASSERT_ARG(db); if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } error = GIT_ERROR; for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (b->readstream != NULL) { ++reads; error = b->readstream(stream, len, type, b, oid); } } git_mutex_unlock(&db->lock); if (error == GIT_PASSTHROUGH) error = 0; if (error < 0 && !reads) error = git_odb__error_unsupported_in_backend("read object streamed"); return error; } int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_indexer_progress_cb progress_cb, void *progress_payload) { size_t i, writes = 0; int error = GIT_ERROR; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(db); if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } error = GIT_ERROR; for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; /* we don't write in alternates! */ if (internal->is_alternate) continue; if (b->writepack != NULL) { ++writes; error = b->writepack(out, b, db, progress_cb, progress_payload); } } git_mutex_unlock(&db->lock); if (error == GIT_PASSTHROUGH) error = 0; if (error < 0 && !writes) error = git_odb__error_unsupported_in_backend("write pack"); return error; } int git_odb_write_multi_pack_index(git_odb *db) { size_t i, writes = 0; int error = GIT_ERROR; GIT_ASSERT_ARG(db); for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; /* we don't write in alternates! */ if (internal->is_alternate) continue; if (b->writemidx != NULL) { ++writes; error = b->writemidx(b); } } if (error == GIT_PASSTHROUGH) error = 0; if (error < 0 && !writes) error = git_odb__error_unsupported_in_backend("write multi-pack-index"); return error; } void *git_odb_backend_data_alloc(git_odb_backend *backend, size_t len) { GIT_UNUSED(backend); return git__malloc(len); } #ifndef GIT_DEPRECATE_HARD void *git_odb_backend_malloc(git_odb_backend *backend, size_t len) { return git_odb_backend_data_alloc(backend, len); } #endif void git_odb_backend_data_free(git_odb_backend *backend, void *data) { GIT_UNUSED(backend); git__free(data); } int git_odb_refresh(struct git_odb *db) { size_t i; int error; GIT_ASSERT_ARG(db); if ((error = git_mutex_lock(&db->lock)) < 0) { git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); return error; } for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (b->refresh != NULL) { int error = b->refresh(b); if (error < 0) { git_mutex_unlock(&db->lock); return error; } } } if (db->cgraph) git_commit_graph_refresh(db->cgraph); git_mutex_unlock(&db->lock); return 0; } int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual) { char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1]; git_oid_tostr(expected_oid, sizeof(expected_oid), expected); git_oid_tostr(actual_oid, sizeof(actual_oid), actual); git_error_set(GIT_ERROR_ODB, "object hash mismatch - expected %s but got %s", expected_oid, actual_oid); return GIT_EMISMATCH; } int git_odb__error_notfound( const char *message, const git_oid *oid, size_t oid_len) { if (oid != NULL) { char oid_str[GIT_OID_HEXSZ + 1]; git_oid_tostr(oid_str, oid_len+1, oid); git_error_set(GIT_ERROR_ODB, "object not found - %s (%.*s)", message, (int) oid_len, oid_str); } else git_error_set(GIT_ERROR_ODB, "object not found - %s", message); return GIT_ENOTFOUND; } static int error_null_oid(int error, const char *message) { git_error_set(GIT_ERROR_ODB, "odb: %s: null OID cannot exist", message); return error; } int git_odb__error_ambiguous(const char *message) { git_error_set(GIT_ERROR_ODB, "ambiguous SHA1 prefix - %s", message); return GIT_EAMBIGUOUS; } int git_odb_init_backend(git_odb_backend *backend, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT); return 0; } git2r/src/libgit2/src/tag.h0000644000175000017500000000120314125111754015266 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_tag_h__ #define INCLUDE_tag_h__ #include "common.h" #include "git2/tag.h" #include "repository.h" #include "odb.h" struct git_tag { git_object object; git_oid target; git_object_t type; char *tag_name; git_signature *tagger; char *message; }; void git_tag__free(void *tag); int git_tag__parse(void *tag, git_odb_object *obj); int git_tag__parse_raw(void *tag, const char *data, size_t size); #endif git2r/src/libgit2/src/userdiff.h0000644000175000017500000001674614125111754016344 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_userdiff_h__ #define INCLUDE_userdiff_h__ #include "regexp.h" /* * This file isolates the built in diff driver function name patterns. * Most of these patterns are taken from Git (with permission from the * original authors for relicensing to libgit2). */ typedef struct { const char *name; const char *fns; const char *words; int flags; } git_diff_driver_definition; #define WORD_DEFAULT "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" /* * These builtin driver definition macros have same signature as in core * git userdiff.c so that the data can be extracted verbatim */ #define PATTERNS(NAME, FN_PATS, WORD_PAT) \ { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, 0 } #define IPATTERN(NAME, FN_PATS, WORD_PAT) \ { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, GIT_REGEXP_ICASE } /* * The table of diff driver patterns * * Function name patterns are a list of newline separated patterns that * match a function declaration (i.e. the line you want in the hunk header), * or a negative pattern prefixed with a '!' to reject a pattern (such as * rejecting goto labels in C code). * * Word boundary patterns are just a simple pattern that will be OR'ed with * the default value above (i.e. whitespace or non-ASCII characters). */ static git_diff_driver_definition builtin_defs[] = { IPATTERN("ada", "!^(.*[ \t])?(is[ \t]+new|renames|is[ \t]+separate)([ \t].*)?$\n" "!^[ \t]*with[ \t].*$\n" "^[ \t]*((procedure|function)[ \t]+.*)$\n" "^[ \t]*((package|protected|task)[ \t]+.*)$", /* -- */ "[a-zA-Z][a-zA-Z0-9_]*" "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?" "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"), IPATTERN("fortran", "!^([C*]|[ \t]*!)\n" "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n" "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA" "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$", /* -- */ "[a-zA-Z][a-zA-Z0-9_]*" "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\." /* numbers and format statements like 2E14.4, or ES12.6, 9X. * Don't worry about format statements without leading digits since * they would have been matched above as a variable anyway. */ "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?" "|//|\\*\\*|::|[/<>=]="), PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", "[^<>= \t]+"), PATTERNS("java", "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n" "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=" "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"), PATTERNS("matlab", "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$", "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"), PATTERNS("objc", /* Negate C statements that can look like functions */ "!^[ \t]*(do|for|if|else|return|switch|while)\n" /* Objective-C methods */ "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n" /* C functions */ "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n" /* Objective-C class/protocol definitions */ "^(@(implementation|interface|protocol)[ \t].*)$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), PATTERNS("pascal", "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|" "implementation|initialization|finalization)[ \t]*.*)$" "\n" "^(.*=[ \t]*(class|record).*)$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+" "|<>|<=|>=|:=|\\.\\."), PATTERNS("perl", "^package .*\n" "^sub [[:alnum:]_':]+[ \t]*" "(\\([^)]*\\)[ \t]*)?" /* prototype */ /* * Attributes. A regex can't count nested parentheses, * so just slurp up whatever we see, taking care not * to accept lines like "sub foo; # defined elsewhere". * * An attribute could contain a semicolon, but at that * point it seems reasonable enough to give up. */ "(:[^;#]*)?" "(\\{[ \t]*)?" /* brace can come here or on the next line */ "(#.*)?$\n" /* comment */ "^(BEGIN|END|INIT|CHECK|UNITCHECK|AUTOLOAD|DESTROY)[ \t]*" "(\\{[ \t]*)?" /* brace can come here or on the next line */ "(#.*)?$\n" "^=head[0-9] .*", /* POD */ /* -- */ "[[:alpha:]_'][[:alnum:]_']*" "|0[xb]?[0-9a-fA-F_]*" /* taking care not to interpret 3..5 as (3.)(.5) */ "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?" "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::" "|&&=|\\|\\|=|//=|\\*\\*=" "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?" "|[-+*/%.^&<>=!|]=" "|=~|!~" "|<<|<>|<=>|>>"), PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"), PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$", /* -- */ "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?." "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"), PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", "[={}\"]|[^={}\" \t]+"), PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"), PATTERNS("cpp", /* Jump targets or access declarations */ "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n" /* functions/methods, variables, and compounds at top level */ "^((::[[:space:]]*)?[A-Za-z_].*)$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*" "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"), PATTERNS("csharp", /* Keywords */ "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n" /* Methods and constructors */ "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n" /* Properties */ "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n" /* Type definitions */ "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n" /* Namespace */ "^[ \t]*(namespace[ \t]+.*)$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), PATTERNS("php", "^[ \t]*(((public|private|protected|static|final)[ \t]+)*((class|function)[ \t].*))$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), PATTERNS("javascript", "([a-zA-Z_$][a-zA-Z0-9_$]*(\\.[a-zA-Z0-9_$]+)*[ \t]*=[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n" "([a-zA-Z_$][a-zA-Z0-9_$]*[ \t]*:[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n" "[^a-zA-Z0-9_\\$](function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), }; #undef IPATTERN #undef PATTERNS #undef WORD_DEFAULT #endif git2r/src/libgit2/src/strarray.c0000644000175000017500000000221414125111754016360 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "util.h" #include "common.h" int git_strarray_copy(git_strarray *tgt, const git_strarray *src) { size_t i; GIT_ASSERT_ARG(tgt); GIT_ASSERT_ARG(src); memset(tgt, 0, sizeof(*tgt)); if (!src->count) return 0; tgt->strings = git__calloc(src->count, sizeof(char *)); GIT_ERROR_CHECK_ALLOC(tgt->strings); for (i = 0; i < src->count; ++i) { if (!src->strings[i]) continue; tgt->strings[tgt->count] = git__strdup(src->strings[i]); if (!tgt->strings[tgt->count]) { git_strarray_dispose(tgt); memset(tgt, 0, sizeof(*tgt)); return -1; } tgt->count++; } return 0; } void git_strarray_dispose(git_strarray *array) { size_t i; if (array == NULL) return; for (i = 0; i < array->count; ++i) git__free(array->strings[i]); git__free(array->strings); memset(array, 0, sizeof(*array)); } #ifndef GIT_DEPRECATE_HARD void git_strarray_free(git_strarray *array) { git_strarray_dispose(array); } #endif git2r/src/libgit2/src/patch_parse.c0000644000175000017500000010022314125111754017001 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "patch_parse.h" #include "git2/patch.h" #include "patch.h" #include "diff_parse.h" #include "path.h" typedef struct { git_patch base; git_patch_parse_ctx *ctx; /* the paths from the `diff --git` header, these will be used if this is not * a rename (and rename paths are specified) or if no `+++`/`---` line specify * the paths. */ char *header_old_path, *header_new_path; /* renamed paths are precise and are not prefixed */ char *rename_old_path, *rename_new_path; /* the paths given in `---` and `+++` lines */ char *old_path, *new_path; /* the prefixes from the old/new paths */ char *old_prefix, *new_prefix; } git_patch_parsed; static int git_parse_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2); static int git_parse_err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); git_error_vset(GIT_ERROR_PATCH, fmt, ap); va_end(ap); return -1; } static size_t header_path_len(git_patch_parse_ctx *ctx) { bool inquote = 0; bool quoted = git_parse_ctx_contains_s(&ctx->parse_ctx, "\""); size_t len; for (len = quoted; len < ctx->parse_ctx.line_len; len++) { if (!quoted && git__isspace(ctx->parse_ctx.line[len])) break; else if (quoted && !inquote && ctx->parse_ctx.line[len] == '"') { len++; break; } inquote = (!inquote && ctx->parse_ctx.line[len] == '\\'); } return len; } static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx, size_t path_len) { int error; if ((error = git_buf_put(path, ctx->parse_ctx.line, path_len)) < 0) return error; git_parse_advance_chars(&ctx->parse_ctx, path_len); git_buf_rtrim(path); if (path->size > 0 && path->ptr[0] == '"' && (error = git_buf_unquote(path)) < 0) return error; git_path_squash_slashes(path); if (!path->size) return git_parse_err("patch contains empty path at line %"PRIuZ, ctx->parse_ctx.line_num); return 0; } static int parse_header_path(char **out, git_patch_parse_ctx *ctx) { git_buf path = GIT_BUF_INIT; int error; if ((error = parse_header_path_buf(&path, ctx, header_path_len(ctx))) < 0) goto out; *out = git_buf_detach(&path); out: git_buf_dispose(&path); return error; } static int parse_header_git_oldpath( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { git_buf old_path = GIT_BUF_INIT; int error; if (patch->old_path) { error = git_parse_err("patch contains duplicate old path at line %"PRIuZ, ctx->parse_ctx.line_num); goto out; } if ((error = parse_header_path_buf(&old_path, ctx, ctx->parse_ctx.line_len - 1)) < 0) goto out; patch->old_path = git_buf_detach(&old_path); out: git_buf_dispose(&old_path); return error; } static int parse_header_git_newpath( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { git_buf new_path = GIT_BUF_INIT; int error; if (patch->new_path) { error = git_parse_err("patch contains duplicate new path at line %"PRIuZ, ctx->parse_ctx.line_num); goto out; } if ((error = parse_header_path_buf(&new_path, ctx, ctx->parse_ctx.line_len - 1)) < 0) goto out; patch->new_path = git_buf_detach(&new_path); out: git_buf_dispose(&new_path); return error; } static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx) { int64_t m; if ((git_parse_advance_digit(&m, &ctx->parse_ctx, 8)) < 0) return git_parse_err("invalid file mode at line %"PRIuZ, ctx->parse_ctx.line_num); if (m > UINT16_MAX) return -1; *mode = (uint16_t)m; return 0; } static int parse_header_oid( git_oid *oid, uint16_t *oid_len, git_patch_parse_ctx *ctx) { size_t len; for (len = 0; len < ctx->parse_ctx.line_len && len < GIT_OID_HEXSZ; len++) { if (!git__isxdigit(ctx->parse_ctx.line[len])) break; } if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ || git_oid_fromstrn(oid, ctx->parse_ctx.line, len) < 0) return git_parse_err("invalid hex formatted object id at line %"PRIuZ, ctx->parse_ctx.line_num); git_parse_advance_chars(&ctx->parse_ctx, len); *oid_len = (uint16_t)len; return 0; } static int parse_header_git_index( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { char c; if (parse_header_oid(&patch->base.delta->old_file.id, &patch->base.delta->old_file.id_abbrev, ctx) < 0 || git_parse_advance_expected_str(&ctx->parse_ctx, "..") < 0 || parse_header_oid(&patch->base.delta->new_file.id, &patch->base.delta->new_file.id_abbrev, ctx) < 0) return -1; if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ' ') { uint16_t mode = 0; git_parse_advance_chars(&ctx->parse_ctx, 1); if (parse_header_mode(&mode, ctx) < 0) return -1; if (!patch->base.delta->new_file.mode) patch->base.delta->new_file.mode = mode; if (!patch->base.delta->old_file.mode) patch->base.delta->old_file.mode = mode; } return 0; } static int parse_header_git_oldmode( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { return parse_header_mode(&patch->base.delta->old_file.mode, ctx); } static int parse_header_git_newmode( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { return parse_header_mode(&patch->base.delta->new_file.mode, ctx); } static int parse_header_git_deletedfilemode( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { git__free((char *)patch->base.delta->new_file.path); patch->base.delta->new_file.path = NULL; patch->base.delta->status = GIT_DELTA_DELETED; patch->base.delta->nfiles = 1; return parse_header_mode(&patch->base.delta->old_file.mode, ctx); } static int parse_header_git_newfilemode( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { git__free((char *)patch->base.delta->old_file.path); patch->base.delta->old_file.path = NULL; patch->base.delta->status = GIT_DELTA_ADDED; patch->base.delta->nfiles = 1; return parse_header_mode(&patch->base.delta->new_file.mode, ctx); } static int parse_header_rename( char **out, git_patch_parse_ctx *ctx) { git_buf path = GIT_BUF_INIT; if (parse_header_path_buf(&path, ctx, header_path_len(ctx)) < 0) return -1; /* Note: the `rename from` and `rename to` lines include the literal * filename. They do *not* include the prefix. (Who needs consistency?) */ *out = git_buf_detach(&path); return 0; } static int parse_header_renamefrom( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { patch->base.delta->status = GIT_DELTA_RENAMED; return parse_header_rename(&patch->rename_old_path, ctx); } static int parse_header_renameto( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { patch->base.delta->status = GIT_DELTA_RENAMED; return parse_header_rename(&patch->rename_new_path, ctx); } static int parse_header_copyfrom( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { patch->base.delta->status = GIT_DELTA_COPIED; return parse_header_rename(&patch->rename_old_path, ctx); } static int parse_header_copyto( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { patch->base.delta->status = GIT_DELTA_COPIED; return parse_header_rename(&patch->rename_new_path, ctx); } static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx) { int64_t val; if (git_parse_advance_digit(&val, &ctx->parse_ctx, 10) < 0) return -1; if (git_parse_advance_expected_str(&ctx->parse_ctx, "%") < 0) return -1; if (val < 0 || val > 100) return -1; *out = (uint16_t)val; return 0; } static int parse_header_similarity( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0) return git_parse_err("invalid similarity percentage at line %"PRIuZ, ctx->parse_ctx.line_num); return 0; } static int parse_header_dissimilarity( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { uint16_t dissimilarity; if (parse_header_percent(&dissimilarity, ctx) < 0) return git_parse_err("invalid similarity percentage at line %"PRIuZ, ctx->parse_ctx.line_num); patch->base.delta->similarity = 100 - dissimilarity; return 0; } static int parse_header_start(git_patch_parsed *patch, git_patch_parse_ctx *ctx) { if (parse_header_path(&patch->header_old_path, ctx) < 0) return git_parse_err("corrupt old path in git diff header at line %"PRIuZ, ctx->parse_ctx.line_num); if (git_parse_advance_ws(&ctx->parse_ctx) < 0 || parse_header_path(&patch->header_new_path, ctx) < 0) return git_parse_err("corrupt new path in git diff header at line %"PRIuZ, ctx->parse_ctx.line_num); /* * We cannot expect to be able to always parse paths correctly at this * point. Due to the possibility of unquoted names, whitespaces in * filenames and custom prefixes we have to allow that, though, and just * proceeed here. We then hope for the "---" and "+++" lines to fix that * for us. */ if (!git_parse_ctx_contains(&ctx->parse_ctx, "\n", 1) && !git_parse_ctx_contains(&ctx->parse_ctx, "\r\n", 2)) { git_parse_advance_chars(&ctx->parse_ctx, ctx->parse_ctx.line_len - 1); git__free(patch->header_old_path); patch->header_old_path = NULL; git__free(patch->header_new_path); patch->header_new_path = NULL; } return 0; } typedef enum { STATE_START, STATE_DIFF, STATE_FILEMODE, STATE_MODE, STATE_INDEX, STATE_PATH, STATE_SIMILARITY, STATE_RENAME, STATE_COPY, STATE_END, } parse_header_state; typedef struct { const char *str; parse_header_state expected_state; parse_header_state next_state; int(*fn)(git_patch_parsed *, git_patch_parse_ctx *); } parse_header_transition; static const parse_header_transition transitions[] = { /* Start */ { "diff --git " , STATE_START, STATE_DIFF, parse_header_start }, { "deleted file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_deletedfilemode }, { "new file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_newfilemode }, { "old mode " , STATE_DIFF, STATE_MODE, parse_header_git_oldmode }, { "new mode " , STATE_MODE, STATE_END, parse_header_git_newmode }, { "index " , STATE_FILEMODE, STATE_INDEX, parse_header_git_index }, { "index " , STATE_DIFF, STATE_INDEX, parse_header_git_index }, { "index " , STATE_END, STATE_INDEX, parse_header_git_index }, { "--- " , STATE_DIFF, STATE_PATH, parse_header_git_oldpath }, { "--- " , STATE_INDEX, STATE_PATH, parse_header_git_oldpath }, { "--- " , STATE_FILEMODE, STATE_PATH, parse_header_git_oldpath }, { "+++ " , STATE_PATH, STATE_END, parse_header_git_newpath }, { "GIT binary patch" , STATE_INDEX, STATE_END, NULL }, { "Binary files " , STATE_INDEX, STATE_END, NULL }, { "similarity index " , STATE_END, STATE_SIMILARITY, parse_header_similarity }, { "similarity index " , STATE_DIFF, STATE_SIMILARITY, parse_header_similarity }, { "dissimilarity index ", STATE_DIFF, STATE_SIMILARITY, parse_header_dissimilarity }, { "rename from " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom }, { "rename old " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom }, { "copy from " , STATE_SIMILARITY, STATE_COPY, parse_header_copyfrom }, { "rename to " , STATE_RENAME, STATE_END, parse_header_renameto }, { "rename new " , STATE_RENAME, STATE_END, parse_header_renameto }, { "copy to " , STATE_COPY, STATE_END, parse_header_copyto }, /* Next patch */ { "diff --git " , STATE_END, 0, NULL }, { "@@ -" , STATE_END, 0, NULL }, { "-- " , STATE_INDEX, 0, NULL }, { "-- " , STATE_END, 0, NULL }, }; static int parse_header_git( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { size_t i; int error = 0; parse_header_state state = STATE_START; /* Parse remaining header lines */ for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) { bool found = false; if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') break; for (i = 0; i < ARRAY_SIZE(transitions); i++) { const parse_header_transition *transition = &transitions[i]; size_t op_len = strlen(transition->str); if (transition->expected_state != state || git__prefixcmp(ctx->parse_ctx.line, transition->str) != 0) continue; state = transition->next_state; /* Do not advance if this is the patch separator */ if (transition->fn == NULL) goto done; git_parse_advance_chars(&ctx->parse_ctx, op_len); if ((error = transition->fn(patch, ctx)) < 0) goto done; git_parse_advance_ws(&ctx->parse_ctx); if (git_parse_advance_expected_str(&ctx->parse_ctx, "\n") < 0 || ctx->parse_ctx.line_len > 0) { error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } found = true; break; } if (!found) { error = git_parse_err("invalid patch header at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } } if (state != STATE_END) { error = git_parse_err("unexpected header line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } done: return error; } static int parse_int(int *out, git_patch_parse_ctx *ctx) { int64_t num; if (git_parse_advance_digit(&num, &ctx->parse_ctx, 10) < 0 || !git__is_int(num)) return -1; *out = (int)num; return 0; } static int parse_hunk_header( git_patch_hunk *hunk, git_patch_parse_ctx *ctx) { const char *header_start = ctx->parse_ctx.line; char c; hunk->hunk.old_lines = 1; hunk->hunk.new_lines = 1; if (git_parse_advance_expected_str(&ctx->parse_ctx, "@@ -") < 0 || parse_int(&hunk->hunk.old_start, ctx) < 0) goto fail; if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') { if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 || parse_int(&hunk->hunk.old_lines, ctx) < 0) goto fail; } if (git_parse_advance_expected_str(&ctx->parse_ctx, " +") < 0 || parse_int(&hunk->hunk.new_start, ctx) < 0) goto fail; if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') { if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 || parse_int(&hunk->hunk.new_lines, ctx) < 0) goto fail; } if (git_parse_advance_expected_str(&ctx->parse_ctx, " @@") < 0) goto fail; git_parse_advance_line(&ctx->parse_ctx); if (!hunk->hunk.old_lines && !hunk->hunk.new_lines) goto fail; hunk->hunk.header_len = ctx->parse_ctx.line - header_start; if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1)) return git_parse_err("oversized patch hunk header at line %"PRIuZ, ctx->parse_ctx.line_num); memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len); hunk->hunk.header[hunk->hunk.header_len] = '\0'; return 0; fail: git_error_set(GIT_ERROR_PATCH, "invalid patch hunk header at line %"PRIuZ, ctx->parse_ctx.line_num); return -1; } static int eof_for_origin(int origin) { if (origin == GIT_DIFF_LINE_ADDITION) return GIT_DIFF_LINE_ADD_EOFNL; if (origin == GIT_DIFF_LINE_DELETION) return GIT_DIFF_LINE_DEL_EOFNL; return GIT_DIFF_LINE_CONTEXT_EOFNL; } static int parse_hunk_body( git_patch_parsed *patch, git_patch_hunk *hunk, git_patch_parse_ctx *ctx) { git_diff_line *line; int error = 0; int oldlines = hunk->hunk.old_lines; int newlines = hunk->hunk.new_lines; int last_origin = 0; for (; ctx->parse_ctx.remain_len > 1 && (oldlines || newlines) && !git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -"); git_parse_advance_line(&ctx->parse_ctx)) { int old_lineno, new_lineno, origin, prefix = 1; char c; if (git__add_int_overflow(&old_lineno, hunk->hunk.old_start, hunk->hunk.old_lines) || git__sub_int_overflow(&old_lineno, old_lineno, oldlines) || git__add_int_overflow(&new_lineno, hunk->hunk.new_start, hunk->hunk.new_lines) || git__sub_int_overflow(&new_lineno, new_lineno, newlines)) { error = git_parse_err("unrepresentable line count at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') { error = git_parse_err("invalid patch instruction at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } git_parse_peek(&c, &ctx->parse_ctx, 0); switch (c) { case '\n': prefix = 0; /* fall through */ case ' ': origin = GIT_DIFF_LINE_CONTEXT; oldlines--; newlines--; break; case '-': origin = GIT_DIFF_LINE_DELETION; oldlines--; new_lineno = -1; break; case '+': origin = GIT_DIFF_LINE_ADDITION; newlines--; old_lineno = -1; break; case '\\': /* * If there are no oldlines left, then this is probably * the "\ No newline at end of file" marker. Do not * verify its format, as it may be localized. */ if (!oldlines) { prefix = 0; origin = eof_for_origin(last_origin); old_lineno = -1; new_lineno = -1; break; } /* fall through */ default: error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } line = git_array_alloc(patch->base.lines); GIT_ERROR_CHECK_ALLOC(line); memset(line, 0x0, sizeof(git_diff_line)); line->content_len = ctx->parse_ctx.line_len - prefix; line->content = git__strndup(ctx->parse_ctx.line + prefix, line->content_len); GIT_ERROR_CHECK_ALLOC(line->content); line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len; line->origin = origin; line->num_lines = 1; line->old_lineno = old_lineno; line->new_lineno = new_lineno; hunk->line_count++; last_origin = origin; } if (oldlines || newlines) { error = git_parse_err( "invalid patch hunk, expected %d old lines and %d new lines", hunk->hunk.old_lines, hunk->hunk.new_lines); goto done; } /* * Handle "\ No newline at end of file". Only expect the leading * backslash, though, because the rest of the string could be * localized. Because `diff` optimizes for the case where you * want to apply the patch by hand. */ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "\\ ") && git_array_size(patch->base.lines) > 0) { line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1); if (line->content_len < 1) { error = git_parse_err("last line has no trailing newline"); goto done; } line = git_array_alloc(patch->base.lines); GIT_ERROR_CHECK_ALLOC(line); memset(line, 0x0, sizeof(git_diff_line)); line->content_len = ctx->parse_ctx.line_len; line->content = git__strndup(ctx->parse_ctx.line, line->content_len); GIT_ERROR_CHECK_ALLOC(line->content); line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len; line->origin = eof_for_origin(last_origin); line->num_lines = 1; line->old_lineno = -1; line->new_lineno = -1; hunk->line_count++; git_parse_advance_line(&ctx->parse_ctx); } done: return error; } static int parse_patch_header( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { int error = 0; for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) { /* This line is too short to be a patch header. */ if (ctx->parse_ctx.line_len < 6) continue; /* This might be a hunk header without a patch header, provide a * sensible error message. */ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) { size_t line_num = ctx->parse_ctx.line_num; git_patch_hunk hunk; /* If this cannot be parsed as a hunk header, it's just leading * noise, continue. */ if (parse_hunk_header(&hunk, ctx) < 0) { git_error_clear(); continue; } error = git_parse_err("invalid hunk header outside patch at line %"PRIuZ, line_num); goto done; } /* This buffer is too short to contain a patch. */ if (ctx->parse_ctx.remain_len < ctx->parse_ctx.line_len + 6) break; /* A proper git patch */ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "diff --git ")) { error = parse_header_git(patch, ctx); goto done; } error = 0; continue; } git_error_set(GIT_ERROR_PATCH, "no patch found"); error = GIT_ENOTFOUND; done: return error; } static int parse_patch_binary_side( git_diff_binary_file *binary, git_patch_parse_ctx *ctx) { git_diff_binary_t type = GIT_DIFF_BINARY_NONE; git_buf base85 = GIT_BUF_INIT, decoded = GIT_BUF_INIT; int64_t len; int error = 0; if (git_parse_ctx_contains_s(&ctx->parse_ctx, "literal ")) { type = GIT_DIFF_BINARY_LITERAL; git_parse_advance_chars(&ctx->parse_ctx, 8); } else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "delta ")) { type = GIT_DIFF_BINARY_DELTA; git_parse_advance_chars(&ctx->parse_ctx, 6); } else { error = git_parse_err( "unknown binary delta type at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } if (git_parse_advance_digit(&len, &ctx->parse_ctx, 10) < 0 || git_parse_advance_nl(&ctx->parse_ctx) < 0 || len < 0) { error = git_parse_err("invalid binary size at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } while (ctx->parse_ctx.line_len) { char c; size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size; git_parse_peek(&c, &ctx->parse_ctx, 0); if (c == '\n') break; else if (c >= 'A' && c <= 'Z') decoded_len = c - 'A' + 1; else if (c >= 'a' && c <= 'z') decoded_len = c - 'a' + (('z' - 'a') + 1) + 1; if (!decoded_len) { error = git_parse_err("invalid binary length at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } git_parse_advance_chars(&ctx->parse_ctx, 1); encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5; if (!encoded_len || !ctx->parse_ctx.line_len || encoded_len > ctx->parse_ctx.line_len - 1) { error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } if ((error = git_buf_decode_base85( &decoded, ctx->parse_ctx.line, encoded_len, decoded_len)) < 0) goto done; if (decoded.size - decoded_orig != decoded_len) { error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } git_parse_advance_chars(&ctx->parse_ctx, encoded_len); if (git_parse_advance_nl(&ctx->parse_ctx) < 0) { error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } } binary->type = type; binary->inflatedlen = (size_t)len; binary->datalen = decoded.size; binary->data = git_buf_detach(&decoded); done: git_buf_dispose(&base85); git_buf_dispose(&decoded); return error; } static int parse_patch_binary( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { int error; if (git_parse_advance_expected_str(&ctx->parse_ctx, "GIT binary patch") < 0 || git_parse_advance_nl(&ctx->parse_ctx) < 0) return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num); /* parse old->new binary diff */ if ((error = parse_patch_binary_side( &patch->base.binary.new_file, ctx)) < 0) return error; if (git_parse_advance_nl(&ctx->parse_ctx) < 0) return git_parse_err("corrupt git binary separator at line %"PRIuZ, ctx->parse_ctx.line_num); /* parse new->old binary diff */ if ((error = parse_patch_binary_side( &patch->base.binary.old_file, ctx)) < 0) return error; if (git_parse_advance_nl(&ctx->parse_ctx) < 0) return git_parse_err("corrupt git binary patch separator at line %"PRIuZ, ctx->parse_ctx.line_num); patch->base.binary.contains_data = 1; patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY; return 0; } static int parse_patch_binary_nodata( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { const char *old = patch->old_path ? patch->old_path : patch->header_old_path; const char *new = patch->new_path ? patch->new_path : patch->header_new_path; if (!old || !new) return git_parse_err("corrupt binary data without paths at line %"PRIuZ, ctx->parse_ctx.line_num); if (patch->base.delta->status == GIT_DELTA_ADDED) old = "/dev/null"; else if (patch->base.delta->status == GIT_DELTA_DELETED) new = "/dev/null"; if (git_parse_advance_expected_str(&ctx->parse_ctx, "Binary files ") < 0 || git_parse_advance_expected_str(&ctx->parse_ctx, old) < 0 || git_parse_advance_expected_str(&ctx->parse_ctx, " and ") < 0 || git_parse_advance_expected_str(&ctx->parse_ctx, new) < 0 || git_parse_advance_expected_str(&ctx->parse_ctx, " differ") < 0 || git_parse_advance_nl(&ctx->parse_ctx) < 0) return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num); patch->base.binary.contains_data = 0; patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY; return 0; } static int parse_patch_hunks( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { git_patch_hunk *hunk; int error = 0; while (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) { hunk = git_array_alloc(patch->base.hunks); GIT_ERROR_CHECK_ALLOC(hunk); memset(hunk, 0, sizeof(git_patch_hunk)); hunk->line_start = git_array_size(patch->base.lines); hunk->line_count = 0; if ((error = parse_hunk_header(hunk, ctx)) < 0 || (error = parse_hunk_body(patch, hunk, ctx)) < 0) goto done; } patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; done: return error; } static int parse_patch_body( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { if (git_parse_ctx_contains_s(&ctx->parse_ctx, "GIT binary patch")) return parse_patch_binary(patch, ctx); else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "Binary files ")) return parse_patch_binary_nodata(patch, ctx); else return parse_patch_hunks(patch, ctx); } static int check_header_names( const char *one, const char *two, const char *old_or_new, bool two_null) { if (!one || !two) return 0; if (two_null && strcmp(two, "/dev/null") != 0) return git_parse_err("expected %s path of '/dev/null'", old_or_new); else if (!two_null && strcmp(one, two) != 0) return git_parse_err("mismatched %s path names", old_or_new); return 0; } static int check_prefix( char **out, size_t *out_len, git_patch_parsed *patch, const char *path_start) { const char *path = path_start; size_t prefix_len = patch->ctx->opts.prefix_len; size_t remain_len = prefix_len; *out = NULL; *out_len = 0; if (prefix_len == 0) goto done; /* leading slashes do not count as part of the prefix in git apply */ while (*path == '/') path++; while (*path && remain_len) { if (*path == '/') remain_len--; path++; } if (remain_len || !*path) return git_parse_err( "header filename does not contain %"PRIuZ" path components", prefix_len); done: *out_len = (path - path_start); *out = git__strndup(path_start, *out_len); return (*out == NULL) ? -1 : 0; } static int check_filenames(git_patch_parsed *patch) { const char *prefixed_new, *prefixed_old; size_t old_prefixlen = 0, new_prefixlen = 0; bool added = (patch->base.delta->status == GIT_DELTA_ADDED); bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED); if (patch->old_path && !patch->new_path) return git_parse_err("missing new path"); if (!patch->old_path && patch->new_path) return git_parse_err("missing old path"); /* Ensure (non-renamed) paths match */ if (check_header_names(patch->header_old_path, patch->old_path, "old", added) < 0 || check_header_names(patch->header_new_path, patch->new_path, "new", deleted) < 0) return -1; prefixed_old = (!added && patch->old_path) ? patch->old_path : patch->header_old_path; prefixed_new = (!deleted && patch->new_path) ? patch->new_path : patch->header_new_path; if ((prefixed_old && check_prefix(&patch->old_prefix, &old_prefixlen, patch, prefixed_old) < 0) || (prefixed_new && check_prefix(&patch->new_prefix, &new_prefixlen, patch, prefixed_new) < 0)) return -1; /* Prefer the rename filenames as they are unambiguous and unprefixed */ if (patch->rename_old_path) patch->base.delta->old_file.path = patch->rename_old_path; else if (prefixed_old) patch->base.delta->old_file.path = prefixed_old + old_prefixlen; else patch->base.delta->old_file.path = NULL; if (patch->rename_new_path) patch->base.delta->new_file.path = patch->rename_new_path; else if (prefixed_new) patch->base.delta->new_file.path = prefixed_new + new_prefixlen; else patch->base.delta->new_file.path = NULL; if (!patch->base.delta->old_file.path && !patch->base.delta->new_file.path) return git_parse_err("git diff header lacks old / new paths"); return 0; } static int check_patch(git_patch_parsed *patch) { git_diff_delta *delta = patch->base.delta; if (check_filenames(patch) < 0) return -1; if (delta->old_file.path && delta->status != GIT_DELTA_DELETED && !delta->new_file.mode) delta->new_file.mode = delta->old_file.mode; if (delta->status == GIT_DELTA_MODIFIED && !(delta->flags & GIT_DIFF_FLAG_BINARY) && delta->new_file.mode == delta->old_file.mode && git_array_size(patch->base.hunks) == 0) return git_parse_err("patch with no hunks"); if (delta->status == GIT_DELTA_ADDED) { memset(&delta->old_file.id, 0x0, sizeof(git_oid)); delta->old_file.id_abbrev = 0; } if (delta->status == GIT_DELTA_DELETED) { memset(&delta->new_file.id, 0x0, sizeof(git_oid)); delta->new_file.id_abbrev = 0; } return 0; } git_patch_parse_ctx *git_patch_parse_ctx_init( const char *content, size_t content_len, const git_patch_options *opts) { git_patch_parse_ctx *ctx; git_patch_options default_opts = GIT_PATCH_OPTIONS_INIT; if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL) return NULL; if ((git_parse_ctx_init(&ctx->parse_ctx, content, content_len)) < 0) { git__free(ctx); return NULL; } if (opts) memcpy(&ctx->opts, opts, sizeof(git_patch_options)); else memcpy(&ctx->opts, &default_opts, sizeof(git_patch_options)); GIT_REFCOUNT_INC(ctx); return ctx; } static void patch_parse_ctx_free(git_patch_parse_ctx *ctx) { if (!ctx) return; git_parse_ctx_clear(&ctx->parse_ctx); git__free(ctx); } void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx) { GIT_REFCOUNT_DEC(ctx, patch_parse_ctx_free); } int git_patch_parsed_from_diff(git_patch **out, git_diff *d, size_t idx) { git_diff_parsed *diff = (git_diff_parsed *)d; git_patch *p; if ((p = git_vector_get(&diff->patches, idx)) == NULL) return -1; GIT_REFCOUNT_INC(p); *out = p; return 0; } static void patch_parsed__free(git_patch *p) { git_patch_parsed *patch = (git_patch_parsed *)p; git_diff_line *line; size_t i; if (!patch) return; git_patch_parse_ctx_free(patch->ctx); git__free((char *)patch->base.binary.old_file.data); git__free((char *)patch->base.binary.new_file.data); git_array_clear(patch->base.hunks); git_array_foreach(patch->base.lines, i, line) git__free((char *) line->content); git_array_clear(patch->base.lines); git__free(patch->base.delta); git__free(patch->old_prefix); git__free(patch->new_prefix); git__free(patch->header_old_path); git__free(patch->header_new_path); git__free(patch->rename_old_path); git__free(patch->rename_new_path); git__free(patch->old_path); git__free(patch->new_path); git__free(patch); } int git_patch_parse( git_patch **out, git_patch_parse_ctx *ctx) { git_patch_parsed *patch; size_t start, used; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(ctx); *out = NULL; patch = git__calloc(1, sizeof(git_patch_parsed)); GIT_ERROR_CHECK_ALLOC(patch); patch->ctx = ctx; GIT_REFCOUNT_INC(patch->ctx); patch->base.free_fn = patch_parsed__free; patch->base.delta = git__calloc(1, sizeof(git_diff_delta)); GIT_ERROR_CHECK_ALLOC(patch->base.delta); patch->base.delta->status = GIT_DELTA_MODIFIED; patch->base.delta->nfiles = 2; start = ctx->parse_ctx.remain_len; if ((error = parse_patch_header(patch, ctx)) < 0 || (error = parse_patch_body(patch, ctx)) < 0 || (error = check_patch(patch)) < 0) goto done; used = start - ctx->parse_ctx.remain_len; ctx->parse_ctx.remain += used; patch->base.diff_opts.old_prefix = patch->old_prefix; patch->base.diff_opts.new_prefix = patch->new_prefix; patch->base.diff_opts.flags |= GIT_DIFF_SHOW_BINARY; GIT_REFCOUNT_INC(&patch->base); *out = &patch->base; done: if (error < 0) patch_parsed__free(&patch->base); return error; } int git_patch_from_buffer( git_patch **out, const char *content, size_t content_len, const git_patch_options *opts) { git_patch_parse_ctx *ctx; int error; ctx = git_patch_parse_ctx_init(content, content_len, opts); GIT_ERROR_CHECK_ALLOC(ctx); error = git_patch_parse(out, ctx); git_patch_parse_ctx_free(ctx); return error; } git2r/src/libgit2/src/attr_file.h0000644000175000017500000001347614125111754016503 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_attr_file_h__ #define INCLUDE_attr_file_h__ #include "common.h" #include "git2/oid.h" #include "git2/attr.h" #include "vector.h" #include "pool.h" #include "buffer.h" #include "futils.h" #define GIT_ATTR_FILE ".gitattributes" #define GIT_ATTR_FILE_INREPO "attributes" #define GIT_ATTR_FILE_SYSTEM "gitattributes" #define GIT_ATTR_FILE_XDG "attributes" #define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0) #define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1) #define GIT_ATTR_FNMATCH_FULLPATH (1U << 2) #define GIT_ATTR_FNMATCH_MACRO (1U << 3) #define GIT_ATTR_FNMATCH_IGNORE (1U << 4) #define GIT_ATTR_FNMATCH_HASWILD (1U << 5) #define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6) #define GIT_ATTR_FNMATCH_ICASE (1U << 7) #define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8) #define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9) #define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10) #define GIT_ATTR_FNMATCH__INCOMING \ (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO) typedef enum { GIT_ATTR_FILE_SOURCE_MEMORY = 0, GIT_ATTR_FILE_SOURCE_FILE = 1, GIT_ATTR_FILE_SOURCE_INDEX = 2, GIT_ATTR_FILE_SOURCE_HEAD = 3, GIT_ATTR_FILE_SOURCE_COMMIT = 4, GIT_ATTR_FILE_NUM_SOURCES = 5 } git_attr_file_source_t; typedef struct { /* The source location for the attribute file. */ git_attr_file_source_t type; /* * The filename of the attribute file to read (relative to the * given base path). */ const char *base; const char *filename; /* * The commit ID when the given source type is a commit (or NULL * for the repository's HEAD commit.) */ git_oid *commit_id; } git_attr_file_source; extern const char *git_attr__true; extern const char *git_attr__false; extern const char *git_attr__unset; typedef struct { char *pattern; size_t length; char *containing_dir; size_t containing_dir_length; unsigned int flags; } git_attr_fnmatch; typedef struct { git_attr_fnmatch match; git_vector assigns; /* vector of */ } git_attr_rule; typedef struct { git_refcount unused; const char *name; uint32_t name_hash; } git_attr_name; typedef struct { git_refcount rc; /* for macros */ char *name; uint32_t name_hash; const char *value; } git_attr_assignment; typedef struct git_attr_file_entry git_attr_file_entry; typedef struct { git_refcount rc; git_mutex lock; git_attr_file_entry *entry; git_attr_file_source source; git_vector rules; /* vector of or */ git_pool pool; unsigned int nonexistent:1; int session_key; union { git_oid oid; git_futils_filestamp stamp; } cache_data; } git_attr_file; struct git_attr_file_entry { git_attr_file *file[GIT_ATTR_FILE_NUM_SOURCES]; const char *path; /* points into fullpath */ char fullpath[GIT_FLEX_ARRAY]; }; typedef struct { git_buf full; char *path; char *basename; int is_dir; } git_attr_path; /* A git_attr_session can provide an "instance" of reading, to prevent cache * invalidation during a single operation instance (like checkout). */ typedef struct { int key; unsigned int init_setup:1, init_sysdir:1; git_buf sysdir; git_buf tmp; } git_attr_session; extern int git_attr_session__init(git_attr_session *attr_session, git_repository *repo); extern void git_attr_session__free(git_attr_session *session); extern int git_attr_get_many_with_session( const char **values_out, git_repository *repo, git_attr_session *attr_session, git_attr_options *opts, const char *path, size_t num_attr, const char **names); typedef int (*git_attr_file_parser)( git_repository *repo, git_attr_file *file, const char *data, bool allow_macros); /* * git_attr_file API */ int git_attr_file__new( git_attr_file **out, git_attr_file_entry *entry, git_attr_file_source *source); void git_attr_file__free(git_attr_file *file); int git_attr_file__load( git_attr_file **out, git_repository *repo, git_attr_session *attr_session, git_attr_file_entry *ce, git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros); int git_attr_file__load_standalone( git_attr_file **out, const char *path); int git_attr_file__out_of_date( git_repository *repo, git_attr_session *session, git_attr_file *file, git_attr_file_source *source); int git_attr_file__parse_buffer( git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros); int git_attr_file__clear_rules( git_attr_file *file, bool need_lock); int git_attr_file__lookup_one( git_attr_file *file, git_attr_path *path, const char *attr, const char **value); /* loop over rules in file from bottom to top */ #define git_attr_file__foreach_matching_rule(file, path, iter, rule) \ git_vector_rforeach(&(file)->rules, (iter), (rule)) \ if (git_attr_rule__match((rule), (path))) uint32_t git_attr_file__name_hash(const char *name); /* * other utilities */ extern int git_attr_fnmatch__parse( git_attr_fnmatch *spec, git_pool *pool, const char *source, const char **base); extern bool git_attr_fnmatch__match( git_attr_fnmatch *rule, git_attr_path *path); extern void git_attr_rule__free(git_attr_rule *rule); extern bool git_attr_rule__match( git_attr_rule *rule, git_attr_path *path); extern git_attr_assignment *git_attr_rule__lookup_assignment( git_attr_rule *rule, const char *name); typedef enum { GIT_DIR_FLAG_TRUE = 1, GIT_DIR_FLAG_FALSE = 0, GIT_DIR_FLAG_UNKNOWN = -1 } git_dir_flag; extern int git_attr_path__init( git_attr_path *out, const char *path, const char *base, git_dir_flag is_dir); extern void git_attr_path__free(git_attr_path *info); extern int git_attr_assignment__parse( git_repository *repo, /* needed to expand macros */ git_pool *pool, git_vector *assigns, const char **scan); #endif git2r/src/libgit2/src/refspec.h0000644000175000017500000000216114125111754016146 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_refspec_h__ #define INCLUDE_refspec_h__ #include "common.h" #include "git2/refspec.h" #include "buffer.h" #include "vector.h" struct git_refspec { char *string; char *src; char *dst; unsigned int force :1, push : 1, pattern :1, matching :1; }; #define GIT_REFSPEC_TAGS "refs/tags/*:refs/tags/*" int git_refspec__parse( struct git_refspec *refspec, const char *str, bool is_fetch); void git_refspec__dispose(git_refspec *refspec); int git_refspec__serialize(git_buf *out, const git_refspec *refspec); /** * Determines if a refspec is a wildcard refspec. * * @param spec the refspec * @return 1 if the refspec is a wildcard, 0 otherwise */ int git_refspec_is_wildcard(const git_refspec *spec); /** * DWIM `spec` with `refs` existing on the remote, append the dwim'ed * result in `out`. */ int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs); #endif git2r/src/libgit2/src/indexer.c0000644000175000017500000010112214125111754016145 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "indexer.h" #include "git2/indexer.h" #include "git2/object.h" #include "commit.h" #include "tree.h" #include "tag.h" #include "pack.h" #include "mwindow.h" #include "posix.h" #include "pack.h" #include "filebuf.h" #include "oid.h" #include "oidarray.h" #include "oidmap.h" #include "zstream.h" #include "object.h" size_t git_indexer__max_objects = UINT32_MAX; #define UINT31_MAX (0x7FFFFFFF) struct entry { git_oid oid; uint32_t crc; uint32_t offset; uint64_t offset_long; }; struct git_indexer { unsigned int parsed_header :1, pack_committed :1, have_stream :1, have_delta :1, do_fsync :1, do_verify :1; struct git_pack_header hdr; struct git_pack_file *pack; unsigned int mode; off64_t off; off64_t entry_start; git_object_t entry_type; git_buf entry_data; git_packfile_stream stream; size_t nr_objects; git_vector objects; git_vector deltas; unsigned int fanout[256]; git_hash_ctx hash_ctx; git_oid hash; git_indexer_progress_cb progress_cb; void *progress_payload; char objbuf[8*1024]; /* OIDs referenced from pack objects. Used for verification. */ git_oidmap *expected_oids; /* Needed to look up objects which we want to inject to fix a thin pack */ git_odb *odb; /* Fields for calculating the packfile trailer (hash of everything before it) */ char inbuf[GIT_OID_RAWSZ]; size_t inbuf_len; git_hash_ctx trailer; }; struct delta_info { off64_t delta_off; }; const git_oid *git_indexer_hash(const git_indexer *idx) { return &idx->hash; } static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack) { int error; git_map map; if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0) return error; memcpy(hdr, map.data, sizeof(*hdr)); p_munmap(&map); /* Verify we recognize this pack file format. */ if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) { git_error_set(GIT_ERROR_INDEXER, "wrong pack signature"); return -1; } if (!pack_version_ok(hdr->hdr_version)) { git_error_set(GIT_ERROR_INDEXER, "wrong pack version"); return -1; } return 0; } static int objects_cmp(const void *a, const void *b) { const struct entry *entrya = a; const struct entry *entryb = b; return git_oid__cmp(&entrya->oid, &entryb->oid); } int git_indexer_options_init(git_indexer_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_indexer_options, GIT_INDEXER_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_indexer_init_options(git_indexer_options *opts, unsigned int version) { return git_indexer_options_init(opts, version); } #endif int git_indexer_new( git_indexer **out, const char *prefix, unsigned int mode, git_odb *odb, git_indexer_options *in_opts) { git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT; git_indexer *idx; git_buf path = GIT_BUF_INIT, tmp_path = GIT_BUF_INIT; static const char suff[] = "/pack"; int error, fd = -1; if (in_opts) memcpy(&opts, in_opts, sizeof(opts)); idx = git__calloc(1, sizeof(git_indexer)); GIT_ERROR_CHECK_ALLOC(idx); idx->odb = odb; idx->progress_cb = opts.progress_cb; idx->progress_payload = opts.progress_cb_payload; idx->mode = mode ? mode : GIT_PACK_FILE_MODE; git_buf_init(&idx->entry_data, 0); if ((error = git_hash_ctx_init(&idx->hash_ctx)) < 0 || (error = git_hash_ctx_init(&idx->trailer)) < 0 || (error = git_oidmap_new(&idx->expected_oids)) < 0) goto cleanup; idx->do_verify = opts.verify; if (git_repository__fsync_gitdir) idx->do_fsync = 1; error = git_buf_joinpath(&path, prefix, suff); if (error < 0) goto cleanup; fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode); git_buf_dispose(&path); if (fd < 0) goto cleanup; error = git_packfile_alloc(&idx->pack, git_buf_cstr(&tmp_path)); git_buf_dispose(&tmp_path); if (error < 0) goto cleanup; idx->pack->mwf.fd = fd; if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0) goto cleanup; *out = idx; return 0; cleanup: if (fd != -1) p_close(fd); if (git_buf_len(&tmp_path) > 0) p_unlink(git_buf_cstr(&tmp_path)); if (idx->pack != NULL) p_unlink(idx->pack->pack_name); git_buf_dispose(&path); git_buf_dispose(&tmp_path); git__free(idx); return -1; } void git_indexer__set_fsync(git_indexer *idx, int do_fsync) { idx->do_fsync = !!do_fsync; } /* Try to store the delta so we can try to resolve it later */ static int store_delta(git_indexer *idx) { struct delta_info *delta; delta = git__calloc(1, sizeof(struct delta_info)); GIT_ERROR_CHECK_ALLOC(delta); delta->delta_off = idx->entry_start; if (git_vector_insert(&idx->deltas, delta) < 0) return -1; return 0; } static int hash_header(git_hash_ctx *ctx, off64_t len, git_object_t type) { char buffer[64]; size_t hdrlen; int error; if ((error = git_odb__format_object_header(&hdrlen, buffer, sizeof(buffer), (size_t)len, type)) < 0) return error; return git_hash_update(ctx, buffer, hdrlen); } static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream) { ssize_t read; GIT_ASSERT_ARG(idx); GIT_ASSERT_ARG(stream); do { if ((read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf))) < 0) break; if (idx->do_verify) git_buf_put(&idx->entry_data, idx->objbuf, read); git_hash_update(&idx->hash_ctx, idx->objbuf, read); } while (read > 0); if (read < 0) return (int)read; return 0; } /* In order to create the packfile stream, we need to skip over the delta base description */ static int advance_delta_offset(git_indexer *idx, git_object_t type) { git_mwindow *w = NULL; GIT_ASSERT_ARG(type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA); if (type == GIT_OBJECT_REF_DELTA) { idx->off += GIT_OID_RAWSZ; } else { off64_t base_off; int error = get_delta_base(&base_off, idx->pack, &w, &idx->off, type, idx->entry_start); git_mwindow_close(&w); if (error < 0) return error; } return 0; } /* Read from the stream and discard any output */ static int read_object_stream(git_indexer *idx, git_packfile_stream *stream) { ssize_t read; GIT_ASSERT_ARG(stream); do { read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf)); } while (read > 0); if (read < 0) return (int)read; return 0; } static int crc_object(uint32_t *crc_out, git_mwindow_file *mwf, off64_t start, off64_t size) { void *ptr; uint32_t crc; unsigned int left, len; git_mwindow *w = NULL; crc = crc32(0L, Z_NULL, 0); while (size) { ptr = git_mwindow_open(mwf, &w, start, (size_t)size, &left); if (ptr == NULL) return -1; len = min(left, (unsigned int)size); crc = crc32(crc, ptr, len); size -= len; start += len; git_mwindow_close(&w); } *crc_out = htonl(crc); return 0; } static int add_expected_oid(git_indexer *idx, const git_oid *oid) { /* * If we know about that object because it is stored in our ODB or * because we have already processed it as part of our pack file, we do * not have to expect it. */ if ((!idx->odb || !git_odb_exists(idx->odb, oid)) && !git_oidmap_exists(idx->pack->idx_cache, oid) && !git_oidmap_exists(idx->expected_oids, oid)) { git_oid *dup = git__malloc(sizeof(*oid)); GIT_ERROR_CHECK_ALLOC(dup); git_oid_cpy(dup, oid); return git_oidmap_set(idx->expected_oids, dup, dup); } return 0; } static int check_object_connectivity(git_indexer *idx, const git_rawobj *obj) { git_object *object; git_oid *expected; int error; if (obj->type != GIT_OBJECT_BLOB && obj->type != GIT_OBJECT_TREE && obj->type != GIT_OBJECT_COMMIT && obj->type != GIT_OBJECT_TAG) return 0; if ((error = git_object__from_raw(&object, obj->data, obj->len, obj->type)) < 0) goto out; if ((expected = git_oidmap_get(idx->expected_oids, &object->cached.oid)) != NULL) { git_oidmap_delete(idx->expected_oids, &object->cached.oid); git__free(expected); } /* * Check whether this is a known object. If so, we can just continue as * we assume that the ODB has a complete graph. */ if (idx->odb && git_odb_exists(idx->odb, &object->cached.oid)) return 0; switch (obj->type) { case GIT_OBJECT_TREE: { git_tree *tree = (git_tree *) object; git_tree_entry *entry; size_t i; git_array_foreach(tree->entries, i, entry) if (add_expected_oid(idx, entry->oid) < 0) goto out; break; } case GIT_OBJECT_COMMIT: { git_commit *commit = (git_commit *) object; git_oid *parent_oid; size_t i; git_array_foreach(commit->parent_ids, i, parent_oid) if (add_expected_oid(idx, parent_oid) < 0) goto out; if (add_expected_oid(idx, &commit->tree_id) < 0) goto out; break; } case GIT_OBJECT_TAG: { git_tag *tag = (git_tag *) object; if (add_expected_oid(idx, &tag->target) < 0) goto out; break; } case GIT_OBJECT_BLOB: default: break; } out: git_object_free(object); return error; } static int store_object(git_indexer *idx) { int i, error; git_oid oid; struct entry *entry; off64_t entry_size; struct git_pack_entry *pentry; off64_t entry_start = idx->entry_start; entry = git__calloc(1, sizeof(*entry)); GIT_ERROR_CHECK_ALLOC(entry); pentry = git__calloc(1, sizeof(struct git_pack_entry)); GIT_ERROR_CHECK_ALLOC(pentry); if (git_hash_final(&oid, &idx->hash_ctx)) { git__free(pentry); goto on_error; } entry_size = idx->off - entry_start; if (entry_start > UINT31_MAX) { entry->offset = UINT32_MAX; entry->offset_long = entry_start; } else { entry->offset = (uint32_t)entry_start; } if (idx->do_verify) { git_rawobj rawobj = { idx->entry_data.ptr, idx->entry_data.size, idx->entry_type }; if ((error = check_object_connectivity(idx, &rawobj)) < 0) goto on_error; } git_oid_cpy(&pentry->sha1, &oid); pentry->offset = entry_start; if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1)) { git_error_set(GIT_ERROR_INDEXER, "duplicate object %s found in pack", git_oid_tostr_s(&pentry->sha1)); git__free(pentry); goto on_error; } if ((error = git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry)) < 0) { git__free(pentry); git_error_set_oom(); goto on_error; } git_oid_cpy(&entry->oid, &oid); if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0) goto on_error; /* Add the object to the list */ if (git_vector_insert(&idx->objects, entry) < 0) goto on_error; for (i = oid.id[0]; i < 256; ++i) { idx->fanout[i]++; } return 0; on_error: git__free(entry); return -1; } GIT_INLINE(bool) has_entry(git_indexer *idx, git_oid *id) { return git_oidmap_exists(idx->pack->idx_cache, id); } static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, off64_t entry_start) { int i; if (entry_start > UINT31_MAX) { entry->offset = UINT32_MAX; entry->offset_long = entry_start; } else { entry->offset = (uint32_t)entry_start; } pentry->offset = entry_start; if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1) || git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry) < 0) { git_error_set(GIT_ERROR_INDEXER, "cannot insert object into pack"); return -1; } /* Add the object to the list */ if (git_vector_insert(&idx->objects, entry) < 0) return -1; for (i = entry->oid.id[0]; i < 256; ++i) { idx->fanout[i]++; } return 0; } static int hash_and_save(git_indexer *idx, git_rawobj *obj, off64_t entry_start) { git_oid oid; size_t entry_size; struct entry *entry; struct git_pack_entry *pentry = NULL; entry = git__calloc(1, sizeof(*entry)); GIT_ERROR_CHECK_ALLOC(entry); if (git_odb__hashobj(&oid, obj) < 0) { git_error_set(GIT_ERROR_INDEXER, "failed to hash object"); goto on_error; } pentry = git__calloc(1, sizeof(struct git_pack_entry)); GIT_ERROR_CHECK_ALLOC(pentry); git_oid_cpy(&pentry->sha1, &oid); git_oid_cpy(&entry->oid, &oid); entry->crc = crc32(0L, Z_NULL, 0); entry_size = (size_t)(idx->off - entry_start); if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0) goto on_error; return save_entry(idx, entry, pentry, entry_start); on_error: git__free(pentry); git__free(entry); git__free(obj->data); return -1; } static int do_progress_callback(git_indexer *idx, git_indexer_progress *stats) { if (idx->progress_cb) return git_error_set_after_callback_function( idx->progress_cb(stats, idx->progress_payload), "indexer progress"); return 0; } /* Hash everything but the last 20B of input */ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size) { size_t to_expell, to_keep; if (size == 0) return; /* Easy case, dump the buffer and the data minus the last 20 bytes */ if (size >= GIT_OID_RAWSZ) { git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len); git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ); data += size - GIT_OID_RAWSZ; memcpy(idx->inbuf, data, GIT_OID_RAWSZ); idx->inbuf_len = GIT_OID_RAWSZ; return; } /* We can just append */ if (idx->inbuf_len + size <= GIT_OID_RAWSZ) { memcpy(idx->inbuf + idx->inbuf_len, data, size); idx->inbuf_len += size; return; } /* We need to partially drain the buffer and then append */ to_keep = GIT_OID_RAWSZ - size; to_expell = idx->inbuf_len - to_keep; git_hash_update(&idx->trailer, idx->inbuf, to_expell); memmove(idx->inbuf, idx->inbuf + to_expell, to_keep); memcpy(idx->inbuf + to_keep, data, size); idx->inbuf_len += size - to_expell; } #if defined(NO_MMAP) || !defined(GIT_WIN32) static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size) { size_t remaining_size = size; const char *ptr = (const char *)data; /* Handle data size larger that ssize_t */ while (remaining_size > 0) { ssize_t nb; HANDLE_EINTR(nb, p_pwrite(idx->pack->mwf.fd, (void *)ptr, remaining_size, offset)); if (nb <= 0) return -1; ptr += nb; offset += nb; remaining_size -= nb; } return 0; } static int append_to_pack(git_indexer *idx, const void *data, size_t size) { if (write_at(idx, data, idx->pack->mwf.size, size) < 0) { git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name); return -1; } return 0; } #else /* * Windows may keep different views to a networked file for the mmap- and * open-accessed versions of a file, so any writes done through * `write(2)`/`pwrite(2)` may not be reflected on the data that `mmap(2)` is * able to read. */ static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size) { git_file fd = idx->pack->mwf.fd; size_t mmap_alignment; size_t page_offset; off64_t page_start; unsigned char *map_data; git_map map; int error; GIT_ASSERT_ARG(data); GIT_ASSERT_ARG(size); if ((error = git__mmap_alignment(&mmap_alignment)) < 0) return error; /* the offset needs to be at the mmap boundary for the platform */ page_offset = offset % mmap_alignment; page_start = offset - page_offset; if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0) return error; map_data = (unsigned char *)map.data; memcpy(map_data + page_offset, data, size); p_munmap(&map); return 0; } static int append_to_pack(git_indexer *idx, const void *data, size_t size) { off64_t new_size; size_t mmap_alignment; size_t page_offset; off64_t page_start; off64_t current_size = idx->pack->mwf.size; int error; if (!size) return 0; if ((error = git__mmap_alignment(&mmap_alignment)) < 0) return error; /* Write a single byte to force the file system to allocate space now or * report an error, since we can't report errors when writing using mmap. * Round the size up to the nearest page so that we only need to perform file * I/O when we add a page, instead of whenever we write even a single byte. */ new_size = current_size + size; page_offset = new_size % mmap_alignment; page_start = new_size - page_offset; if (p_pwrite(idx->pack->mwf.fd, data, 1, page_start + mmap_alignment - 1) < 0) { git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name); return -1; } return write_at(idx, data, idx->pack->mwf.size, size); } #endif static int read_stream_object(git_indexer *idx, git_indexer_progress *stats) { git_packfile_stream *stream = &idx->stream; off64_t entry_start = idx->off; size_t entry_size; git_object_t type; git_mwindow *w = NULL; int error; if (idx->pack->mwf.size <= idx->off + 20) return GIT_EBUFS; if (!idx->have_stream) { error = git_packfile_unpack_header(&entry_size, &type, idx->pack, &w, &idx->off); if (error == GIT_EBUFS) { idx->off = entry_start; return error; } if (error < 0) return error; git_mwindow_close(&w); idx->entry_start = entry_start; git_hash_init(&idx->hash_ctx); git_buf_clear(&idx->entry_data); if (type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA) { error = advance_delta_offset(idx, type); if (error == GIT_EBUFS) { idx->off = entry_start; return error; } if (error < 0) return error; idx->have_delta = 1; } else { idx->have_delta = 0; error = hash_header(&idx->hash_ctx, entry_size, type); if (error < 0) return error; } idx->have_stream = 1; idx->entry_type = type; error = git_packfile_stream_open(stream, idx->pack, idx->off); if (error < 0) return error; } if (idx->have_delta) { error = read_object_stream(idx, stream); } else { error = hash_object_stream(idx, stream); } idx->off = stream->curpos; if (error == GIT_EBUFS) return error; /* We want to free the stream reasorces no matter what here */ idx->have_stream = 0; git_packfile_stream_dispose(stream); if (error < 0) return error; if (idx->have_delta) { error = store_delta(idx); } else { error = store_object(idx); } if (error < 0) return error; if (!idx->have_delta) { stats->indexed_objects++; } stats->received_objects++; if ((error = do_progress_callback(idx, stats)) != 0) return error; return 0; } int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_indexer_progress *stats) { int error = -1; struct git_pack_header *hdr = &idx->hdr; git_mwindow_file *mwf = &idx->pack->mwf; GIT_ASSERT_ARG(idx); GIT_ASSERT_ARG(data); GIT_ASSERT_ARG(stats); if ((error = append_to_pack(idx, data, size)) < 0) return error; hash_partially(idx, data, (int)size); /* Make sure we set the new size of the pack */ idx->pack->mwf.size += size; if (!idx->parsed_header) { unsigned int total_objects; if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header)) return 0; if ((error = parse_header(&idx->hdr, idx->pack)) < 0) return error; idx->parsed_header = 1; idx->nr_objects = ntohl(hdr->hdr_entries); idx->off = sizeof(struct git_pack_header); if (idx->nr_objects <= git_indexer__max_objects) { total_objects = (unsigned int)idx->nr_objects; } else { git_error_set(GIT_ERROR_INDEXER, "too many objects"); return -1; } if (git_oidmap_new(&idx->pack->idx_cache) < 0) return -1; idx->pack->has_cache = 1; if (git_vector_init(&idx->objects, total_objects, objects_cmp) < 0) return -1; if (git_vector_init(&idx->deltas, total_objects / 2, NULL) < 0) return -1; stats->received_objects = 0; stats->local_objects = 0; stats->total_deltas = 0; stats->indexed_deltas = 0; stats->indexed_objects = 0; stats->total_objects = total_objects; if ((error = do_progress_callback(idx, stats)) != 0) return error; } /* Now that we have data in the pack, let's try to parse it */ /* As the file grows any windows we try to use will be out of date */ if ((error = git_mwindow_free_all(mwf)) < 0) goto on_error; while (stats->indexed_objects < idx->nr_objects) { if ((error = read_stream_object(idx, stats)) != 0) { if (error == GIT_EBUFS) break; else goto on_error; } } return 0; on_error: git_mwindow_free_all(mwf); return error; } static int index_path(git_buf *path, git_indexer *idx, const char *suffix) { const char prefix[] = "pack-"; size_t slash = (size_t)path->size; /* search backwards for '/' */ while (slash > 0 && path->ptr[slash - 1] != '/') slash--; if (git_buf_grow(path, slash + 1 + strlen(prefix) + GIT_OID_HEXSZ + strlen(suffix) + 1) < 0) return -1; git_buf_truncate(path, slash); git_buf_puts(path, prefix); git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash); path->size += GIT_OID_HEXSZ; git_buf_puts(path, suffix); return git_buf_oom(path) ? -1 : 0; } /** * Rewind the packfile by the trailer, as we might need to fix the * packfile by injecting objects at the tail and must overwrite it. */ static int seek_back_trailer(git_indexer *idx) { idx->pack->mwf.size -= GIT_OID_RAWSZ; return git_mwindow_free_all(&idx->pack->mwf); } static int inject_object(git_indexer *idx, git_oid *id) { git_odb_object *obj = NULL; struct entry *entry = NULL; struct git_pack_entry *pentry = NULL; git_oid foo = {{0}}; unsigned char hdr[64]; git_buf buf = GIT_BUF_INIT; off64_t entry_start; const void *data; size_t len, hdr_len; int error; if ((error = seek_back_trailer(idx)) < 0) goto cleanup; entry_start = idx->pack->mwf.size; if ((error = git_odb_read(&obj, idx->odb, id)) < 0) { git_error_set(GIT_ERROR_INDEXER, "missing delta bases"); goto cleanup; } data = git_odb_object_data(obj); len = git_odb_object_size(obj); entry = git__calloc(1, sizeof(*entry)); GIT_ERROR_CHECK_ALLOC(entry); entry->crc = crc32(0L, Z_NULL, 0); /* Write out the object header */ if ((error = git_packfile__object_header(&hdr_len, hdr, len, git_odb_object_type(obj))) < 0 || (error = append_to_pack(idx, hdr, hdr_len)) < 0) goto cleanup; idx->pack->mwf.size += hdr_len; entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len); if ((error = git_zstream_deflatebuf(&buf, data, len)) < 0) goto cleanup; /* And then the compressed object */ if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0) goto cleanup; idx->pack->mwf.size += buf.size; entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size)); git_buf_dispose(&buf); /* Write a fake trailer so the pack functions play ball */ if ((error = append_to_pack(idx, &foo, GIT_OID_RAWSZ)) < 0) goto cleanup; idx->pack->mwf.size += GIT_OID_RAWSZ; pentry = git__calloc(1, sizeof(struct git_pack_entry)); GIT_ERROR_CHECK_ALLOC(pentry); git_oid_cpy(&pentry->sha1, id); git_oid_cpy(&entry->oid, id); idx->off = entry_start + hdr_len + len; error = save_entry(idx, entry, pentry, entry_start); cleanup: if (error) { git__free(entry); git__free(pentry); } git_odb_object_free(obj); return error; } static int fix_thin_pack(git_indexer *idx, git_indexer_progress *stats) { int error, found_ref_delta = 0; unsigned int i; struct delta_info *delta; size_t size; git_object_t type; git_mwindow *w = NULL; off64_t curpos = 0; unsigned char *base_info; unsigned int left = 0; git_oid base; GIT_ASSERT(git_vector_length(&idx->deltas) > 0); if (idx->odb == NULL) { git_error_set(GIT_ERROR_INDEXER, "cannot fix a thin pack without an ODB"); return -1; } /* Loop until we find the first REF delta */ git_vector_foreach(&idx->deltas, i, delta) { if (!delta) continue; curpos = delta->delta_off; error = git_packfile_unpack_header(&size, &type, idx->pack, &w, &curpos); if (error < 0) return error; if (type == GIT_OBJECT_REF_DELTA) { found_ref_delta = 1; break; } } if (!found_ref_delta) { git_error_set(GIT_ERROR_INDEXER, "no REF_DELTA found, cannot inject object"); return -1; } /* curpos now points to the base information, which is an OID */ base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_RAWSZ, &left); if (base_info == NULL) { git_error_set(GIT_ERROR_INDEXER, "failed to map delta information"); return -1; } git_oid_fromraw(&base, base_info); git_mwindow_close(&w); if (has_entry(idx, &base)) return 0; if (inject_object(idx, &base) < 0) return -1; stats->local_objects++; return 0; } static int resolve_deltas(git_indexer *idx, git_indexer_progress *stats) { unsigned int i; int error; struct delta_info *delta; int progressed = 0, non_null = 0, progress_cb_result; while (idx->deltas.length > 0) { progressed = 0; non_null = 0; git_vector_foreach(&idx->deltas, i, delta) { git_rawobj obj = {0}; if (!delta) continue; non_null = 1; idx->off = delta->delta_off; if ((error = git_packfile_unpack(&obj, idx->pack, &idx->off)) < 0) { if (error == GIT_PASSTHROUGH) { /* We have not seen the base object, we'll try again later. */ continue; } return -1; } if (idx->do_verify && check_object_connectivity(idx, &obj) < 0) /* TODO: error? continue? */ continue; if (hash_and_save(idx, &obj, delta->delta_off) < 0) continue; git__free(obj.data); stats->indexed_objects++; stats->indexed_deltas++; progressed = 1; if ((progress_cb_result = do_progress_callback(idx, stats)) < 0) return progress_cb_result; /* remove from the list */ git_vector_set(NULL, &idx->deltas, i, NULL); git__free(delta); } /* if none were actually set, we're done */ if (!non_null) break; if (!progressed && (fix_thin_pack(idx, stats) < 0)) { return -1; } } return 0; } static int update_header_and_rehash(git_indexer *idx, git_indexer_progress *stats) { void *ptr; size_t chunk = 1024*1024; off64_t hashed = 0; git_mwindow *w = NULL; git_mwindow_file *mwf; unsigned int left; mwf = &idx->pack->mwf; git_hash_init(&idx->trailer); /* Update the header to include the numer of local objects we injected */ idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects); if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0) return -1; /* * We now use the same technique as before to determine the * hash. We keep reading up to the end and let * hash_partially() keep the existing trailer out of the * calculation. */ if (git_mwindow_free_all(mwf) < 0) return -1; idx->inbuf_len = 0; while (hashed < mwf->size) { ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left); if (ptr == NULL) return -1; hash_partially(idx, ptr, left); hashed += left; git_mwindow_close(&w); } return 0; } int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats) { git_mwindow *w = NULL; unsigned int i, long_offsets = 0, left; int error; struct git_pack_idx_header hdr; git_buf filename = GIT_BUF_INIT; struct entry *entry; git_oid trailer_hash, file_hash; git_filebuf index_file = {0}; void *packfile_trailer; if (!idx->parsed_header) { git_error_set(GIT_ERROR_INDEXER, "incomplete pack header"); return -1; } /* Test for this before resolve_deltas(), as it plays with idx->off */ if (idx->off + 20 < idx->pack->mwf.size) { git_error_set(GIT_ERROR_INDEXER, "unexpected data at the end of the pack"); return -1; } if (idx->off + 20 > idx->pack->mwf.size) { git_error_set(GIT_ERROR_INDEXER, "missing trailer at the end of the pack"); return -1; } packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left); if (packfile_trailer == NULL) { git_mwindow_close(&w); goto on_error; } /* Compare the packfile trailer as it was sent to us and what we calculated */ git_oid_fromraw(&file_hash, packfile_trailer); git_mwindow_close(&w); git_hash_final(&trailer_hash, &idx->trailer); if (git_oid_cmp(&file_hash, &trailer_hash)) { git_error_set(GIT_ERROR_INDEXER, "packfile trailer mismatch"); return -1; } /* Freeze the number of deltas */ stats->total_deltas = stats->total_objects - stats->indexed_objects; if ((error = resolve_deltas(idx, stats)) < 0) return error; if (stats->indexed_objects != stats->total_objects) { git_error_set(GIT_ERROR_INDEXER, "early EOF"); return -1; } if (stats->local_objects > 0) { if (update_header_and_rehash(idx, stats) < 0) return -1; git_hash_final(&trailer_hash, &idx->trailer); write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ); } /* * Is the resulting graph fully connected or are we still * missing some objects? In the second case, we can * bail out due to an incomplete and thus corrupt * packfile. */ if (git_oidmap_size(idx->expected_oids) > 0) { git_error_set(GIT_ERROR_INDEXER, "packfile is missing %"PRIuZ" objects", git_oidmap_size(idx->expected_oids)); return -1; } git_vector_sort(&idx->objects); /* Use the trailer hash as the pack file name to ensure * files with different contents have different names */ git_oid_cpy(&idx->hash, &trailer_hash); git_buf_sets(&filename, idx->pack->pack_name); git_buf_shorten(&filename, strlen("pack")); git_buf_puts(&filename, "idx"); if (git_buf_oom(&filename)) return -1; if (git_filebuf_open(&index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS | (idx->do_fsync ? GIT_FILEBUF_FSYNC : 0), idx->mode) < 0) goto on_error; /* Write out the header */ hdr.idx_signature = htonl(PACK_IDX_SIGNATURE); hdr.idx_version = htonl(2); git_filebuf_write(&index_file, &hdr, sizeof(hdr)); /* Write out the fanout table */ for (i = 0; i < 256; ++i) { uint32_t n = htonl(idx->fanout[i]); git_filebuf_write(&index_file, &n, sizeof(n)); } /* Write out the object names (SHA-1 hashes) */ git_vector_foreach(&idx->objects, i, entry) { git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid)); } /* Write out the CRC32 values */ git_vector_foreach(&idx->objects, i, entry) { git_filebuf_write(&index_file, &entry->crc, sizeof(uint32_t)); } /* Write out the offsets */ git_vector_foreach(&idx->objects, i, entry) { uint32_t n; if (entry->offset == UINT32_MAX) n = htonl(0x80000000 | long_offsets++); else n = htonl(entry->offset); git_filebuf_write(&index_file, &n, sizeof(uint32_t)); } /* Write out the long offsets */ git_vector_foreach(&idx->objects, i, entry) { uint32_t split[2]; if (entry->offset != UINT32_MAX) continue; split[0] = htonl(entry->offset_long >> 32); split[1] = htonl(entry->offset_long & 0xffffffff); git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2); } /* Write out the packfile trailer to the index */ if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0) goto on_error; /* Write out the hash of the idx */ if (git_filebuf_hash(&trailer_hash, &index_file) < 0) goto on_error; git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid)); /* Figure out what the final name should be */ if (index_path(&filename, idx, ".idx") < 0) goto on_error; /* Commit file */ if (git_filebuf_commit_at(&index_file, filename.ptr) < 0) goto on_error; if (git_mwindow_free_all(&idx->pack->mwf) < 0) goto on_error; #if !defined(NO_MMAP) && defined(GIT_WIN32) /* * Some non-Windows remote filesystems fail when truncating files if the * file permissions change after opening the file (done by p_mkstemp). * * Truncation is only needed when mmap is used to undo rounding up to next * page_size in append_to_pack. */ if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) { git_error_set(GIT_ERROR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name); return -1; } #endif if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) { git_error_set(GIT_ERROR_OS, "failed to fsync packfile"); goto on_error; } /* We need to close the descriptor here so Windows doesn't choke on commit_at */ if (p_close(idx->pack->mwf.fd) < 0) { git_error_set(GIT_ERROR_OS, "failed to close packfile"); goto on_error; } idx->pack->mwf.fd = -1; if (index_path(&filename, idx, ".pack") < 0) goto on_error; /* And don't forget to rename the packfile to its new place. */ if (p_rename(idx->pack->pack_name, git_buf_cstr(&filename)) < 0) goto on_error; /* And fsync the parent directory if we're asked to. */ if (idx->do_fsync && git_futils_fsync_parent(git_buf_cstr(&filename)) < 0) goto on_error; idx->pack_committed = 1; git_buf_dispose(&filename); return 0; on_error: git_mwindow_free_all(&idx->pack->mwf); git_filebuf_cleanup(&index_file); git_buf_dispose(&filename); return -1; } void git_indexer_free(git_indexer *idx) { const git_oid *key; git_oid *value; size_t iter; if (idx == NULL) return; if (idx->have_stream) git_packfile_stream_dispose(&idx->stream); git_vector_free_deep(&idx->objects); if (idx->pack->idx_cache) { struct git_pack_entry *pentry; git_oidmap_foreach_value(idx->pack->idx_cache, pentry, { git__free(pentry); }); git_oidmap_free(idx->pack->idx_cache); } git_vector_free_deep(&idx->deltas); git_packfile_free(idx->pack, !idx->pack_committed); iter = 0; while (git_oidmap_iterate((void **) &value, idx->expected_oids, &iter, &key) == 0) git__free(value); git_hash_ctx_cleanup(&idx->trailer); git_hash_ctx_cleanup(&idx->hash_ctx); git_buf_dispose(&idx->entry_data); git_oidmap_free(idx->expected_oids); git__free(idx); } git2r/src/libgit2/src/graph.c0000644000175000017500000001275314125111754015623 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "revwalk.h" #include "merge.h" #include "git2/graph.h" static int interesting(git_pqueue *list, git_commit_list *roots) { unsigned int i; for (i = 0; i < git_pqueue_size(list); i++) { git_commit_list_node *commit = git_pqueue_get(list, i); if ((commit->flags & STALE) == 0) return 1; } while(roots) { if ((roots->item->flags & STALE) == 0) return 1; roots = roots->next; } return 0; } static int mark_parents(git_revwalk *walk, git_commit_list_node *one, git_commit_list_node *two) { unsigned int i; git_commit_list *roots = NULL; git_pqueue list; /* if the commit is repeated, we have a our merge base already */ if (one == two) { one->flags |= PARENT1 | PARENT2 | RESULT; return 0; } if (git_pqueue_init(&list, 0, 2, git_commit_list_generation_cmp) < 0) return -1; if (git_commit_list_parse(walk, one) < 0) goto on_error; one->flags |= PARENT1; if (git_pqueue_insert(&list, one) < 0) goto on_error; if (git_commit_list_parse(walk, two) < 0) goto on_error; two->flags |= PARENT2; if (git_pqueue_insert(&list, two) < 0) goto on_error; /* as long as there are non-STALE commits */ while (interesting(&list, roots)) { git_commit_list_node *commit = git_pqueue_pop(&list); unsigned int flags; if (commit == NULL) break; flags = commit->flags & (PARENT1 | PARENT2 | STALE); if (flags == (PARENT1 | PARENT2)) { if (!(commit->flags & RESULT)) commit->flags |= RESULT; /* we mark the parents of a merge stale */ flags |= STALE; } for (i = 0; i < commit->out_degree; i++) { git_commit_list_node *p = commit->parents[i]; if ((p->flags & flags) == flags) continue; if (git_commit_list_parse(walk, p) < 0) goto on_error; p->flags |= flags; if (git_pqueue_insert(&list, p) < 0) goto on_error; } /* Keep track of root commits, to make sure the path gets marked */ if (commit->out_degree == 0) { if (git_commit_list_insert(commit, &roots) == NULL) goto on_error; } } git_commit_list_free(&roots); git_pqueue_free(&list); return 0; on_error: git_commit_list_free(&roots); git_pqueue_free(&list); return -1; } static int ahead_behind(git_commit_list_node *one, git_commit_list_node *two, size_t *ahead, size_t *behind) { git_commit_list_node *commit; git_pqueue pq; int error = 0, i; *ahead = 0; *behind = 0; if (git_pqueue_init(&pq, 0, 2, git_commit_list_time_cmp) < 0) return -1; if ((error = git_pqueue_insert(&pq, one)) < 0 || (error = git_pqueue_insert(&pq, two)) < 0) goto done; while ((commit = git_pqueue_pop(&pq)) != NULL) { if (commit->flags & RESULT || (commit->flags & (PARENT1 | PARENT2)) == (PARENT1 | PARENT2)) continue; else if (commit->flags & PARENT1) (*ahead)++; else if (commit->flags & PARENT2) (*behind)++; for (i = 0; i < commit->out_degree; i++) { git_commit_list_node *p = commit->parents[i]; if ((error = git_pqueue_insert(&pq, p)) < 0) goto done; } commit->flags |= RESULT; } done: git_pqueue_free(&pq); return error; } int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream) { git_revwalk *walk; git_commit_list_node *commit_u, *commit_l; if (git_revwalk_new(&walk, repo) < 0) return -1; commit_u = git_revwalk__commit_lookup(walk, upstream); if (commit_u == NULL) goto on_error; commit_l = git_revwalk__commit_lookup(walk, local); if (commit_l == NULL) goto on_error; if (mark_parents(walk, commit_l, commit_u) < 0) goto on_error; if (ahead_behind(commit_l, commit_u, ahead, behind) < 0) goto on_error; git_revwalk_free(walk); return 0; on_error: git_revwalk_free(walk); return -1; } int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor) { if (git_oid_equal(commit, ancestor)) return 0; return git_graph_reachable_from_any(repo, ancestor, commit, 1); } int git_graph_reachable_from_any( git_repository *repo, const git_oid *commit_id, const git_oid descendant_array[], size_t length) { git_revwalk *walk = NULL; git_vector list; git_commit_list *result = NULL; git_commit_list_node *commit; size_t i; uint32_t minimum_generation = 0xffffffff; int error = 0; if (!length) return 0; for (i = 0; i < length; ++i) { if (git_oid_equal(commit_id, &descendant_array[i])) return 1; } if ((error = git_vector_init(&list, length + 1, NULL)) < 0) return error; if ((error = git_revwalk_new(&walk, repo)) < 0) goto done; for (i = 0; i < length; i++) { commit = git_revwalk__commit_lookup(walk, &descendant_array[i]); if (commit == NULL) { error = -1; goto done; } git_vector_insert(&list, commit); if (minimum_generation > commit->generation) minimum_generation = commit->generation; } commit = git_revwalk__commit_lookup(walk, commit_id); if (commit == NULL) { error = -1; goto done; } if (minimum_generation > commit->generation) minimum_generation = commit->generation; if ((error = git_merge__bases_many(&result, walk, commit, &list, minimum_generation)) < 0) goto done; if (result) { error = git_oid_equal(commit_id, &result->item->oid); } else { /* No merge-base found, it's not a descendant */ error = 0; } done: git_commit_list_free(&result); git_vector_free(&list); git_revwalk_free(walk); return error; } git2r/src/libgit2/src/annotated_commit.c0000644000175000017500000001206014125111754020036 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "annotated_commit.h" #include "refs.h" #include "cache.h" #include "git2/commit.h" #include "git2/refs.h" #include "git2/repository.h" #include "git2/annotated_commit.h" #include "git2/revparse.h" #include "git2/tree.h" #include "git2/index.h" static int annotated_commit_init( git_annotated_commit **out, git_commit *commit, const char *description) { git_annotated_commit *annotated_commit; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(commit); *out = NULL; annotated_commit = git__calloc(1, sizeof(git_annotated_commit)); GIT_ERROR_CHECK_ALLOC(annotated_commit); annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL; if ((error = git_commit_dup(&annotated_commit->commit, commit)) < 0) goto done; git_oid_fmt(annotated_commit->id_str, git_commit_id(commit)); annotated_commit->id_str[GIT_OID_HEXSZ] = '\0'; if (!description) description = annotated_commit->id_str; annotated_commit->description = git__strdup(description); GIT_ERROR_CHECK_ALLOC(annotated_commit->description); done: if (!error) *out = annotated_commit; return error; } static int annotated_commit_init_from_id( git_annotated_commit **out, git_repository *repo, const git_oid *id, const char *description) { git_commit *commit = NULL; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(id); *out = NULL; if ((error = git_commit_lookup(&commit, repo, id)) < 0) goto done; error = annotated_commit_init(out, commit, description); done: git_commit_free(commit); return error; } int git_annotated_commit_lookup( git_annotated_commit **out, git_repository *repo, const git_oid *id) { return annotated_commit_init_from_id(out, repo, id, NULL); } int git_annotated_commit_from_commit( git_annotated_commit **out, git_commit *commit) { return annotated_commit_init(out, commit, NULL); } int git_annotated_commit_from_revspec( git_annotated_commit **out, git_repository *repo, const char *revspec) { git_object *obj, *commit; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(revspec); if ((error = git_revparse_single(&obj, repo, revspec)) < 0) return error; if ((error = git_object_peel(&commit, obj, GIT_OBJECT_COMMIT))) { git_object_free(obj); return error; } error = annotated_commit_init(out, (git_commit *)commit, revspec); git_object_free(obj); git_object_free(commit); return error; } int git_annotated_commit_from_ref( git_annotated_commit **out, git_repository *repo, const git_reference *ref) { git_object *peeled; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(ref); *out = NULL; if ((error = git_reference_peel(&peeled, ref, GIT_OBJECT_COMMIT)) < 0) return error; error = annotated_commit_init_from_id(out, repo, git_object_id(peeled), git_reference_name(ref)); if (!error) { (*out)->ref_name = git__strdup(git_reference_name(ref)); GIT_ERROR_CHECK_ALLOC((*out)->ref_name); } git_object_free(peeled); return error; } int git_annotated_commit_from_head( git_annotated_commit **out, git_repository *repo) { git_reference *head; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0) return -1; error = git_annotated_commit_from_ref(out, repo, head); git_reference_free(head); return error; } int git_annotated_commit_from_fetchhead( git_annotated_commit **out, git_repository *repo, const char *branch_name, const char *remote_url, const git_oid *id) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(branch_name); GIT_ASSERT_ARG(remote_url); GIT_ASSERT_ARG(id); if (annotated_commit_init_from_id(out, repo, id, branch_name) < 0) return -1; (*out)->ref_name = git__strdup(branch_name); GIT_ERROR_CHECK_ALLOC((*out)->ref_name); (*out)->remote_url = git__strdup(remote_url); GIT_ERROR_CHECK_ALLOC((*out)->remote_url); return 0; } const git_oid *git_annotated_commit_id( const git_annotated_commit *annotated_commit) { GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL); return git_commit_id(annotated_commit->commit); } const char *git_annotated_commit_ref( const git_annotated_commit *annotated_commit) { GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL); return annotated_commit->ref_name; } void git_annotated_commit_free(git_annotated_commit *annotated_commit) { if (annotated_commit == NULL) return; switch (annotated_commit->type) { case GIT_ANNOTATED_COMMIT_REAL: git_commit_free(annotated_commit->commit); git_tree_free(annotated_commit->tree); git__free((char *)annotated_commit->description); git__free((char *)annotated_commit->ref_name); git__free((char *)annotated_commit->remote_url); break; case GIT_ANNOTATED_COMMIT_VIRTUAL: git_index_free(annotated_commit->index); git_array_clear(annotated_commit->parents); break; default: abort(); } git__free(annotated_commit); } git2r/src/libgit2/src/mwindow.h0000644000175000017500000000273614125111754016213 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_mwindow__ #define INCLUDE_mwindow__ #include "common.h" #include "map.h" #include "vector.h" typedef struct git_mwindow { struct git_mwindow *next; git_map window_map; off64_t offset; size_t last_used; size_t inuse_cnt; } git_mwindow; typedef struct git_mwindow_file { git_mutex lock; /* protects updates to fd */ git_mwindow *windows; int fd; off64_t size; } git_mwindow_file; typedef struct git_mwindow_ctl { size_t mapped; unsigned int open_windows; unsigned int mmap_calls; unsigned int peak_open_windows; size_t peak_mapped; size_t used_ctr; git_vector windowfiles; } git_mwindow_ctl; int git_mwindow_contains(git_mwindow *win, off64_t offset); int git_mwindow_free_all(git_mwindow_file *mwf); /* locks */ unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, off64_t offset, size_t extra, unsigned int *left); int git_mwindow_file_register(git_mwindow_file *mwf); void git_mwindow_file_deregister(git_mwindow_file *mwf); void git_mwindow_close(git_mwindow **w_cursor); extern int git_mwindow_global_init(void); struct git_pack_file; /* just declaration to avoid cyclical includes */ int git_mwindow_get_pack(struct git_pack_file **out, const char *path); int git_mwindow_put_pack(struct git_pack_file *pack); #endif git2r/src/libgit2/src/xdiff/0000755000175000017500000000000014145550337015454 5ustar nileshnileshgit2r/src/libgit2/src/xdiff/xmacros.h0000644000175000017500000000355514125111754017303 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XMACROS_H) #define XMACROS_H #define XDL_MIN(a, b) ((a) < (b) ? (a): (b)) #define XDL_MAX(a, b) ((a) > (b) ? (a): (b)) #define XDL_ABS(v) ((v) >= 0 ? (v): -(v)) #define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9') #define XDL_ISSPACE(c) (isspace((unsigned char)(c))) #define XDL_ADDBITS(v,b) ((v) + ((v) >> (b))) #define XDL_MASKBITS(b) ((1UL << (b)) - 1) #define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b)) #define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0) #define XDL_LE32_PUT(p, v) \ do { \ unsigned char *__p = (unsigned char *) (p); \ *__p++ = (unsigned char) (v); \ *__p++ = (unsigned char) ((v) >> 8); \ *__p++ = (unsigned char) ((v) >> 16); \ *__p = (unsigned char) ((v) >> 24); \ } while (0) #define XDL_LE32_GET(p, v) \ do { \ unsigned char const *__p = (unsigned char const *) (p); \ (v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \ ((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \ } while (0) #endif /* #if !defined(XMACROS_H) */ git2r/src/libgit2/src/xdiff/xprepare.h0000644000175000017500000000204214125111754017443 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XPREPARE_H) #define XPREPARE_H int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *xe); void xdl_free_env(xdfenv_t *xe); #endif /* #if !defined(XPREPARE_H) */ git2r/src/libgit2/src/xdiff/xpatience.c0000644000175000017500000002516014125111754017576 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003-2016 Davide Libenzi, Johannes E. Schindelin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #include "xinclude.h" #include "xtypes.h" #include "xdiff.h" /* * The basic idea of patience diff is to find lines that are unique in * both files. These are intuitively the ones that we want to see as * common lines. * * The maximal ordered sequence of such line pairs (where ordered means * that the order in the sequence agrees with the order of the lines in * both files) naturally defines an initial set of common lines. * * Now, the algorithm tries to extend the set of common lines by growing * the line ranges where the files have identical lines. * * Between those common lines, the patience diff algorithm is applied * recursively, until no unique line pairs can be found; these line ranges * are handled by the well-known Myers algorithm. */ #define NON_UNIQUE ULONG_MAX /* * This is a hash mapping from line hash to line numbers in the first and * second file. */ struct hashmap { int nr, alloc; struct entry { unsigned long hash; /* * 0 = unused entry, 1 = first line, 2 = second, etc. * line2 is NON_UNIQUE if the line is not unique * in either the first or the second file. */ unsigned long line1, line2; /* * "next" & "previous" are used for the longest common * sequence; * initially, "next" reflects only the order in file1. */ struct entry *next, *previous; /* * If 1, this entry can serve as an anchor. See * Documentation/diff-options.txt for more information. */ unsigned anchor : 1; } *entries, *first, *last; /* were common records found? */ unsigned long has_matches; mmfile_t *file1, *file2; xdfenv_t *env; xpparam_t const *xpp; }; static int is_anchor(xpparam_t const *xpp, const char *line) { unsigned long i; for (i = 0; i < xpp->anchors_nr; i++) { if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i]))) return 1; } return 0; } /* The argument "pass" is 1 for the first file, 2 for the second. */ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map, int pass) { xrecord_t **records = pass == 1 ? map->env->xdf1.recs : map->env->xdf2.recs; xrecord_t *record = records[line - 1], *other; /* * After xdl_prepare_env() (or more precisely, due to * xdl_classify_record()), the "ha" member of the records (AKA lines) * is _not_ the hash anymore, but a linearized version of it. In * other words, the "ha" member is guaranteed to start with 0 and * the second record's ha can only be 0 or 1, etc. * * So we multiply ha by 2 in the hope that the hashing was * "unique enough". */ int index = (int)((record->ha << 1) % map->alloc); while (map->entries[index].line1) { other = map->env->xdf1.recs[map->entries[index].line1 - 1]; if (map->entries[index].hash != record->ha || !xdl_recmatch(record->ptr, record->size, other->ptr, other->size, map->xpp->flags)) { if (++index >= map->alloc) index = 0; continue; } if (pass == 2) map->has_matches = 1; if (pass == 1 || map->entries[index].line2) map->entries[index].line2 = NON_UNIQUE; else map->entries[index].line2 = line; return; } if (pass == 2) return; map->entries[index].line1 = line; map->entries[index].hash = record->ha; map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr); if (!map->first) map->first = map->entries + index; if (map->last) { map->last->next = map->entries + index; map->entries[index].previous = map->last; } map->last = map->entries + index; map->nr++; } /* * This function has to be called for each recursion into the inter-hunk * parts, as previously non-unique lines can become unique when being * restricted to a smaller part of the files. * * It is assumed that env has been prepared using xdl_prepare(). */ static int fill_hashmap(mmfile_t *file1, mmfile_t *file2, xpparam_t const *xpp, xdfenv_t *env, struct hashmap *result, int line1, int count1, int line2, int count2) { result->file1 = file1; result->file2 = file2; result->xpp = xpp; result->env = env; /* We know exactly how large we want the hash map */ result->alloc = count1 * 2; result->entries = (struct entry *) xdl_malloc(result->alloc * sizeof(struct entry)); if (!result->entries) return -1; memset(result->entries, 0, result->alloc * sizeof(struct entry)); /* First, fill with entries from the first file */ while (count1--) insert_record(xpp, line1++, result, 1); /* Then search for matches in the second file */ while (count2--) insert_record(xpp, line2++, result, 2); return 0; } /* * Find the longest sequence with a smaller last element (meaning a smaller * line2, as we construct the sequence with entries ordered by line1). */ static int binary_search(struct entry **sequence, int longest, struct entry *entry) { int left = -1, right = longest; while (left + 1 < right) { int middle = left + (right - left) / 2; /* by construction, no two entries can be equal */ if (sequence[middle]->line2 > entry->line2) right = middle; else left = middle; } /* return the index in "sequence", _not_ the sequence length */ return left; } /* * The idea is to start with the list of common unique lines sorted by * the order in file1. For each of these pairs, the longest (partial) * sequence whose last element's line2 is smaller is determined. * * For efficiency, the sequences are kept in a list containing exactly one * item per sequence length: the sequence with the smallest last * element (in terms of line2). */ static struct entry *find_longest_common_sequence(struct hashmap *map) { struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *)); int longest = 0, i; struct entry *entry; /* * If not -1, this entry in sequence must never be overridden. * Therefore, overriding entries before this has no effect, so * do not do that either. */ int anchor_i = -1; if (!sequence) return NULL; for (entry = map->first; entry; entry = entry->next) { if (!entry->line2 || entry->line2 == NON_UNIQUE) continue; i = binary_search(sequence, longest, entry); entry->previous = i < 0 ? NULL : sequence[i]; ++i; if (i <= anchor_i) continue; sequence[i] = entry; if (entry->anchor) { anchor_i = i; longest = anchor_i + 1; } else if (i == longest) { longest++; } } /* No common unique lines were found */ if (!longest) { xdl_free(sequence); return NULL; } /* Iterate starting at the last element, adjusting the "next" members */ entry = sequence[longest - 1]; entry->next = NULL; while (entry->previous) { entry->previous->next = entry; entry = entry->previous; } xdl_free(sequence); return entry; } static int match(struct hashmap *map, int line1, int line2) { xrecord_t *record1 = map->env->xdf1.recs[line1 - 1]; xrecord_t *record2 = map->env->xdf2.recs[line2 - 1]; return xdl_recmatch(record1->ptr, record1->size, record2->ptr, record2->size, map->xpp->flags); } static int patience_diff(mmfile_t *file1, mmfile_t *file2, xpparam_t const *xpp, xdfenv_t *env, int line1, int count1, int line2, int count2); static int walk_common_sequence(struct hashmap *map, struct entry *first, int line1, int count1, int line2, int count2) { int end1 = line1 + count1, end2 = line2 + count2; int next1, next2; for (;;) { /* Try to grow the line ranges of common lines */ if (first) { next1 = first->line1; next2 = first->line2; while (next1 > line1 && next2 > line2 && match(map, next1 - 1, next2 - 1)) { next1--; next2--; } } else { next1 = end1; next2 = end2; } while (line1 < next1 && line2 < next2 && match(map, line1, line2)) { line1++; line2++; } /* Recurse */ if (next1 > line1 || next2 > line2) { struct hashmap submap; memset(&submap, 0, sizeof(submap)); if (patience_diff(map->file1, map->file2, map->xpp, map->env, line1, next1 - line1, line2, next2 - line2)) return -1; } if (!first) return 0; while (first->next && first->next->line1 == first->line1 + 1 && first->next->line2 == first->line2 + 1) first = first->next; line1 = first->line1 + 1; line2 = first->line2 + 1; first = first->next; } } static int fall_back_to_classic_diff(struct hashmap *map, int line1, int count1, int line2, int count2) { xpparam_t xpp; xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; return xdl_fall_back_diff(map->env, &xpp, line1, count1, line2, count2); } /* * Recursively find the longest common sequence of unique lines, * and if none was found, ask xdl_do_diff() to do the job. * * This function assumes that env was prepared with xdl_prepare_env(). */ static int patience_diff(mmfile_t *file1, mmfile_t *file2, xpparam_t const *xpp, xdfenv_t *env, int line1, int count1, int line2, int count2) { struct hashmap map; struct entry *first; int result = 0; /* trivial case: one side is empty */ if (!count1) { while(count2--) env->xdf2.rchg[line2++ - 1] = 1; return 0; } else if (!count2) { while(count1--) env->xdf1.rchg[line1++ - 1] = 1; return 0; } memset(&map, 0, sizeof(map)); if (fill_hashmap(file1, file2, xpp, env, &map, line1, count1, line2, count2)) return -1; /* are there any matching lines at all? */ if (!map.has_matches) { while(count1--) env->xdf1.rchg[line1++ - 1] = 1; while(count2--) env->xdf2.rchg[line2++ - 1] = 1; xdl_free(map.entries); return 0; } first = find_longest_common_sequence(&map); if (first) result = walk_common_sequence(&map, first, line1, count1, line2, count2); else result = fall_back_to_classic_diff(&map, line1, count1, line2, count2); xdl_free(map.entries); return result; } int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2, xpparam_t const *xpp, xdfenv_t *env) { if (xdl_prepare_env(file1, file2, xpp, env) < 0) return -1; /* environment is cleaned up in xdl_diff() */ return patience_diff(file1, file2, xpp, env, 1, env->xdf1.nrec, 1, env->xdf2.nrec); } git2r/src/libgit2/src/xdiff/xdiffi.h0000644000175000017500000000362014125111754017071 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XDIFFI_H) #define XDIFFI_H typedef struct s_diffdata { long nrec; unsigned long const *ha; long *rindex; char *rchg; } diffdata_t; typedef struct s_xdalgoenv { long mxcost; long snake_cnt; long heur_min; } xdalgoenv_t; typedef struct s_xdchange { struct s_xdchange *next; long i1, i2; long chg1, chg2; int ignore; } xdchange_t; int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, diffdata_t *dd2, long off2, long lim2, long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv); int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *xe); int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags); int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr); void xdl_free_script(xdchange_t *xscr); int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg); int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *env); int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *env); #endif /* #if !defined(XDIFFI_H) */ git2r/src/libgit2/src/xdiff/xemit.c0000644000175000017500000001706614125111754016752 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #include "xinclude.h" static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) { *rec = xdf->recs[ri]->ptr; return xdf->recs[ri]->size; } static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) { long size, psize = (long)strlen(pre); char const *rec; size = xdl_get_rec(xdf, ri, &rec); if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) { return -1; } return 0; } /* * Starting at the passed change atom, find the latest change atom to be included * inside the differential hunk according to the specified configuration. * Also advance xscr if the first changes must be discarded. */ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) { xdchange_t *xch, *xchp, *lxch; long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen; long max_ignorable = xecfg->ctxlen; unsigned long ignored = 0; /* number of ignored blank lines */ /* remove ignorable changes that are too far before other changes */ for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) { xch = xchp->next; if (xch == NULL || xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable) *xscr = xch; } if (*xscr == NULL) return NULL; lxch = *xscr; for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) { long distance = xch->i1 - (xchp->i1 + xchp->chg1); if (distance > max_common) break; if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) { lxch = xch; ignored = 0; } else if (distance < max_ignorable && xch->ignore) { ignored += xch->chg2; } else if (lxch != xchp && xch->i1 + ignored - (lxch->i1 + lxch->chg1) > (unsigned long)max_common) { break; } else if (!xch->ignore) { lxch = xch; ignored = 0; } else { ignored += xch->chg2; } } return lxch; } static long def_ff(const char *rec, long len, char *buf, long sz, void *priv) { (void)priv; if (len > 0 && (isalpha((unsigned char)*rec) || /* identifier? */ *rec == '_' || /* also identifier? */ *rec == '$')) { /* identifiers from VMS and other esoterico */ if (len > sz) len = sz; while (0 < len && isspace((unsigned char)rec[len - 1])) len--; memcpy(buf, rec, len); return len; } return -1; } static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri, char *buf, long sz) { const char *rec; long len = xdl_get_rec(xdf, ri, &rec); if (!xecfg->find_func) return def_ff(rec, len, buf, sz, xecfg->find_func_priv); return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv); } static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri) { char dummy[1]; return match_func_rec(xdf, xecfg, ri, dummy, sizeof(dummy)) >= 0; } struct func_line { long len; char buf[80]; }; static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg, struct func_line *func_line, long start, long limit) { long l, size, step = (start > limit) ? -1 : 1; char *buf, dummy[1]; buf = func_line ? func_line->buf : dummy; size = func_line ? sizeof(func_line->buf) : sizeof(dummy); for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) { long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size); if (len >= 0) { if (func_line) func_line->len = len; return l; } } return -1; } static int is_empty_rec(xdfile_t *xdf, long ri) { const char *rec; long len = xdl_get_rec(xdf, ri, &rec); while (len > 0 && XDL_ISSPACE(*rec)) { rec++; len--; } return !len; } int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg) { long s1, s2, e1, e2, lctx; xdchange_t *xch, *xche; long funclineprev = -1; struct func_line func_line = { 0 }; for (xch = xscr; xch; xch = xche->next) { xche = xdl_get_hunk(&xch, xecfg); if (!xch) break; s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { long fs1, i1 = xch->i1; /* Appended chunk? */ if (i1 >= xe->xdf1.nrec) { long i2 = xch->i2; /* * We don't need additional context if * a whole function was added. */ while (i2 < xe->xdf2.nrec) { if (is_func_rec(&xe->xdf2, xecfg, i2)) goto post_context_calculation; i2++; } /* * Otherwise get more context from the * pre-image. */ i1 = xe->xdf1.nrec - 1; } fs1 = get_func_line(xe, xecfg, NULL, i1, -1); while (fs1 > 0 && !is_empty_rec(&xe->xdf1, fs1 - 1) && !is_func_rec(&xe->xdf1, xecfg, fs1 - 1)) fs1--; if (fs1 < 0) fs1 = 0; if (fs1 < s1) { s2 -= s1 - fs1; s1 = fs1; } } post_context_calculation: lctx = xecfg->ctxlen; lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1)); lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2)); e1 = xche->i1 + xche->chg1 + lctx; e2 = xche->i2 + xche->chg2 + lctx; if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { long fe1 = get_func_line(xe, xecfg, NULL, xche->i1 + xche->chg1, xe->xdf1.nrec); while (fe1 > 0 && is_empty_rec(&xe->xdf1, fe1 - 1)) fe1--; if (fe1 < 0) fe1 = xe->xdf1.nrec; if (fe1 > e1) { e2 += fe1 - e1; e1 = fe1; } /* * Overlap with next change? Then include it * in the current hunk and start over to find * its new end. */ if (xche->next) { long l = XDL_MIN(xche->next->i1, xe->xdf1.nrec - 1); if (l - xecfg->ctxlen <= e1 || get_func_line(xe, xecfg, NULL, l, e1) < 0) { xche = xche->next; goto post_context_calculation; } } } /* * Emit current hunk header. */ if (xecfg->flags & XDL_EMIT_FUNCNAMES) { get_func_line(xe, xecfg, &func_line, s1 - 1, funclineprev); funclineprev = s1 - 1; } if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, func_line.buf, func_line.len, ecb) < 0) return -1; /* * Emit pre-context. */ for (; s2 < xch->i2; s2++) if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) return -1; for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) { /* * Merge previous with current change atom. */ for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++) if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) return -1; /* * Removes lines from the first file. */ for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++) if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0) return -1; /* * Adds lines from the second file. */ for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++) if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0) return -1; if (xch == xche) break; s1 = xch->i1 + xch->chg1; s2 = xch->i2 + xch->chg2; } /* * Emit post-context. */ for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++) if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) return -1; } return 0; } git2r/src/libgit2/src/xdiff/xprepare.c0000644000175000017500000002772214125111754017452 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #include "xinclude.h" #define XDL_KPDIS_RUN 4 #define XDL_MAX_EQLIMIT 1024 #define XDL_SIMSCAN_WINDOW 100 #define XDL_GUESS_NLINES1 256 #define XDL_GUESS_NLINES2 20 typedef struct s_xdlclass { struct s_xdlclass *next; unsigned long ha; char const *line; long size; long idx; long len1, len2; } xdlclass_t; typedef struct s_xdlclassifier { unsigned int hbits; long hsize; xdlclass_t **rchash; chastore_t ncha; xdlclass_t **rcrecs; long alloc; long count; long flags; } xdlclassifier_t; static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags); static void xdl_free_classifier(xdlclassifier_t *cf); static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash, unsigned int hbits, xrecord_t *rec); static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp, xdlclassifier_t *cf, xdfile_t *xdf); static void xdl_free_ctx(xdfile_t *xdf); static int xdl_clean_mmatch(char const *dis, long i, long s, long e); static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2); static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2); static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2); static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) { cf->flags = flags; cf->hbits = xdl_hashbits((unsigned int) size); cf->hsize = 1 << cf->hbits; if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) { return -1; } if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) { xdl_cha_free(&cf->ncha); return -1; } memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *)); cf->alloc = size; if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) { xdl_free(cf->rchash); xdl_cha_free(&cf->ncha); return -1; } cf->count = 0; return 0; } static void xdl_free_classifier(xdlclassifier_t *cf) { xdl_free(cf->rcrecs); xdl_free(cf->rchash); xdl_cha_free(&cf->ncha); } static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash, unsigned int hbits, xrecord_t *rec) { long hi; char const *line; xdlclass_t *rcrec; xdlclass_t **rcrecs; line = rec->ptr; hi = (long) XDL_HASHLONG(rec->ha, cf->hbits); for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next) if (rcrec->ha == rec->ha && xdl_recmatch(rcrec->line, rcrec->size, rec->ptr, rec->size, cf->flags)) break; if (!rcrec) { if (!(rcrec = xdl_cha_alloc(&cf->ncha))) { return -1; } rcrec->idx = cf->count++; if (cf->count > cf->alloc) { cf->alloc *= 2; if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) { return -1; } cf->rcrecs = rcrecs; } cf->rcrecs[rcrec->idx] = rcrec; rcrec->line = line; rcrec->size = rec->size; rcrec->ha = rec->ha; rcrec->len1 = rcrec->len2 = 0; rcrec->next = cf->rchash[hi]; cf->rchash[hi] = rcrec; } (pass == 1) ? rcrec->len1++ : rcrec->len2++; rec->ha = (unsigned long) rcrec->idx; hi = (long) XDL_HASHLONG(rec->ha, hbits); rec->next = rhash[hi]; rhash[hi] = rec; return 0; } static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp, xdlclassifier_t *cf, xdfile_t *xdf) { unsigned int hbits; long nrec, hsize, bsize; unsigned long hav; char const *blk, *cur, *top, *prev; xrecord_t *crec; xrecord_t **recs, **rrecs; xrecord_t **rhash; unsigned long *ha; char *rchg; long *rindex; ha = NULL; rindex = NULL; rchg = NULL; rhash = NULL; recs = NULL; if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0) goto abort; if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) goto abort; if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) hbits = hsize = 0; else { hbits = xdl_hashbits((unsigned int) narec); hsize = 1 << hbits; if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) goto abort; memset(rhash, 0, hsize * sizeof(xrecord_t *)); } nrec = 0; if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) { for (top = blk + bsize; cur < top; ) { prev = cur; hav = xdl_hash_record(&cur, top, xpp->flags); if (nrec >= narec) { narec *= 2; if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) goto abort; recs = rrecs; } if (!(crec = xdl_cha_alloc(&xdf->rcha))) goto abort; crec->ptr = prev; crec->size = (long) (cur - prev); crec->ha = hav; recs[nrec++] = crec; if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) goto abort; } } if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char)))) goto abort; memset(rchg, 0, (nrec + 2) * sizeof(char)); if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) goto abort; if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) goto abort; xdf->nrec = nrec; xdf->recs = recs; xdf->hbits = hbits; xdf->rhash = rhash; xdf->rchg = rchg + 1; xdf->rindex = rindex; xdf->nreff = 0; xdf->ha = ha; xdf->dstart = 0; xdf->dend = nrec - 1; return 0; abort: xdl_free(ha); xdl_free(rindex); xdl_free(rchg); xdl_free(rhash); xdl_free(recs); xdl_cha_free(&xdf->rcha); return -1; } static void xdl_free_ctx(xdfile_t *xdf) { xdl_free(xdf->rhash); xdl_free(xdf->rindex); xdl_free(xdf->rchg - 1); xdl_free(xdf->ha); xdl_free(xdf->recs); xdl_cha_free(&xdf->rcha); } int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *xe) { long enl1, enl2, sample; xdlclassifier_t cf; memset(&cf, 0, sizeof(cf)); /* * For histogram diff, we can afford a smaller sample size and * thus a poorer estimate of the number of lines, as the hash * table (rhash) won't be filled up/grown. The number of lines * (nrecs) will be updated correctly anyway by * xdl_prepare_ctx(). */ sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1); enl1 = xdl_guess_lines(mf1, sample) + 1; enl2 = xdl_guess_lines(mf2, sample) + 1; if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF && xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) return -1; if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) { xdl_free_classifier(&cf); return -1; } if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) { xdl_free_ctx(&xe->xdf1); xdl_free_classifier(&cf); return -1; } if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) && (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) { xdl_free_ctx(&xe->xdf2); xdl_free_ctx(&xe->xdf1); xdl_free_classifier(&cf); return -1; } if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) xdl_free_classifier(&cf); return 0; } void xdl_free_env(xdfenv_t *xe) { xdl_free_ctx(&xe->xdf2); xdl_free_ctx(&xe->xdf1); } static int xdl_clean_mmatch(char const *dis, long i, long s, long e) { long r, rdis0, rpdis0, rdis1, rpdis1; /* * Limits the window the is examined during the similar-lines * scan. The loops below stops when dis[i - r] == 1 (line that * has no match), but there are corner cases where the loop * proceed all the way to the extremities by causing huge * performance penalties in case of big files. */ if (i - s > XDL_SIMSCAN_WINDOW) s = i - XDL_SIMSCAN_WINDOW; if (e - i > XDL_SIMSCAN_WINDOW) e = i + XDL_SIMSCAN_WINDOW; /* * Scans the lines before 'i' to find a run of lines that either * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1). * Note that we always call this function with dis[i] > 1, so the * current line (i) is already a multimatch line. */ for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) { if (!dis[i - r]) rdis0++; else if (dis[i - r] == 2) rpdis0++; else break; } /* * If the run before the line 'i' found only multimatch lines, we * return 0 and hence we don't make the current line (i) discarded. * We want to discard multimatch lines only when they appear in the * middle of runs with nomatch lines (dis[j] == 0). */ if (rdis0 == 0) return 0; for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) { if (!dis[i + r]) rdis1++; else if (dis[i + r] == 2) rpdis1++; else break; } /* * If the run after the line 'i' found only multimatch lines, we * return 0 and hence we don't make the current line (i) discarded. */ if (rdis1 == 0) return 0; rdis1 += rdis0; rpdis1 += rpdis0; return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1); } /* * Try to reduce the problem complexity, discard records that have no * matches on the other file. Also, lines that have multiple matches * might be potentially discarded if they happear in a run of discardable. */ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) { long i, nm, nreff, mlim; xrecord_t **recs; xdlclass_t *rcrec; char *dis, *dis1, *dis2; if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) { return -1; } memset(dis, 0, xdf1->nrec + xdf2->nrec + 2); dis1 = dis; dis2 = dis1 + xdf1->nrec + 1; if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT) mlim = XDL_MAX_EQLIMIT; for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) { rcrec = cf->rcrecs[(*recs)->ha]; nm = rcrec ? rcrec->len2 : 0; dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1; } if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT) mlim = XDL_MAX_EQLIMIT; for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) { rcrec = cf->rcrecs[(*recs)->ha]; nm = rcrec ? rcrec->len1 : 0; dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1; } for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) { if (dis1[i] == 1 || (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) { xdf1->rindex[nreff] = i; xdf1->ha[nreff] = (*recs)->ha; nreff++; } else xdf1->rchg[i] = 1; } xdf1->nreff = nreff; for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) { if (dis2[i] == 1 || (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) { xdf2->rindex[nreff] = i; xdf2->ha[nreff] = (*recs)->ha; nreff++; } else xdf2->rchg[i] = 1; } xdf2->nreff = nreff; xdl_free(dis); return 0; } /* * Early trim initial and terminal matching records. */ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) { long i, lim; xrecord_t **recs1, **recs2; recs1 = xdf1->recs; recs2 = xdf2->recs; for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim; i++, recs1++, recs2++) if ((*recs1)->ha != (*recs2)->ha) break; xdf1->dstart = xdf2->dstart = i; recs1 = xdf1->recs + xdf1->nrec - 1; recs2 = xdf2->recs + xdf2->nrec - 1; for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--) if ((*recs1)->ha != (*recs2)->ha) break; xdf1->dend = xdf1->nrec - i - 1; xdf2->dend = xdf2->nrec - i - 1; return 0; } static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) { if (xdl_trim_ends(xdf1, xdf2) < 0 || xdl_cleanup_records(cf, xdf1, xdf2) < 0) { return -1; } return 0; } git2r/src/libgit2/src/xdiff/xutils.h0000644000175000017500000000336314125111754017154 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XUTILS_H) #define XUTILS_H long xdl_bogosqrt(long n); int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, xdemitcb_t *ecb); int xdl_cha_init(chastore_t *cha, long isize, long icount); void xdl_cha_free(chastore_t *cha); void *xdl_cha_alloc(chastore_t *cha); long xdl_guess_lines(mmfile_t *mf, long sample); int xdl_blankline(const char *line, long size, long flags); int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags); unsigned long xdl_hash_record(char const **data, char const *top, long flags); unsigned int xdl_hashbits(unsigned int size); int xdl_num_out(char *out, long val); int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, const char *func, long funclen, xdemitcb_t *ecb); int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, int line1, int count1, int line2, int count2); #endif /* #if !defined(XUTILS_H) */ git2r/src/libgit2/src/xdiff/xhistogram.c0000644000175000017500000002225014125111754020000 0ustar nileshnilesh/* * Copyright (C) 2010, Google Inc. * and other copyright owners as documented in JGit's IP log. * * This program and the accompanying materials are made available * under the terms of the Eclipse Distribution License v1.0 which * accompanies this distribution, is reproduced below, and is * available at http://www.eclipse.org/org/documents/edl-v10.php * * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * - Neither the name of the Eclipse Foundation, Inc. nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "xinclude.h" #include "xtypes.h" #include "xdiff.h" #define MAX_PTR UINT_MAX #define MAX_CNT UINT_MAX #define LINE_END(n) (line##n + count##n - 1) #define LINE_END_PTR(n) (*line##n + *count##n - 1) struct histindex { struct record { unsigned int ptr, cnt; struct record *next; } **records, /* an occurrence */ **line_map; /* map of line to record chain */ chastore_t rcha; unsigned int *next_ptrs; unsigned int table_bits, records_size, line_map_size; unsigned int max_chain_length, key_shift, ptr_shift; unsigned int cnt, has_common; xdfenv_t *env; xpparam_t const *xpp; }; struct region { unsigned int begin1, end1; unsigned int begin2, end2; }; #define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift]) #define NEXT_PTR(index, ptr) \ (index->next_ptrs[(ptr) - index->ptr_shift]) #define CNT(index, ptr) \ ((LINE_MAP(index, ptr))->cnt) #define REC(env, s, l) \ (env->xdf##s.recs[l - 1]) static int cmp_recs(xpparam_t const *xpp, xrecord_t *r1, xrecord_t *r2) { return r1->ha == r2->ha && xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size, xpp->flags); } #define CMP_ENV(xpp, env, s1, l1, s2, l2) \ (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2))) #define CMP(i, s1, l1, s2, l2) \ (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2))) #define TABLE_HASH(index, side, line) \ XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits) static int scanA(struct histindex *index, unsigned int line1, unsigned int count1) { unsigned int ptr; unsigned int tbl_idx; unsigned int chain_len; struct record **rec_chain, *rec; for (ptr = LINE_END(1); line1 <= ptr; ptr--) { tbl_idx = TABLE_HASH(index, 1, ptr); rec_chain = index->records + tbl_idx; rec = *rec_chain; chain_len = 0; while (rec) { if (CMP(index, 1, rec->ptr, 1, ptr)) { /* * ptr is identical to another element. Insert * it onto the front of the existing element * chain. */ NEXT_PTR(index, ptr) = rec->ptr; rec->ptr = ptr; /* cap rec->cnt at MAX_CNT */ rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1); LINE_MAP(index, ptr) = rec; goto continue_scan; } rec = rec->next; chain_len++; } if (chain_len == index->max_chain_length) return -1; /* * This is the first time we have ever seen this particular * element in the sequence. Construct a new chain for it. */ if (!(rec = xdl_cha_alloc(&index->rcha))) return -1; rec->ptr = ptr; rec->cnt = 1; rec->next = *rec_chain; *rec_chain = rec; LINE_MAP(index, ptr) = rec; continue_scan: ; /* no op */ } return 0; } static int try_lcs( struct histindex *index, struct region *lcs, unsigned int b_ptr, unsigned int line1, unsigned int count1, unsigned int line2, unsigned int count2) { unsigned int b_next = b_ptr + 1; struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)]; unsigned int as, ae, bs, be, np, rc; int should_break; for (; rec; rec = rec->next) { if (rec->cnt > index->cnt) { if (!index->has_common) index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr); continue; } as = rec->ptr; if (!CMP(index, 1, as, 2, b_ptr)) continue; index->has_common = 1; for (;;) { should_break = 0; np = NEXT_PTR(index, as); bs = b_ptr; ae = as; be = bs; rc = rec->cnt; while (line1 < as && line2 < bs && CMP(index, 1, as - 1, 2, bs - 1)) { as--; bs--; if (1 < rc) rc = XDL_MIN(rc, CNT(index, as)); } while (ae < LINE_END(1) && be < LINE_END(2) && CMP(index, 1, ae + 1, 2, be + 1)) { ae++; be++; if (1 < rc) rc = XDL_MIN(rc, CNT(index, ae)); } if (b_next <= be) b_next = be + 1; if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) { lcs->begin1 = as; lcs->begin2 = bs; lcs->end1 = ae; lcs->end2 = be; index->cnt = rc; } if (np == 0) break; while (np <= ae) { np = NEXT_PTR(index, np); if (np == 0) { should_break = 1; break; } } if (should_break) break; as = np; } } return b_next; } static int find_lcs( struct histindex *index, struct region *lcs, unsigned int line1, unsigned int count1, unsigned int line2, unsigned int count2) { unsigned int b_ptr; if (scanA(index, line1, count1)) return -1; index->cnt = index->max_chain_length + 1; for (b_ptr = line2; b_ptr <= LINE_END(2); ) b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2); return index->has_common && index->max_chain_length < index->cnt; } static int fall_back_to_classic_diff(struct histindex *index, int line1, int count1, int line2, int count2) { xpparam_t xpp; xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; return xdl_fall_back_diff(index->env, &xpp, line1, count1, line2, count2); } static int histogram_diff( xpparam_t const *xpp, xdfenv_t *env, unsigned int line1, unsigned int count1, unsigned int line2, unsigned int count2) { struct histindex index; struct region lcs; size_t sz; int result = -1; if (count1 <= 0 && count2 <= 0) return 0; if (LINE_END(1) >= MAX_PTR) return -1; if (!count1) { while(count2--) env->xdf2.rchg[line2++ - 1] = 1; return 0; } else if (!count2) { while(count1--) env->xdf1.rchg[line1++ - 1] = 1; return 0; } memset(&index, 0, sizeof(index)); index.env = env; index.xpp = xpp; index.records = NULL; index.line_map = NULL; /* in case of early xdl_cha_free() */ index.rcha.head = NULL; index.table_bits = xdl_hashbits(count1); sz = index.records_size = 1 << index.table_bits; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&sz, sz, sizeof(struct record *)); if (!(index.records = (struct record **) xdl_malloc(sz))) goto cleanup; memset(index.records, 0, sz); sz = index.line_map_size = count1; sz *= sizeof(struct record *); if (!(index.line_map = (struct record **) xdl_malloc(sz))) goto cleanup; memset(index.line_map, 0, sz); sz = index.line_map_size; sz *= sizeof(unsigned int); if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz))) goto cleanup; memset(index.next_ptrs, 0, sz); /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */ if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0) goto cleanup; index.ptr_shift = line1; index.max_chain_length = 64; memset(&lcs, 0, sizeof(lcs)); if (find_lcs(&index, &lcs, line1, count1, line2, count2)) result = fall_back_to_classic_diff(&index, line1, count1, line2, count2); else { if (lcs.begin1 == 0 && lcs.begin2 == 0) { while (count1--) env->xdf1.rchg[line1++ - 1] = 1; while (count2--) env->xdf2.rchg[line2++ - 1] = 1; result = 0; } else { result = histogram_diff(xpp, env, line1, lcs.begin1 - line1, line2, lcs.begin2 - line2); if (result) goto cleanup; result = histogram_diff(xpp, env, lcs.end1 + 1, LINE_END(1) - lcs.end1, lcs.end2 + 1, LINE_END(2) - lcs.end2); if (result) goto cleanup; } } cleanup: xdl_free(index.records); xdl_free(index.line_map); xdl_free(index.next_ptrs); xdl_cha_free(&index.rcha); return result; } int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2, xpparam_t const *xpp, xdfenv_t *env) { if (xdl_prepare_env(file1, file2, xpp, env) < 0) return -1; return histogram_diff(xpp, env, env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1, env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1); } git2r/src/libgit2/src/xdiff/xdiff.h0000644000175000017500000000742714125111754016731 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XDIFF_H) #define XDIFF_H #ifdef __cplusplus extern "C" { #endif /* #ifdef __cplusplus */ /* xpparm_t.flags */ #define XDF_NEED_MINIMAL (1 << 0) #define XDF_IGNORE_WHITESPACE (1 << 1) #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 2) #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 3) #define XDF_IGNORE_CR_AT_EOL (1 << 4) #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | \ XDF_IGNORE_WHITESPACE_CHANGE | \ XDF_IGNORE_WHITESPACE_AT_EOL | \ XDF_IGNORE_CR_AT_EOL) #define XDF_IGNORE_BLANK_LINES (1 << 7) #define XDF_PATIENCE_DIFF (1 << 14) #define XDF_HISTOGRAM_DIFF (1 << 15) #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF) #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK) #define XDF_INDENT_HEURISTIC (1 << 23) /* xdemitconf_t.flags */ #define XDL_EMIT_FUNCNAMES (1 << 0) #define XDL_EMIT_FUNCCONTEXT (1 << 2) #define XDL_MMB_READONLY (1 << 0) #define XDL_MMF_ATOMIC (1 << 0) #define XDL_BDOP_INS 1 #define XDL_BDOP_CPY 2 #define XDL_BDOP_INSB 3 /* merge simplification levels */ #define XDL_MERGE_MINIMAL 0 #define XDL_MERGE_EAGER 1 #define XDL_MERGE_ZEALOUS 2 #define XDL_MERGE_ZEALOUS_ALNUM 3 /* merge favor modes */ #define XDL_MERGE_FAVOR_OURS 1 #define XDL_MERGE_FAVOR_THEIRS 2 #define XDL_MERGE_FAVOR_UNION 3 /* merge output styles */ #define XDL_MERGE_DIFF3 1 typedef struct s_mmfile { char *ptr; size_t size; } mmfile_t; typedef struct s_mmbuffer { char *ptr; size_t size; } mmbuffer_t; typedef struct s_xpparam { unsigned long flags; /* See Documentation/diff-options.txt. */ char **anchors; size_t anchors_nr; } xpparam_t; typedef struct s_xdemitcb { void *priv; int (*outf)(void *, mmbuffer_t *, int); } xdemitcb_t; typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv); typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a, long start_b, long count_b, void *cb_data); typedef struct s_xdemitconf { long ctxlen; long interhunkctxlen; unsigned long flags; find_func_t find_func; void *find_func_priv; xdl_emit_hunk_consume_func_t hunk_func; } xdemitconf_t; typedef struct s_bdiffparam { long bsize; } bdiffparam_t; #define xdl_malloc(x) git__malloc(x) #define xdl_free(ptr) git__free(ptr) #define xdl_realloc(ptr,x) git__realloc(ptr,x) void *xdl_mmfile_first(mmfile_t *mmf, long *size); long xdl_mmfile_size(mmfile_t *mmf); int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb); typedef struct s_xmparam { xpparam_t xpp; int marker_size; int level; int favor; int style; const char *ancestor; /* label for orig */ const char *file1; /* label for mf1 */ const char *file2; /* label for mf2 */ } xmparam_t; #define DEFAULT_CONFLICT_MARKER_SIZE 7 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, xmparam_t const *xmp, mmbuffer_t *result); #ifdef __cplusplus } #endif /* #ifdef __cplusplus */ #endif /* #if !defined(XDIFF_H) */ git2r/src/libgit2/src/xdiff/xinclude.h0000644000175000017500000000232514125111754017434 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XINCLUDE_H) #define XINCLUDE_H #include #include #include #include #include #ifdef _WIN32 #else #include #endif #include "xmacros.h" #include "xdiff.h" #include "xtypes.h" #include "xutils.h" #include "xprepare.h" #include "xdiffi.h" #include "xemit.h" #include "common.h" #endif /* #if !defined(XINCLUDE_H) */ git2r/src/libgit2/src/xdiff/xmerge.c0000644000175000017500000004445714125111754017117 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #include "xinclude.h" typedef struct s_xdmerge { struct s_xdmerge *next; /* * 0 = conflict, * 1 = no conflict, take first, * 2 = no conflict, take second. * 3 = no conflict, take both. */ int mode; /* * These point at the respective postimages. E.g. is * how side #1 wants to change the common ancestor; if there is no * overlap, lines before i1 in the postimage of side #1 appear * in the merge result as a region touched by neither side. */ long i1, i2; long chg1, chg2; /* * These point at the preimage; of course there is just one * preimage, that is from the shared common ancestor. */ long i0; long chg0; } xdmerge_t; static int xdl_append_merge(xdmerge_t **merge, int mode, long i0, long chg0, long i1, long chg1, long i2, long chg2) { xdmerge_t *m = *merge; if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) { if (mode != m->mode) m->mode = 0; m->chg0 = i0 + chg0 - m->i0; m->chg1 = i1 + chg1 - m->i1; m->chg2 = i2 + chg2 - m->i2; } else { m = xdl_malloc(sizeof(xdmerge_t)); if (!m) return -1; m->next = NULL; m->mode = mode; m->i0 = i0; m->chg0 = chg0; m->i1 = i1; m->chg1 = chg1; m->i2 = i2; m->chg2 = chg2; if (*merge) (*merge)->next = m; *merge = m; } return 0; } static int xdl_cleanup_merge(xdmerge_t *c) { int count = 0; xdmerge_t *next_c; /* were there conflicts? */ for (; c; c = next_c) { if (c->mode == 0) count++; next_c = c->next; free(c); } return count; } static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, int line_count, long flags) { int i; xrecord_t **rec1 = xe1->xdf2.recs + i1; xrecord_t **rec2 = xe2->xdf2.recs + i2; for (i = 0; i < line_count; i++) { int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size, rec2[i]->ptr, rec2[i]->size, flags); if (!result) return -1; } return 0; } static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) { xrecord_t **recs; size_t size = 0; *out = 0; recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i; if (count < 1) return 0; for (i = 0; i < count; ) { if (dest) memcpy(dest + size, recs[i]->ptr, recs[i]->size); GIT_ERROR_CHECK_ALLOC_ADD(&size, size, recs[i++]->size); } if (add_nl) { i = recs[count - 1]->size; if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') { if (needs_cr) { if (dest) dest[size] = '\r'; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, 1); } if (dest) dest[size] = '\n'; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, 1); } } *out = size; return 0; } static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) { return xdl_recs_copy_0(out, 0, xe, i, count, needs_cr, add_nl, dest); } static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) { return xdl_recs_copy_0(out, 1, xe, i, count, needs_cr, add_nl, dest); } /* * Returns 1 if the i'th line ends in CR/LF (if it is the last line and * has no eol, the preceding line, if any), 0 if it ends in LF-only, and * -1 if the line ending cannot be determined. */ static int is_eol_crlf(xdfile_t *file, int i) { long size; if (i < file->nrec - 1) /* All lines before the last *must* end in LF */ return (size = file->recs[i]->size) > 1 && file->recs[i]->ptr[size - 2] == '\r'; if (!file->nrec) /* Cannot determine eol style from empty file */ return -1; if ((size = file->recs[i]->size) && file->recs[i]->ptr[size - 1] == '\n') /* Last line; ends in LF; Is it CR/LF? */ return size > 1 && file->recs[i]->ptr[size - 2] == '\r'; if (!i) /* The only line has no eol */ return -1; /* Determine eol from second-to-last line */ return (size = file->recs[i - 1]->size) > 1 && file->recs[i - 1]->ptr[size - 2] == '\r'; } static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m) { int needs_cr; /* Match post-images' preceding, or first, lines' end-of-line style */ needs_cr = is_eol_crlf(&xe1->xdf2, m->i1 ? m->i1 - 1 : 0); if (needs_cr) needs_cr = is_eol_crlf(&xe2->xdf2, m->i2 ? m->i2 - 1 : 0); /* Look at pre-image's first line, unless we already settled on LF */ if (needs_cr) needs_cr = is_eol_crlf(&xe1->xdf1, 0); /* If still undecided, use LF-only */ return needs_cr < 0 ? 0 : needs_cr; } static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, const char *name3, size_t size, int i, int style, xdmerge_t *m, char *dest, int marker_size) { int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0); int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0); int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0); int needs_cr = is_cr_needed(xe1, xe2, m); size_t copied; *out = 0; if (marker_size <= 0) marker_size = DEFAULT_CONFLICT_MARKER_SIZE; /* Before conflicting part */ if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0, dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); if (!dest) { GIT_ERROR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker1_size); } else { memset(dest + size, '<', marker_size); size += marker_size; if (marker1_size) { dest[size] = ' '; memcpy(dest + size + 1, name1, marker1_size - 1); size += marker1_size; } if (needs_cr) dest[size++] = '\r'; dest[size++] = '\n'; } /* Postimage from side #1 */ if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, 1, dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); if (style == XDL_MERGE_DIFF3) { /* Shared preimage */ if (!dest) { GIT_ERROR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker3_size); } else { memset(dest + size, '|', marker_size); size += marker_size; if (marker3_size) { dest[size] = ' '; memcpy(dest + size + 1, name3, marker3_size - 1); size += marker3_size; } if (needs_cr) dest[size++] = '\r'; dest[size++] = '\n'; } if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, needs_cr, 1, dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); } if (!dest) { GIT_ERROR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, needs_cr); } else { memset(dest + size, '=', marker_size); size += marker_size; if (needs_cr) dest[size++] = '\r'; dest[size++] = '\n'; } /* Postimage from side #2 */ if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, needs_cr, 1, dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); if (!dest) { GIT_ERROR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker2_size); } else { memset(dest + size, '>', marker_size); size += marker_size; if (marker2_size) { dest[size] = ' '; memcpy(dest + size + 1, name2, marker2_size - 1); size += marker2_size; } if (needs_cr) dest[size++] = '\r'; dest[size++] = '\n'; } *out = size; return 0; } static int xdl_fill_merge_buffer(size_t *out, xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, const char *ancestor_name, int favor, xdmerge_t *m, char *dest, int style, int marker_size) { size_t size, copied; int i; *out = 0; for (size = i = 0; m; m = m->next) { if (favor && !m->mode) m->mode = favor; if (m->mode == 0) { if (fill_conflict_hunk(&size, xe1, name1, xe2, name2, ancestor_name, size, i, style, m, dest, marker_size) < 0) return -1; } else if (m->mode & 3) { /* Before conflicting part */ if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0, dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); /* Postimage from side #1 */ if (m->mode & 1) { int needs_cr = is_cr_needed(xe1, xe2, m); if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, (m->mode & 2), dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); } /* Postimage from side #2 */ if (m->mode & 2) { if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0, 0, dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); } } else continue; i = m->i1 + m->chg1; } if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0, 0, dest ? dest + size : NULL) < 0) return -1; GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); *out = size; return 0; } /* * Sometimes, changes are not quite identical, but differ in only a few * lines. Try hard to show only these few lines as conflicting. */ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m, xpparam_t const *xpp) { for (; m; m = m->next) { mmfile_t t1, t2; xdfenv_t xe; xdchange_t *xscr, *x; int i1 = m->i1, i2 = m->i2; /* let's handle just the conflicts */ if (m->mode) continue; /* no sense refining a conflict when one side is empty */ if (m->chg1 == 0 || m->chg2 == 0) continue; /* * This probably does not work outside git, since * we have a very simple mmfile structure. */ t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr; t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr; t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr; t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr; if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0) return -1; if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 || xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 || xdl_build_script(&xe, &xscr) < 0) { xdl_free_env(&xe); return -1; } if (!xscr) { /* If this happens, the changes are identical. */ xdl_free_env(&xe); m->mode = 4; continue; } x = xscr; m->i1 = xscr->i1 + i1; m->chg1 = xscr->chg1; m->i2 = xscr->i2 + i2; m->chg2 = xscr->chg2; while (xscr->next) { xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t)); if (!m2) { xdl_free_env(&xe); xdl_free_script(x); return -1; } xscr = xscr->next; m2->next = m->next; m->next = m2; m = m2; m->mode = 0; m->i1 = xscr->i1 + i1; m->chg1 = xscr->chg1; m->i2 = xscr->i2 + i2; m->chg2 = xscr->chg2; } xdl_free_env(&xe); xdl_free_script(x); } return 0; } static int line_contains_alnum(const char *ptr, long size) { while (size--) if (isalnum((unsigned char)*(ptr++))) return 1; return 0; } static int lines_contain_alnum(xdfenv_t *xe, int i, int chg) { for (; chg; chg--, i++) if (line_contains_alnum(xe->xdf2.recs[i]->ptr, xe->xdf2.recs[i]->size)) return 1; return 0; } /* * This function merges m and m->next, marking everything between those hunks * as conflicting, too. */ static void xdl_merge_two_conflicts(xdmerge_t *m) { xdmerge_t *next_m = m->next; m->chg1 = next_m->i1 + next_m->chg1 - m->i1; m->chg2 = next_m->i2 + next_m->chg2 - m->i2; m->next = next_m->next; free(next_m); } /* * If there are less than 3 non-conflicting lines between conflicts, * it appears simpler -- because it takes up less (or as many) lines -- * if the lines are moved into the conflicts. */ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m, int simplify_if_no_alnum) { int result = 0; if (!m) return result; for (;;) { xdmerge_t *next_m = m->next; int begin, end; if (!next_m) return result; begin = m->i1 + m->chg1; end = next_m->i1; if (m->mode != 0 || next_m->mode != 0 || (end - begin > 3 && (!simplify_if_no_alnum || lines_contain_alnum(xe1, begin, end - begin)))) { m = next_m; } else { result++; xdl_merge_two_conflicts(m); } } } /* * level == 0: mark all overlapping changes as conflict * level == 1: mark overlapping changes as conflict only if not identical * level == 2: analyze non-identical changes for minimal conflict set * level == 3: analyze non-identical changes for minimal conflict set, but * treat hunks not containing any letter or number as conflicting * * returns < 0 on error, == 0 for no conflicts, else number of conflicts */ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, xdfenv_t *xe2, xdchange_t *xscr2, xmparam_t const *xmp, mmbuffer_t *result) { xdmerge_t *changes, *c; xpparam_t const *xpp = &xmp->xpp; const char *const ancestor_name = xmp->ancestor; const char *const name1 = xmp->file1; const char *const name2 = xmp->file2; int i0, i1, i2, chg0, chg1, chg2; int level = xmp->level; int style = xmp->style; int favor = xmp->favor; if (style == XDL_MERGE_DIFF3) { /* * "diff3 -m" output does not make sense for anything * more aggressive than XDL_MERGE_EAGER. */ if (XDL_MERGE_EAGER < level) level = XDL_MERGE_EAGER; } c = changes = NULL; while (xscr1 && xscr2) { if (!changes) changes = c; if (xscr1->i1 + xscr1->chg1 < xscr2->i1) { i0 = xscr1->i1; i1 = xscr1->i2; i2 = xscr2->i2 - xscr2->i1 + xscr1->i1; chg0 = xscr1->chg1; chg1 = xscr1->chg2; chg2 = xscr1->chg1; if (xdl_append_merge(&c, 1, i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } xscr1 = xscr1->next; continue; } if (xscr2->i1 + xscr2->chg1 < xscr1->i1) { i0 = xscr2->i1; i1 = xscr1->i2 - xscr1->i1 + xscr2->i1; i2 = xscr2->i2; chg0 = xscr2->chg1; chg1 = xscr2->chg1; chg2 = xscr2->chg2; if (xdl_append_merge(&c, 2, i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } xscr2 = xscr2->next; continue; } if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 || xscr1->chg1 != xscr2->chg1 || xscr1->chg2 != xscr2->chg2 || xdl_merge_cmp_lines(xe1, xscr1->i2, xe2, xscr2->i2, xscr1->chg2, xpp->flags)) { /* conflict */ int off = xscr1->i1 - xscr2->i1; int ffo = off + xscr1->chg1 - xscr2->chg1; i0 = xscr1->i1; i1 = xscr1->i2; i2 = xscr2->i2; if (off > 0) { i0 -= off; i1 -= off; } else i2 += off; chg0 = xscr1->i1 + xscr1->chg1 - i0; chg1 = xscr1->i2 + xscr1->chg2 - i1; chg2 = xscr2->i2 + xscr2->chg2 - i2; if (ffo < 0) { chg0 -= ffo; chg1 -= ffo; } else chg2 += ffo; if (xdl_append_merge(&c, 0, i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } } i1 = xscr1->i1 + xscr1->chg1; i2 = xscr2->i1 + xscr2->chg1; if (i1 >= i2) xscr2 = xscr2->next; if (i2 >= i1) xscr1 = xscr1->next; } while (xscr1) { if (!changes) changes = c; i0 = xscr1->i1; i1 = xscr1->i2; i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec; chg0 = xscr1->chg1; chg1 = xscr1->chg2; chg2 = xscr1->chg1; if (xdl_append_merge(&c, 1, i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } xscr1 = xscr1->next; } while (xscr2) { if (!changes) changes = c; i0 = xscr2->i1; i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec; i2 = xscr2->i2; chg0 = xscr2->chg1; chg1 = xscr2->chg1; chg2 = xscr2->chg2; if (xdl_append_merge(&c, 2, i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } xscr2 = xscr2->next; } if (!changes) changes = c; /* refine conflicts */ if (XDL_MERGE_ZEALOUS <= level && (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 || xdl_simplify_non_conflicts(xe1, changes, XDL_MERGE_ZEALOUS < level) < 0)) { xdl_cleanup_merge(changes); return -1; } /* output */ if (result) { int marker_size = xmp->marker_size; size_t size; if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, ancestor_name, favor, changes, NULL, style, marker_size) < 0) return -1; result->ptr = xdl_malloc(size); if (!result->ptr) { xdl_cleanup_merge(changes); return -1; } result->size = size; if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, ancestor_name, favor, changes, result->ptr, style, marker_size) < 0) return -1; } return xdl_cleanup_merge(changes); } int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, xmparam_t const *xmp, mmbuffer_t *result) { xdchange_t *xscr1, *xscr2; xdfenv_t xe1, xe2; int status; xpparam_t const *xpp = &xmp->xpp; result->ptr = NULL; result->size = 0; if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0) { return -1; } if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0) { xdl_free_env(&xe1); return -1; } if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 || xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 || xdl_build_script(&xe1, &xscr1) < 0) { xdl_free_env(&xe1); return -1; } if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 || xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 || xdl_build_script(&xe2, &xscr2) < 0) { xdl_free_script(xscr1); xdl_free_env(&xe1); xdl_free_env(&xe2); return -1; } status = 0; if (!xscr1) { result->ptr = xdl_malloc(mf2->size); if (!result->ptr) { xdl_free_script(xscr2); xdl_free_env(&xe1); xdl_free_env(&xe2); return -1; } memcpy(result->ptr, mf2->ptr, mf2->size); result->size = mf2->size; } else if (!xscr2) { result->ptr = xdl_malloc(mf1->size); if (!result->ptr) { xdl_free_script(xscr1); xdl_free_env(&xe1); xdl_free_env(&xe2); return -1; } memcpy(result->ptr, mf1->ptr, mf1->size); result->size = mf1->size; } else { status = xdl_do_merge(&xe1, xscr1, &xe2, xscr2, xmp, result); } xdl_free_script(xscr1); xdl_free_script(xscr2); xdl_free_env(&xe1); xdl_free_env(&xe2); return status; } git2r/src/libgit2/src/xdiff/xtypes.h0000644000175000017500000000277114125111754017162 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XTYPES_H) #define XTYPES_H typedef struct s_chanode { struct s_chanode *next; long icurr; } chanode_t; typedef struct s_chastore { chanode_t *head, *tail; long isize, nsize; chanode_t *ancur; chanode_t *sncur; long scurr; } chastore_t; typedef struct s_xrecord { struct s_xrecord *next; char const *ptr; long size; unsigned long ha; } xrecord_t; typedef struct s_xdfile { chastore_t rcha; long nrec; unsigned int hbits; xrecord_t **rhash; long dstart, dend; xrecord_t **recs; char *rchg; long *rindex; long nreff; unsigned long *ha; } xdfile_t; typedef struct s_xdfenv { xdfile_t xdf1, xdf2; } xdfenv_t; #endif /* #if !defined(XTYPES_H) */ git2r/src/libgit2/src/xdiff/xdiffi.c0000644000175000017500000006705014125111754017073 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #include "xinclude.h" #include "integer.h" #define XDL_MAX_COST_MIN 256 #define XDL_HEUR_MIN_COST 256 #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1) #define XDL_SNAKE_CNT 20 #define XDL_K_HEUR 4 /** Declare a function as always inlined. */ #if defined(_MSC_VER) # define XDL_INLINE(type) static __inline type #elif defined(__GNUC__) # define XDL_INLINE(type) static __inline__ type #else # define XDL_INLINE(type) static type #endif typedef struct s_xdpsplit { long i1, i2; int min_lo, min_hi; } xdpsplit_t; static long xdl_split(unsigned long const *ha1, long off1, long lim1, unsigned long const *ha2, long off2, long lim2, long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, xdalgoenv_t *xenv); static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2); /* * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers. * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both * the forward diagonal starting from (off1, off2) and the backward diagonal * starting from (lim1, lim2). If the K values on the same diagonal crosses * returns the furthest point of reach. We might end up having to expensive * cases using this algorithm is full, so a little bit of heuristic is needed * to cut the search and to return a suboptimal point. */ static long xdl_split(unsigned long const *ha1, long off1, long lim1, unsigned long const *ha2, long off2, long lim2, long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, xdalgoenv_t *xenv) { long dmin = off1 - lim2, dmax = lim1 - off2; long fmid = off1 - off2, bmid = lim1 - lim2; long odd = (fmid - bmid) & 1; long fmin = fmid, fmax = fmid; long bmin = bmid, bmax = bmid; long ec, d, i1, i2, prev1, best, dd, v, k; /* * Set initial diagonal values for both forward and backward path. */ kvdf[fmid] = off1; kvdb[bmid] = lim1; for (ec = 1;; ec++) { int got_snake = 0; /* * We need to extent the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the * opposite direction because (max - min) must be a power of two. * Also we initialize the external K value to -1 so that we can * avoid extra conditions check inside the core loop. */ if (fmin > dmin) kvdf[--fmin - 1] = -1; else ++fmin; if (fmax < dmax) kvdf[++fmax + 1] = -1; else --fmax; for (d = fmax; d >= fmin; d -= 2) { if (kvdf[d - 1] >= kvdf[d + 1]) i1 = kvdf[d - 1] + 1; else i1 = kvdf[d + 1]; prev1 = i1; i2 = i1 - d; for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++); if (i1 - prev1 > xenv->snake_cnt) got_snake = 1; kvdf[d] = i1; if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) { spl->i1 = i1; spl->i2 = i2; spl->min_lo = spl->min_hi = 1; return ec; } } /* * We need to extent the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the * opposite direction because (max - min) must be a power of two. * Also we initialize the external K value to -1 so that we can * avoid extra conditions check inside the core loop. */ if (bmin > dmin) kvdb[--bmin - 1] = XDL_LINE_MAX; else ++bmin; if (bmax < dmax) kvdb[++bmax + 1] = XDL_LINE_MAX; else --bmax; for (d = bmax; d >= bmin; d -= 2) { if (kvdb[d - 1] < kvdb[d + 1]) i1 = kvdb[d - 1]; else i1 = kvdb[d + 1] - 1; prev1 = i1; i2 = i1 - d; for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--); if (prev1 - i1 > xenv->snake_cnt) got_snake = 1; kvdb[d] = i1; if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) { spl->i1 = i1; spl->i2 = i2; spl->min_lo = spl->min_hi = 1; return ec; } } if (need_min) continue; /* * If the edit cost is above the heuristic trigger and if * we got a good snake, we sample current diagonals to see * if some of the, have reached an "interesting" path. Our * measure is a function of the distance from the diagonal * corner (i1 + i2) penalized with the distance from the * mid diagonal itself. If this value is above the current * edit cost times a magic factor (XDL_K_HEUR) we consider * it interesting. */ if (got_snake && ec > xenv->heur_min) { for (best = 0, d = fmax; d >= fmin; d -= 2) { dd = d > fmid ? d - fmid: fmid - d; i1 = kvdf[d]; i2 = i1 - d; v = (i1 - off1) + (i2 - off2) - dd; if (v > XDL_K_HEUR * ec && v > best && off1 + xenv->snake_cnt <= i1 && i1 < lim1 && off2 + xenv->snake_cnt <= i2 && i2 < lim2) { for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++) if (k == xenv->snake_cnt) { best = v; spl->i1 = i1; spl->i2 = i2; break; } } } if (best > 0) { spl->min_lo = 1; spl->min_hi = 0; return ec; } for (best = 0, d = bmax; d >= bmin; d -= 2) { dd = d > bmid ? d - bmid: bmid - d; i1 = kvdb[d]; i2 = i1 - d; v = (lim1 - i1) + (lim2 - i2) - dd; if (v > XDL_K_HEUR * ec && v > best && off1 < i1 && i1 <= lim1 - xenv->snake_cnt && off2 < i2 && i2 <= lim2 - xenv->snake_cnt) { for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++) if (k == xenv->snake_cnt - 1) { best = v; spl->i1 = i1; spl->i2 = i2; break; } } } if (best > 0) { spl->min_lo = 0; spl->min_hi = 1; return ec; } } /* * Enough is enough. We spent too much time here and now we collect * the furthest reaching path using the (i1 + i2) measure. */ if (ec >= xenv->mxcost) { long fbest, fbest1, bbest, bbest1; fbest = fbest1 = -1; for (d = fmax; d >= fmin; d -= 2) { i1 = XDL_MIN(kvdf[d], lim1); i2 = i1 - d; if (lim2 < i2) i1 = lim2 + d, i2 = lim2; if (fbest < i1 + i2) { fbest = i1 + i2; fbest1 = i1; } } bbest = bbest1 = XDL_LINE_MAX; for (d = bmax; d >= bmin; d -= 2) { i1 = XDL_MAX(off1, kvdb[d]); i2 = i1 - d; if (i2 < off2) i1 = off2 + d, i2 = off2; if (i1 + i2 < bbest) { bbest = i1 + i2; bbest1 = i1; } } if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) { spl->i1 = fbest1; spl->i2 = fbest - fbest1; spl->min_lo = 1; spl->min_hi = 0; } else { spl->i1 = bbest1; spl->i2 = bbest - bbest1; spl->min_lo = 0; spl->min_hi = 1; } return ec; } } } /* * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling * the box splitting function. Note that the real job (marking changed lines) * is done in the two boundary reaching checks. */ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, diffdata_t *dd2, long off2, long lim2, long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) { unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha; /* * Shrink the box by walking through each diagonal snake (SW and NE). */ for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++); for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--); /* * If one dimension is empty, then all records on the other one must * be obviously changed. */ if (off1 == lim1) { char *rchg2 = dd2->rchg; long *rindex2 = dd2->rindex; for (; off2 < lim2; off2++) rchg2[rindex2[off2]] = 1; } else if (off2 == lim2) { char *rchg1 = dd1->rchg; long *rindex1 = dd1->rindex; for (; off1 < lim1; off1++) rchg1[rindex1[off1]] = 1; } else { xdpsplit_t spl; spl.i1 = spl.i2 = 0; /* * Divide ... */ if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, need_min, &spl, xenv) < 0) { return -1; } /* * ... et Impera. */ if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2, kvdf, kvdb, spl.min_lo, xenv) < 0 || xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2, kvdf, kvdb, spl.min_hi, xenv) < 0) { return -1; } } return 0; } int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *xe) { size_t ndiags, allocsize; long *kvd, *kvdf, *kvdb; xdalgoenv_t xenv; diffdata_t dd1, dd2; if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) return xdl_do_patience_diff(mf1, mf2, xpp, xe); if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) return xdl_do_histogram_diff(mf1, mf2, xpp, xe); if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) { return -1; } /* * Allocate and setup K vectors to be used by the differential algorithm. * One is to store the forward path and one to store the backward path. */ GIT_ERROR_CHECK_ALLOC_ADD3(&ndiags, xe->xdf1.nreff, xe->xdf2.nreff, 3); GIT_ERROR_CHECK_ALLOC_MULTIPLY(&allocsize, ndiags, 2); GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, allocsize, 2); GIT_ERROR_CHECK_ALLOC_MULTIPLY(&allocsize, allocsize, sizeof(long)); if (!(kvd = (long *) xdl_malloc(allocsize))) { xdl_free_env(xe); return -1; } kvdf = kvd; kvdb = kvdf + ndiags; kvdf += xe->xdf2.nreff + 1; kvdb += xe->xdf2.nreff + 1; xenv.mxcost = xdl_bogosqrt(ndiags); if (xenv.mxcost < XDL_MAX_COST_MIN) xenv.mxcost = XDL_MAX_COST_MIN; xenv.snake_cnt = XDL_SNAKE_CNT; xenv.heur_min = XDL_HEUR_MIN_COST; dd1.nrec = xe->xdf1.nreff; dd1.ha = xe->xdf1.ha; dd1.rchg = xe->xdf1.rchg; dd1.rindex = xe->xdf1.rindex; dd2.nrec = xe->xdf2.nreff; dd2.ha = xe->xdf2.ha; dd2.rchg = xe->xdf2.rchg; dd2.rindex = xe->xdf2.rindex; if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec, kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) { xdl_free(kvd); xdl_free_env(xe); return -1; } xdl_free(kvd); return 0; } static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) { xdchange_t *xch; if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t)))) return NULL; xch->next = xscr; xch->i1 = i1; xch->i2 = i2; xch->chg1 = chg1; xch->chg2 = chg2; xch->ignore = 0; return xch; } static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags) { return (rec1->ha == rec2->ha && xdl_recmatch(rec1->ptr, rec1->size, rec2->ptr, rec2->size, flags)); } /* * If a line is indented more than this, get_indent() just returns this value. * This avoids having to do absurd amounts of work for data that are not * human-readable text, and also ensures that the output of get_indent fits within * an int. */ #define MAX_INDENT 200 /* * Return the amount of indentation of the specified line, treating TAB as 8 * columns. Return -1 if line is empty or contains only whitespace. Clamp the * output value at MAX_INDENT. */ static int get_indent(xrecord_t *rec) { long i; int ret = 0; for (i = 0; i < rec->size; i++) { char c = rec->ptr[i]; if (!XDL_ISSPACE(c)) return ret; else if (c == ' ') ret += 1; else if (c == '\t') ret += 8 - ret % 8; /* ignore other whitespace characters */ if (ret >= MAX_INDENT) return MAX_INDENT; } /* The line contains only whitespace. */ return -1; } /* * If more than this number of consecutive blank rows are found, just return this * value. This avoids requiring O(N^2) work for pathological cases, and also * ensures that the output of score_split fits in an int. */ #define MAX_BLANKS 20 /* Characteristics measured about a hypothetical split position. */ struct split_measurement { /* * Is the split at the end of the file (aside from any blank lines)? */ int end_of_file; /* * How much is the line immediately following the split indented (or -1 if * the line is blank): */ int indent; /* * How many consecutive lines above the split are blank? */ int pre_blank; /* * How much is the nearest non-blank line above the split indented (or -1 * if there is no such line)? */ int pre_indent; /* * How many lines after the line following the split are blank? */ int post_blank; /* * How much is the nearest non-blank line after the line following the * split indented (or -1 if there is no such line)? */ int post_indent; }; struct split_score { /* The effective indent of this split (smaller is preferred). */ int effective_indent; /* Penalty for this split (smaller is preferred). */ int penalty; }; /* * Fill m with information about a hypothetical split of xdf above line split. */ static void measure_split(const xdfile_t *xdf, long split, struct split_measurement *m) { long i; if (split >= xdf->nrec) { m->end_of_file = 1; m->indent = -1; } else { m->end_of_file = 0; m->indent = get_indent(xdf->recs[split]); } m->pre_blank = 0; m->pre_indent = -1; for (i = split - 1; i >= 0; i--) { m->pre_indent = get_indent(xdf->recs[i]); if (m->pre_indent != -1) break; m->pre_blank += 1; if (m->pre_blank == MAX_BLANKS) { m->pre_indent = 0; break; } } m->post_blank = 0; m->post_indent = -1; for (i = split + 1; i < xdf->nrec; i++) { m->post_indent = get_indent(xdf->recs[i]); if (m->post_indent != -1) break; m->post_blank += 1; if (m->post_blank == MAX_BLANKS) { m->post_indent = 0; break; } } } /* * The empirically-determined weight factors used by score_split() below. * Larger values means that the position is a less favorable place to split. * * Note that scores are only ever compared against each other, so multiplying * all of these weight/penalty values by the same factor wouldn't change the * heuristic's behavior. Still, we need to set that arbitrary scale *somehow*. * In practice, these numbers are chosen to be large enough that they can be * adjusted relative to each other with sufficient precision despite using * integer math. */ /* Penalty if there are no non-blank lines before the split */ #define START_OF_FILE_PENALTY 1 /* Penalty if there are no non-blank lines after the split */ #define END_OF_FILE_PENALTY 21 /* Multiplier for the number of blank lines around the split */ #define TOTAL_BLANK_WEIGHT (-30) /* Multiplier for the number of blank lines after the split */ #define POST_BLANK_WEIGHT 6 /* * Penalties applied if the line is indented more than its predecessor */ #define RELATIVE_INDENT_PENALTY (-4) #define RELATIVE_INDENT_WITH_BLANK_PENALTY 10 /* * Penalties applied if the line is indented less than both its predecessor and * its successor */ #define RELATIVE_OUTDENT_PENALTY 24 #define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17 /* * Penalties applied if the line is indented less than its predecessor but not * less than its successor */ #define RELATIVE_DEDENT_PENALTY 23 #define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17 /* * We only consider whether the sum of the effective indents for splits are * less than (-1), equal to (0), or greater than (+1) each other. The resulting * value is multiplied by the following weight and combined with the penalty to * determine the better of two scores. */ #define INDENT_WEIGHT 60 /* * Compute a badness score for the hypothetical split whose measurements are * stored in m. The weight factors were determined empirically using the tools and * corpus described in * * https://github.com/mhagger/diff-slider-tools * * Also see that project if you want to improve the weights based on, for example, * a larger or more diverse corpus. */ static void score_add_split(const struct split_measurement *m, struct split_score *s) { /* * A place to accumulate penalty factors (positive makes this index more * favored): */ int post_blank, total_blank, indent, any_blanks; if (m->pre_indent == -1 && m->pre_blank == 0) s->penalty += START_OF_FILE_PENALTY; if (m->end_of_file) s->penalty += END_OF_FILE_PENALTY; /* * Set post_blank to the number of blank lines following the split, * including the line immediately after the split: */ post_blank = (m->indent == -1) ? 1 + m->post_blank : 0; total_blank = m->pre_blank + post_blank; /* Penalties based on nearby blank lines: */ s->penalty += TOTAL_BLANK_WEIGHT * total_blank; s->penalty += POST_BLANK_WEIGHT * post_blank; if (m->indent != -1) indent = m->indent; else indent = m->post_indent; any_blanks = (total_blank != 0); /* Note that the effective indent is -1 at the end of the file: */ s->effective_indent += indent; if (indent == -1) { /* No additional adjustments needed. */ } else if (m->pre_indent == -1) { /* No additional adjustments needed. */ } else if (indent > m->pre_indent) { /* * The line is indented more than its predecessor. */ s->penalty += any_blanks ? RELATIVE_INDENT_WITH_BLANK_PENALTY : RELATIVE_INDENT_PENALTY; } else if (indent == m->pre_indent) { /* * The line has the same indentation level as its predecessor. * No additional adjustments needed. */ } else { /* * The line is indented less than its predecessor. It could be * the block terminator of the previous block, but it could * also be the start of a new block (e.g., an "else" block, or * maybe the previous block didn't have a block terminator). * Try to distinguish those cases based on what comes next: */ if (m->post_indent != -1 && m->post_indent > indent) { /* * The following line is indented more. So it is likely * that this line is the start of a block. */ s->penalty += any_blanks ? RELATIVE_OUTDENT_WITH_BLANK_PENALTY : RELATIVE_OUTDENT_PENALTY; } else { /* * That was probably the end of a block. */ s->penalty += any_blanks ? RELATIVE_DEDENT_WITH_BLANK_PENALTY : RELATIVE_DEDENT_PENALTY; } } } static int score_cmp(struct split_score *s1, struct split_score *s2) { /* -1 if s1.effective_indent < s2->effective_indent, etc. */ int cmp_indents = ((s1->effective_indent > s2->effective_indent) - (s1->effective_indent < s2->effective_indent)); return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty); } /* * Represent a group of changed lines in an xdfile_t (i.e., a contiguous group * of lines that was inserted or deleted from the corresponding version of the * file). We consider there to be such a group at the beginning of the file, at * the end of the file, and between any two unchanged lines, though most such * groups will usually be empty. * * If the first line in a group is equal to the line following the group, then * the group can be slid down. Similarly, if the last line in a group is equal * to the line preceding the group, then the group can be slid up. See * group_slide_down() and group_slide_up(). * * Note that loops that are testing for changed lines in xdf->rchg do not need * index bounding since the array is prepared with a zero at position -1 and N. */ struct xdlgroup { /* * The index of the first changed line in the group, or the index of * the unchanged line above which the (empty) group is located. */ long start; /* * The index of the first unchanged line after the group. For an empty * group, end is equal to start. */ long end; }; /* * Initialize g to point at the first group in xdf. */ static void group_init(xdfile_t *xdf, struct xdlgroup *g) { g->start = g->end = 0; while (xdf->rchg[g->end]) g->end++; } /* * Move g to describe the next (possibly empty) group in xdf and return 0. If g * is already at the end of the file, do nothing and return -1. */ XDL_INLINE(int) group_next(xdfile_t *xdf, struct xdlgroup *g) { if (g->end == xdf->nrec) return -1; g->start = g->end + 1; for (g->end = g->start; xdf->rchg[g->end]; g->end++) ; return 0; } /* * Move g to describe the previous (possibly empty) group in xdf and return 0. * If g is already at the beginning of the file, do nothing and return -1. */ XDL_INLINE(int) group_previous(xdfile_t *xdf, struct xdlgroup *g) { if (g->start == 0) return -1; g->end = g->start - 1; for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--) ; return 0; } /* * If g can be slid toward the end of the file, do so, and if it bumps into a * following group, expand this group to include it. Return 0 on success or -1 * if g cannot be slid down. */ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags) { if (g->end < xdf->nrec && recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) { xdf->rchg[g->start++] = 0; xdf->rchg[g->end++] = 1; while (xdf->rchg[g->end]) g->end++; return 0; } else { return -1; } } /* * If g can be slid toward the beginning of the file, do so, and if it bumps * into a previous group, expand this group to include it. Return 0 on success * or -1 if g cannot be slid up. */ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags) { if (g->start > 0 && recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) { xdf->rchg[--g->start] = 1; xdf->rchg[--g->end] = 0; while (xdf->rchg[g->start - 1]) g->start--; return 0; } else { return -1; } } static void xdl_bug(const char *msg) { fprintf(stderr, "BUG: %s\n", msg); exit(1); } /* * Move back and forward change groups for a consistent and pretty diff output. * This also helps in finding joinable change groups and reducing the diff * size. */ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { struct xdlgroup g, go; long earliest_end, end_matching_other; long groupsize; group_init(xdf, &g); group_init(xdfo, &go); while (1) { /* If the group is empty in the to-be-compacted file, skip it: */ if (g.end == g.start) goto next; /* * Now shift the change up and then down as far as possible in * each direction. If it bumps into any other changes, merge them. */ do { groupsize = g.end - g.start; /* * Keep track of the last "end" index that causes this * group to align with a group of changed lines in the * other file. -1 indicates that we haven't found such * a match yet: */ end_matching_other = -1; /* Shift the group backward as much as possible: */ while (!group_slide_up(xdf, &g, flags)) if (group_previous(xdfo, &go)) xdl_bug("group sync broken sliding up"); /* * This is this highest that this group can be shifted. * Record its end index: */ earliest_end = g.end; if (go.end > go.start) end_matching_other = g.end; /* Now shift the group forward as far as possible: */ while (1) { if (group_slide_down(xdf, &g, flags)) break; if (group_next(xdfo, &go)) xdl_bug("group sync broken sliding down"); if (go.end > go.start) end_matching_other = g.end; } } while (groupsize != g.end - g.start); /* * If the group can be shifted, then we can possibly use this * freedom to produce a more intuitive diff. * * The group is currently shifted as far down as possible, so the * heuristics below only have to handle upwards shifts. */ if (g.end == earliest_end) { /* no shifting was possible */ } else if (end_matching_other != -1) { /* * Move the possibly merged group of changes back to line * up with the last group of changes from the other file * that it can align with. */ while (go.end == go.start) { if (group_slide_up(xdf, &g, flags)) xdl_bug("match disappeared"); if (group_previous(xdfo, &go)) xdl_bug("group sync broken sliding to match"); } } else if (flags & XDF_INDENT_HEURISTIC) { /* * Indent heuristic: a group of pure add/delete lines * implies two splits, one between the end of the "before" * context and the start of the group, and another between * the end of the group and the beginning of the "after" * context. Some splits are aesthetically better and some * are worse. We compute a badness "score" for each split, * and add the scores for the two splits to define a * "score" for each position that the group can be shifted * to. Then we pick the shift with the lowest score. */ long shift, best_shift = -1; struct split_score best_score; for (shift = earliest_end; shift <= g.end; shift++) { struct split_measurement m; struct split_score score = {0, 0}; measure_split(xdf, shift, &m); score_add_split(&m, &score); measure_split(xdf, shift - groupsize, &m); score_add_split(&m, &score); if (best_shift == -1 || score_cmp(&score, &best_score) <= 0) { best_score.effective_indent = score.effective_indent; best_score.penalty = score.penalty; best_shift = shift; } } while (g.end > best_shift) { if (group_slide_up(xdf, &g, flags)) xdl_bug("best shift unreached"); if (group_previous(xdfo, &go)) xdl_bug("group sync broken sliding to blank line"); } } next: /* Move past the just-processed group: */ if (group_next(xdf, &g)) break; if (group_next(xdfo, &go)) xdl_bug("group sync broken moving to next group"); } if (!group_next(xdfo, &go)) xdl_bug("group sync broken at end of file"); return 0; } int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) { xdchange_t *cscr = NULL, *xch; char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg; long i1, i2, l1, l2; /* * Trivial. Collects "groups" of changes and creates an edit script. */ for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--) if (rchg1[i1 - 1] || rchg2[i2 - 1]) { for (l1 = i1; rchg1[i1 - 1]; i1--); for (l2 = i2; rchg2[i2 - 1]; i2--); if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) { xdl_free_script(cscr); return -1; } cscr = xch; } *xscr = cscr; return 0; } void xdl_free_script(xdchange_t *xscr) { xdchange_t *xch; while ((xch = xscr) != NULL) { xscr = xscr->next; xdl_free(xch); } } static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg) { xdchange_t *xch, *xche; (void)xe; for (xch = xscr; xch; xch = xche->next) { xche = xdl_get_hunk(&xch, xecfg); if (!xch) break; if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1, xch->i2, xche->i2 + xche->chg2 - xch->i2, ecb->priv) < 0) return -1; } return 0; } static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) { xdchange_t *xch; for (xch = xscr; xch; xch = xch->next) { int ignore = 1; xrecord_t **rec; long i; rec = &xe->xdf1.recs[xch->i1]; for (i = 0; i < xch->chg1 && ignore; i++) ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); rec = &xe->xdf2.recs[xch->i2]; for (i = 0; i < xch->chg2 && ignore; i++) ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); xch->ignore = ignore; } } int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb) { xdchange_t *xscr; xdfenv_t xe; emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff; if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) { return -1; } if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 || xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 || xdl_build_script(&xe, &xscr) < 0) { xdl_free_env(&xe); return -1; } if (xscr) { if (xpp->flags & XDF_IGNORE_BLANK_LINES) xdl_mark_ignorable(xscr, &xe, xpp->flags); if (ef(&xe, xscr, ecb, xecfg) < 0) { xdl_free_script(xscr); xdl_free_env(&xe); return -1; } xdl_free_script(xscr); } xdl_free_env(&xe); return 0; } git2r/src/libgit2/src/xdiff/xemit.h0000644000175000017500000000226414125111754016751 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #if !defined(XEMIT_H) #define XEMIT_H typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg); xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg); int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg); #endif /* #if !defined(XEMIT_H) */ git2r/src/libgit2/src/xdiff/xutils.c0000644000175000017500000002244514125111754017151 0ustar nileshnilesh/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . * * Davide Libenzi * */ #include "xinclude.h" long xdl_bogosqrt(long n) { long i; /* * Classical integer square root approximation using shifts. */ for (i = 1; n > 0; n >>= 2) i <<= 1; return i; } int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, xdemitcb_t *ecb) { int i = 2; mmbuffer_t mb[3]; mb[0].ptr = (char *) pre; mb[0].size = psize; mb[1].ptr = (char *) rec; mb[1].size = size; if (size > 0 && rec[size - 1] != '\n') { mb[2].ptr = (char *) "\n\\ No newline at end of file\n"; mb[2].size = strlen(mb[2].ptr); i++; } if (ecb->outf(ecb->priv, mb, i) < 0) { return -1; } return 0; } void *xdl_mmfile_first(mmfile_t *mmf, long *size) { *size = mmf->size; return mmf->ptr; } long xdl_mmfile_size(mmfile_t *mmf) { return mmf->size; } int xdl_cha_init(chastore_t *cha, long isize, long icount) { cha->head = cha->tail = NULL; cha->isize = isize; cha->nsize = icount * isize; cha->ancur = cha->sncur = NULL; cha->scurr = 0; return 0; } void xdl_cha_free(chastore_t *cha) { chanode_t *cur, *tmp; for (cur = cha->head; (tmp = cur) != NULL;) { cur = cur->next; xdl_free(tmp); } } void *xdl_cha_alloc(chastore_t *cha) { chanode_t *ancur; void *data; if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) { if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) { return NULL; } ancur->icurr = 0; ancur->next = NULL; if (cha->tail) cha->tail->next = ancur; if (!cha->head) cha->head = ancur; cha->tail = ancur; cha->ancur = ancur; } data = (char *) ancur + sizeof(chanode_t) + ancur->icurr; ancur->icurr += cha->isize; return data; } long xdl_guess_lines(mmfile_t *mf, long sample) { long nl = 0, size, tsize = 0; char const *data, *cur, *top; if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) { for (top = data + size; nl < sample && cur < top; ) { nl++; if (!(cur = memchr(cur, '\n', top - cur))) cur = top; else cur++; } tsize += (long) (cur - data); } if (nl && tsize) nl = xdl_mmfile_size(mf) / (tsize / nl); return nl + 1; } int xdl_blankline(const char *line, long size, long flags) { long i; if (!(flags & XDF_WHITESPACE_FLAGS)) return (size <= 1); for (i = 0; i < size && XDL_ISSPACE(line[i]); i++) ; return (i == size); } /* * Have we eaten everything on the line, except for an optional * CR at the very end? */ static int ends_with_optional_cr(const char *l, long s, long i) { int complete = s && l[s-1] == '\n'; if (complete) s--; if (s == i) return 1; /* do not ignore CR at the end of an incomplete line */ if (complete && s == i + 1 && l[i] == '\r') return 1; return 0; } int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) { int i1, i2; if (s1 == s2 && !memcmp(l1, l2, s1)) return 1; if (!(flags & XDF_WHITESPACE_FLAGS)) return 0; i1 = 0; i2 = 0; /* * -w matches everything that matches with -b, and -b in turn * matches everything that matches with --ignore-space-at-eol, * which in turn matches everything that matches with --ignore-cr-at-eol. * * Each flavor of ignoring needs different logic to skip whitespaces * while we have both sides to compare. */ if (flags & XDF_IGNORE_WHITESPACE) { goto skip_ws; while (i1 < s1 && i2 < s2) { if (l1[i1++] != l2[i2++]) return 0; skip_ws: while (i1 < s1 && XDL_ISSPACE(l1[i1])) i1++; while (i2 < s2 && XDL_ISSPACE(l2[i2])) i2++; } } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { while (i1 < s1 && i2 < s2) { if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) { /* Skip matching spaces and try again */ while (i1 < s1 && XDL_ISSPACE(l1[i1])) i1++; while (i2 < s2 && XDL_ISSPACE(l2[i2])) i2++; continue; } if (l1[i1++] != l2[i2++]) return 0; } } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) { while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { i1++; i2++; } } else if (flags & XDF_IGNORE_CR_AT_EOL) { /* Find the first difference and see how the line ends */ while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { i1++; i2++; } return (ends_with_optional_cr(l1, s1, i1) && ends_with_optional_cr(l2, s2, i2)); } /* * After running out of one side, the remaining side must have * nothing but whitespace for the lines to match. Note that * ignore-whitespace-at-eol case may break out of the loop * while there still are characters remaining on both lines. */ if (i1 < s1) { while (i1 < s1 && XDL_ISSPACE(l1[i1])) i1++; if (s1 != i1) return 0; } if (i2 < s2) { while (i2 < s2 && XDL_ISSPACE(l2[i2])) i2++; return (s2 == i2); } return 1; } static unsigned long xdl_hash_record_with_whitespace(char const **data, char const *top, long flags) { unsigned long ha = 5381; char const *ptr = *data; int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL; for (; ptr < top && *ptr != '\n'; ptr++) { if (cr_at_eol_only) { /* do not ignore CR at the end of an incomplete line */ if (*ptr == '\r' && (ptr + 1 < top && ptr[1] == '\n')) continue; } else if (XDL_ISSPACE(*ptr)) { const char *ptr2 = ptr; int at_eol; while (ptr + 1 < top && XDL_ISSPACE(ptr[1]) && ptr[1] != '\n') ptr++; at_eol = (top <= ptr + 1 || ptr[1] == '\n'); if (flags & XDF_IGNORE_WHITESPACE) ; /* already handled */ else if (flags & XDF_IGNORE_WHITESPACE_CHANGE && !at_eol) { ha += (ha << 5); ha ^= (unsigned long) ' '; } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL && !at_eol) { while (ptr2 != ptr + 1) { ha += (ha << 5); ha ^= (unsigned long) *ptr2; ptr2++; } } continue; } ha += (ha << 5); ha ^= (unsigned long) *ptr; } *data = ptr < top ? ptr + 1: ptr; return ha; } unsigned long xdl_hash_record(char const **data, char const *top, long flags) { unsigned long ha = 5381; char const *ptr = *data; if (flags & XDF_WHITESPACE_FLAGS) return xdl_hash_record_with_whitespace(data, top, flags); for (; ptr < top && *ptr != '\n'; ptr++) { ha += (ha << 5); ha ^= (unsigned long) *ptr; } *data = ptr < top ? ptr + 1: ptr; return ha; } unsigned int xdl_hashbits(unsigned int size) { unsigned int val = 1, bits = 0; for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++); return bits ? bits: 1; } int xdl_num_out(char *out, long val) { char *ptr, *str = out; char buf[32]; ptr = buf + sizeof(buf) - 1; *ptr = '\0'; if (val < 0) { *--ptr = '-'; val = -val; } for (; val && ptr > buf; val /= 10) *--ptr = "0123456789"[val % 10]; if (*ptr) for (; *ptr; ptr++, str++) *str = *ptr; else *str++ = '0'; *str = '\0'; return str - out; } int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, const char *func, long funclen, xdemitcb_t *ecb) { int nb = 0; mmbuffer_t mb; char buf[128]; memcpy(buf, "@@ -", 4); nb += 4; nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1); if (c1 != 1) { memcpy(buf + nb, ",", 1); nb += 1; nb += xdl_num_out(buf + nb, c1); } memcpy(buf + nb, " +", 2); nb += 2; nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1); if (c2 != 1) { memcpy(buf + nb, ",", 1); nb += 1; nb += xdl_num_out(buf + nb, c2); } memcpy(buf + nb, " @@", 3); nb += 3; if (func && funclen) { buf[nb++] = ' '; if (funclen > (long)(sizeof(buf) - nb - 1)) funclen = sizeof(buf) - nb - 1; memcpy(buf + nb, func, funclen); nb += funclen; } buf[nb++] = '\n'; mb.ptr = buf; mb.size = nb; if (ecb->outf(ecb->priv, &mb, 1) < 0) return -1; return 0; } int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, int line1, int count1, int line2, int count2) { /* * This probably does not work outside Git, since * we have a very simple mmfile structure. * * Note: ideally, we would reuse the prepared environment, but * the libxdiff interface does not (yet) allow for diffing only * ranges of lines instead of the whole files. */ mmfile_t subfile1, subfile2; xdfenv_t env; subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr; subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr + diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr; subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr; subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr + diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr; if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0) return -1; memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1); memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2); xdl_free_env(&env); return 0; } git2r/src/libgit2/src/mailmap.h0000644000175000017500000000207414125111754016142 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_mailmap_h__ #define INCLUDE_mailmap_h__ #include "git2/mailmap.h" #include "vector.h" /* * A mailmap is stored as a sorted vector of 'git_mailmap_entry's. These entries * are sorted first by 'replace_email', and then by 'replace_name'. NULL * replace_names are ordered first. * * Looking up a name and email in the mailmap is done with a binary search. */ struct git_mailmap { git_vector entries; }; /* Single entry parsed from a mailmap */ typedef struct git_mailmap_entry { char *real_name; /**< the real name (may be NULL) */ char *real_email; /**< the real email (may be NULL) */ char *replace_name; /**< the name to replace (may be NULL) */ char *replace_email; /**< the email to replace */ } git_mailmap_entry; const git_mailmap_entry *git_mailmap_entry_lookup( const git_mailmap *mm, const char *name, const char *email); #endif git2r/src/libgit2/src/zstream.h0000644000175000017500000000255614125111754016214 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_zstream_h__ #define INCLUDE_zstream_h__ #include "common.h" #include #include "buffer.h" typedef enum { GIT_ZSTREAM_INFLATE, GIT_ZSTREAM_DEFLATE, } git_zstream_t; typedef struct { z_stream z; git_zstream_t type; const char *in; size_t in_len; int flush; int zerr; } git_zstream; #define GIT_ZSTREAM_INIT {{0}} int git_zstream_init(git_zstream *zstream, git_zstream_t type); void git_zstream_free(git_zstream *zstream); int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len); size_t git_zstream_suggest_output_len(git_zstream *zstream); /* get as much output as is available in the input buffer */ int git_zstream_get_output_chunk( void *out, size_t *out_len, git_zstream *zstream); /* get all the output from the entire input buffer */ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream); bool git_zstream_done(git_zstream *zstream); bool git_zstream_eos(git_zstream *zstream); void git_zstream_reset(git_zstream *zstream); int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len); int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len); #endif git2r/src/libgit2/src/oidarray.c0000644000175000017500000000153414125111754016327 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "oidarray.h" #include "git2/oidarray.h" #include "array.h" void git_oidarray_dispose(git_oidarray *arr) { git__free(arr->ids); } void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array) { arr->count = array->size; arr->ids = array->ptr; } void git_oidarray__reverse(git_oidarray *arr) { size_t i; git_oid tmp; for (i = 0; i < arr->count / 2; i++) { git_oid_cpy(&tmp, &arr->ids[i]); git_oid_cpy(&arr->ids[i], &arr->ids[(arr->count-1)-i]); git_oid_cpy(&arr->ids[(arr->count-1)-i], &tmp); } } #ifndef GIT_DEPRECATE_HARD void git_oidarray_free(git_oidarray *arr) { git_oidarray_dispose(arr); } #endif git2r/src/libgit2/src/fetchhead.h0000644000175000017500000000147614125111754016442 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_fetchhead_h__ #define INCLUDE_fetchhead_h__ #include "common.h" #include "oid.h" #include "vector.h" typedef struct git_fetchhead_ref { git_oid oid; unsigned int is_merge; char *ref_name; char *remote_url; } git_fetchhead_ref; int git_fetchhead_ref_create( git_fetchhead_ref **fetchhead_ref_out, git_oid *oid, unsigned int is_merge, const char *ref_name, const char *remote_url); int git_fetchhead_ref_cmp(const void *a, const void *b); int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs); void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref); #endif git2r/src/libgit2/src/midx.h0000644000175000017500000000547114125111754015467 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_midx_h__ #define INCLUDE_midx_h__ #include "common.h" #include #include "git2/sys/midx.h" #include "map.h" #include "mwindow.h" #include "odb.h" /* * A multi-pack-index file. * * This file contains a merged index for multiple independent .pack files. This * can help speed up locating objects without requiring a garbage collection * cycle to create a single .pack file. * * Support for this feature was added in git 2.21, and requires the * `core.multiPackIndex` config option to be set. */ typedef struct git_midx_file { git_map index_map; /* The table of Packfile Names. */ git_vector packfile_names; /* The OID Fanout table. */ const uint32_t *oid_fanout; /* The total number of objects in the index. */ uint32_t num_objects; /* The OID Lookup table. */ git_oid *oid_lookup; /* The Object Offsets table. Each entry has two 4-byte fields with the pack index and the offset. */ const unsigned char *object_offsets; /* The Object Large Offsets table. */ const unsigned char *object_large_offsets; /* The number of entries in the Object Large Offsets table. Each entry has an 8-byte with an offset */ size_t num_object_large_offsets; /* The trailer of the file. Contains the SHA1-checksum of the whole file. */ git_oid checksum; /* something like ".git/objects/pack/multi-pack-index". */ git_buf filename; } git_midx_file; /* * An entry in the multi-pack-index file. Similar in purpose to git_pack_entry. */ typedef struct git_midx_entry { /* The index within idx->packfile_names where the packfile name can be found. */ size_t pack_index; /* The offset within the .pack file where the requested object is found. */ off64_t offset; /* The SHA-1 hash of the requested object. */ git_oid sha1; } git_midx_entry; /* * A writer for `multi-pack-index` files. */ struct git_midx_writer { /* * The path of the directory where the .pack/.idx files are stored. The * `multi-pack-index` file will be written to the same directory. */ git_buf pack_dir; /* The list of `git_pack_file`s. */ git_vector packs; }; int git_midx_open( git_midx_file **idx_out, const char *path); bool git_midx_needs_refresh( const git_midx_file *idx, const char *path); int git_midx_entry_find( git_midx_entry *e, git_midx_file *idx, const git_oid *short_oid, size_t len); int git_midx_foreach_entry( git_midx_file *idx, git_odb_foreach_cb cb, void *data); int git_midx_close(git_midx_file *idx); void git_midx_free(git_midx_file *idx); /* This is exposed for use in the fuzzers. */ int git_midx_parse( git_midx_file *idx, const unsigned char *data, size_t size); #endif git2r/src/libgit2/src/reflog.c0000644000175000017500000001216014125111754015770 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "reflog.h" #include "repository.h" #include "filebuf.h" #include "signature.h" #include "refdb.h" #include "git2/sys/refdb_backend.h" #include "git2/sys/reflog.h" void git_reflog_entry__free(git_reflog_entry *entry) { git_signature_free(entry->committer); git__free(entry->msg); git__free(entry); } void git_reflog_free(git_reflog *reflog) { size_t i; git_reflog_entry *entry; if (reflog == NULL) return; if (reflog->db) GIT_REFCOUNT_DEC(reflog->db, git_refdb__free); for (i=0; i < reflog->entries.length; i++) { entry = git_vector_get(&reflog->entries, i); git_reflog_entry__free(entry); } git_vector_free(&reflog->entries); git__free(reflog->ref_name); git__free(reflog); } int git_reflog_read(git_reflog **reflog, git_repository *repo, const char *name) { git_refdb *refdb; int error; GIT_ASSERT_ARG(reflog); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) return error; return git_refdb_reflog_read(reflog, refdb, name); } int git_reflog_write(git_reflog *reflog) { git_refdb *db; GIT_ASSERT_ARG(reflog); GIT_ASSERT_ARG(reflog->db); db = reflog->db; return db->backend->reflog_write(db->backend, reflog); } int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg) { const git_reflog_entry *previous; git_reflog_entry *entry; GIT_ASSERT_ARG(reflog); GIT_ASSERT_ARG(new_oid); GIT_ASSERT_ARG(committer); entry = git__calloc(1, sizeof(git_reflog_entry)); GIT_ERROR_CHECK_ALLOC(entry); if ((git_signature_dup(&entry->committer, committer)) < 0) goto cleanup; if (msg != NULL) { size_t i, msglen = strlen(msg); if ((entry->msg = git__strndup(msg, msglen)) == NULL) goto cleanup; /* * Replace all newlines with spaces, except for * the final trailing newline. */ for (i = 0; i < msglen; i++) if (entry->msg[i] == '\n') entry->msg[i] = ' '; } previous = git_reflog_entry_byindex(reflog, 0); if (previous == NULL) git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO); else git_oid_cpy(&entry->oid_old, &previous->oid_cur); git_oid_cpy(&entry->oid_cur, new_oid); if (git_vector_insert(&reflog->entries, entry) < 0) goto cleanup; return 0; cleanup: git_reflog_entry__free(entry); return -1; } int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name) { git_refdb *refdb; int error; if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) return -1; return refdb->backend->reflog_rename(refdb->backend, old_name, new_name); } int git_reflog_delete(git_repository *repo, const char *name) { git_refdb *refdb; int error; if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) return -1; return refdb->backend->reflog_delete(refdb->backend, name); } size_t git_reflog_entrycount(git_reflog *reflog) { GIT_ASSERT_ARG_WITH_RETVAL(reflog, 0); return reflog->entries.length; } const git_reflog_entry *git_reflog_entry_byindex(const git_reflog *reflog, size_t idx) { GIT_ASSERT_ARG_WITH_RETVAL(reflog, NULL); if (idx >= reflog->entries.length) return NULL; return git_vector_get( &reflog->entries, reflog_inverse_index(idx, reflog->entries.length)); } const git_oid *git_reflog_entry_id_old(const git_reflog_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL); return &entry->oid_old; } const git_oid *git_reflog_entry_id_new(const git_reflog_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL); return &entry->oid_cur; } const git_signature *git_reflog_entry_committer(const git_reflog_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL); return entry->committer; } const char *git_reflog_entry_message(const git_reflog_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL); return entry->msg; } int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry) { size_t entrycount; git_reflog_entry *entry, *previous; entrycount = git_reflog_entrycount(reflog); entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx); if (entry == NULL) { git_error_set(GIT_ERROR_REFERENCE, "no reflog entry at index %"PRIuZ, idx); return GIT_ENOTFOUND; } git_reflog_entry__free(entry); if (git_vector_remove( &reflog->entries, reflog_inverse_index(idx, entrycount)) < 0) return -1; if (!rewrite_previous_entry) return 0; /* No need to rewrite anything when removing the most recent entry */ if (idx == 0) return 0; /* Have the latest entry just been dropped? */ if (entrycount == 1) return 0; entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1); /* If the oldest entry has just been removed... */ if (idx == entrycount - 1) { /* ...clear the oid_old member of the "new" oldest entry */ if (git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO) < 0) return -1; return 0; } previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx); git_oid_cpy(&entry->oid_old, &previous->oid_cur); return 0; } git2r/src/libgit2/src/blame.h0000644000175000017500000000405114125111754015577 0ustar nileshnilesh#ifndef INCLUDE_blame_h__ #define INCLUDE_blame_h__ #include "common.h" #include "git2/blame.h" #include "vector.h" #include "diff.h" #include "array.h" #include "git2/oid.h" /* * One blob in a commit that is being suspected */ typedef struct git_blame__origin { int refcnt; struct git_blame__origin *previous; git_commit *commit; git_blob *blob; char path[GIT_FLEX_ARRAY]; } git_blame__origin; /* * Each group of lines is described by a git_blame__entry; it can be split * as we pass blame to the parents. They form a linked list in the * scoreboard structure, sorted by the target line number. */ typedef struct git_blame__entry { struct git_blame__entry *prev; struct git_blame__entry *next; /* the first line of this group in the final image; * internally all line numbers are 0 based. */ size_t lno; /* how many lines this group has */ size_t num_lines; /* the commit that introduced this group into the final image */ git_blame__origin *suspect; /* true if the suspect is truly guilty; false while we have not * checked if the group came from one of its parents. */ bool guilty; /* true if the entry has been scanned for copies in the current parent */ bool scanned; /* the line number of the first line of this group in the * suspect's file; internally all line numbers are 0 based. */ size_t s_lno; /* how significant this entry is -- cached to avoid * scanning the lines over and over. */ unsigned score; /* Whether this entry has been tracked to a boundary commit. */ bool is_boundary; } git_blame__entry; struct git_blame { char *path; git_repository *repository; git_mailmap *mailmap; git_blame_options options; git_vector hunks; git_vector paths; git_blob *final_blob; git_array_t(size_t) line_index; size_t current_diff_line; git_blame_hunk *current_hunk; /* Scoreboard fields */ git_commit *final; git_blame__entry *ent; int num_lines; const char *final_buf; size_t final_buf_size; }; git_blame *git_blame__alloc( git_repository *repo, git_blame_options opts, const char *path); #endif git2r/src/libgit2/src/sysdir.h0000644000175000017500000000656614125111754016051 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sysdir_h__ #define INCLUDE_sysdir_h__ #include "common.h" #include "posix.h" #include "buffer.h" /** * Find a "global" file (i.e. one in a user's home directory). * * @param path buffer to write the full path into * @param filename name of file to find in the home directory * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error */ extern int git_sysdir_find_global_file(git_buf *path, const char *filename); /** * Find an "XDG" file (i.e. one in user's XDG config path). * * @param path buffer to write the full path into * @param filename name of file to find in the home directory * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error */ extern int git_sysdir_find_xdg_file(git_buf *path, const char *filename); /** * Find a "system" file (i.e. one shared for all users of the system). * * @param path buffer to write the full path into * @param filename name of file to find in the home directory * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error */ extern int git_sysdir_find_system_file(git_buf *path, const char *filename); /** * Find a "ProgramData" file (i.e. one in %PROGRAMDATA%) * * @param path buffer to write the full path into * @param filename name of file to find in the ProgramData directory * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error */ extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename); /** * Find template directory. * * @param path buffer to write the full path into * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error */ extern int git_sysdir_find_template_dir(git_buf *path); /** * Expand the name of a "global" file (i.e. one in a user's home * directory). Unlike `find_global_file` (above), this makes no * attempt to check for the existence of the file, and is useful if * you want the full path regardless of existence. * * @param path buffer to write the full path into * @param filename name of file in the home directory * @return 0 on success or -1 on error */ extern int git_sysdir_expand_global_file(git_buf *path, const char *filename); typedef enum { GIT_SYSDIR_SYSTEM = 0, GIT_SYSDIR_GLOBAL = 1, GIT_SYSDIR_XDG = 2, GIT_SYSDIR_PROGRAMDATA = 3, GIT_SYSDIR_TEMPLATE = 4, GIT_SYSDIR__MAX = 5, } git_sysdir_t; /** * Configures global data for configuration file search paths. * * @return 0 on success, <0 on failure */ extern int git_sysdir_global_init(void); /** * Get the search path for global/system/xdg files * * @param out pointer to git_buf containing search path * @param which which list of paths to return * @return 0 on success, <0 on failure */ extern int git_sysdir_get(const git_buf **out, git_sysdir_t which); /** * Set search paths for global/system/xdg files * * The first occurrence of the magic string "$PATH" in the new value will * be replaced with the old value of the search path. * * @param which Which search path to modify * @param paths New search path (separated by GIT_PATH_LIST_SEPARATOR) * @return 0 on success, <0 on failure (allocation error) */ extern int git_sysdir_set(git_sysdir_t which, const char *paths); #endif git2r/src/libgit2/src/idxmap.c0000644000175000017500000000705314125111754016001 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "idxmap.h" #define kmalloc git__malloc #define kcalloc git__calloc #define krealloc git__realloc #define kreallocarray git__reallocarray #define kfree git__free #include "khash.h" __KHASH_TYPE(idx, const git_index_entry *, git_index_entry *) __KHASH_TYPE(idxicase, const git_index_entry *, git_index_entry *) /* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */ static kh_inline khint_t idxentry_hash(const git_index_entry *e) { const char *s = e->path; khint_t h = (khint_t)git__tolower(*s); if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s); return h + GIT_INDEX_ENTRY_STAGE(e); } #define idxentry_equal(a, b) (GIT_INDEX_ENTRY_STAGE(a) == GIT_INDEX_ENTRY_STAGE(b) && strcmp(a->path, b->path) == 0) #define idxentry_icase_equal(a, b) (GIT_INDEX_ENTRY_STAGE(a) == GIT_INDEX_ENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0) __KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal) __KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal) int git_idxmap_new(git_idxmap **out) { *out = kh_init(idx); GIT_ERROR_CHECK_ALLOC(*out); return 0; } int git_idxmap_icase_new(git_idxmap_icase **out) { *out = kh_init(idxicase); GIT_ERROR_CHECK_ALLOC(*out); return 0; } void git_idxmap_free(git_idxmap *map) { kh_destroy(idx, map); } void git_idxmap_icase_free(git_idxmap_icase *map) { kh_destroy(idxicase, map); } void git_idxmap_clear(git_idxmap *map) { kh_clear(idx, map); } void git_idxmap_icase_clear(git_idxmap_icase *map) { kh_clear(idxicase, map); } int git_idxmap_resize(git_idxmap *map, size_t size) { if (!git__is_uint32(size) || kh_resize(idx, map, (khiter_t)size) < 0) { git_error_set_oom(); return -1; } return 0; } int git_idxmap_icase_resize(git_idxmap_icase *map, size_t size) { if (!git__is_uint32(size) || kh_resize(idxicase, map, (khiter_t)size) < 0) { git_error_set_oom(); return -1; } return 0; } void *git_idxmap_get(git_idxmap *map, const git_index_entry *key) { size_t idx = kh_get(idx, map, key); if (idx == kh_end(map) || !kh_exist(map, idx)) return NULL; return kh_val(map, idx); } int git_idxmap_set(git_idxmap *map, const git_index_entry *key, void *value) { size_t idx; int rval; idx = kh_put(idx, map, key, &rval); if (rval < 0) return -1; if (rval == 0) kh_key(map, idx) = key; kh_val(map, idx) = value; return 0; } int git_idxmap_icase_set(git_idxmap_icase *map, const git_index_entry *key, void *value) { size_t idx; int rval; idx = kh_put(idxicase, map, key, &rval); if (rval < 0) return -1; if (rval == 0) kh_key(map, idx) = key; kh_val(map, idx) = value; return 0; } void *git_idxmap_icase_get(git_idxmap_icase *map, const git_index_entry *key) { size_t idx = kh_get(idxicase, map, key); if (idx == kh_end(map) || !kh_exist(map, idx)) return NULL; return kh_val(map, idx); } int git_idxmap_delete(git_idxmap *map, const git_index_entry *key) { khiter_t idx = kh_get(idx, map, key); if (idx == kh_end(map)) return GIT_ENOTFOUND; kh_del(idx, map, idx); return 0; } int git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key) { khiter_t idx = kh_get(idxicase, map, key); if (idx == kh_end(map)) return GIT_ENOTFOUND; kh_del(idxicase, map, idx); return 0; } git2r/src/libgit2/src/pathspec.h0000644000175000017500000000364214125111754016333 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_pathspec_h__ #define INCLUDE_pathspec_h__ #include "common.h" #include "git2/pathspec.h" #include "buffer.h" #include "vector.h" #include "pool.h" #include "array.h" /* public compiled pathspec */ struct git_pathspec { git_refcount rc; char *prefix; git_vector pathspec; git_pool pool; }; enum { PATHSPEC_DATATYPE_STRINGS = 0, PATHSPEC_DATATYPE_DIFF = 1, }; typedef git_array_t(char *) git_pathspec_string_array_t; /* public interface to pathspec matching */ struct git_pathspec_match_list { git_pathspec *pathspec; git_array_t(void *) matches; git_pathspec_string_array_t failures; git_pool pool; int datatype; }; /* what is the common non-wildcard prefix for all items in the pathspec */ extern char *git_pathspec_prefix(const git_strarray *pathspec); /* is there anything in the spec that needs to be filtered on */ extern bool git_pathspec_is_empty(const git_strarray *pathspec); /* build a vector of fnmatch patterns to evaluate efficiently */ extern int git_pathspec__vinit( git_vector *vspec, const git_strarray *strspec, git_pool *strpool); /* free data from the pathspec vector */ extern void git_pathspec__vfree(git_vector *vspec); #define GIT_PATHSPEC_NOMATCH ((size_t)-1) /* * Match a path against the vectorized pathspec. * The matched pathspec is passed back into the `matched_pathspec` parameter, * unless it is passed as NULL by the caller. */ extern bool git_pathspec__match( const git_vector *vspec, const char *path, bool disable_fnmatch, bool casefold, const char **matched_pathspec, size_t *matched_at); /* easy pathspec setup */ extern int git_pathspec__init(git_pathspec *ps, const git_strarray *paths); extern void git_pathspec__clear(git_pathspec *ps); #endif git2r/src/libgit2/src/hash/0000755000175000017500000000000014146643373015303 5ustar nileshnileshgit2r/src/libgit2/src/hash/sha1/0000755000175000017500000000000014146643373016137 5ustar nileshnileshgit2r/src/libgit2/src/hash/sha1/collisiondetect.h0000644000175000017500000000063214125111754021463 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_hash_sha1_collisiondetect_h__ #define INCLUDE_hash_sha1_collisiondetect_h__ #include "hash/sha1.h" #include "sha1dc/sha1.h" struct git_hash_sha1_ctx { SHA1_CTX c; }; #endif git2r/src/libgit2/src/hash/sha1/openssl.h0000644000175000017500000000061114125111754017757 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_hash_sha1_openssl_h__ #define INCLUDE_hash_sha1_openssl_h__ #include "hash/sha1.h" #include struct git_hash_sha1_ctx { SHA_CTX c; }; #endif git2r/src/libgit2/src/hash/sha1/sha1dc/0000755000175000017500000000000014125111754017270 5ustar nileshnileshgit2r/src/libgit2/src/hash/sha1/sha1dc/ubc_check.c0000644000175000017500000013536714125111754021361 0ustar nileshnilesh/*** * Copyright 2017 Marc Stevens , Dan Shumow * Distributed under the MIT Software License. * See accompanying file LICENSE.txt or copy at * https://opensource.org/licenses/MIT ***/ /* // this file was generated by the 'parse_bitrel' program in the tools section // using the data files from directory 'tools/data/3565' // // sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check // dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper) // dm[80] is the expanded message block XOR-difference defined by the DV // testt is the step to do the recompression from for collision detection // maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check // // ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs // it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met // thus one needs to do the recompression check for each DV that has its bit set // // ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded // a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c // ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section */ #ifndef SHA1DC_NO_STANDARD_INCLUDES #include #endif #ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C #include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C #endif #include "ubc_check.h" static const uint32_t DV_I_43_0_bit = (uint32_t)(1) << 0; static const uint32_t DV_I_44_0_bit = (uint32_t)(1) << 1; static const uint32_t DV_I_45_0_bit = (uint32_t)(1) << 2; static const uint32_t DV_I_46_0_bit = (uint32_t)(1) << 3; static const uint32_t DV_I_46_2_bit = (uint32_t)(1) << 4; static const uint32_t DV_I_47_0_bit = (uint32_t)(1) << 5; static const uint32_t DV_I_47_2_bit = (uint32_t)(1) << 6; static const uint32_t DV_I_48_0_bit = (uint32_t)(1) << 7; static const uint32_t DV_I_48_2_bit = (uint32_t)(1) << 8; static const uint32_t DV_I_49_0_bit = (uint32_t)(1) << 9; static const uint32_t DV_I_49_2_bit = (uint32_t)(1) << 10; static const uint32_t DV_I_50_0_bit = (uint32_t)(1) << 11; static const uint32_t DV_I_50_2_bit = (uint32_t)(1) << 12; static const uint32_t DV_I_51_0_bit = (uint32_t)(1) << 13; static const uint32_t DV_I_51_2_bit = (uint32_t)(1) << 14; static const uint32_t DV_I_52_0_bit = (uint32_t)(1) << 15; static const uint32_t DV_II_45_0_bit = (uint32_t)(1) << 16; static const uint32_t DV_II_46_0_bit = (uint32_t)(1) << 17; static const uint32_t DV_II_46_2_bit = (uint32_t)(1) << 18; static const uint32_t DV_II_47_0_bit = (uint32_t)(1) << 19; static const uint32_t DV_II_48_0_bit = (uint32_t)(1) << 20; static const uint32_t DV_II_49_0_bit = (uint32_t)(1) << 21; static const uint32_t DV_II_49_2_bit = (uint32_t)(1) << 22; static const uint32_t DV_II_50_0_bit = (uint32_t)(1) << 23; static const uint32_t DV_II_50_2_bit = (uint32_t)(1) << 24; static const uint32_t DV_II_51_0_bit = (uint32_t)(1) << 25; static const uint32_t DV_II_51_2_bit = (uint32_t)(1) << 26; static const uint32_t DV_II_52_0_bit = (uint32_t)(1) << 27; static const uint32_t DV_II_53_0_bit = (uint32_t)(1) << 28; static const uint32_t DV_II_54_0_bit = (uint32_t)(1) << 29; static const uint32_t DV_II_55_0_bit = (uint32_t)(1) << 30; static const uint32_t DV_II_56_0_bit = (uint32_t)(1) << 31; dv_info_t sha1_dvs[] = { {1,43,0,58,0,0, { 0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161,0x80000599 } } , {1,44,0,58,0,1, { 0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161 } } , {1,45,0,58,0,2, { 0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803 } } , {1,46,0,58,0,3, { 0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c } } , {1,46,2,58,0,4, { 0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a,0x00000132 } } , {1,47,0,58,0,5, { 0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6 } } , {1,47,2,58,0,6, { 0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a } } , {1,48,0,58,0,7, { 0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408 } } , {1,48,2,58,0,8, { 0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020 } } , {1,49,0,58,0,9, { 0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164 } } , {1,49,2,58,0,10, { 0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590 } } , {1,50,0,65,0,11, { 0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018 } } , {1,50,2,65,0,12, { 0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060 } } , {1,51,0,65,0,13, { 0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202 } } , {1,51,2,65,0,14, { 0xa0000003,0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a } } , {1,52,0,65,0,15, { 0x04000010,0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012 } } , {2,45,0,58,0,16, { 0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054,0x00000967 } } , {2,46,0,58,0,17, { 0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054 } } , {2,46,2,58,0,18, { 0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6,0x0000106a,0x00000b90,0x00000152 } } , {2,47,0,58,0,19, { 0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4 } } , {2,48,0,58,0,20, { 0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a } } , {2,49,0,58,0,21, { 0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d } } , {2,49,2,58,0,22, { 0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6 } } , {2,50,0,65,0,23, { 0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b } } , {2,50,2,65,0,24, { 0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c } } , {2,51,0,65,0,25, { 0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b } } , {2,51,2,65,0,26, { 0x00000043,0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e } } , {2,52,0,65,0,27, { 0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014 } } , {2,53,0,65,0,28, { 0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089 } } , {2,54,0,65,0,29, { 0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107 } } , {2,55,0,65,0,30, { 0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b } } , {2,56,0,65,0,31, { 0x2600001a,0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046 } } , {0,0,0,0,0,0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}} }; void ubc_check(const uint32_t W[80], uint32_t dvmask[1]) { uint32_t mask = ~((uint32_t)(0)); mask &= (((((W[44]^W[45])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_I_51_0_bit|DV_I_52_0_bit|DV_II_45_0_bit|DV_II_46_0_bit|DV_II_50_0_bit|DV_II_51_0_bit)); mask &= (((((W[49]^W[50])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_II_45_0_bit|DV_II_50_0_bit|DV_II_51_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); mask &= (((((W[48]^W[49])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_52_0_bit|DV_II_49_0_bit|DV_II_50_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); mask &= ((((W[47]^(W[50]>>25))&(1<<4))-(1<<4)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); mask &= (((((W[47]^W[48])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_51_0_bit|DV_II_48_0_bit|DV_II_49_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); mask &= (((((W[46]>>4)^(W[49]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_50_0_bit|DV_II_55_0_bit)); mask &= (((((W[46]^W[47])>>29)&1)-1) | ~(DV_I_43_0_bit|DV_I_50_0_bit|DV_II_47_0_bit|DV_II_48_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); mask &= (((((W[45]>>4)^(W[48]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_49_0_bit|DV_II_54_0_bit)); mask &= (((((W[45]^W[46])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_51_0_bit|DV_II_52_0_bit)); mask &= (((((W[44]>>4)^(W[47]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_53_0_bit)); mask &= (((((W[43]>>4)^(W[46]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_52_0_bit)); mask &= (((((W[43]^W[44])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_I_50_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_49_0_bit|DV_II_50_0_bit)); mask &= (((((W[42]>>4)^(W[45]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_51_0_bit)); mask &= (((((W[41]>>4)^(W[44]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_50_0_bit)); mask &= (((((W[40]^W[41])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_47_0_bit|DV_I_48_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_56_0_bit)); mask &= (((((W[54]^W[55])>>29)&1)-1) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_50_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); mask &= (((((W[53]^W[54])>>29)&1)-1) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_49_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); mask &= (((((W[52]^W[53])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); mask &= ((((W[50]^(W[53]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_48_0_bit|DV_II_54_0_bit)); mask &= (((((W[50]^W[51])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_II_46_0_bit|DV_II_51_0_bit|DV_II_52_0_bit|DV_II_56_0_bit)); mask &= ((((W[49]^(W[52]>>25))&(1<<4))-(1<<4)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_47_0_bit|DV_II_53_0_bit)); mask &= ((((W[48]^(W[51]>>25))&(1<<4))-(1<<4)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_52_0_bit)); mask &= (((((W[42]^W[43])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); mask &= (((((W[41]^W[42])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_48_0_bit)); mask &= (((((W[40]>>4)^(W[43]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_50_0_bit|DV_II_49_0_bit|DV_II_56_0_bit)); mask &= (((((W[39]>>4)^(W[42]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_49_0_bit|DV_II_48_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) mask &= (((((W[38]>>4)^(W[41]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); mask &= (((((W[37]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_47_0_bit|DV_II_46_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)) mask &= (((((W[55]^W[56])>>29)&1)-1) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)) mask &= ((((W[52]^(W[55]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)); if (mask & (DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)) mask &= ((((W[51]^(W[54]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)) mask &= (((((W[51]^W[52])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); if (mask & (DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)) mask &= (((((W[36]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)); if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)) mask &= ((0-(((W[53]^W[56])>>29)&1)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); if (mask & (DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)) mask &= ((0-(((W[51]^W[54])>>29)&1)) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)); if (mask & (DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)) mask &= ((0-(((W[50]^W[52])>>29)&1)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)); if (mask & (DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)) mask &= ((0-(((W[49]^W[51])>>29)&1)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)); if (mask & (DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)) mask &= ((0-(((W[48]^W[50])>>29)&1)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)); if (mask & (DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)) mask &= ((0-(((W[47]^W[49])>>29)&1)) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)); if (mask & (DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)) mask &= ((0-(((W[46]^W[48])>>29)&1)) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)); mask &= ((((W[45]^W[47])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit|DV_I_51_2_bit)); if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)) mask &= ((0-(((W[45]^W[47])>>29)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)); mask &= (((((W[44]^W[46])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit|DV_I_50_2_bit)); if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)) mask &= ((0-(((W[44]^W[46])>>29)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)); mask &= ((0-((W[41]^(W[42]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_II_46_2_bit|DV_II_51_2_bit)); mask &= ((0-((W[40]^(W[41]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_51_2_bit|DV_II_50_2_bit)); if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)) mask &= ((0-(((W[40]^W[42])>>4)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)); mask &= ((0-((W[39]^(W[40]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_50_2_bit|DV_II_49_2_bit)); if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)) mask &= ((0-(((W[39]^W[41])>>4)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) mask &= ((0-(((W[38]^W[40])>>4)&1)) | ~(DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); if (mask & (DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)) mask &= ((0-(((W[37]^W[39])>>4)&1)) | ~(DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); mask &= ((0-((W[36]^(W[37]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_50_2_bit|DV_II_46_2_bit)); if (mask & (DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)) mask &= (((((W[35]>>4)^(W[39]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)); if (mask & (DV_I_48_0_bit|DV_II_48_0_bit)) mask &= ((0-((W[63]^(W[64]>>5))&(1<<0))) | ~(DV_I_48_0_bit|DV_II_48_0_bit)); if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) mask &= ((0-((W[63]^(W[64]>>5))&(1<<1))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); if (mask & (DV_I_47_0_bit|DV_II_47_0_bit)) mask &= ((0-((W[62]^(W[63]>>5))&(1<<0))) | ~(DV_I_47_0_bit|DV_II_47_0_bit)); if (mask & (DV_I_46_0_bit|DV_II_46_0_bit)) mask &= ((0-((W[61]^(W[62]>>5))&(1<<0))) | ~(DV_I_46_0_bit|DV_II_46_0_bit)); mask &= ((0-((W[61]^(W[62]>>5))&(1<<2))) | ~(DV_I_46_2_bit|DV_II_46_2_bit)); if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) mask &= ((0-((W[60]^(W[61]>>5))&(1<<0))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); if (mask & (DV_II_51_0_bit|DV_II_54_0_bit)) mask &= (((((W[58]^W[59])>>29)&1)-1) | ~(DV_II_51_0_bit|DV_II_54_0_bit)); if (mask & (DV_II_50_0_bit|DV_II_53_0_bit)) mask &= (((((W[57]^W[58])>>29)&1)-1) | ~(DV_II_50_0_bit|DV_II_53_0_bit)); if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) mask &= ((((W[56]^(W[59]>>25))&(1<<4))-(1<<4)) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); if (mask & (DV_II_51_0_bit|DV_II_52_0_bit)) mask &= ((0-(((W[56]^W[59])>>29)&1)) | ~(DV_II_51_0_bit|DV_II_52_0_bit)); if (mask & (DV_II_49_0_bit|DV_II_52_0_bit)) mask &= (((((W[56]^W[57])>>29)&1)-1) | ~(DV_II_49_0_bit|DV_II_52_0_bit)); if (mask & (DV_II_51_0_bit|DV_II_53_0_bit)) mask &= ((((W[55]^(W[58]>>25))&(1<<4))-(1<<4)) | ~(DV_II_51_0_bit|DV_II_53_0_bit)); if (mask & (DV_II_50_0_bit|DV_II_52_0_bit)) mask &= ((((W[54]^(W[57]>>25))&(1<<4))-(1<<4)) | ~(DV_II_50_0_bit|DV_II_52_0_bit)); if (mask & (DV_II_49_0_bit|DV_II_51_0_bit)) mask &= ((((W[53]^(W[56]>>25))&(1<<4))-(1<<4)) | ~(DV_II_49_0_bit|DV_II_51_0_bit)); mask &= ((((W[51]^(W[50]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); mask &= ((((W[48]^W[50])&(1<<6))-(1<<6)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); if (mask & (DV_I_51_0_bit|DV_I_52_0_bit)) mask &= ((0-(((W[48]^W[55])>>29)&1)) | ~(DV_I_51_0_bit|DV_I_52_0_bit)); mask &= ((((W[47]^W[49])&(1<<6))-(1<<6)) | ~(DV_I_49_2_bit|DV_I_51_2_bit)); mask &= ((((W[48]^(W[47]>>5))&(1<<1))-(1<<1)) | ~(DV_I_47_2_bit|DV_II_51_2_bit)); mask &= ((((W[46]^W[48])&(1<<6))-(1<<6)) | ~(DV_I_48_2_bit|DV_I_50_2_bit)); mask &= ((((W[47]^(W[46]>>5))&(1<<1))-(1<<1)) | ~(DV_I_46_2_bit|DV_II_50_2_bit)); mask &= ((0-((W[44]^(W[45]>>5))&(1<<1))) | ~(DV_I_51_2_bit|DV_II_49_2_bit)); mask &= ((((W[43]^W[45])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit)); mask &= (((((W[42]^W[44])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit)); mask &= ((((W[43]^(W[42]>>5))&(1<<1))-(1<<1)) | ~(DV_II_46_2_bit|DV_II_51_2_bit)); mask &= ((((W[42]^(W[41]>>5))&(1<<1))-(1<<1)) | ~(DV_I_51_2_bit|DV_II_50_2_bit)); mask &= ((((W[41]^(W[40]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_49_2_bit)); if (mask & (DV_I_52_0_bit|DV_II_51_0_bit)) mask &= ((((W[39]^(W[43]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_51_0_bit)); if (mask & (DV_I_51_0_bit|DV_II_50_0_bit)) mask &= ((((W[38]^(W[42]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_50_0_bit)); if (mask & (DV_I_48_2_bit|DV_I_51_2_bit)) mask &= ((0-((W[37]^(W[38]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_I_51_2_bit)); if (mask & (DV_I_50_0_bit|DV_II_49_0_bit)) mask &= ((((W[37]^(W[41]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_II_49_0_bit)); if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) mask &= ((0-((W[36]^W[38])&(1<<4))) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); mask &= ((0-((W[35]^(W[36]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_49_2_bit)); if (mask & (DV_I_51_0_bit|DV_II_47_0_bit)) mask &= ((((W[35]^(W[39]>>25))&(1<<3))-(1<<3)) | ~(DV_I_51_0_bit|DV_II_47_0_bit)); if (mask) { if (mask & DV_I_43_0_bit) if ( !((W[61]^(W[62]>>5)) & (1<<1)) || !(!((W[59]^(W[63]>>25)) & (1<<5))) || !((W[58]^(W[63]>>30)) & (1<<0)) ) mask &= ~DV_I_43_0_bit; if (mask & DV_I_44_0_bit) if ( !((W[62]^(W[63]>>5)) & (1<<1)) || !(!((W[60]^(W[64]>>25)) & (1<<5))) || !((W[59]^(W[64]>>30)) & (1<<0)) ) mask &= ~DV_I_44_0_bit; if (mask & DV_I_46_2_bit) mask &= ((~((W[40]^W[42])>>2)) | ~DV_I_46_2_bit); if (mask & DV_I_47_2_bit) if ( !((W[62]^(W[63]>>5)) & (1<<2)) || !(!((W[41]^W[43]) & (1<<6))) ) mask &= ~DV_I_47_2_bit; if (mask & DV_I_48_2_bit) if ( !((W[63]^(W[64]>>5)) & (1<<2)) || !(!((W[48]^(W[49]<<5)) & (1<<6))) ) mask &= ~DV_I_48_2_bit; if (mask & DV_I_49_2_bit) if ( !(!((W[49]^(W[50]<<5)) & (1<<6))) || !((W[42]^W[50]) & (1<<1)) || !(!((W[39]^(W[40]<<5)) & (1<<6))) || !((W[38]^W[40]) & (1<<1)) ) mask &= ~DV_I_49_2_bit; if (mask & DV_I_50_0_bit) mask &= ((((W[36]^W[37])<<7)) | ~DV_I_50_0_bit); if (mask & DV_I_50_2_bit) mask &= ((((W[43]^W[51])<<11)) | ~DV_I_50_2_bit); if (mask & DV_I_51_0_bit) mask &= ((((W[37]^W[38])<<9)) | ~DV_I_51_0_bit); if (mask & DV_I_51_2_bit) if ( !(!((W[51]^(W[52]<<5)) & (1<<6))) || !(!((W[49]^W[51]) & (1<<6))) || !(!((W[37]^(W[37]>>5)) & (1<<1))) || !(!((W[35]^(W[39]>>25)) & (1<<5))) ) mask &= ~DV_I_51_2_bit; if (mask & DV_I_52_0_bit) mask &= ((((W[38]^W[39])<<11)) | ~DV_I_52_0_bit); if (mask & DV_II_46_2_bit) mask &= ((((W[47]^W[51])<<17)) | ~DV_II_46_2_bit); if (mask & DV_II_48_0_bit) if ( !(!((W[36]^(W[40]>>25)) & (1<<3))) || !((W[35]^(W[40]<<2)) & (1<<30)) ) mask &= ~DV_II_48_0_bit; if (mask & DV_II_49_0_bit) if ( !(!((W[37]^(W[41]>>25)) & (1<<3))) || !((W[36]^(W[41]<<2)) & (1<<30)) ) mask &= ~DV_II_49_0_bit; if (mask & DV_II_49_2_bit) if ( !(!((W[53]^(W[54]<<5)) & (1<<6))) || !(!((W[51]^W[53]) & (1<<6))) || !((W[50]^W[54]) & (1<<1)) || !(!((W[45]^(W[46]<<5)) & (1<<6))) || !(!((W[37]^(W[41]>>25)) & (1<<5))) || !((W[36]^(W[41]>>30)) & (1<<0)) ) mask &= ~DV_II_49_2_bit; if (mask & DV_II_50_0_bit) if ( !((W[55]^W[58]) & (1<<29)) || !(!((W[38]^(W[42]>>25)) & (1<<3))) || !((W[37]^(W[42]<<2)) & (1<<30)) ) mask &= ~DV_II_50_0_bit; if (mask & DV_II_50_2_bit) if ( !(!((W[54]^(W[55]<<5)) & (1<<6))) || !(!((W[52]^W[54]) & (1<<6))) || !((W[51]^W[55]) & (1<<1)) || !((W[45]^W[47]) & (1<<1)) || !(!((W[38]^(W[42]>>25)) & (1<<5))) || !((W[37]^(W[42]>>30)) & (1<<0)) ) mask &= ~DV_II_50_2_bit; if (mask & DV_II_51_0_bit) if ( !(!((W[39]^(W[43]>>25)) & (1<<3))) || !((W[38]^(W[43]<<2)) & (1<<30)) ) mask &= ~DV_II_51_0_bit; if (mask & DV_II_51_2_bit) if ( !(!((W[55]^(W[56]<<5)) & (1<<6))) || !(!((W[53]^W[55]) & (1<<6))) || !((W[52]^W[56]) & (1<<1)) || !((W[46]^W[48]) & (1<<1)) || !(!((W[39]^(W[43]>>25)) & (1<<5))) || !((W[38]^(W[43]>>30)) & (1<<0)) ) mask &= ~DV_II_51_2_bit; if (mask & DV_II_52_0_bit) if ( !(!((W[59]^W[60]) & (1<<29))) || !(!((W[40]^(W[44]>>25)) & (1<<3))) || !(!((W[40]^(W[44]>>25)) & (1<<4))) || !((W[39]^(W[44]<<2)) & (1<<30)) ) mask &= ~DV_II_52_0_bit; if (mask & DV_II_53_0_bit) if ( !((W[58]^W[61]) & (1<<29)) || !(!((W[57]^(W[61]>>25)) & (1<<4))) || !(!((W[41]^(W[45]>>25)) & (1<<3))) || !(!((W[41]^(W[45]>>25)) & (1<<4))) ) mask &= ~DV_II_53_0_bit; if (mask & DV_II_54_0_bit) if ( !(!((W[58]^(W[62]>>25)) & (1<<4))) || !(!((W[42]^(W[46]>>25)) & (1<<3))) || !(!((W[42]^(W[46]>>25)) & (1<<4))) ) mask &= ~DV_II_54_0_bit; if (mask & DV_II_55_0_bit) if ( !(!((W[59]^(W[63]>>25)) & (1<<4))) || !(!((W[57]^(W[59]>>25)) & (1<<4))) || !(!((W[43]^(W[47]>>25)) & (1<<3))) || !(!((W[43]^(W[47]>>25)) & (1<<4))) ) mask &= ~DV_II_55_0_bit; if (mask & DV_II_56_0_bit) if ( !(!((W[60]^(W[64]>>25)) & (1<<4))) || !(!((W[44]^(W[48]>>25)) & (1<<3))) || !(!((W[44]^(W[48]>>25)) & (1<<4))) ) mask &= ~DV_II_56_0_bit; } dvmask[0]=mask; } #ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C #include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C #endif git2r/src/libgit2/src/hash/sha1/sha1dc/sha1.h0000644000175000017500000001003014125111754020267 0ustar nileshnilesh/*** * Copyright 2017 Marc Stevens , Dan Shumow * Distributed under the MIT Software License. * See accompanying file LICENSE.txt or copy at * https://opensource.org/licenses/MIT ***/ #ifndef SHA1DC_SHA1_H #define SHA1DC_SHA1_H #if defined(__cplusplus) extern "C" { #endif #ifndef SHA1DC_NO_STANDARD_INCLUDES #include #endif /* sha-1 compression function that takes an already expanded message, and additionally store intermediate states */ /* only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is defined in ubc_check.h */ void sha1_compression_states(uint32_t[5], const uint32_t[16], uint32_t[80], uint32_t[80][5]); /* // Function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]). // Where 0 <= T < 80 // me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference.) // state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block. // The function will return: // ihvin: The reconstructed input chaining value. // ihvout: The reconstructed output chaining value. */ typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*); /* A callback function type that can be set to be called when a collision block has been found: */ /* void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t ihvin2[5], const uint32_t m1[80], const uint32_t m2[80]) */ typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*); /* The SHA-1 context. */ typedef struct { uint64_t total; uint32_t ihv[5]; unsigned char buffer[64]; int found_collision; int safe_hash; int detect_coll; int ubc_check; int reduced_round_coll; collision_block_callback callback; uint32_t ihv1[5]; uint32_t ihv2[5]; uint32_t m1[80]; uint32_t m2[80]; uint32_t states[80][5]; } SHA1_CTX; /* Initialize SHA-1 context. */ void SHA1DCInit(SHA1_CTX*); /* Function to enable safe SHA-1 hashing: Collision attacks are thwarted by hashing a detected near-collision block 3 times. Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: The best collision attacks against SHA-1 have complexity about 2^60, thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. An attacker would be better off using a generic birthday search of complexity 2^80. Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected, but it will result in a different SHA-1 hash for messages where a collision attack was detected. This will automatically invalidate SHA-1 based digital signature forgeries. Enabled by default. */ void SHA1DCSetSafeHash(SHA1_CTX*, int); /* Function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up). Enabled by default */ void SHA1DCSetUseUBC(SHA1_CTX*, int); /* Function to disable or enable the use of Collision Detection. Enabled by default. */ void SHA1DCSetUseDetectColl(SHA1_CTX*, int); /* function to disable or enable the detection of reduced-round SHA-1 collisions */ /* disabled by default */ void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX*, int); /* function to set a callback function, pass NULL to disable */ /* by default no callback set */ void SHA1DCSetCallback(SHA1_CTX*, collision_block_callback); /* update SHA-1 context with buffer contents */ void SHA1DCUpdate(SHA1_CTX*, const char*, size_t); /* obtain SHA-1 hash from SHA-1 context */ /* returns: 0 = no collision detected, otherwise = collision found => warn user for active attack */ int SHA1DCFinal(unsigned char[20], SHA1_CTX*); #if defined(__cplusplus) } #endif #ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H #include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H #endif #endif git2r/src/libgit2/src/hash/sha1/sha1dc/sha1.c0000644000175000017500000016054614125111754020304 0ustar nileshnilesh/*** * Copyright 2017 Marc Stevens , Dan Shumow (danshu@microsoft.com) * Distributed under the MIT Software License. * See accompanying file LICENSE.txt or copy at * https://opensource.org/licenses/MIT ***/ #ifndef SHA1DC_NO_STANDARD_INCLUDES #include #include #include #include #include /* make sure macros like _BIG_ENDIAN visible */ #endif #ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C #include SHA1DC_CUSTOM_INCLUDE_SHA1_C #endif #ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT #define SHA1DC_INIT_SAFE_HASH_DEFAULT 1 #endif #include "sha1.h" #include "ubc_check.h" #if (defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \ defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \ defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \ defined(__386) || defined(_M_X64) || defined(_M_AMD64)) #define SHA1DC_ON_INTEL_LIKE_PROCESSOR #endif /* Because Little-Endian architectures are most common, we only set SHA1DC_BIGENDIAN if one of these conditions is met. Note that all MSFT platforms are little endian, so none of these will be defined under the MSC compiler. If you are compiling on a big endian platform and your compiler does not define one of these, you will have to add whatever macros your tool chain defines to indicate Big-Endianness. */ #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) /* * Should detect Big Endian under GCC since at least 4.6.0 (gcc svn * rev #165881). See * https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html * * This also works under clang since 3.2, it copied the GCC-ism. See * clang.git's 3b198a97d2 ("Preprocessor: add __BYTE_ORDER__ * predefined macro", 2012-07-27) */ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define SHA1DC_BIGENDIAN #endif /* Not under GCC-alike */ #elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) /* * Should detect Big Endian under glibc.git since 14245eb70e ("entered * into RCS", 1992-11-25). Defined in which will have been * brought in by standard headers. See glibc.git and * https://sourceforge.net/p/predef/wiki/Endianness/ */ #if __BYTE_ORDER == __BIG_ENDIAN #define SHA1DC_BIGENDIAN #endif /* Not under GCC-alike or glibc */ #elif defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) /* * *BSD and newlib (embeded linux, cygwin, etc). * the defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) part prevents * this condition from matching with Solaris/sparc. * (Solaris defines only one endian macro) */ #if _BYTE_ORDER == _BIG_ENDIAN #define SHA1DC_BIGENDIAN #endif /* Not under GCC-alike or glibc or *BSD or newlib */ #elif (defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \ defined(__sparc)) /* * Should define Big Endian for a whitelist of known processors. See * https://sourceforge.net/p/predef/wiki/Endianness/ and * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html */ #define SHA1DC_BIGENDIAN /* Not under GCC-alike or glibc or *BSD or newlib or */ #elif (defined(_AIX) || defined(__hpux)) /* * Defines Big Endian on a whitelist of OSs that are known to be Big * Endian-only. See * https://public-inbox.org/git/93056823-2740-d072-1ebd-46b440b33d7e@felt.demon.nl/ */ #define SHA1DC_BIGENDIAN /* Not under GCC-alike or glibc or *BSD or newlib or or */ #elif defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR) /* * As a last resort before we do anything else we're not 100% sure * about below, we blacklist specific processors here. We could add * more, see e.g. https://wiki.debian.org/ArchitectureSpecificsMemo */ #else /* Not under GCC-alike or glibc or *BSD or newlib or or or */ /* We do nothing more here for now */ /*#error "Uncomment this to see if you fall through all the detection"*/ #endif /* Big Endian detection */ #if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN)) #undef SHA1DC_BIGENDIAN #endif #if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN)) #define SHA1DC_BIGENDIAN #endif /*ENDIANNESS SELECTION*/ #ifndef SHA1DC_FORCE_ALIGNED_ACCESS #if defined(SHA1DC_FORCE_UNALIGNED_ACCESS) || defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR) #define SHA1DC_ALLOW_UNALIGNED_ACCESS #endif /*UNALIGNED ACCESS DETECTION*/ #endif /*FORCE ALIGNED ACCESS*/ #define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n)))) #define rotate_left(x,n) (((x)<<(n))|((x)>>(32-(n)))) #define sha1_bswap32(x) \ {x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); x = (x << 16) | (x >> 16);} #define sha1_mix(W, t) (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1)) #ifdef SHA1DC_BIGENDIAN #define sha1_load(m, t, temp) { temp = m[t]; } #else #define sha1_load(m, t, temp) { temp = m[t]; sha1_bswap32(temp); } #endif #define sha1_store(W, t, x) *(volatile uint32_t *)&W[t] = x #define sha1_f1(b,c,d) ((d)^((b)&((c)^(d)))) #define sha1_f2(b,c,d) ((b)^(c)^(d)) #define sha1_f3(b,c,d) (((b)&(c))+((d)&((b)^(c)))) #define sha1_f4(b,c,d) ((b)^(c)^(d)) #define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; } #define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; } #define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; } #define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; } #define SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, t, temp) \ {sha1_load(m, t, temp); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30);} #define SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(a, b, c, d, e, W, t, temp) \ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30); } #define SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, t, temp) \ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1; b = rotate_left(b, 30); } #define SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, t, temp) \ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC; b = rotate_left(b, 30); } #define SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, t, temp) \ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6; b = rotate_left(b, 30); } #define SHA1_STORE_STATE(i) states[i][0] = a; states[i][1] = b; states[i][2] = c; states[i][3] = d; states[i][4] = e; #ifdef BUILDNOCOLLDETECTSHA1COMPRESSION void sha1_compression(uint32_t ihv[5], const uint32_t m[16]) { uint32_t W[80]; uint32_t a,b,c,d,e; unsigned i; memcpy(W, m, 16 * 4); for (i = 16; i < 80; ++i) W[i] = sha1_mix(W, i); a = ihv[0]; b = ihv[1]; c = ihv[2]; d = ihv[3]; e = ihv[4]; HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; } #endif /*BUILDNOCOLLDETECTSHA1COMPRESSION*/ static void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]) { uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; } void sha1_compression_states(uint32_t ihv[5], const uint32_t m[16], uint32_t W[80], uint32_t states[80][5]) { uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; uint32_t temp; #ifdef DOSTORESTATE00 SHA1_STORE_STATE(0) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 0, temp); #ifdef DOSTORESTATE01 SHA1_STORE_STATE(1) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 1, temp); #ifdef DOSTORESTATE02 SHA1_STORE_STATE(2) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 2, temp); #ifdef DOSTORESTATE03 SHA1_STORE_STATE(3) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 3, temp); #ifdef DOSTORESTATE04 SHA1_STORE_STATE(4) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 4, temp); #ifdef DOSTORESTATE05 SHA1_STORE_STATE(5) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 5, temp); #ifdef DOSTORESTATE06 SHA1_STORE_STATE(6) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 6, temp); #ifdef DOSTORESTATE07 SHA1_STORE_STATE(7) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 7, temp); #ifdef DOSTORESTATE08 SHA1_STORE_STATE(8) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 8, temp); #ifdef DOSTORESTATE09 SHA1_STORE_STATE(9) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 9, temp); #ifdef DOSTORESTATE10 SHA1_STORE_STATE(10) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 10, temp); #ifdef DOSTORESTATE11 SHA1_STORE_STATE(11) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 11, temp); #ifdef DOSTORESTATE12 SHA1_STORE_STATE(12) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 12, temp); #ifdef DOSTORESTATE13 SHA1_STORE_STATE(13) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 13, temp); #ifdef DOSTORESTATE14 SHA1_STORE_STATE(14) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 14, temp); #ifdef DOSTORESTATE15 SHA1_STORE_STATE(15) #endif SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 15, temp); #ifdef DOSTORESTATE16 SHA1_STORE_STATE(16) #endif SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(e, a, b, c, d, W, 16, temp); #ifdef DOSTORESTATE17 SHA1_STORE_STATE(17) #endif SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(d, e, a, b, c, W, 17, temp); #ifdef DOSTORESTATE18 SHA1_STORE_STATE(18) #endif SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(c, d, e, a, b, W, 18, temp); #ifdef DOSTORESTATE19 SHA1_STORE_STATE(19) #endif SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(b, c, d, e, a, W, 19, temp); #ifdef DOSTORESTATE20 SHA1_STORE_STATE(20) #endif SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 20, temp); #ifdef DOSTORESTATE21 SHA1_STORE_STATE(21) #endif SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 21, temp); #ifdef DOSTORESTATE22 SHA1_STORE_STATE(22) #endif SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 22, temp); #ifdef DOSTORESTATE23 SHA1_STORE_STATE(23) #endif SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 23, temp); #ifdef DOSTORESTATE24 SHA1_STORE_STATE(24) #endif SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 24, temp); #ifdef DOSTORESTATE25 SHA1_STORE_STATE(25) #endif SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 25, temp); #ifdef DOSTORESTATE26 SHA1_STORE_STATE(26) #endif SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 26, temp); #ifdef DOSTORESTATE27 SHA1_STORE_STATE(27) #endif SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 27, temp); #ifdef DOSTORESTATE28 SHA1_STORE_STATE(28) #endif SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 28, temp); #ifdef DOSTORESTATE29 SHA1_STORE_STATE(29) #endif SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 29, temp); #ifdef DOSTORESTATE30 SHA1_STORE_STATE(30) #endif SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 30, temp); #ifdef DOSTORESTATE31 SHA1_STORE_STATE(31) #endif SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 31, temp); #ifdef DOSTORESTATE32 SHA1_STORE_STATE(32) #endif SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 32, temp); #ifdef DOSTORESTATE33 SHA1_STORE_STATE(33) #endif SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 33, temp); #ifdef DOSTORESTATE34 SHA1_STORE_STATE(34) #endif SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 34, temp); #ifdef DOSTORESTATE35 SHA1_STORE_STATE(35) #endif SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 35, temp); #ifdef DOSTORESTATE36 SHA1_STORE_STATE(36) #endif SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 36, temp); #ifdef DOSTORESTATE37 SHA1_STORE_STATE(37) #endif SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 37, temp); #ifdef DOSTORESTATE38 SHA1_STORE_STATE(38) #endif SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 38, temp); #ifdef DOSTORESTATE39 SHA1_STORE_STATE(39) #endif SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 39, temp); #ifdef DOSTORESTATE40 SHA1_STORE_STATE(40) #endif SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 40, temp); #ifdef DOSTORESTATE41 SHA1_STORE_STATE(41) #endif SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 41, temp); #ifdef DOSTORESTATE42 SHA1_STORE_STATE(42) #endif SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 42, temp); #ifdef DOSTORESTATE43 SHA1_STORE_STATE(43) #endif SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 43, temp); #ifdef DOSTORESTATE44 SHA1_STORE_STATE(44) #endif SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 44, temp); #ifdef DOSTORESTATE45 SHA1_STORE_STATE(45) #endif SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 45, temp); #ifdef DOSTORESTATE46 SHA1_STORE_STATE(46) #endif SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 46, temp); #ifdef DOSTORESTATE47 SHA1_STORE_STATE(47) #endif SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 47, temp); #ifdef DOSTORESTATE48 SHA1_STORE_STATE(48) #endif SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 48, temp); #ifdef DOSTORESTATE49 SHA1_STORE_STATE(49) #endif SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 49, temp); #ifdef DOSTORESTATE50 SHA1_STORE_STATE(50) #endif SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 50, temp); #ifdef DOSTORESTATE51 SHA1_STORE_STATE(51) #endif SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 51, temp); #ifdef DOSTORESTATE52 SHA1_STORE_STATE(52) #endif SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 52, temp); #ifdef DOSTORESTATE53 SHA1_STORE_STATE(53) #endif SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 53, temp); #ifdef DOSTORESTATE54 SHA1_STORE_STATE(54) #endif SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 54, temp); #ifdef DOSTORESTATE55 SHA1_STORE_STATE(55) #endif SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 55, temp); #ifdef DOSTORESTATE56 SHA1_STORE_STATE(56) #endif SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 56, temp); #ifdef DOSTORESTATE57 SHA1_STORE_STATE(57) #endif SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 57, temp); #ifdef DOSTORESTATE58 SHA1_STORE_STATE(58) #endif SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 58, temp); #ifdef DOSTORESTATE59 SHA1_STORE_STATE(59) #endif SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 59, temp); #ifdef DOSTORESTATE60 SHA1_STORE_STATE(60) #endif SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 60, temp); #ifdef DOSTORESTATE61 SHA1_STORE_STATE(61) #endif SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 61, temp); #ifdef DOSTORESTATE62 SHA1_STORE_STATE(62) #endif SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 62, temp); #ifdef DOSTORESTATE63 SHA1_STORE_STATE(63) #endif SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 63, temp); #ifdef DOSTORESTATE64 SHA1_STORE_STATE(64) #endif SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 64, temp); #ifdef DOSTORESTATE65 SHA1_STORE_STATE(65) #endif SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 65, temp); #ifdef DOSTORESTATE66 SHA1_STORE_STATE(66) #endif SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 66, temp); #ifdef DOSTORESTATE67 SHA1_STORE_STATE(67) #endif SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 67, temp); #ifdef DOSTORESTATE68 SHA1_STORE_STATE(68) #endif SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 68, temp); #ifdef DOSTORESTATE69 SHA1_STORE_STATE(69) #endif SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 69, temp); #ifdef DOSTORESTATE70 SHA1_STORE_STATE(70) #endif SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 70, temp); #ifdef DOSTORESTATE71 SHA1_STORE_STATE(71) #endif SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 71, temp); #ifdef DOSTORESTATE72 SHA1_STORE_STATE(72) #endif SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 72, temp); #ifdef DOSTORESTATE73 SHA1_STORE_STATE(73) #endif SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 73, temp); #ifdef DOSTORESTATE74 SHA1_STORE_STATE(74) #endif SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 74, temp); #ifdef DOSTORESTATE75 SHA1_STORE_STATE(75) #endif SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 75, temp); #ifdef DOSTORESTATE76 SHA1_STORE_STATE(76) #endif SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 76, temp); #ifdef DOSTORESTATE77 SHA1_STORE_STATE(77) #endif SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 77, temp); #ifdef DOSTORESTATE78 SHA1_STORE_STATE(78) #endif SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 78, temp); #ifdef DOSTORESTATE79 SHA1_STORE_STATE(79) #endif SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 79, temp); ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; } #define SHA1_RECOMPRESS(t) \ static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \ { \ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \ if (t > 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \ if (t > 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \ if (t > 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \ if (t > 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \ if (t > 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \ if (t > 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \ if (t > 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \ if (t > 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \ if (t > 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \ if (t > 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \ if (t > 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \ if (t > 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \ if (t > 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \ if (t > 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \ if (t > 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \ if (t > 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \ if (t > 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \ if (t > 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \ if (t > 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \ if (t > 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \ if (t > 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \ if (t > 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \ if (t > 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \ if (t > 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \ if (t > 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \ if (t > 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \ if (t > 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \ if (t > 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \ if (t > 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \ if (t > 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \ if (t > 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \ if (t > 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \ if (t > 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \ if (t > 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \ if (t > 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \ if (t > 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \ if (t > 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \ if (t > 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \ if (t > 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \ if (t > 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \ if (t > 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \ if (t > 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \ if (t > 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \ if (t > 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \ if (t > 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \ if (t > 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \ if (t > 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \ if (t > 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \ if (t > 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \ if (t > 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \ if (t > 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \ if (t > 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \ if (t > 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \ if (t > 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \ if (t > 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \ if (t > 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \ if (t > 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \ if (t > 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \ if (t > 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \ if (t > 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \ if (t > 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \ if (t > 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \ if (t > 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \ if (t > 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \ if (t > 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \ if (t > 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \ if (t > 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \ if (t > 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \ if (t > 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \ if (t > 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \ if (t > 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \ if (t > 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \ if (t > 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \ if (t > 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \ if (t > 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \ if (t > 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \ if (t > 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \ if (t > 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \ if (t > 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \ if (t > 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \ ihvin[0] = a; ihvin[1] = b; ihvin[2] = c; ihvin[3] = d; ihvin[4] = e; \ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; \ if (t <= 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \ if (t <= 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \ if (t <= 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \ if (t <= 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \ if (t <= 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \ if (t <= 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \ if (t <= 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \ if (t <= 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \ if (t <= 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \ if (t <= 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \ if (t <= 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \ if (t <= 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \ if (t <= 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \ if (t <= 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \ if (t <= 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \ if (t <= 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \ if (t <= 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \ if (t <= 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \ if (t <= 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \ if (t <= 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \ if (t <= 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \ if (t <= 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \ if (t <= 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \ if (t <= 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \ if (t <= 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \ if (t <= 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \ if (t <= 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \ if (t <= 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \ if (t <= 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \ if (t <= 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \ if (t <= 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \ if (t <= 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \ if (t <= 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \ if (t <= 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \ if (t <= 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \ if (t <= 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \ if (t <= 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \ if (t <= 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \ if (t <= 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \ if (t <= 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \ if (t <= 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \ if (t <= 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \ if (t <= 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \ if (t <= 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \ if (t <= 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \ if (t <= 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \ if (t <= 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \ if (t <= 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \ if (t <= 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \ if (t <= 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \ if (t <= 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \ if (t <= 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \ if (t <= 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \ if (t <= 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \ if (t <= 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \ if (t <= 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \ if (t <= 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \ if (t <= 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \ if (t <= 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \ if (t <= 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \ if (t <= 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \ if (t <= 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \ if (t <= 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \ if (t <= 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \ if (t <= 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \ if (t <= 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \ if (t <= 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \ if (t <= 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \ if (t <= 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \ if (t <= 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \ if (t <= 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \ if (t <= 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \ if (t <= 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \ if (t <= 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \ if (t <= 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \ if (t <= 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \ if (t <= 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \ if (t <= 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \ if (t <= 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \ if (t <= 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \ ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \ } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4127) /* Compiler complains about the checks in the above macro being constant. */ #endif #ifdef DOSTORESTATE0 SHA1_RECOMPRESS(0) #endif #ifdef DOSTORESTATE1 SHA1_RECOMPRESS(1) #endif #ifdef DOSTORESTATE2 SHA1_RECOMPRESS(2) #endif #ifdef DOSTORESTATE3 SHA1_RECOMPRESS(3) #endif #ifdef DOSTORESTATE4 SHA1_RECOMPRESS(4) #endif #ifdef DOSTORESTATE5 SHA1_RECOMPRESS(5) #endif #ifdef DOSTORESTATE6 SHA1_RECOMPRESS(6) #endif #ifdef DOSTORESTATE7 SHA1_RECOMPRESS(7) #endif #ifdef DOSTORESTATE8 SHA1_RECOMPRESS(8) #endif #ifdef DOSTORESTATE9 SHA1_RECOMPRESS(9) #endif #ifdef DOSTORESTATE10 SHA1_RECOMPRESS(10) #endif #ifdef DOSTORESTATE11 SHA1_RECOMPRESS(11) #endif #ifdef DOSTORESTATE12 SHA1_RECOMPRESS(12) #endif #ifdef DOSTORESTATE13 SHA1_RECOMPRESS(13) #endif #ifdef DOSTORESTATE14 SHA1_RECOMPRESS(14) #endif #ifdef DOSTORESTATE15 SHA1_RECOMPRESS(15) #endif #ifdef DOSTORESTATE16 SHA1_RECOMPRESS(16) #endif #ifdef DOSTORESTATE17 SHA1_RECOMPRESS(17) #endif #ifdef DOSTORESTATE18 SHA1_RECOMPRESS(18) #endif #ifdef DOSTORESTATE19 SHA1_RECOMPRESS(19) #endif #ifdef DOSTORESTATE20 SHA1_RECOMPRESS(20) #endif #ifdef DOSTORESTATE21 SHA1_RECOMPRESS(21) #endif #ifdef DOSTORESTATE22 SHA1_RECOMPRESS(22) #endif #ifdef DOSTORESTATE23 SHA1_RECOMPRESS(23) #endif #ifdef DOSTORESTATE24 SHA1_RECOMPRESS(24) #endif #ifdef DOSTORESTATE25 SHA1_RECOMPRESS(25) #endif #ifdef DOSTORESTATE26 SHA1_RECOMPRESS(26) #endif #ifdef DOSTORESTATE27 SHA1_RECOMPRESS(27) #endif #ifdef DOSTORESTATE28 SHA1_RECOMPRESS(28) #endif #ifdef DOSTORESTATE29 SHA1_RECOMPRESS(29) #endif #ifdef DOSTORESTATE30 SHA1_RECOMPRESS(30) #endif #ifdef DOSTORESTATE31 SHA1_RECOMPRESS(31) #endif #ifdef DOSTORESTATE32 SHA1_RECOMPRESS(32) #endif #ifdef DOSTORESTATE33 SHA1_RECOMPRESS(33) #endif #ifdef DOSTORESTATE34 SHA1_RECOMPRESS(34) #endif #ifdef DOSTORESTATE35 SHA1_RECOMPRESS(35) #endif #ifdef DOSTORESTATE36 SHA1_RECOMPRESS(36) #endif #ifdef DOSTORESTATE37 SHA1_RECOMPRESS(37) #endif #ifdef DOSTORESTATE38 SHA1_RECOMPRESS(38) #endif #ifdef DOSTORESTATE39 SHA1_RECOMPRESS(39) #endif #ifdef DOSTORESTATE40 SHA1_RECOMPRESS(40) #endif #ifdef DOSTORESTATE41 SHA1_RECOMPRESS(41) #endif #ifdef DOSTORESTATE42 SHA1_RECOMPRESS(42) #endif #ifdef DOSTORESTATE43 SHA1_RECOMPRESS(43) #endif #ifdef DOSTORESTATE44 SHA1_RECOMPRESS(44) #endif #ifdef DOSTORESTATE45 SHA1_RECOMPRESS(45) #endif #ifdef DOSTORESTATE46 SHA1_RECOMPRESS(46) #endif #ifdef DOSTORESTATE47 SHA1_RECOMPRESS(47) #endif #ifdef DOSTORESTATE48 SHA1_RECOMPRESS(48) #endif #ifdef DOSTORESTATE49 SHA1_RECOMPRESS(49) #endif #ifdef DOSTORESTATE50 SHA1_RECOMPRESS(50) #endif #ifdef DOSTORESTATE51 SHA1_RECOMPRESS(51) #endif #ifdef DOSTORESTATE52 SHA1_RECOMPRESS(52) #endif #ifdef DOSTORESTATE53 SHA1_RECOMPRESS(53) #endif #ifdef DOSTORESTATE54 SHA1_RECOMPRESS(54) #endif #ifdef DOSTORESTATE55 SHA1_RECOMPRESS(55) #endif #ifdef DOSTORESTATE56 SHA1_RECOMPRESS(56) #endif #ifdef DOSTORESTATE57 SHA1_RECOMPRESS(57) #endif #ifdef DOSTORESTATE58 SHA1_RECOMPRESS(58) #endif #ifdef DOSTORESTATE59 SHA1_RECOMPRESS(59) #endif #ifdef DOSTORESTATE60 SHA1_RECOMPRESS(60) #endif #ifdef DOSTORESTATE61 SHA1_RECOMPRESS(61) #endif #ifdef DOSTORESTATE62 SHA1_RECOMPRESS(62) #endif #ifdef DOSTORESTATE63 SHA1_RECOMPRESS(63) #endif #ifdef DOSTORESTATE64 SHA1_RECOMPRESS(64) #endif #ifdef DOSTORESTATE65 SHA1_RECOMPRESS(65) #endif #ifdef DOSTORESTATE66 SHA1_RECOMPRESS(66) #endif #ifdef DOSTORESTATE67 SHA1_RECOMPRESS(67) #endif #ifdef DOSTORESTATE68 SHA1_RECOMPRESS(68) #endif #ifdef DOSTORESTATE69 SHA1_RECOMPRESS(69) #endif #ifdef DOSTORESTATE70 SHA1_RECOMPRESS(70) #endif #ifdef DOSTORESTATE71 SHA1_RECOMPRESS(71) #endif #ifdef DOSTORESTATE72 SHA1_RECOMPRESS(72) #endif #ifdef DOSTORESTATE73 SHA1_RECOMPRESS(73) #endif #ifdef DOSTORESTATE74 SHA1_RECOMPRESS(74) #endif #ifdef DOSTORESTATE75 SHA1_RECOMPRESS(75) #endif #ifdef DOSTORESTATE76 SHA1_RECOMPRESS(76) #endif #ifdef DOSTORESTATE77 SHA1_RECOMPRESS(77) #endif #ifdef DOSTORESTATE78 SHA1_RECOMPRESS(78) #endif #ifdef DOSTORESTATE79 SHA1_RECOMPRESS(79) #endif #ifdef _MSC_VER #pragma warning(pop) #endif static void sha1_recompression_step(uint32_t step, uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) { switch (step) { #ifdef DOSTORESTATE0 case 0: sha1recompress_fast_0(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE1 case 1: sha1recompress_fast_1(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE2 case 2: sha1recompress_fast_2(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE3 case 3: sha1recompress_fast_3(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE4 case 4: sha1recompress_fast_4(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE5 case 5: sha1recompress_fast_5(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE6 case 6: sha1recompress_fast_6(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE7 case 7: sha1recompress_fast_7(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE8 case 8: sha1recompress_fast_8(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE9 case 9: sha1recompress_fast_9(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE10 case 10: sha1recompress_fast_10(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE11 case 11: sha1recompress_fast_11(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE12 case 12: sha1recompress_fast_12(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE13 case 13: sha1recompress_fast_13(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE14 case 14: sha1recompress_fast_14(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE15 case 15: sha1recompress_fast_15(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE16 case 16: sha1recompress_fast_16(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE17 case 17: sha1recompress_fast_17(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE18 case 18: sha1recompress_fast_18(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE19 case 19: sha1recompress_fast_19(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE20 case 20: sha1recompress_fast_20(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE21 case 21: sha1recompress_fast_21(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE22 case 22: sha1recompress_fast_22(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE23 case 23: sha1recompress_fast_23(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE24 case 24: sha1recompress_fast_24(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE25 case 25: sha1recompress_fast_25(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE26 case 26: sha1recompress_fast_26(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE27 case 27: sha1recompress_fast_27(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE28 case 28: sha1recompress_fast_28(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE29 case 29: sha1recompress_fast_29(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE30 case 30: sha1recompress_fast_30(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE31 case 31: sha1recompress_fast_31(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE32 case 32: sha1recompress_fast_32(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE33 case 33: sha1recompress_fast_33(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE34 case 34: sha1recompress_fast_34(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE35 case 35: sha1recompress_fast_35(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE36 case 36: sha1recompress_fast_36(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE37 case 37: sha1recompress_fast_37(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE38 case 38: sha1recompress_fast_38(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE39 case 39: sha1recompress_fast_39(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE40 case 40: sha1recompress_fast_40(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE41 case 41: sha1recompress_fast_41(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE42 case 42: sha1recompress_fast_42(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE43 case 43: sha1recompress_fast_43(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE44 case 44: sha1recompress_fast_44(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE45 case 45: sha1recompress_fast_45(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE46 case 46: sha1recompress_fast_46(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE47 case 47: sha1recompress_fast_47(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE48 case 48: sha1recompress_fast_48(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE49 case 49: sha1recompress_fast_49(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE50 case 50: sha1recompress_fast_50(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE51 case 51: sha1recompress_fast_51(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE52 case 52: sha1recompress_fast_52(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE53 case 53: sha1recompress_fast_53(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE54 case 54: sha1recompress_fast_54(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE55 case 55: sha1recompress_fast_55(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE56 case 56: sha1recompress_fast_56(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE57 case 57: sha1recompress_fast_57(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE58 case 58: sha1recompress_fast_58(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE59 case 59: sha1recompress_fast_59(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE60 case 60: sha1recompress_fast_60(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE61 case 61: sha1recompress_fast_61(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE62 case 62: sha1recompress_fast_62(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE63 case 63: sha1recompress_fast_63(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE64 case 64: sha1recompress_fast_64(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE65 case 65: sha1recompress_fast_65(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE66 case 66: sha1recompress_fast_66(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE67 case 67: sha1recompress_fast_67(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE68 case 68: sha1recompress_fast_68(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE69 case 69: sha1recompress_fast_69(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE70 case 70: sha1recompress_fast_70(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE71 case 71: sha1recompress_fast_71(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE72 case 72: sha1recompress_fast_72(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE73 case 73: sha1recompress_fast_73(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE74 case 74: sha1recompress_fast_74(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE75 case 75: sha1recompress_fast_75(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE76 case 76: sha1recompress_fast_76(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE77 case 77: sha1recompress_fast_77(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE78 case 78: sha1recompress_fast_78(ihvin, ihvout, me2, state); break; #endif #ifdef DOSTORESTATE79 case 79: sha1recompress_fast_79(ihvin, ihvout, me2, state); break; #endif default: abort(); } } static void sha1_process(SHA1_CTX *ctx, const uint32_t block[16]) { unsigned i, j; uint32_t ubc_dv_mask[DVMASKSIZE] = { 0xFFFFFFFF }; uint32_t ihvtmp[5]; ctx->ihv1[0] = ctx->ihv[0]; ctx->ihv1[1] = ctx->ihv[1]; ctx->ihv1[2] = ctx->ihv[2]; ctx->ihv1[3] = ctx->ihv[3]; ctx->ihv1[4] = ctx->ihv[4]; sha1_compression_states(ctx->ihv, block, ctx->m1, ctx->states); if (ctx->detect_coll) { if (ctx->ubc_check) { ubc_check(ctx->m1, ubc_dv_mask); } if (ubc_dv_mask[0] != 0) { for (i = 0; sha1_dvs[i].dvType != 0; ++i) { if (ubc_dv_mask[0] & ((uint32_t)(1) << sha1_dvs[i].maskb)) { for (j = 0; j < 80; ++j) ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j]; sha1_recompression_step(sha1_dvs[i].testt, ctx->ihv2, ihvtmp, ctx->m2, ctx->states[sha1_dvs[i].testt]); /* to verify SHA-1 collision detection code with collisions for reduced-step SHA-1 */ if ((0 == ((ihvtmp[0] ^ ctx->ihv[0]) | (ihvtmp[1] ^ ctx->ihv[1]) | (ihvtmp[2] ^ ctx->ihv[2]) | (ihvtmp[3] ^ ctx->ihv[3]) | (ihvtmp[4] ^ ctx->ihv[4]))) || (ctx->reduced_round_coll && 0==((ctx->ihv1[0] ^ ctx->ihv2[0]) | (ctx->ihv1[1] ^ ctx->ihv2[1]) | (ctx->ihv1[2] ^ ctx->ihv2[2]) | (ctx->ihv1[3] ^ ctx->ihv2[3]) | (ctx->ihv1[4] ^ ctx->ihv2[4])))) { ctx->found_collision = 1; if (ctx->safe_hash) { sha1_compression_W(ctx->ihv, ctx->m1); sha1_compression_W(ctx->ihv, ctx->m1); } break; } } } } } } void SHA1DCInit(SHA1_CTX *ctx) { ctx->total = 0; ctx->ihv[0] = 0x67452301; ctx->ihv[1] = 0xEFCDAB89; ctx->ihv[2] = 0x98BADCFE; ctx->ihv[3] = 0x10325476; ctx->ihv[4] = 0xC3D2E1F0; ctx->found_collision = 0; ctx->safe_hash = SHA1DC_INIT_SAFE_HASH_DEFAULT; ctx->ubc_check = 1; ctx->detect_coll = 1; ctx->reduced_round_coll = 0; ctx->callback = NULL; } void SHA1DCSetSafeHash(SHA1_CTX *ctx, int safehash) { if (safehash) ctx->safe_hash = 1; else ctx->safe_hash = 0; } void SHA1DCSetUseUBC(SHA1_CTX *ctx, int ubc_check) { if (ubc_check) ctx->ubc_check = 1; else ctx->ubc_check = 0; } void SHA1DCSetUseDetectColl(SHA1_CTX *ctx, int detect_coll) { if (detect_coll) ctx->detect_coll = 1; else ctx->detect_coll = 0; } void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX *ctx, int reduced_round_coll) { if (reduced_round_coll) ctx->reduced_round_coll = 1; else ctx->reduced_round_coll = 0; } void SHA1DCSetCallback(SHA1_CTX *ctx, collision_block_callback callback) { ctx->callback = callback; } void SHA1DCUpdate(SHA1_CTX *ctx, const char *buf, size_t len) { unsigned left, fill; if (len == 0) return; left = ctx->total & 63; fill = 64 - left; if (left && len >= fill) { ctx->total += fill; memcpy(ctx->buffer + left, buf, fill); sha1_process(ctx, (uint32_t*)(ctx->buffer)); buf += fill; len -= fill; left = 0; } while (len >= 64) { ctx->total += 64; #if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) sha1_process(ctx, (uint32_t*)(buf)); #else memcpy(ctx->buffer, buf, 64); sha1_process(ctx, (uint32_t*)(ctx->buffer)); #endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */ buf += 64; len -= 64; } if (len > 0) { ctx->total += len; memcpy(ctx->buffer + left, buf, len); } } static const unsigned char sha1_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx) { uint32_t last = ctx->total & 63; uint32_t padn = (last < 56) ? (56 - last) : (120 - last); uint64_t total; SHA1DCUpdate(ctx, (const char*)(sha1_padding), padn); total = ctx->total - padn; total <<= 3; ctx->buffer[56] = (unsigned char)(total >> 56); ctx->buffer[57] = (unsigned char)(total >> 48); ctx->buffer[58] = (unsigned char)(total >> 40); ctx->buffer[59] = (unsigned char)(total >> 32); ctx->buffer[60] = (unsigned char)(total >> 24); ctx->buffer[61] = (unsigned char)(total >> 16); ctx->buffer[62] = (unsigned char)(total >> 8); ctx->buffer[63] = (unsigned char)(total); sha1_process(ctx, (uint32_t*)(ctx->buffer)); output[0] = (unsigned char)(ctx->ihv[0] >> 24); output[1] = (unsigned char)(ctx->ihv[0] >> 16); output[2] = (unsigned char)(ctx->ihv[0] >> 8); output[3] = (unsigned char)(ctx->ihv[0]); output[4] = (unsigned char)(ctx->ihv[1] >> 24); output[5] = (unsigned char)(ctx->ihv[1] >> 16); output[6] = (unsigned char)(ctx->ihv[1] >> 8); output[7] = (unsigned char)(ctx->ihv[1]); output[8] = (unsigned char)(ctx->ihv[2] >> 24); output[9] = (unsigned char)(ctx->ihv[2] >> 16); output[10] = (unsigned char)(ctx->ihv[2] >> 8); output[11] = (unsigned char)(ctx->ihv[2]); output[12] = (unsigned char)(ctx->ihv[3] >> 24); output[13] = (unsigned char)(ctx->ihv[3] >> 16); output[14] = (unsigned char)(ctx->ihv[3] >> 8); output[15] = (unsigned char)(ctx->ihv[3]); output[16] = (unsigned char)(ctx->ihv[4] >> 24); output[17] = (unsigned char)(ctx->ihv[4] >> 16); output[18] = (unsigned char)(ctx->ihv[4] >> 8); output[19] = (unsigned char)(ctx->ihv[4]); return ctx->found_collision; } #ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C #include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C #endif git2r/src/libgit2/src/hash/sha1/sha1dc/ubc_check.h0000644000175000017500000000326714125111754021357 0ustar nileshnilesh/*** * Copyright 2017 Marc Stevens , Dan Shumow * Distributed under the MIT Software License. * See accompanying file LICENSE.txt or copy at * https://opensource.org/licenses/MIT ***/ /* // this file was generated by the 'parse_bitrel' program in the tools section // using the data files from directory 'tools/data/3565' // // sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check // dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper) // dm[80] is the expanded message block XOR-difference defined by the DV // testt is the step to do the recompression from for collision detection // maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check // // ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs // it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met // thus one needs to do the recompression check for each DV that has its bit set */ #ifndef SHA1DC_UBC_CHECK_H #define SHA1DC_UBC_CHECK_H #if defined(__cplusplus) extern "C" { #endif #ifndef SHA1DC_NO_STANDARD_INCLUDES #include #endif #define DVMASKSIZE 1 typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t; extern dv_info_t sha1_dvs[]; void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]); #define DOSTORESTATE58 #define DOSTORESTATE65 #define CHECK_DVMASK(_DVMASK) (0 != _DVMASK[0]) #if defined(__cplusplus) } #endif #ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H #include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H #endif #endif git2r/src/libgit2/src/hash/sha1/collisiondetect.c0000644000175000017500000000166114125111754021461 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "collisiondetect.h" int git_hash_sha1_global_init(void) { return 0; } int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx) { return git_hash_sha1_init(ctx); } void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx) { GIT_UNUSED(ctx); } int git_hash_sha1_init(git_hash_sha1_ctx *ctx) { GIT_ASSERT_ARG(ctx); SHA1DCInit(&ctx->c); return 0; } int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len) { GIT_ASSERT_ARG(ctx); SHA1DCUpdate(&ctx->c, data, len); return 0; } int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *ctx) { GIT_ASSERT_ARG(ctx); if (SHA1DCFinal(out->id, &ctx->c)) { git_error_set(GIT_ERROR_SHA1, "SHA1 collision attack detected"); return -1; } return 0; } git2r/src/libgit2/src/hash/sha1/openssl.c0000644000175000017500000000221114125111754017750 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "openssl.h" int git_hash_sha1_global_init(void) { return 0; } int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx) { return git_hash_sha1_init(ctx); } void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx) { GIT_UNUSED(ctx); } int git_hash_sha1_init(git_hash_sha1_ctx *ctx) { GIT_ASSERT_ARG(ctx); if (SHA1_Init(&ctx->c) != 1) { git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to initialize hash context"); return -1; } return 0; } int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len) { GIT_ASSERT_ARG(ctx); if (SHA1_Update(&ctx->c, data, len) != 1) { git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to update hash"); return -1; } return 0; } int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *ctx) { GIT_ASSERT_ARG(ctx); if (SHA1_Final(out->id, &ctx->c) != 1) { git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to finalize hash"); return -1; } return 0; } git2r/src/libgit2/src/hash/sha1.h0000644000175000017500000000203114125111754016272 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_hash_sha1_h__ #define INCLUDE_hash_sha1_h__ #include "common.h" typedef struct git_hash_sha1_ctx git_hash_sha1_ctx; #if defined(GIT_SHA1_COLLISIONDETECT) # include "sha1/collisiondetect.h" #elif defined(GIT_SHA1_COMMON_CRYPTO) # include "sha1/common_crypto.h" #elif defined(GIT_SHA1_OPENSSL) # include "sha1/openssl.h" #elif defined(GIT_SHA1_WIN32) # include "sha1/win32.h" #elif defined(GIT_SHA1_MBEDTLS) # include "sha1/mbedtls.h" #else # include "sha1/generic.h" #endif int git_hash_sha1_global_init(void); int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx); void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx); int git_hash_sha1_init(git_hash_sha1_ctx *c); int git_hash_sha1_update(git_hash_sha1_ctx *c, const void *data, size_t len); int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *c); #endif git2r/src/libgit2/src/patch.h0000644000175000017500000000271314125111754015621 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_patch_h__ #define INCLUDE_patch_h__ #include "common.h" #include "git2/patch.h" #include "array.h" /* cached information about a hunk in a patch */ typedef struct git_patch_hunk { git_diff_hunk hunk; size_t line_start; size_t line_count; } git_patch_hunk; struct git_patch { git_refcount rc; git_repository *repo; /* may be null */ git_diff_options diff_opts; git_diff_delta *delta; git_diff_binary binary; git_array_t(git_patch_hunk) hunks; git_array_t(git_diff_line) lines; size_t header_size; size_t content_size; size_t context_size; void (*free_fn)(git_patch *patch); }; extern int git_patch__invoke_callbacks( git_patch *patch, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); extern int git_patch_line_stats( size_t *total_ctxt, size_t *total_adds, size_t *total_dels, const git_patch *patch); /** Options for parsing patch files. */ typedef struct { /** * The length of the prefix (in path segments) for the filenames. * This prefix will be removed when looking for files. The default is 1. */ uint32_t prefix_len; } git_patch_options; #define GIT_PATCH_OPTIONS_INIT { 1 } extern void git_patch_free(git_patch *patch); #endif git2r/src/libgit2/src/reflog.h0000644000175000017500000000141214125111754015773 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_reflog_h__ #define INCLUDE_reflog_h__ #include "common.h" #include "git2/reflog.h" #include "vector.h" #define GIT_REFLOG_DIR "logs/" #define GIT_REFLOG_DIR_MODE 0777 #define GIT_REFLOG_FILE_MODE 0666 #define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17) struct git_reflog_entry { git_oid oid_old; git_oid oid_cur; git_signature *committer; char *msg; }; struct git_reflog { git_refdb *db; char *ref_name; git_vector entries; }; GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total) { return (total - 1) - idx; } #endif git2r/src/libgit2/src/apply.c0000644000175000017500000005343714125111754015653 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "git2/apply.h" #include "git2/patch.h" #include "git2/filter.h" #include "git2/blob.h" #include "git2/index.h" #include "git2/checkout.h" #include "git2/repository.h" #include "array.h" #include "patch.h" #include "futils.h" #include "delta.h" #include "zstream.h" #include "reader.h" #include "index.h" #include "apply.h" typedef struct { /* The lines that we allocate ourself are allocated out of the pool. * (Lines may have been allocated out of the diff.) */ git_pool pool; git_vector lines; } patch_image; static int apply_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2); static int apply_err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); git_error_vset(GIT_ERROR_PATCH, fmt, ap); va_end(ap); return GIT_EAPPLYFAIL; } static void patch_line_init( git_diff_line *out, const char *in, size_t in_len, size_t in_offset) { out->content = in; out->content_len = in_len; out->content_offset = in_offset; } #define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT } static int patch_image_init_fromstr( patch_image *out, const char *in, size_t in_len) { git_diff_line *line; const char *start, *end; memset(out, 0x0, sizeof(patch_image)); if (git_pool_init(&out->pool, sizeof(git_diff_line)) < 0) return -1; if (!in_len) return 0; for (start = in; start < in + in_len; start = end) { end = memchr(start, '\n', in_len - (start - in)); if (end == NULL) end = in + in_len; else if (end < in + in_len) end++; line = git_pool_mallocz(&out->pool, 1); GIT_ERROR_CHECK_ALLOC(line); if (git_vector_insert(&out->lines, line) < 0) return -1; patch_line_init(line, start, (end - start), (start - in)); } return 0; } static void patch_image_free(patch_image *image) { if (image == NULL) return; git_pool_clear(&image->pool); git_vector_free(&image->lines); } static bool match_hunk( patch_image *image, patch_image *preimage, size_t linenum) { bool match = 0; size_t i; /* Ensure this hunk is within the image boundaries. */ if (git_vector_length(&preimage->lines) + linenum > git_vector_length(&image->lines)) return 0; match = 1; /* Check exact match. */ for (i = 0; i < git_vector_length(&preimage->lines); i++) { git_diff_line *preimage_line = git_vector_get(&preimage->lines, i); git_diff_line *image_line = git_vector_get(&image->lines, linenum + i); if (preimage_line->content_len != image_line->content_len || memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) { match = 0; break; } } return match; } static bool find_hunk_linenum( size_t *out, patch_image *image, patch_image *preimage, size_t linenum) { size_t max = git_vector_length(&image->lines); bool match; if (linenum > max) linenum = max; match = match_hunk(image, preimage, linenum); *out = linenum; return match; } static int update_hunk( patch_image *image, size_t linenum, patch_image *preimage, patch_image *postimage) { size_t postlen = git_vector_length(&postimage->lines); size_t prelen = git_vector_length(&preimage->lines); size_t i; int error = 0; if (postlen > prelen) error = git_vector_insert_null( &image->lines, linenum, (postlen - prelen)); else if (prelen > postlen) error = git_vector_remove_range( &image->lines, linenum, (prelen - postlen)); if (error) { git_error_set_oom(); return -1; } for (i = 0; i < git_vector_length(&postimage->lines); i++) { image->lines.contents[linenum + i] = git_vector_get(&postimage->lines, i); } return 0; } typedef struct { git_apply_options opts; size_t skipped_new_lines; size_t skipped_old_lines; } apply_hunks_ctx; static int apply_hunk( patch_image *image, git_patch *patch, git_patch_hunk *hunk, apply_hunks_ctx *ctx) { patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT; size_t line_num, i; int error = 0; if (ctx->opts.hunk_cb) { error = ctx->opts.hunk_cb(&hunk->hunk, ctx->opts.payload); if (error) { if (error > 0) { ctx->skipped_new_lines += hunk->hunk.new_lines; ctx->skipped_old_lines += hunk->hunk.old_lines; error = 0; } goto done; } } for (i = 0; i < hunk->line_count; i++) { size_t linenum = hunk->line_start + i; git_diff_line *line = git_array_get(patch->lines, linenum), *prev; if (!line) { error = apply_err("preimage does not contain line %"PRIuZ, linenum); goto done; } switch (line->origin) { case GIT_DIFF_LINE_CONTEXT_EOFNL: case GIT_DIFF_LINE_DEL_EOFNL: case GIT_DIFF_LINE_ADD_EOFNL: prev = i ? git_array_get(patch->lines, linenum - 1) : NULL; if (prev && prev->content[prev->content_len - 1] == '\n') prev->content_len -= 1; break; case GIT_DIFF_LINE_CONTEXT: if ((error = git_vector_insert(&preimage.lines, line)) < 0 || (error = git_vector_insert(&postimage.lines, line)) < 0) goto done; break; case GIT_DIFF_LINE_DELETION: if ((error = git_vector_insert(&preimage.lines, line)) < 0) goto done; break; case GIT_DIFF_LINE_ADDITION: if ((error = git_vector_insert(&postimage.lines, line)) < 0) goto done; break; } } if (hunk->hunk.new_start) { line_num = hunk->hunk.new_start - ctx->skipped_new_lines + ctx->skipped_old_lines - 1; } else { line_num = 0; } if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) { error = apply_err("hunk at line %d did not apply", hunk->hunk.new_start); goto done; } error = update_hunk(image, line_num, &preimage, &postimage); done: patch_image_free(&preimage); patch_image_free(&postimage); return error; } static int apply_hunks( git_buf *out, const char *source, size_t source_len, git_patch *patch, apply_hunks_ctx *ctx) { git_patch_hunk *hunk; git_diff_line *line; patch_image image; size_t i; int error = 0; if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0) goto done; git_array_foreach(patch->hunks, i, hunk) { if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0) goto done; } git_vector_foreach(&image.lines, i, line) git_buf_put(out, line->content, line->content_len); done: patch_image_free(&image); return error; } static int apply_binary_delta( git_buf *out, const char *source, size_t source_len, git_diff_binary_file *binary_file) { git_buf inflated = GIT_BUF_INIT; int error = 0; /* no diff means identical contents */ if (binary_file->datalen == 0) return git_buf_put(out, source, source_len); error = git_zstream_inflatebuf(&inflated, binary_file->data, binary_file->datalen); if (!error && inflated.size != binary_file->inflatedlen) { error = apply_err("inflated delta does not match expected length"); git_buf_dispose(out); } if (error < 0) goto done; if (binary_file->type == GIT_DIFF_BINARY_DELTA) { void *data; size_t data_len; error = git_delta_apply(&data, &data_len, (void *)source, source_len, (void *)inflated.ptr, inflated.size); out->ptr = data; out->size = data_len; out->asize = data_len; } else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) { git_buf_swap(out, &inflated); } else { error = apply_err("unknown binary delta type"); goto done; } done: git_buf_dispose(&inflated); return error; } static int apply_binary( git_buf *out, const char *source, size_t source_len, git_patch *patch) { git_buf reverse = GIT_BUF_INIT; int error = 0; if (!patch->binary.contains_data) { error = apply_err("patch does not contain binary data"); goto done; } if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen) goto done; /* first, apply the new_file delta to the given source */ if ((error = apply_binary_delta(out, source, source_len, &patch->binary.new_file)) < 0) goto done; /* second, apply the old_file delta to sanity check the result */ if ((error = apply_binary_delta(&reverse, out->ptr, out->size, &patch->binary.old_file)) < 0) goto done; /* Verify that the resulting file with the reverse patch applied matches the source file */ if (source_len != reverse.size || (source_len && memcmp(source, reverse.ptr, source_len) != 0)) { error = apply_err("binary patch did not apply cleanly"); goto done; } done: if (error < 0) git_buf_dispose(out); git_buf_dispose(&reverse); return error; } int git_apply__patch( git_buf *contents_out, char **filename_out, unsigned int *mode_out, const char *source, size_t source_len, git_patch *patch, const git_apply_options *given_opts) { apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT }; char *filename = NULL; unsigned int mode = 0; int error = 0; GIT_ASSERT_ARG(contents_out); GIT_ASSERT_ARG(filename_out); GIT_ASSERT_ARG(mode_out); GIT_ASSERT_ARG(source || !source_len); GIT_ASSERT_ARG(patch); if (given_opts) memcpy(&ctx.opts, given_opts, sizeof(git_apply_options)); *filename_out = NULL; *mode_out = 0; if (patch->delta->status != GIT_DELTA_DELETED) { const git_diff_file *newfile = &patch->delta->new_file; filename = git__strdup(newfile->path); mode = newfile->mode ? newfile->mode : GIT_FILEMODE_BLOB; } if (patch->delta->flags & GIT_DIFF_FLAG_BINARY) error = apply_binary(contents_out, source, source_len, patch); else if (patch->hunks.size) error = apply_hunks(contents_out, source, source_len, patch, &ctx); else error = git_buf_put(contents_out, source, source_len); if (error) goto done; if (patch->delta->status == GIT_DELTA_DELETED && git_buf_len(contents_out) > 0) { error = apply_err("removal patch leaves file contents"); goto done; } *filename_out = filename; *mode_out = mode; done: if (error < 0) git__free(filename); return error; } static int apply_one( git_repository *repo, git_reader *preimage_reader, git_index *preimage, git_reader *postimage_reader, git_index *postimage, git_diff *diff, git_strmap *removed_paths, size_t i, const git_apply_options *opts) { git_patch *patch = NULL; git_buf pre_contents = GIT_BUF_INIT, post_contents = GIT_BUF_INIT; const git_diff_delta *delta; char *filename = NULL; unsigned int mode; git_oid pre_id, post_id; git_filemode_t pre_filemode; git_index_entry pre_entry, post_entry; bool skip_preimage = false; int error; if ((error = git_patch_from_diff(&patch, diff, i)) < 0) goto done; delta = git_patch_get_delta(patch); if (opts->delta_cb) { error = opts->delta_cb(delta, opts->payload); if (error) { if (error > 0) error = 0; goto done; } } /* * Ensure that the file has not been deleted or renamed if we're * applying a modification delta. */ if (delta->status != GIT_DELTA_RENAMED && delta->status != GIT_DELTA_ADDED) { if (git_strmap_exists(removed_paths, delta->old_file.path)) { error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path); goto done; } } /* * We may be applying a second delta to an already seen file. If so, * use the already modified data in the postimage instead of the * content from the index or working directory. (Don't do this in * the case of a rename, which must be specified before additional * deltas since we apply deltas to the target filename.) */ if (delta->status != GIT_DELTA_RENAMED) { if ((error = git_reader_read(&pre_contents, &pre_id, &pre_filemode, postimage_reader, delta->old_file.path)) == 0) { skip_preimage = true; } else if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } else { goto done; } } if (!skip_preimage && delta->status != GIT_DELTA_ADDED) { error = git_reader_read(&pre_contents, &pre_id, &pre_filemode, preimage_reader, delta->old_file.path); /* ENOTFOUND means the preimage was not found; apply failed. */ if (error == GIT_ENOTFOUND) error = GIT_EAPPLYFAIL; /* When applying to BOTH, the index did not match the workdir. */ if (error == GIT_READER_MISMATCH) error = apply_err("%s: does not match index", delta->old_file.path); if (error < 0) goto done; /* * We need to populate the preimage data structure with the * contents that we are using as the preimage for this file. * This allows us to apply patches to files that have been * modified in the working directory. During checkout, * we will use this expected preimage as the baseline, and * limit checkout to only the paths affected by patch * application. (Without this, we would fail to write the * postimage contents to any file that had been modified * from HEAD on-disk, even if the patch application succeeded.) * Use the contents from the delta where available - some * fields may not be available, like the old file mode (eg in * an exact rename situation) so trust the patch parsing to * validate and use the preimage data in that case. */ if (preimage) { memset(&pre_entry, 0, sizeof(git_index_entry)); pre_entry.path = delta->old_file.path; pre_entry.mode = delta->old_file.mode ? delta->old_file.mode : pre_filemode; git_oid_cpy(&pre_entry.id, &pre_id); if ((error = git_index_add(preimage, &pre_entry)) < 0) goto done; } } if (delta->status != GIT_DELTA_DELETED) { if ((error = git_apply__patch(&post_contents, &filename, &mode, pre_contents.ptr, pre_contents.size, patch, opts)) < 0 || (error = git_blob_create_from_buffer(&post_id, repo, post_contents.ptr, post_contents.size)) < 0) goto done; memset(&post_entry, 0, sizeof(git_index_entry)); post_entry.path = filename; post_entry.mode = mode; git_oid_cpy(&post_entry.id, &post_id); if ((error = git_index_add(postimage, &post_entry)) < 0) goto done; } if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_DELETED) error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path); if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_ADDED) git_strmap_delete(removed_paths, delta->new_file.path); done: git_buf_dispose(&pre_contents); git_buf_dispose(&post_contents); git__free(filename); git_patch_free(patch); return error; } static int apply_deltas( git_repository *repo, git_reader *pre_reader, git_index *preimage, git_reader *post_reader, git_index *postimage, git_diff *diff, const git_apply_options *opts) { git_strmap *removed_paths; size_t i; int error = 0; if (git_strmap_new(&removed_paths) < 0) return -1; for (i = 0; i < git_diff_num_deltas(diff); i++) { if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, removed_paths, i, opts)) < 0) goto done; } done: git_strmap_free(removed_paths); return error; } int git_apply_to_tree( git_index **out, git_repository *repo, git_tree *preimage, git_diff *diff, const git_apply_options *given_opts) { git_index *postimage = NULL; git_reader *pre_reader = NULL, *post_reader = NULL; git_apply_options opts = GIT_APPLY_OPTIONS_INIT; const git_diff_delta *delta; size_t i; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(preimage); GIT_ASSERT_ARG(diff); *out = NULL; if (given_opts) memcpy(&opts, given_opts, sizeof(git_apply_options)); if ((error = git_reader_for_tree(&pre_reader, preimage)) < 0) goto done; /* * put the current tree into the postimage as-is - the diff will * replace any entries contained therein */ if ((error = git_index_new(&postimage)) < 0 || (error = git_index_read_tree(postimage, preimage)) < 0 || (error = git_reader_for_index(&post_reader, repo, postimage)) < 0) goto done; /* * Remove the old paths from the index before applying diffs - * we need to do a full pass to remove them before adding deltas, * in order to handle rename situations. */ for (i = 0; i < git_diff_num_deltas(diff); i++) { delta = git_diff_get_delta(diff, i); if (delta->status == GIT_DELTA_DELETED || delta->status == GIT_DELTA_RENAMED) { if ((error = git_index_remove(postimage, delta->old_file.path, 0)) < 0) goto done; } } if ((error = apply_deltas(repo, pre_reader, NULL, post_reader, postimage, diff, &opts)) < 0) goto done; *out = postimage; done: if (error < 0) git_index_free(postimage); git_reader_free(pre_reader); git_reader_free(post_reader); return error; } static int git_apply__to_workdir( git_repository *repo, git_diff *diff, git_index *preimage, git_index *postimage, git_apply_location_t location, git_apply_options *opts) { git_vector paths = GIT_VECTOR_INIT; git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; const git_diff_delta *delta; size_t i; int error; GIT_UNUSED(opts); /* * Limit checkout to the paths affected by the diff; this ensures * that other modifications in the working directory are unaffected. */ if ((error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL)) < 0) goto done; for (i = 0; i < git_diff_num_deltas(diff); i++) { delta = git_diff_get_delta(diff, i); if ((error = git_vector_insert(&paths, (void *)delta->old_file.path)) < 0) goto done; if (strcmp(delta->old_file.path, delta->new_file.path) && (error = git_vector_insert(&paths, (void *)delta->new_file.path)) < 0) goto done; } checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE; checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH; checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX; if (location == GIT_APPLY_LOCATION_WORKDIR) checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX; checkout_opts.paths.strings = (char **)paths.contents; checkout_opts.paths.count = paths.length; checkout_opts.baseline_index = preimage; error = git_checkout_index(repo, postimage, &checkout_opts); done: git_vector_free(&paths); return error; } static int git_apply__to_index( git_repository *repo, git_diff *diff, git_index *preimage, git_index *postimage, git_apply_options *opts) { git_index *index = NULL; const git_diff_delta *delta; const git_index_entry *entry; size_t i; int error; GIT_UNUSED(preimage); GIT_UNUSED(opts); if ((error = git_repository_index(&index, repo)) < 0) goto done; /* Remove deleted (or renamed) paths from the index. */ for (i = 0; i < git_diff_num_deltas(diff); i++) { delta = git_diff_get_delta(diff, i); if (delta->status == GIT_DELTA_DELETED || delta->status == GIT_DELTA_RENAMED) { if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0) goto done; } } /* Then add the changes back to the index. */ for (i = 0; i < git_index_entrycount(postimage); i++) { entry = git_index_get_byindex(postimage, i); if ((error = git_index_add(index, entry)) < 0) goto done; } done: git_index_free(index); return error; } int git_apply_options_init(git_apply_options *opts, unsigned int version) { GIT_ASSERT_ARG(opts); GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT); return 0; } /* * Handle the three application options ("locations"): * * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`. * Applies the diff only to the workdir items and ignores the index * entirely. * * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`. * Applies the diff only to the index items and ignores the workdir * completely. * * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`. * Applies the diff to both the index items and the working directory * items. */ int git_apply( git_repository *repo, git_diff *diff, git_apply_location_t location, const git_apply_options *given_opts) { git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; git_index *index = NULL, *preimage = NULL, *postimage = NULL; git_reader *pre_reader = NULL, *post_reader = NULL; git_apply_options opts = GIT_APPLY_OPTIONS_INIT; int error = GIT_EINVALID; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(diff); GIT_ERROR_CHECK_VERSION( given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options"); if (given_opts) memcpy(&opts, given_opts, sizeof(git_apply_options)); /* * by default, we apply a patch directly to the working directory; * in `--cached` or `--index` mode, we apply to the contents already * in the index. */ switch (location) { case GIT_APPLY_LOCATION_BOTH: error = git_reader_for_workdir(&pre_reader, repo, true); break; case GIT_APPLY_LOCATION_INDEX: error = git_reader_for_index(&pre_reader, repo, NULL); break; case GIT_APPLY_LOCATION_WORKDIR: error = git_reader_for_workdir(&pre_reader, repo, false); break; default: GIT_ASSERT(false); } if (error < 0) goto done; /* * Build the preimage and postimage (differences). Note that * this is not the complete preimage or postimage, it only * contains the files affected by the patch. We want to avoid * having the full repo index, so we will limit our checkout * to only write these files that were affected by the diff. */ if ((error = git_index_new(&preimage)) < 0 || (error = git_index_new(&postimage)) < 0 || (error = git_reader_for_index(&post_reader, repo, postimage)) < 0) goto done; if (!(opts.flags & GIT_APPLY_CHECK)) if ((error = git_repository_index(&index, repo)) < 0 || (error = git_indexwriter_init(&indexwriter, index)) < 0) goto done; if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0) goto done; if ((opts.flags & GIT_APPLY_CHECK)) goto done; switch (location) { case GIT_APPLY_LOCATION_BOTH: error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts); break; case GIT_APPLY_LOCATION_INDEX: error = git_apply__to_index(repo, diff, preimage, postimage, &opts); break; case GIT_APPLY_LOCATION_WORKDIR: error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts); break; default: GIT_ASSERT(false); } if (error < 0) goto done; error = git_indexwriter_commit(&indexwriter); done: git_indexwriter_cleanup(&indexwriter); git_index_free(postimage); git_index_free(preimage); git_index_free(index); git_reader_free(pre_reader); git_reader_free(post_reader); return error; } git2r/src/libgit2/src/vector.c0000644000175000017500000002023014125111754016011 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "vector.h" #include "integer.h" /* In elements, not bytes */ #define MIN_ALLOCSIZE 8 GIT_INLINE(size_t) compute_new_size(git_vector *v) { size_t new_size = v->_alloc_size; /* Use a resize factor of 1.5, which is quick to compute using integer * instructions and less than the golden ratio (1.618...) */ if (new_size < MIN_ALLOCSIZE) new_size = MIN_ALLOCSIZE; else if (new_size <= (SIZE_MAX / 3) * 2) new_size += new_size / 2; else new_size = SIZE_MAX; return new_size; } GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size) { void *new_contents; if (new_size == 0) return 0; new_contents = git__reallocarray(v->contents, new_size, sizeof(void *)); GIT_ERROR_CHECK_ALLOC(new_contents); v->_alloc_size = new_size; v->contents = new_contents; return 0; } int git_vector_size_hint(git_vector *v, size_t size_hint) { if (v->_alloc_size >= size_hint) return 0; return resize_vector(v, size_hint); } int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp) { GIT_ASSERT_ARG(v); GIT_ASSERT_ARG(src); v->_alloc_size = 0; v->contents = NULL; v->_cmp = cmp ? cmp : src->_cmp; v->length = src->length; v->flags = src->flags; if (cmp != src->_cmp) git_vector_set_sorted(v, 0); if (src->length) { size_t bytes; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *)); v->contents = git__malloc(bytes); GIT_ERROR_CHECK_ALLOC(v->contents); v->_alloc_size = src->length; memcpy(v->contents, src->contents, bytes); } return 0; } void git_vector_free(git_vector *v) { if (!v) return; git__free(v->contents); v->contents = NULL; v->length = 0; v->_alloc_size = 0; } void git_vector_free_deep(git_vector *v) { size_t i; if (!v) return; for (i = 0; i < v->length; ++i) { git__free(v->contents[i]); v->contents[i] = NULL; } git_vector_free(v); } int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp) { GIT_ASSERT_ARG(v); v->_alloc_size = 0; v->_cmp = cmp; v->length = 0; v->flags = GIT_VECTOR_SORTED; v->contents = NULL; return resize_vector(v, max(initial_size, MIN_ALLOCSIZE)); } void **git_vector_detach(size_t *size, size_t *asize, git_vector *v) { void **data = v->contents; if (size) *size = v->length; if (asize) *asize = v->_alloc_size; v->_alloc_size = 0; v->length = 0; v->contents = NULL; return data; } int git_vector_insert(git_vector *v, void *element) { GIT_ASSERT_ARG(v); if (v->length >= v->_alloc_size && resize_vector(v, compute_new_size(v)) < 0) return -1; v->contents[v->length++] = element; git_vector_set_sorted(v, v->length <= 1); return 0; } int git_vector_insert_sorted( git_vector *v, void *element, int (*on_dup)(void **old, void *new)) { int result; size_t pos; GIT_ASSERT_ARG(v); GIT_ASSERT(v->_cmp); if (!git_vector_is_sorted(v)) git_vector_sort(v); if (v->length >= v->_alloc_size && resize_vector(v, compute_new_size(v)) < 0) return -1; /* If we find the element and have a duplicate handler callback, * invoke it. If it returns non-zero, then cancel insert, otherwise * proceed with normal insert. */ if (!git__bsearch(v->contents, v->length, element, v->_cmp, &pos) && on_dup && (result = on_dup(&v->contents[pos], element)) < 0) return result; /* shift elements to the right */ if (pos < v->length) memmove(v->contents + pos + 1, v->contents + pos, (v->length - pos) * sizeof(void *)); v->contents[pos] = element; v->length++; return 0; } void git_vector_sort(git_vector *v) { if (git_vector_is_sorted(v) || !v->_cmp) return; if (v->length > 1) git__tsort(v->contents, v->length, v->_cmp); git_vector_set_sorted(v, 1); } int git_vector_bsearch2( size_t *at_pos, git_vector *v, git_vector_cmp key_lookup, const void *key) { GIT_ASSERT_ARG(v); GIT_ASSERT_ARG(key); GIT_ASSERT(key_lookup); /* need comparison function to sort the vector */ if (!v->_cmp) return -1; git_vector_sort(v); return git__bsearch(v->contents, v->length, key, key_lookup, at_pos); } int git_vector_search2( size_t *at_pos, const git_vector *v, git_vector_cmp key_lookup, const void *key) { size_t i; GIT_ASSERT_ARG(v); GIT_ASSERT_ARG(key); GIT_ASSERT(key_lookup); for (i = 0; i < v->length; ++i) { if (key_lookup(key, v->contents[i]) == 0) { if (at_pos) *at_pos = i; return 0; } } return GIT_ENOTFOUND; } static int strict_comparison(const void *a, const void *b) { return (a == b) ? 0 : -1; } int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry) { return git_vector_search2(at_pos, v, v->_cmp ? v->_cmp : strict_comparison, entry); } int git_vector_remove(git_vector *v, size_t idx) { size_t shift_count; GIT_ASSERT_ARG(v); if (idx >= v->length) return GIT_ENOTFOUND; shift_count = v->length - idx - 1; if (shift_count) memmove(&v->contents[idx], &v->contents[idx + 1], shift_count * sizeof(void *)); v->length--; return 0; } void git_vector_pop(git_vector *v) { if (v->length > 0) v->length--; } void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *)) { git_vector_cmp cmp; size_t i, j; if (v->length <= 1) return; git_vector_sort(v); cmp = v->_cmp ? v->_cmp : strict_comparison; for (i = 0, j = 1 ; j < v->length; ++j) if (!cmp(v->contents[i], v->contents[j])) { if (git_free_cb) git_free_cb(v->contents[i]); v->contents[i] = v->contents[j]; } else v->contents[++i] = v->contents[j]; v->length -= j - i - 1; } void git_vector_remove_matching( git_vector *v, int (*match)(const git_vector *v, size_t idx, void *payload), void *payload) { size_t i, j; for (i = 0, j = 0; j < v->length; ++j) { v->contents[i] = v->contents[j]; if (!match(v, i, payload)) i++; } v->length = i; } void git_vector_clear(git_vector *v) { v->length = 0; git_vector_set_sorted(v, 1); } void git_vector_swap(git_vector *a, git_vector *b) { git_vector t; if (a != b) { memcpy(&t, a, sizeof(t)); memcpy(a, b, sizeof(t)); memcpy(b, &t, sizeof(t)); } } int git_vector_resize_to(git_vector *v, size_t new_length) { if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0) return -1; if (new_length > v->length) memset(&v->contents[v->length], 0, sizeof(void *) * (new_length - v->length)); v->length = new_length; return 0; } int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len) { size_t new_length; GIT_ASSERT_ARG(insert_len > 0); GIT_ASSERT_ARG(idx <= v->length); GIT_ERROR_CHECK_ALLOC_ADD(&new_length, v->length, insert_len); if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0) return -1; memmove(&v->contents[idx + insert_len], &v->contents[idx], sizeof(void *) * (v->length - idx)); memset(&v->contents[idx], 0, sizeof(void *) * insert_len); v->length = new_length; return 0; } int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len) { size_t new_length = v->length - remove_len; size_t end_idx = 0; GIT_ASSERT_ARG(remove_len > 0); if (git__add_sizet_overflow(&end_idx, idx, remove_len)) GIT_ASSERT(0); GIT_ASSERT(end_idx <= v->length); if (end_idx < v->length) memmove(&v->contents[idx], &v->contents[end_idx], sizeof(void *) * (v->length - end_idx)); memset(&v->contents[new_length], 0, sizeof(void *) * remove_len); v->length = new_length; return 0; } int git_vector_set(void **old, git_vector *v, size_t position, void *value) { if (position + 1 > v->length) { if (git_vector_resize_to(v, position + 1) < 0) return -1; } if (old != NULL) *old = v->contents[position]; v->contents[position] = value; return 0; } int git_vector_verify_sorted(const git_vector *v) { size_t i; if (!git_vector_is_sorted(v)) return -1; for (i = 1; i < v->length; ++i) { if (v->_cmp(v->contents[i - 1], v->contents[i]) > 0) return -1; } return 0; } void git_vector_reverse(git_vector *v) { size_t a, b; if (v->length == 0) return; a = 0; b = v->length - 1; while (a < b) { void *tmp = v->contents[a]; v->contents[a] = v->contents[b]; v->contents[b] = tmp; a++; b--; } } git2r/src/libgit2/src/config_entries.h0000644000175000017500000000213414125111754017515 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/sys/config.h" #include "config.h" typedef struct git_config_entries git_config_entries; int git_config_entries_new(git_config_entries **out); int git_config_entries_dup(git_config_entries **out, git_config_entries *entries); int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry); void git_config_entries_incref(git_config_entries *entries); void git_config_entries_free(git_config_entries *entries); /* Add or append the new config option */ int git_config_entries_append(git_config_entries *entries, git_config_entry *entry); int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key); int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key); int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries); git2r/src/libgit2/src/strmap.h0000644000175000017500000000754514125111754016040 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_strmap_h__ #define INCLUDE_strmap_h__ #include "common.h" /** A map with C strings as key. */ typedef struct kh_str_s git_strmap; /** * Allocate a new string map. * * @param out Pointer to the map that shall be allocated. * @return 0 on success, an error code if allocation has failed. */ int git_strmap_new(git_strmap **out); /** * Free memory associated with the map. * * Note that this function will _not_ free keys or values added * to this map. * * @param map Pointer to the map that is to be free'd. May be * `NULL`. */ void git_strmap_free(git_strmap *map); /** * Clear all entries from the map. * * This function will remove all entries from the associated map. * Memory associated with it will not be released, though. * * @param map Pointer to the map that shall be cleared. May be * `NULL`. */ void git_strmap_clear(git_strmap *map); /** * Return the number of elements in the map. * * @parameter map map containing the elements * @return number of elements in the map */ size_t git_strmap_size(git_strmap *map); /** * Return value associated with the given key. * * @param map map to search key in * @param key key to search for * @return value associated with the given key or NULL if the key was not found */ void *git_strmap_get(git_strmap *map, const char *key); /** * Set the entry for key to value. * * If the map has no corresponding entry for the given key, a new * entry will be created with the given value. If an entry exists * already, its value will be updated to match the given value. * * @param map map to create new entry in * @param key key to set * @param value value to associate the key with; may be NULL * @return zero if the key was successfully set, a negative error * code otherwise */ int git_strmap_set(git_strmap *map, const char *key, void *value); /** * Delete an entry from the map. * * Delete the given key and its value from the map. If no such * key exists, this will do nothing. * * @param map map to delete key in * @param key key to delete * @return `0` if the key has been deleted, GIT_ENOTFOUND if no * such key was found, a negative code in case of an * error */ int git_strmap_delete(git_strmap *map, const char *key); /** * Check whether a key exists in the given map. * * @param map map to query for the key * @param key key to search for * @return 0 if the key has not been found, 1 otherwise */ int git_strmap_exists(git_strmap *map, const char *key); /** * Iterate over entries of the map. * * This functions allows to iterate over all key-value entries of * the map. The current position is stored in the `iter` variable * and should be initialized to `0` before the first call to this * function. * * @param map map to iterate over * @param value pointer to the variable where to store the current * value. May be NULL. * @param iter iterator storing the current position. Initialize * with zero previous to the first call. * @param key pointer to the variable where to store the current * key. May be NULL. * @return `0` if the next entry was correctly retrieved. * GIT_ITEROVER if no entries are left. A negative error * code otherwise. */ int git_strmap_iterate(void **value, git_strmap *map, size_t *iter, const char **key); #define git_strmap_foreach(h, kvar, vvar, code) { size_t __i = 0; \ while (git_strmap_iterate((void **) &(vvar), h, &__i, &(kvar)) == 0) { \ code; \ } } #define git_strmap_foreach_value(h, vvar, code) { size_t __i = 0; \ while (git_strmap_iterate((void **) &(vvar), h, &__i, NULL) == 0) { \ code; \ } } #endif git2r/src/libgit2/src/tag.c0000644000175000017500000003175414125111754015277 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "tag.h" #include "commit.h" #include "signature.h" #include "message.h" #include "wildmatch.h" #include "git2/object.h" #include "git2/repository.h" #include "git2/signature.h" #include "git2/odb_backend.h" void git_tag__free(void *_tag) { git_tag *tag = _tag; git_signature_free(tag->tagger); git__free(tag->message); git__free(tag->tag_name); git__free(tag); } int git_tag_target(git_object **target, const git_tag *t) { GIT_ASSERT_ARG(t); return git_object_lookup(target, t->object.repo, &t->target, t->type); } const git_oid *git_tag_target_id(const git_tag *t) { GIT_ASSERT_ARG_WITH_RETVAL(t, NULL); return &t->target; } git_object_t git_tag_target_type(const git_tag *t) { GIT_ASSERT_ARG_WITH_RETVAL(t, GIT_OBJECT_INVALID); return t->type; } const char *git_tag_name(const git_tag *t) { GIT_ASSERT_ARG_WITH_RETVAL(t, NULL); return t->tag_name; } const git_signature *git_tag_tagger(const git_tag *t) { return t->tagger; } const char *git_tag_message(const git_tag *t) { GIT_ASSERT_ARG_WITH_RETVAL(t, NULL); return t->message; } static int tag_error(const char *str) { git_error_set(GIT_ERROR_TAG, "failed to parse tag: %s", str); return -1; } static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) { static const char *tag_types[] = { NULL, "commit\n", "tree\n", "blob\n", "tag\n" }; size_t text_len, alloc_len; const char *search; unsigned int i; if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0) return tag_error("object field invalid"); if (buffer + 5 >= buffer_end) return tag_error("object too short"); if (memcmp(buffer, "type ", 5) != 0) return tag_error("type field not found"); buffer += 5; tag->type = GIT_OBJECT_INVALID; for (i = 1; i < ARRAY_SIZE(tag_types); ++i) { size_t type_length = strlen(tag_types[i]); if (buffer + type_length >= buffer_end) return tag_error("object too short"); if (memcmp(buffer, tag_types[i], type_length) == 0) { tag->type = i; buffer += type_length; break; } } if (tag->type == GIT_OBJECT_INVALID) return tag_error("invalid object type"); if (buffer + 4 >= buffer_end) return tag_error("object too short"); if (memcmp(buffer, "tag ", 4) != 0) return tag_error("tag field not found"); buffer += 4; search = memchr(buffer, '\n', buffer_end - buffer); if (search == NULL) return tag_error("object too short"); text_len = search - buffer; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1); tag->tag_name = git__malloc(alloc_len); GIT_ERROR_CHECK_ALLOC(tag->tag_name); memcpy(tag->tag_name, buffer, text_len); tag->tag_name[text_len] = '\0'; buffer = search + 1; tag->tagger = NULL; if (buffer < buffer_end && *buffer != '\n') { tag->tagger = git__malloc(sizeof(git_signature)); GIT_ERROR_CHECK_ALLOC(tag->tagger); if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0) return -1; } tag->message = NULL; if (buffer < buffer_end) { /* If we're not at the end of the header, search for it */ if(*buffer != '\n') { search = git__memmem(buffer, buffer_end - buffer, "\n\n", 2); if (search) buffer = search + 1; else return tag_error("tag contains no message"); } text_len = buffer_end - ++buffer; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1); tag->message = git__malloc(alloc_len); GIT_ERROR_CHECK_ALLOC(tag->message); memcpy(tag->message, buffer, text_len); tag->message[text_len] = '\0'; } return 0; } int git_tag__parse_raw(void *_tag, const char *data, size_t size) { return tag_parse(_tag, data, data + size); } int git_tag__parse(void *_tag, git_odb_object *odb_obj) { git_tag *tag = _tag; const char *buffer = git_odb_object_data(odb_obj); const char *buffer_end = buffer + git_odb_object_size(odb_obj); return tag_parse(tag, buffer, buffer_end); } static int retrieve_tag_reference( git_reference **tag_reference_out, git_buf *ref_name_out, git_repository *repo, const char *tag_name) { git_reference *tag_ref; int error; *tag_reference_out = NULL; if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0) return -1; error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr); if (error < 0) return error; /* Be it not foundo or corrupted */ *tag_reference_out = tag_ref; return 0; } static int retrieve_tag_reference_oid( git_oid *oid, git_buf *ref_name_out, git_repository *repo, const char *tag_name) { if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0) return -1; return git_reference_name_to_id(oid, repo, ref_name_out->ptr); } static int write_tag_annotation( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, const git_signature *tagger, const char *message) { git_buf tag = GIT_BUF_INIT; git_odb *odb; git_oid__writebuf(&tag, "object ", git_object_id(target)); git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target))); git_buf_printf(&tag, "tag %s\n", tag_name); git_signature__writebuf(&tag, "tagger ", tagger); git_buf_putc(&tag, '\n'); if (git_buf_puts(&tag, message) < 0) goto on_error; if (git_repository_odb__weakptr(&odb, repo) < 0) goto on_error; if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJECT_TAG) < 0) goto on_error; git_buf_dispose(&tag); return 0; on_error: git_buf_dispose(&tag); git_error_set(GIT_ERROR_OBJECT, "failed to create tag annotation"); return -1; } static int git_tag_create__internal( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, const git_signature *tagger, const char *message, int allow_ref_overwrite, int create_tag_annotation) { git_reference *new_ref = NULL; git_buf ref_name = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(tag_name); GIT_ASSERT_ARG(target); GIT_ASSERT_ARG(!create_tag_annotation || (tagger && message)); if (git_object_owner(target) != repo) { git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository"); return -1; } error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; /** Ensure the tag name doesn't conflict with an already existing * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { git_buf_dispose(&ref_name); git_error_set(GIT_ERROR_TAG, "tag already exists"); return GIT_EEXISTS; } if (create_tag_annotation) { if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0) return -1; } else git_oid_cpy(oid, git_object_id(target)); error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL); cleanup: git_reference_free(new_ref); git_buf_dispose(&ref_name); return error; } int git_tag_create( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, const git_signature *tagger, const char *message, int allow_ref_overwrite) { return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1); } int git_tag_annotation_create( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, const git_signature *tagger, const char *message) { GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(tag_name); GIT_ASSERT_ARG(target); GIT_ASSERT_ARG(tagger); GIT_ASSERT_ARG(message); return write_tag_annotation(oid, repo, tag_name, target, tagger, message); } int git_tag_create_lightweight( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, int allow_ref_overwrite) { return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0); } int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite) { git_tag tag; int error; git_odb *odb; git_odb_stream *stream; git_odb_object *target_obj; git_reference *new_ref = NULL; git_buf ref_name = GIT_BUF_INIT; GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(buffer); memset(&tag, 0, sizeof(tag)); if (git_repository_odb__weakptr(&odb, repo) < 0) return -1; /* validate the buffer */ if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0) return -1; /* validate the target */ if (git_odb_read(&target_obj, odb, &tag.target) < 0) goto on_error; if (tag.type != target_obj->cached.type) { git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid"); goto on_error; } error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name); if (error < 0 && error != GIT_ENOTFOUND) goto on_error; /* We don't need these objects after this */ git_signature_free(tag.tagger); git__free(tag.tag_name); git__free(tag.message); git_odb_object_free(target_obj); /** Ensure the tag name doesn't conflict with an already existing * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { git_error_set(GIT_ERROR_TAG, "tag already exists"); return GIT_EEXISTS; } /* write the buffer */ if ((error = git_odb_open_wstream( &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0) return error; if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer)))) error = git_odb_stream_finalize_write(oid, stream); git_odb_stream_free(stream); if (error < 0) { git_buf_dispose(&ref_name); return error; } error = git_reference_create( &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL); git_reference_free(new_ref); git_buf_dispose(&ref_name); return error; on_error: git_signature_free(tag.tagger); git__free(tag.tag_name); git__free(tag.message); git_odb_object_free(target_obj); return -1; } int git_tag_delete(git_repository *repo, const char *tag_name) { git_reference *tag_ref; git_buf ref_name = GIT_BUF_INIT; int error; error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name); git_buf_dispose(&ref_name); if (error < 0) return error; error = git_reference_delete(tag_ref); git_reference_free(tag_ref); return error; } typedef struct { git_repository *repo; git_tag_foreach_cb cb; void *cb_data; } tag_cb_data; static int tags_cb(const char *ref, void *data) { int error; git_oid oid; tag_cb_data *d = (tag_cb_data *)data; if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0) return 0; /* no tag */ if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) { if ((error = d->cb(ref, &oid, d->cb_data)) != 0) git_error_set_after_callback_function(error, "git_tag_foreach"); } return error; } int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) { tag_cb_data data; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(cb); data.cb = cb; data.cb_data = cb_data; data.repo = repo; return git_reference_foreach_name(repo, &tags_cb, &data); } typedef struct { git_vector *taglist; const char *pattern; } tag_filter_data; #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR) static int tag_list_cb(const char *tag_name, git_oid *oid, void *data) { tag_filter_data *filter = (tag_filter_data *)data; GIT_UNUSED(oid); if (!*filter->pattern || wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) { char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN); GIT_ERROR_CHECK_ALLOC(matched); return git_vector_insert(filter->taglist, matched); } return 0; } int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo) { int error; tag_filter_data filter; git_vector taglist; GIT_ASSERT_ARG(tag_names); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(pattern); if ((error = git_vector_init(&taglist, 8, NULL)) < 0) return error; filter.taglist = &taglist; filter.pattern = pattern; error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter); if (error < 0) git_vector_free(&taglist); tag_names->strings = (char **)git_vector_detach(&tag_names->count, NULL, &taglist); return 0; } int git_tag_list(git_strarray *tag_names, git_repository *repo) { return git_tag_list_match(tag_names, "", repo); } int git_tag_peel(git_object **tag_target, const git_tag *tag) { return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJECT_ANY); } int git_tag_name_is_valid(int *valid, const char *name) { git_buf ref_name = GIT_BUF_INIT; int error = 0; GIT_ASSERT(valid); /* * Discourage tag name starting with dash, * https://github.com/git/git/commit/4f0accd638b8d2 */ if (!name || name[0] == '-') goto done; if ((error = git_buf_puts(&ref_name, GIT_REFS_TAGS_DIR)) < 0 || (error = git_buf_puts(&ref_name, name)) < 0) goto done; error = git_reference_name_is_valid(valid, ref_name.ptr); done: git_buf_dispose(&ref_name); return error; } /* Deprecated Functions */ #ifndef GIT_DEPRECATE_HARD int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite) { return git_tag_create_from_buffer(oid, repo, buffer, allow_ref_overwrite); } #endif git2r/src/libgit2/src/refspec.c0000644000175000017500000002317514125111754016151 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "refspec.h" #include "git2/errors.h" #include "refs.h" #include "util.h" #include "vector.h" #include "wildmatch.h" int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) { /* Ported from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/remote.c#L518-636 */ size_t llen; int is_glob = 0; const char *lhs, *rhs; int valid = 0; unsigned int flags; GIT_ASSERT_ARG(refspec); GIT_ASSERT_ARG(input); memset(refspec, 0x0, sizeof(git_refspec)); refspec->push = !is_fetch; lhs = input; if (*lhs == '+') { refspec->force = 1; lhs++; } rhs = strrchr(lhs, ':'); /* * Before going on, special case ":" (or "+:") as a refspec * for matching refs. */ if (!is_fetch && rhs == lhs && rhs[1] == '\0') { refspec->matching = 1; refspec->string = git__strdup(input); GIT_ERROR_CHECK_ALLOC(refspec->string); refspec->src = git__strdup(""); GIT_ERROR_CHECK_ALLOC(refspec->src); refspec->dst = git__strdup(""); GIT_ERROR_CHECK_ALLOC(refspec->dst); return 0; } if (rhs) { size_t rlen = strlen(++rhs); if (rlen || !is_fetch) { is_glob = (1 <= rlen && strchr(rhs, '*')); refspec->dst = git__strndup(rhs, rlen); } } llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs)); if (1 <= llen && memchr(lhs, '*', llen)) { if ((rhs && !is_glob) || (!rhs && is_fetch)) goto invalid; is_glob = 1; } else if (rhs && is_glob) goto invalid; refspec->pattern = is_glob; refspec->src = git__strndup(lhs, llen); flags = GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL | GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND | (is_glob ? GIT_REFERENCE_FORMAT_REFSPEC_PATTERN : 0); if (is_fetch) { /* * LHS * - empty is allowed; it means HEAD. * - otherwise it must be a valid looking ref. */ if (!*refspec->src) ; /* empty is ok */ else if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0) goto on_error; else if (!valid) goto invalid; /* * RHS * - missing is ok, and is same as empty. * - empty is ok; it means not to store. * - otherwise it must be a valid looking ref. */ if (!refspec->dst) ; /* ok */ else if (!*refspec->dst) ; /* ok */ else if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0) goto on_error; else if (!valid) goto invalid; } else { /* * LHS * - empty is allowed; it means delete. * - when wildcarded, it must be a valid looking ref. * - otherwise, it must be an extended SHA-1, but * there is no existing way to validate this. */ if (!*refspec->src) ; /* empty is ok */ else if (is_glob) { if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0) goto on_error; else if (!valid) goto invalid; } else { ; /* anything goes, for now */ } /* * RHS * - missing is allowed, but LHS then must be a * valid looking ref. * - empty is not allowed. * - otherwise it must be a valid looking ref. */ if (!refspec->dst) { if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0) goto on_error; else if (!valid) goto invalid; } else if (!*refspec->dst) { goto invalid; } else { if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0) goto on_error; else if (!valid) goto invalid; } /* if the RHS is empty, then it's a copy of the LHS */ if (!refspec->dst) { refspec->dst = git__strdup(refspec->src); GIT_ERROR_CHECK_ALLOC(refspec->dst); } } refspec->string = git__strdup(input); GIT_ERROR_CHECK_ALLOC(refspec->string); return 0; invalid: git_error_set(GIT_ERROR_INVALID, "'%s' is not a valid refspec.", input); git_refspec__dispose(refspec); return GIT_EINVALIDSPEC; on_error: git_refspec__dispose(refspec); return -1; } void git_refspec__dispose(git_refspec *refspec) { if (refspec == NULL) return; git__free(refspec->src); git__free(refspec->dst); git__free(refspec->string); memset(refspec, 0x0, sizeof(git_refspec)); } int git_refspec_parse(git_refspec **out_refspec, const char *input, int is_fetch) { git_refspec *refspec; GIT_ASSERT_ARG(out_refspec); GIT_ASSERT_ARG(input); *out_refspec = NULL; refspec = git__malloc(sizeof(git_refspec)); GIT_ERROR_CHECK_ALLOC(refspec); if (git_refspec__parse(refspec, input, !!is_fetch) != 0) { git__free(refspec); return -1; } *out_refspec = refspec; return 0; } void git_refspec_free(git_refspec *refspec) { git_refspec__dispose(refspec); git__free(refspec); } const char *git_refspec_src(const git_refspec *refspec) { return refspec == NULL ? NULL : refspec->src; } const char *git_refspec_dst(const git_refspec *refspec) { return refspec == NULL ? NULL : refspec->dst; } const char *git_refspec_string(const git_refspec *refspec) { return refspec == NULL ? NULL : refspec->string; } int git_refspec_force(const git_refspec *refspec) { GIT_ASSERT_ARG(refspec); return refspec->force; } int git_refspec_src_matches(const git_refspec *refspec, const char *refname) { if (refspec == NULL || refspec->src == NULL) return false; return (wildmatch(refspec->src, refname, 0) == 0); } int git_refspec_dst_matches(const git_refspec *refspec, const char *refname) { if (refspec == NULL || refspec->dst == NULL) return false; return (wildmatch(refspec->dst, refname, 0) == 0); } static int refspec_transform( git_buf *out, const char *from, const char *to, const char *name) { const char *from_star, *to_star; size_t replacement_len, star_offset; int error; if ((error = git_buf_sanitize(out)) < 0) return error; git_buf_clear(out); /* * There are two parts to each side of a refspec, the bit * before the star and the bit after it. The star can be in * the middle of the pattern, so we need to look at each bit * individually. */ from_star = strchr(from, '*'); to_star = strchr(to, '*'); GIT_ASSERT(from_star && to_star); /* star offset, both in 'from' and in 'name' */ star_offset = from_star - from; /* the first half is copied over */ git_buf_put(out, to, to_star - to); /* * Copy over the name, but exclude the trailing part in "from" starting * after the glob */ replacement_len = strlen(name + star_offset) - strlen(from_star + 1); git_buf_put(out, name + star_offset, replacement_len); return git_buf_puts(out, to_star + 1); } int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(spec); GIT_ASSERT_ARG(name); if ((error = git_buf_sanitize(out)) < 0) return error; if (!git_refspec_src_matches(spec, name)) { git_error_set(GIT_ERROR_INVALID, "ref '%s' doesn't match the source", name); return -1; } if (!spec->pattern) return git_buf_puts(out, spec->dst ? spec->dst : ""); return refspec_transform(out, spec->src, spec->dst, name); } int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(spec); GIT_ASSERT_ARG(name); if ((error = git_buf_sanitize(out)) < 0) return error; if (!git_refspec_dst_matches(spec, name)) { git_error_set(GIT_ERROR_INVALID, "ref '%s' doesn't match the destination", name); return -1; } if (!spec->pattern) return git_buf_puts(out, spec->src); return refspec_transform(out, spec->dst, spec->src, name); } int git_refspec__serialize(git_buf *out, const git_refspec *refspec) { if (refspec->force) git_buf_putc(out, '+'); git_buf_printf(out, "%s:%s", refspec->src != NULL ? refspec->src : "", refspec->dst != NULL ? refspec->dst : ""); return git_buf_oom(out) == false; } int git_refspec_is_wildcard(const git_refspec *spec) { GIT_ASSERT_ARG(spec); GIT_ASSERT_ARG(spec->src); return (spec->src[strlen(spec->src) - 1] == '*'); } git_direction git_refspec_direction(const git_refspec *spec) { GIT_ASSERT_ARG(spec); return spec->push; } int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs) { git_buf buf = GIT_BUF_INIT; size_t j, pos; git_remote_head key; git_refspec *cur; const char *formatters[] = { GIT_REFS_DIR "%s", GIT_REFS_TAGS_DIR "%s", GIT_REFS_HEADS_DIR "%s", NULL }; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(spec); GIT_ASSERT_ARG(refs); cur = git__calloc(1, sizeof(git_refspec)); GIT_ERROR_CHECK_ALLOC(cur); cur->force = spec->force; cur->push = spec->push; cur->pattern = spec->pattern; cur->matching = spec->matching; cur->string = git__strdup(spec->string); /* shorthand on the lhs */ if (git__prefixcmp(spec->src, GIT_REFS_DIR)) { for (j = 0; formatters[j]; j++) { git_buf_clear(&buf); git_buf_printf(&buf, formatters[j], spec->src); GIT_ERROR_CHECK_ALLOC_BUF(&buf); key.name = (char *) git_buf_cstr(&buf); if (!git_vector_search(&pos, refs, &key)) { /* we found something to match the shorthand, set src to that */ cur->src = git_buf_detach(&buf); } } } /* No shorthands found, copy over the name */ if (cur->src == NULL && spec->src != NULL) { cur->src = git__strdup(spec->src); GIT_ERROR_CHECK_ALLOC(cur->src); } if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) { /* if it starts with "remotes" then we just prepend "refs/" */ if (!git__prefixcmp(spec->dst, "remotes/")) { git_buf_puts(&buf, GIT_REFS_DIR); } else { git_buf_puts(&buf, GIT_REFS_HEADS_DIR); } git_buf_puts(&buf, spec->dst); GIT_ERROR_CHECK_ALLOC_BUF(&buf); cur->dst = git_buf_detach(&buf); } git_buf_dispose(&buf); if (cur->dst == NULL && spec->dst != NULL) { cur->dst = git__strdup(spec->dst); GIT_ERROR_CHECK_ALLOC(cur->dst); } return git_vector_insert(out, cur); } git2r/src/libgit2/src/config.c0000644000175000017500000007641714125111754015776 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "config.h" #include "git2/config.h" #include "git2/sys/config.h" #include "config_backend.h" #include "regexp.h" #include "sysdir.h" #include "transaction.h" #include "vector.h" #if GIT_WIN32 # include #endif #include void git_config_entry_free(git_config_entry *entry) { if (!entry) return; entry->free(entry); } typedef struct { git_refcount rc; git_config_backend *backend; git_config_level_t level; } backend_internal; static void backend_internal_free(backend_internal *internal) { git_config_backend *backend; backend = internal->backend; backend->free(backend); git__free(internal); } static void config_free(git_config *cfg) { size_t i; backend_internal *internal; for (i = 0; i < cfg->backends.length; ++i) { internal = git_vector_get(&cfg->backends, i); GIT_REFCOUNT_DEC(internal, backend_internal_free); } git_vector_free(&cfg->backends); git__memzero(cfg, sizeof(*cfg)); git__free(cfg); } void git_config_free(git_config *cfg) { if (cfg == NULL) return; GIT_REFCOUNT_DEC(cfg, config_free); } static int config_backend_cmp(const void *a, const void *b) { const backend_internal *bk_a = (const backend_internal *)(a); const backend_internal *bk_b = (const backend_internal *)(b); return bk_b->level - bk_a->level; } int git_config_new(git_config **out) { git_config *cfg; cfg = git__malloc(sizeof(git_config)); GIT_ERROR_CHECK_ALLOC(cfg); memset(cfg, 0x0, sizeof(git_config)); if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) { git__free(cfg); return -1; } *out = cfg; GIT_REFCOUNT_INC(cfg); return 0; } int git_config_add_file_ondisk( git_config *cfg, const char *path, git_config_level_t level, const git_repository *repo, int force) { git_config_backend *file = NULL; struct stat st; int res; GIT_ASSERT_ARG(cfg); GIT_ASSERT_ARG(path); res = p_stat(path, &st); if (res < 0 && errno != ENOENT && errno != ENOTDIR) { git_error_set(GIT_ERROR_CONFIG, "failed to stat '%s'", path); return -1; } if (git_config_backend_from_file(&file, path) < 0) return -1; if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) { /* * free manually; the file is not owned by the config * instance yet and will not be freed on cleanup */ file->free(file); return res; } return 0; } int git_config_open_ondisk(git_config **out, const char *path) { int error; git_config *config; *out = NULL; if (git_config_new(&config) < 0) return -1; if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, NULL, 0)) < 0) git_config_free(config); else *out = config; return error; } int git_config_snapshot(git_config **out, git_config *in) { int error = 0; size_t i; backend_internal *internal; git_config *config; *out = NULL; if (git_config_new(&config) < 0) return -1; git_vector_foreach(&in->backends, i, internal) { git_config_backend *b; if ((error = internal->backend->snapshot(&b, internal->backend)) < 0) break; if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) { b->free(b); break; } } if (error < 0) git_config_free(config); else *out = config; return error; } static int find_backend_by_level( backend_internal **out, const git_config *cfg, git_config_level_t level) { int pos = -1; backend_internal *internal; size_t i; /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config backend * which has the highest level. As config backends are stored in a vector * sorted by decreasing order of level, getting the backend at position 0 * will do the job. */ if (level == GIT_CONFIG_HIGHEST_LEVEL) { pos = 0; } else { git_vector_foreach(&cfg->backends, i, internal) { if (internal->level == level) pos = (int)i; } } if (pos == -1) { git_error_set(GIT_ERROR_CONFIG, "no configuration exists for the given level '%i'", (int)level); return GIT_ENOTFOUND; } *out = git_vector_get(&cfg->backends, pos); return 0; } static int duplicate_level(void **old_raw, void *new_raw) { backend_internal **old = (backend_internal **)old_raw; GIT_UNUSED(new_raw); git_error_set(GIT_ERROR_CONFIG, "there already exists a configuration for the given level (%i)", (int)(*old)->level); return GIT_EEXISTS; } static void try_remove_existing_backend( git_config *cfg, git_config_level_t level) { int pos = -1; backend_internal *internal; size_t i; git_vector_foreach(&cfg->backends, i, internal) { if (internal->level == level) pos = (int)i; } if (pos == -1) return; internal = git_vector_get(&cfg->backends, pos); if (git_vector_remove(&cfg->backends, pos) < 0) return; GIT_REFCOUNT_DEC(internal, backend_internal_free); } static int git_config__add_internal( git_config *cfg, backend_internal *internal, git_config_level_t level, int force) { int result; /* delete existing config backend for level if it exists */ if (force) try_remove_existing_backend(cfg, level); if ((result = git_vector_insert_sorted(&cfg->backends, internal, &duplicate_level)) < 0) return result; git_vector_sort(&cfg->backends); internal->backend->cfg = cfg; GIT_REFCOUNT_INC(internal); return 0; } int git_config_open_global(git_config **cfg_out, git_config *cfg) { if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG)) return 0; return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL); } int git_config_open_level( git_config **cfg_out, const git_config *cfg_parent, git_config_level_t level) { git_config *cfg; backend_internal *internal; int res; if ((res = find_backend_by_level(&internal, cfg_parent, level)) < 0) return res; if ((res = git_config_new(&cfg)) < 0) return res; if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) { git_config_free(cfg); return res; } *cfg_out = cfg; return 0; } int git_config_add_backend( git_config *cfg, git_config_backend *backend, git_config_level_t level, const git_repository *repo, int force) { backend_internal *internal; int result; GIT_ASSERT_ARG(cfg); GIT_ASSERT_ARG(backend); GIT_ERROR_CHECK_VERSION(backend, GIT_CONFIG_BACKEND_VERSION, "git_config_backend"); if ((result = backend->open(backend, level, repo)) < 0) return result; internal = git__malloc(sizeof(backend_internal)); GIT_ERROR_CHECK_ALLOC(internal); memset(internal, 0x0, sizeof(backend_internal)); internal->backend = backend; internal->level = level; if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) { git__free(internal); return result; } return 0; } /* * Loop over all the variables */ typedef struct { git_config_iterator parent; git_config_iterator *current; const git_config *cfg; git_regexp regex; size_t i; } all_iter; static int find_next_backend(size_t *out, const git_config *cfg, size_t i) { backend_internal *internal; for (; i > 0; --i) { internal = git_vector_get(&cfg->backends, i - 1); if (!internal || !internal->backend) continue; *out = i; return 0; } return -1; } static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter) { all_iter *iter = (all_iter *) _iter; backend_internal *internal; git_config_backend *backend; size_t i; int error = 0; if (iter->current != NULL && (error = iter->current->next(entry, iter->current)) == 0) { return 0; } if (error < 0 && error != GIT_ITEROVER) return error; do { if (find_next_backend(&i, iter->cfg, iter->i) < 0) return GIT_ITEROVER; internal = git_vector_get(&iter->cfg->backends, i - 1); backend = internal->backend; iter->i = i - 1; if (iter->current) iter->current->free(iter->current); iter->current = NULL; error = backend->iterator(&iter->current, backend); if (error == GIT_ENOTFOUND) continue; if (error < 0) return error; error = iter->current->next(entry, iter->current); /* If this backend is empty, then keep going */ if (error == GIT_ITEROVER) continue; return error; } while(1); return GIT_ITEROVER; } static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter) { int error; all_iter *iter = (all_iter *) _iter; /* * We use the "normal" function to grab the next one across * backends and then apply the regex */ while ((error = all_iter_next(entry, _iter)) == 0) { /* skip non-matching keys if regexp was provided */ if (git_regexp_match(&iter->regex, (*entry)->name) != 0) continue; /* and simply return if we like the entry's name */ return 0; } return error; } static void all_iter_free(git_config_iterator *_iter) { all_iter *iter = (all_iter *) _iter; if (iter->current) iter->current->free(iter->current); git__free(iter); } static void all_iter_glob_free(git_config_iterator *_iter) { all_iter *iter = (all_iter *) _iter; git_regexp_dispose(&iter->regex); all_iter_free(_iter); } int git_config_iterator_new(git_config_iterator **out, const git_config *cfg) { all_iter *iter; iter = git__calloc(1, sizeof(all_iter)); GIT_ERROR_CHECK_ALLOC(iter); iter->parent.free = all_iter_free; iter->parent.next = all_iter_next; iter->i = cfg->backends.length; iter->cfg = cfg; *out = (git_config_iterator *) iter; return 0; } int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp) { all_iter *iter; int result; if (regexp == NULL) return git_config_iterator_new(out, cfg); iter = git__calloc(1, sizeof(all_iter)); GIT_ERROR_CHECK_ALLOC(iter); if ((result = git_regexp_compile(&iter->regex, regexp, 0)) < 0) { git__free(iter); return -1; } iter->parent.next = all_iter_glob_next; iter->parent.free = all_iter_glob_free; iter->i = cfg->backends.length; iter->cfg = cfg; *out = (git_config_iterator *) iter; return 0; } int git_config_foreach( const git_config *cfg, git_config_foreach_cb cb, void *payload) { return git_config_foreach_match(cfg, NULL, cb, payload); } int git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, git_config_foreach_cb cb, void *payload) { git_config_entry *entry; git_config_iterator *iter; git_regexp regex; int error = 0; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(cb); if (regexp && git_regexp_compile(®ex, regexp, 0) < 0) return -1; if ((error = backend->iterator(&iter, backend)) < 0) { iter = NULL; return -1; } while (!(iter->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ if (regexp && git_regexp_match(®ex, entry->name) != 0) continue; /* abort iterator on non-zero return value */ if ((error = cb(entry, payload)) != 0) { git_error_set_after_callback(error); break; } } if (regexp != NULL) git_regexp_dispose(®ex); iter->free(iter); return error; } int git_config_foreach_match( const git_config *cfg, const char *regexp, git_config_foreach_cb cb, void *payload) { int error; git_config_iterator *iter; git_config_entry *entry; if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0) return error; while (!(error = git_config_next(&entry, iter))) { if ((error = cb(entry, payload)) != 0) { git_error_set_after_callback(error); break; } } git_config_iterator_free(iter); if (error == GIT_ITEROVER) error = 0; return error; } /************** * Setters **************/ typedef enum { BACKEND_USE_SET, BACKEND_USE_DELETE } backend_use; static const char *uses[] = { "set", "delete" }; static int get_backend_for_use(git_config_backend **out, git_config *cfg, const char *name, backend_use use) { size_t i; backend_internal *backend; *out = NULL; if (git_vector_length(&cfg->backends) == 0) { git_error_set(GIT_ERROR_CONFIG, "cannot %s value for '%s' when no config backends exist", uses[use], name); return GIT_ENOTFOUND; } git_vector_foreach(&cfg->backends, i, backend) { if (!backend->backend->readonly) { *out = backend->backend; return 0; } } git_error_set(GIT_ERROR_CONFIG, "cannot %s value for '%s' when all config backends are readonly", uses[use], name); return GIT_ENOTFOUND; } int git_config_delete_entry(git_config *cfg, const char *name) { git_config_backend *backend; if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0) return GIT_ENOTFOUND; return backend->del(backend, name); } int git_config_set_int64(git_config *cfg, const char *name, int64_t value) { char str_value[32]; /* All numbers should fit in here */ p_snprintf(str_value, sizeof(str_value), "%" PRId64, value); return git_config_set_string(cfg, name, str_value); } int git_config_set_int32(git_config *cfg, const char *name, int32_t value) { return git_config_set_int64(cfg, name, (int64_t)value); } int git_config_set_bool(git_config *cfg, const char *name, int value) { return git_config_set_string(cfg, name, value ? "true" : "false"); } int git_config_set_string(git_config *cfg, const char *name, const char *value) { int error; git_config_backend *backend; if (!value) { git_error_set(GIT_ERROR_CONFIG, "the value to set cannot be NULL"); return -1; } if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_SET) < 0) return GIT_ENOTFOUND; error = backend->set(backend, name, value); if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL) git_repository__configmap_lookup_cache_clear(GIT_REFCOUNT_OWNER(cfg)); return error; } int git_config__update_entry( git_config *config, const char *key, const char *value, bool overwrite_existing, bool only_if_existing) { int error = 0; git_config_entry *ce = NULL; if ((error = git_config__lookup_entry(&ce, config, key, false)) < 0) return error; if (!ce && only_if_existing) /* entry doesn't exist */ return 0; if (ce && !overwrite_existing) /* entry would be overwritten */ return 0; if (value && ce && ce->value && !strcmp(ce->value, value)) /* no change */ return 0; if (!value && (!ce || !ce->value)) /* asked to delete absent entry */ return 0; if (!value) error = git_config_delete_entry(config, key); else error = git_config_set_string(config, key, value); git_config_entry_free(ce); return error; } /*********** * Getters ***********/ static int config_error_notfound(const char *name) { git_error_set(GIT_ERROR_CONFIG, "config value '%s' was not found", name); return GIT_ENOTFOUND; } enum { GET_ALL_ERRORS = 0, GET_NO_MISSING = 1, GET_NO_ERRORS = 2 }; static int get_entry( git_config_entry **out, const git_config *cfg, const char *name, bool normalize_name, int want_errors) { int res = GIT_ENOTFOUND; const char *key = name; char *normalized = NULL; size_t i; backend_internal *internal; *out = NULL; if (normalize_name) { if ((res = git_config__normalize_name(name, &normalized)) < 0) goto cleanup; key = normalized; } res = GIT_ENOTFOUND; git_vector_foreach(&cfg->backends, i, internal) { if (!internal || !internal->backend) continue; res = internal->backend->get(internal->backend, key, out); if (res != GIT_ENOTFOUND) break; } git__free(normalized); cleanup: if (res == GIT_ENOTFOUND) res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name); else if (res && (want_errors == GET_NO_ERRORS)) { git_error_clear(); res = 0; } return res; } int git_config_get_entry( git_config_entry **out, const git_config *cfg, const char *name) { return get_entry(out, cfg, name, true, GET_ALL_ERRORS); } int git_config__lookup_entry( git_config_entry **out, const git_config *cfg, const char *key, bool no_errors) { return get_entry( out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING); } int git_config_get_mapped( int *out, const git_config *cfg, const char *name, const git_configmap *maps, size_t map_n) { git_config_entry *entry; int ret; if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; ret = git_config_lookup_map_value(out, maps, map_n, entry->value); git_config_entry_free(entry); return ret; } int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name) { git_config_entry *entry; int ret; if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; ret = git_config_parse_int64(out, entry->value); git_config_entry_free(entry); return ret; } int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name) { git_config_entry *entry; int ret; if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; ret = git_config_parse_int32(out, entry->value); git_config_entry_free(entry); return ret; } int git_config_get_bool(int *out, const git_config *cfg, const char *name) { git_config_entry *entry; int ret; if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; ret = git_config_parse_bool(out, entry->value); git_config_entry_free(entry); return ret; } static int is_readonly(const git_config *cfg) { size_t i; backend_internal *internal; git_vector_foreach(&cfg->backends, i, internal) { if (!internal || !internal->backend) continue; if (!internal->backend->readonly) return 0; } return 1; } int git_config_get_path(git_buf *out, const git_config *cfg, const char *name) { git_config_entry *entry; int error; if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return error; error = git_config_parse_path(out, entry->value); git_config_entry_free(entry); return error; } int git_config_get_string( const char **out, const git_config *cfg, const char *name) { git_config_entry *entry; int ret; if (!is_readonly(cfg)) { git_error_set(GIT_ERROR_CONFIG, "get_string called on a live config object"); return -1; } ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS); *out = !ret ? (entry->value ? entry->value : "") : NULL; git_config_entry_free(entry); return ret; } int git_config_get_string_buf( git_buf *out, const git_config *cfg, const char *name) { git_config_entry *entry; int ret; const char *str; if ((ret = git_buf_sanitize(out)) < 0) return ret; ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS); str = !ret ? (entry->value ? entry->value : "") : NULL; if (str) ret = git_buf_puts(out, str); git_config_entry_free(entry); return ret; } char *git_config__get_string_force( const git_config *cfg, const char *key, const char *fallback_value) { git_config_entry *entry; char *ret; get_entry(&entry, cfg, key, false, GET_NO_ERRORS); ret = (entry && entry->value) ? git__strdup(entry->value) : fallback_value ? git__strdup(fallback_value) : NULL; git_config_entry_free(entry); return ret; } int git_config__get_bool_force( const git_config *cfg, const char *key, int fallback_value) { int val = fallback_value; git_config_entry *entry; get_entry(&entry, cfg, key, false, GET_NO_ERRORS); if (entry && git_config_parse_bool(&val, entry->value) < 0) git_error_clear(); git_config_entry_free(entry); return val; } int git_config__get_int_force( const git_config *cfg, const char *key, int fallback_value) { int32_t val = (int32_t)fallback_value; git_config_entry *entry; get_entry(&entry, cfg, key, false, GET_NO_ERRORS); if (entry && git_config_parse_int32(&val, entry->value) < 0) git_error_clear(); git_config_entry_free(entry); return (int)val; } int git_config_get_multivar_foreach( const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb cb, void *payload) { int err, found; git_config_iterator *iter; git_config_entry *entry; if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0) return err; found = 0; while ((err = iter->next(&entry, iter)) == 0) { found = 1; if ((err = cb(entry, payload)) != 0) { git_error_set_after_callback(err); break; } } iter->free(iter); if (err == GIT_ITEROVER) err = 0; if (found == 0 && err == 0) err = config_error_notfound(name); return err; } typedef struct { git_config_iterator parent; git_config_iterator *iter; char *name; git_regexp regex; int have_regex; } multivar_iter; static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter) { multivar_iter *iter = (multivar_iter *) _iter; int error = 0; while ((error = iter->iter->next(entry, iter->iter)) == 0) { if (git__strcmp(iter->name, (*entry)->name)) continue; if (!iter->have_regex) return 0; if (git_regexp_match(&iter->regex, (*entry)->value) == 0) return 0; } return error; } static void multivar_iter_free(git_config_iterator *_iter) { multivar_iter *iter = (multivar_iter *) _iter; iter->iter->free(iter->iter); git__free(iter->name); if (iter->have_regex) git_regexp_dispose(&iter->regex); git__free(iter); } int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp) { multivar_iter *iter = NULL; git_config_iterator *inner = NULL; int error; if ((error = git_config_iterator_new(&inner, cfg)) < 0) return error; iter = git__calloc(1, sizeof(multivar_iter)); GIT_ERROR_CHECK_ALLOC(iter); if ((error = git_config__normalize_name(name, &iter->name)) < 0) goto on_error; if (regexp != NULL) { if ((error = git_regexp_compile(&iter->regex, regexp, 0)) < 0) goto on_error; iter->have_regex = 1; } iter->iter = inner; iter->parent.free = multivar_iter_free; iter->parent.next = multivar_iter_next; *out = (git_config_iterator *) iter; return 0; on_error: inner->free(inner); git__free(iter); return error; } int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) { git_config_backend *backend; if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0) return GIT_ENOTFOUND; return backend->set_multivar(backend, name, regexp, value); } int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp) { git_config_backend *backend; if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0) return GIT_ENOTFOUND; return backend->del_multivar(backend, name, regexp); } int git_config_next(git_config_entry **entry, git_config_iterator *iter) { return iter->next(entry, iter); } void git_config_iterator_free(git_config_iterator *iter) { if (iter == NULL) return; iter->free(iter); } int git_config_find_global(git_buf *path) { int error; if ((error = git_buf_sanitize(path)) < 0) return error; return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL); } int git_config_find_xdg(git_buf *path) { int error; if ((error = git_buf_sanitize(path)) < 0) return error; return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG); } int git_config_find_system(git_buf *path) { int error; if ((error = git_buf_sanitize(path)) < 0) return error; return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM); } int git_config_find_programdata(git_buf *path) { int ret; if ((ret = git_buf_sanitize(path)) < 0) return ret; ret = git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA); if (ret != GIT_OK) return ret; return git_path_validate_system_file_ownership(path->ptr); } int git_config__global_location(git_buf *buf) { const git_buf *paths; const char *sep, *start; if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0) return -1; /* no paths, so give up */ if (!paths || !git_buf_len(paths)) return -1; /* find unescaped separator or end of string */ for (sep = start = git_buf_cstr(paths); *sep; ++sep) { if (*sep == GIT_PATH_LIST_SEPARATOR && (sep <= start || sep[-1] != '\\')) break; } if (git_buf_set(buf, start, (size_t)(sep - start)) < 0) return -1; return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL); } int git_config_open_default(git_config **out) { int error; git_config *cfg = NULL; git_buf buf = GIT_BUF_INIT; if ((error = git_config_new(&cfg)) < 0) return error; if (!git_config_find_global(&buf) || !git_config__global_location(&buf)) { error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0); } if (!error && !git_config_find_xdg(&buf)) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_XDG, NULL, 0); if (!error && !git_config_find_system(&buf)) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_SYSTEM, NULL, 0); if (!error && !git_config_find_programdata(&buf)) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0); git_buf_dispose(&buf); if (error) { git_config_free(cfg); cfg = NULL; } *out = cfg; return error; } int git_config_lock(git_transaction **out, git_config *cfg) { int error; git_config_backend *backend; backend_internal *internal; GIT_ASSERT_ARG(cfg); internal = git_vector_get(&cfg->backends, 0); if (!internal || !internal->backend) { git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends"); return -1; } backend = internal->backend; if ((error = backend->lock(backend)) < 0) return error; return git_transaction_config_new(out, cfg); } int git_config_unlock(git_config *cfg, int commit) { git_config_backend *backend; backend_internal *internal; GIT_ASSERT_ARG(cfg); internal = git_vector_get(&cfg->backends, 0); if (!internal || !internal->backend) { git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends"); return -1; } backend = internal->backend; return backend->unlock(backend, commit); } /*********** * Parsers ***********/ int git_config_lookup_map_value( int *out, const git_configmap *maps, size_t map_n, const char *value) { size_t i; for (i = 0; i < map_n; ++i) { const git_configmap *m = maps + i; switch (m->type) { case GIT_CONFIGMAP_FALSE: case GIT_CONFIGMAP_TRUE: { int bool_val; if (git_config_parse_bool(&bool_val, value) == 0 && bool_val == (int)m->type) { *out = m->map_value; return 0; } break; } case GIT_CONFIGMAP_INT32: if (git_config_parse_int32(out, value) == 0) return 0; break; case GIT_CONFIGMAP_STRING: if (value && strcasecmp(value, m->str_match) == 0) { *out = m->map_value; return 0; } break; } } git_error_set(GIT_ERROR_CONFIG, "failed to map '%s'", value); return -1; } int git_config_lookup_map_enum(git_configmap_t *type_out, const char **str_out, const git_configmap *maps, size_t map_n, int enum_val) { size_t i; for (i = 0; i < map_n; i++) { const git_configmap *m = &maps[i]; if (m->map_value != enum_val) continue; *type_out = m->type; *str_out = m->str_match; return 0; } git_error_set(GIT_ERROR_CONFIG, "invalid enum value"); return GIT_ENOTFOUND; } int git_config_parse_bool(int *out, const char *value) { if (git__parse_bool(out, value) == 0) return 0; if (git_config_parse_int32(out, value) == 0) { *out = !!(*out); return 0; } git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as a boolean value", value); return -1; } int git_config_parse_int64(int64_t *out, const char *value) { const char *num_end; int64_t num; if (!value || git__strntol64(&num, value, strlen(value), &num_end, 0) < 0) goto fail_parse; switch (*num_end) { case 'g': case 'G': num *= 1024; /* fallthrough */ case 'm': case 'M': num *= 1024; /* fallthrough */ case 'k': case 'K': num *= 1024; /* check that that there are no more characters after the * given modifier suffix */ if (num_end[1] != '\0') return -1; /* fallthrough */ case '\0': *out = num; return 0; default: goto fail_parse; } fail_parse: git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as an integer", value ? value : "(null)"); return -1; } int git_config_parse_int32(int32_t *out, const char *value) { int64_t tmp; int32_t truncate; if (git_config_parse_int64(&tmp, value) < 0) goto fail_parse; truncate = tmp & 0xFFFFFFFF; if (truncate != tmp) goto fail_parse; *out = truncate; return 0; fail_parse: git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as a 32-bit integer", value ? value : "(null)"); return -1; } int git_config_parse_path(git_buf *out, const char *value) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(value); if ((error = git_buf_sanitize(out)) < 0) return error; if (value[0] == '~') { if (value[1] != '\0' && value[1] != '/') { git_error_set(GIT_ERROR_CONFIG, "retrieving a homedir by name is not supported"); return -1; } return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL); } return git_buf_sets(out, value); } static int normalize_section(char *start, char *end) { char *scan; if (start == end) return GIT_EINVALIDSPEC; /* Validate and downcase range */ for (scan = start; *scan; ++scan) { if (end && scan >= end) break; if (isalnum(*scan)) *scan = (char)git__tolower(*scan); else if (*scan != '-' || scan == start) return GIT_EINVALIDSPEC; } if (scan == start) return GIT_EINVALIDSPEC; return 0; } /* Take something the user gave us and make it nice for our hash function */ int git_config__normalize_name(const char *in, char **out) { char *name, *fdot, *ldot; GIT_ASSERT_ARG(in); GIT_ASSERT_ARG(out); name = git__strdup(in); GIT_ERROR_CHECK_ALLOC(name); fdot = strchr(name, '.'); ldot = strrchr(name, '.'); if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1]) goto invalid; /* Validate and downcase up to first dot and after last dot */ if (normalize_section(name, fdot) < 0 || normalize_section(ldot + 1, NULL) < 0) goto invalid; /* If there is a middle range, make sure it doesn't have newlines */ while (fdot < ldot) if (*fdot++ == '\n') goto invalid; *out = name; return 0; invalid: git__free(name); git_error_set(GIT_ERROR_CONFIG, "invalid config item name '%s'", in); return GIT_EINVALIDSPEC; } struct rename_data { git_config *config; git_buf *name; size_t old_len; }; static int rename_config_entries_cb( const git_config_entry *entry, void *payload) { int error = 0; struct rename_data *data = (struct rename_data *)payload; size_t base_len = git_buf_len(data->name); if (base_len > 0 && !(error = git_buf_puts(data->name, entry->name + data->old_len))) { error = git_config_set_string( data->config, git_buf_cstr(data->name), entry->value); git_buf_truncate(data->name, base_len); } if (!error) error = git_config_delete_entry(data->config, entry->name); return error; } int git_config_rename_section( git_repository *repo, const char *old_section_name, const char *new_section_name) { git_config *config; git_buf pattern = GIT_BUF_INIT, replace = GIT_BUF_INIT; int error = 0; struct rename_data data; git_buf_puts_escape_regex(&pattern, old_section_name); if ((error = git_buf_puts(&pattern, "\\..+")) < 0) goto cleanup; if ((error = git_repository_config__weakptr(&config, repo)) < 0) goto cleanup; data.config = config; data.name = &replace; data.old_len = strlen(old_section_name) + 1; if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0) goto cleanup; if (new_section_name != NULL && (error = normalize_section(replace.ptr, strchr(replace.ptr, '.'))) < 0) { git_error_set( GIT_ERROR_CONFIG, "invalid config section '%s'", new_section_name); goto cleanup; } error = git_config_foreach_match( config, git_buf_cstr(&pattern), rename_config_entries_cb, &data); cleanup: git_buf_dispose(&pattern); git_buf_dispose(&replace); return error; } int git_config_init_backend(git_config_backend *backend, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT); return 0; } git2r/src/libgit2/src/stream.h0000644000175000017500000000344014125111754016013 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_stream_h__ #define INCLUDE_stream_h__ #include "common.h" #include "git2/sys/stream.h" GIT_INLINE(int) git_stream_connect(git_stream *st) { return st->connect(st); } GIT_INLINE(int) git_stream_is_encrypted(git_stream *st) { return st->encrypted; } GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st) { if (!st->encrypted) { git_error_set(GIT_ERROR_INVALID, "an unencrypted stream does not have a certificate"); return -1; } return st->certificate(out, st); } GIT_INLINE(int) git_stream_supports_proxy(git_stream *st) { return st->proxy_support; } GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const git_proxy_options *proxy_opts) { if (!st->proxy_support) { git_error_set(GIT_ERROR_INVALID, "proxy not supported on this stream"); return -1; } return st->set_proxy(st, proxy_opts); } GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len) { return st->read(st, data, len); } GIT_INLINE(ssize_t) git_stream_write(git_stream *st, const char *data, size_t len, int flags) { return st->write(st, data, len, flags); } GIT_INLINE(int) git_stream__write_full(git_stream *st, const char *data, size_t len, int flags) { size_t total_written = 0; while (total_written < len) { ssize_t written = git_stream_write(st, data + total_written, len - total_written, flags); if (written <= 0) return -1; total_written += written; } return 0; } GIT_INLINE(int) git_stream_close(git_stream *st) { return st->close(st); } GIT_INLINE(void) git_stream_free(git_stream *st) { if (!st) return; st->free(st); } #endif git2r/src/libgit2/src/thread.c0000644000175000017500000000467114125111754015771 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #if !defined(GIT_THREADS) #define TLSDATA_MAX 16 typedef struct { void *value; void (GIT_SYSTEM_CALL *destroy_fn)(void *); } tlsdata_value; static tlsdata_value tlsdata_values[TLSDATA_MAX]; static int tlsdata_cnt = 0; int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *)) { if (tlsdata_cnt >= TLSDATA_MAX) return -1; tlsdata_values[tlsdata_cnt].value = NULL; tlsdata_values[tlsdata_cnt].destroy_fn = destroy_fn; *key = tlsdata_cnt; tlsdata_cnt++; return 0; } int git_tlsdata_set(git_tlsdata_key key, void *value) { if (key < 0 || key > tlsdata_cnt) return -1; tlsdata_values[key].value = value; return 0; } void *git_tlsdata_get(git_tlsdata_key key) { if (key < 0 || key > tlsdata_cnt) return NULL; return tlsdata_values[key].value; } int git_tlsdata_dispose(git_tlsdata_key key) { void *value; void (*destroy_fn)(void *) = NULL; if (key < 0 || key > tlsdata_cnt) return -1; value = tlsdata_values[key].value; destroy_fn = tlsdata_values[key].destroy_fn; tlsdata_values[key].value = NULL; tlsdata_values[key].destroy_fn = NULL; if (value && destroy_fn) destroy_fn(value); return 0; } #elif defined(GIT_WIN32) int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *)) { DWORD fls_index = FlsAlloc(destroy_fn); if (fls_index == FLS_OUT_OF_INDEXES) return -1; *key = fls_index; return 0; } int git_tlsdata_set(git_tlsdata_key key, void *value) { if (!FlsSetValue(key, value)) return -1; return 0; } void *git_tlsdata_get(git_tlsdata_key key) { return FlsGetValue(key); } int git_tlsdata_dispose(git_tlsdata_key key) { if (!FlsFree(key)) return -1; return 0; } #elif defined(_POSIX_THREADS) int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *)) { if (pthread_key_create(key, destroy_fn) != 0) return -1; return 0; } int git_tlsdata_set(git_tlsdata_key key, void *value) { if (pthread_setspecific(key, value) != 0) return -1; return 0; } void *git_tlsdata_get(git_tlsdata_key key) { return pthread_getspecific(key); } int git_tlsdata_dispose(git_tlsdata_key key) { if (pthread_key_delete(key) != 0) return -1; return 0; } #else # error unknown threading model #endif git2r/src/libgit2/src/worktree.h0000644000175000017500000000201614125111754016360 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_worktree_h__ #define INCLUDE_worktree_h__ #include "common.h" #include "git2/common.h" #include "git2/worktree.h" struct git_worktree { /* Name of the working tree. This is the name of the * containing directory in the `$PARENT/.git/worktrees/` * directory. */ char *name; /* Path to the where the worktree lives in the filesystem */ char *worktree_path; /* Path to the .git file in the working tree's repository */ char *gitlink_path; /* Path to the .git directory inside the parent's * worktrees directory */ char *gitdir_path; /* Path to the common directory contained in the parent * repository */ char *commondir_path; /* Path to the parent's working directory */ char *parent_path; int locked:1; }; char *git_worktree__read_link(const char *base, const char *file); #endif git2r/src/libgit2/src/runtime.h0000644000175000017500000000411414125111754016202 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_runtime_h__ #define INCLUDE_runtime_h__ #include "common.h" typedef int (*git_runtime_init_fn)(void); typedef void (*git_runtime_shutdown_fn)(void); /** * Start up a new runtime. If this is the first time that this * function is called within the context of the current library * or executable, then the given `init_fns` will be invoked. If * it is not the first time, they will be ignored. * * The given initialization functions _may_ register shutdown * handlers using `git_runtime_shutdown_register` to be notified * when the runtime is shutdown. * * @param init_fns The list of initialization functions to call * @param cnt The number of init_fns * @return The number of initializations performed (including this one) or an error */ int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt); /* * Returns the number of initializations active (the number of calls to * `git_runtime_init` minus the number of calls sto `git_runtime_shutdown`). * If 0, the runtime is not currently initialized. * * @return The number of initializations performed or an error */ int git_runtime_init_count(void); /** * Shut down the runtime. If this is the last shutdown call, * such that there are no remaining `init` calls, then any * shutdown hooks that have been registered will be invoked. * * The number of outstanding initializations will be returned. * If this number is 0, then the runtime is shutdown. * * @return The number of outstanding initializations (after this one) or an error */ int git_runtime_shutdown(void); /** * Register a shutdown handler for this runtime. This should be done * by a function invoked by `git_runtime_init` to ensure that the * appropriate locks are taken. * * @param callback The shutdown handler callback * @return 0 or an error code */ int git_runtime_shutdown_register(git_runtime_shutdown_fn callback); #endif git2r/src/libgit2/src/odb_mempack.c0000644000175000017500000001002414125111754016750 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/object.h" #include "git2/sys/odb_backend.h" #include "git2/sys/mempack.h" #include "futils.h" #include "hash.h" #include "odb.h" #include "array.h" #include "oidmap.h" #include "git2/odb_backend.h" #include "git2/types.h" #include "git2/pack.h" struct memobject { git_oid oid; size_t len; git_object_t type; char data[GIT_FLEX_ARRAY]; }; struct memory_packer_db { git_odb_backend parent; git_oidmap *objects; git_array_t(struct memobject *) commits; }; static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type) { struct memory_packer_db *db = (struct memory_packer_db *)_backend; struct memobject *obj = NULL; size_t alloc_len; if (git_oidmap_exists(db->objects, oid)) return 0; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(struct memobject), len); obj = git__malloc(alloc_len); GIT_ERROR_CHECK_ALLOC(obj); memcpy(obj->data, data, len); git_oid_cpy(&obj->oid, oid); obj->len = len; obj->type = type; if (git_oidmap_set(db->objects, &obj->oid, obj) < 0) return -1; if (type == GIT_OBJECT_COMMIT) { struct memobject **store = git_array_alloc(db->commits); GIT_ERROR_CHECK_ALLOC(store); *store = obj; } return 0; } static int impl__exists(git_odb_backend *backend, const git_oid *oid) { struct memory_packer_db *db = (struct memory_packer_db *)backend; return git_oidmap_exists(db->objects, oid); } static int impl__read(void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid) { struct memory_packer_db *db = (struct memory_packer_db *)backend; struct memobject *obj; if ((obj = git_oidmap_get(db->objects, oid)) == NULL) return GIT_ENOTFOUND; *len_p = obj->len; *type_p = obj->type; *buffer_p = git__malloc(obj->len); GIT_ERROR_CHECK_ALLOC(*buffer_p); memcpy(*buffer_p, obj->data, obj->len); return 0; } static int impl__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid) { struct memory_packer_db *db = (struct memory_packer_db *)backend; struct memobject *obj; if ((obj = git_oidmap_get(db->objects, oid)) == NULL) return GIT_ENOTFOUND; *len_p = obj->len; *type_p = obj->type; return 0; } int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *_backend) { struct memory_packer_db *db = (struct memory_packer_db *)_backend; git_packbuilder *packbuilder; uint32_t i; int err = -1; if (git_packbuilder_new(&packbuilder, repo) < 0) return -1; git_packbuilder_set_threads(packbuilder, 0); for (i = 0; i < db->commits.size; ++i) { struct memobject *commit = db->commits.ptr[i]; err = git_packbuilder_insert_commit(packbuilder, &commit->oid); if (err < 0) goto cleanup; } err = git_packbuilder_write_buf(pack, packbuilder); cleanup: git_packbuilder_free(packbuilder); return err; } int git_mempack_reset(git_odb_backend *_backend) { struct memory_packer_db *db = (struct memory_packer_db *)_backend; struct memobject *object = NULL; git_oidmap_foreach_value(db->objects, object, { git__free(object); }); git_array_clear(db->commits); git_oidmap_clear(db->objects); return 0; } static void impl__free(git_odb_backend *_backend) { struct memory_packer_db *db = (struct memory_packer_db *)_backend; git_mempack_reset(_backend); git_oidmap_free(db->objects); git__free(db); } int git_mempack_new(git_odb_backend **out) { struct memory_packer_db *db; GIT_ASSERT_ARG(out); db = git__calloc(1, sizeof(struct memory_packer_db)); GIT_ERROR_CHECK_ALLOC(db); if (git_oidmap_new(&db->objects) < 0) return -1; db->parent.version = GIT_ODB_BACKEND_VERSION; db->parent.read = &impl__read; db->parent.write = &impl__write; db->parent.read_header = &impl__read_header; db->parent.exists = &impl__exists; db->parent.free = &impl__free; *out = (git_odb_backend *)db; return 0; } git2r/src/libgit2/src/commit.c0000644000175000017500000005615714125111754016020 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "commit.h" #include "git2/common.h" #include "git2/object.h" #include "git2/repository.h" #include "git2/signature.h" #include "git2/mailmap.h" #include "git2/sys/commit.h" #include "odb.h" #include "commit.h" #include "signature.h" #include "message.h" #include "refs.h" #include "object.h" #include "array.h" #include "oidarray.h" void git_commit__free(void *_commit) { git_commit *commit = _commit; git_array_clear(commit->parent_ids); git_signature_free(commit->author); git_signature_free(commit->committer); git__free(commit->raw_header); git__free(commit->raw_message); git__free(commit->message_encoding); git__free(commit->summary); git__free(commit->body); git__free(commit); } static int git_commit__create_buffer_internal( git_buf *out, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_oid *tree, git_array_oid_t *parents) { size_t i = 0; const git_oid *parent; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(tree); git_oid__writebuf(out, "tree ", tree); for (i = 0; i < git_array_size(*parents); i++) { parent = git_array_get(*parents, i); git_oid__writebuf(out, "parent ", parent); } git_signature__writebuf(out, "author ", author); git_signature__writebuf(out, "committer ", committer); if (message_encoding != NULL) git_buf_printf(out, "encoding %s\n", message_encoding); git_buf_putc(out, '\n'); if (git_buf_puts(out, message) < 0) goto on_error; return 0; on_error: git_buf_dispose(out); return -1; } static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree, git_commit_parent_callback parent_cb, void *parent_payload, const git_oid *current_id, bool validate) { size_t i; int error; git_oid *parent_cpy; const git_oid *parent; if (validate && !git_object__is_valid(repo, tree, GIT_OBJECT_TREE)) return -1; i = 0; while ((parent = parent_cb(i, parent_payload)) != NULL) { if (validate && !git_object__is_valid(repo, parent, GIT_OBJECT_COMMIT)) { error = -1; goto on_error; } parent_cpy = git_array_alloc(*parents); GIT_ERROR_CHECK_ALLOC(parent_cpy); git_oid_cpy(parent_cpy, parent); i++; } if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) { git_error_set(GIT_ERROR_OBJECT, "failed to create commit: current tip is not the first parent"); error = GIT_EMODIFIED; goto on_error; } return 0; on_error: git_array_clear(*parents); return error; } static int git_commit__create_internal( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_oid *tree, git_commit_parent_callback parent_cb, void *parent_payload, bool validate) { int error; git_odb *odb; git_reference *ref = NULL; git_buf buf = GIT_BUF_INIT; const git_oid *current_id = NULL; git_array_oid_t parents = GIT_ARRAY_INIT; if (update_ref) { error = git_reference_lookup_resolved(&ref, repo, update_ref, 10); if (error < 0 && error != GIT_ENOTFOUND) return error; } git_error_clear(); if (ref) current_id = git_reference_target(ref); if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0) goto cleanup; error = git_commit__create_buffer_internal(&buf, author, committer, message_encoding, message, tree, &parents); if (error < 0) goto cleanup; if (git_repository_odb__weakptr(&odb, repo) < 0) goto cleanup; if (git_odb__freshen(odb, tree) < 0) goto cleanup; if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT) < 0) goto cleanup; if (update_ref != NULL) { error = git_reference__update_for_commit( repo, ref, update_ref, id, "commit"); goto cleanup; } cleanup: git_array_clear(parents); git_reference_free(ref); git_buf_dispose(&buf); return error; } int git_commit_create_from_callback( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_oid *tree, git_commit_parent_callback parent_cb, void *parent_payload) { return git_commit__create_internal( id, repo, update_ref, author, committer, message_encoding, message, tree, parent_cb, parent_payload, true); } typedef struct { size_t total; va_list args; } commit_parent_varargs; static const git_oid *commit_parent_from_varargs(size_t curr, void *payload) { commit_parent_varargs *data = payload; const git_commit *commit; if (curr >= data->total) return NULL; commit = va_arg(data->args, const git_commit *); return commit ? git_commit_id(commit) : NULL; } int git_commit_create_v( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, ...) { int error = 0; commit_parent_varargs data; GIT_ASSERT_ARG(tree); GIT_ASSERT_ARG(git_tree_owner(tree) == repo); data.total = parent_count; va_start(data.args, parent_count); error = git_commit__create_internal( id, repo, update_ref, author, committer, message_encoding, message, git_tree_id(tree), commit_parent_from_varargs, &data, false); va_end(data.args); return error; } typedef struct { size_t total; const git_oid **parents; } commit_parent_oids; static const git_oid *commit_parent_from_ids(size_t curr, void *payload) { commit_parent_oids *data = payload; return (curr < data->total) ? data->parents[curr] : NULL; } int git_commit_create_from_ids( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_oid *tree, size_t parent_count, const git_oid *parents[]) { commit_parent_oids data = { parent_count, parents }; return git_commit__create_internal( id, repo, update_ref, author, committer, message_encoding, message, tree, commit_parent_from_ids, &data, true); } typedef struct { size_t total; const git_commit **parents; git_repository *repo; } commit_parent_data; static const git_oid *commit_parent_from_array(size_t curr, void *payload) { commit_parent_data *data = payload; const git_commit *commit; if (curr >= data->total) return NULL; commit = data->parents[curr]; if (git_commit_owner(commit) != data->repo) return NULL; return git_commit_id(commit); } int git_commit_create( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, const git_commit *parents[]) { commit_parent_data data = { parent_count, parents, repo }; GIT_ASSERT_ARG(tree); GIT_ASSERT_ARG(git_tree_owner(tree) == repo); return git_commit__create_internal( id, repo, update_ref, author, committer, message_encoding, message, git_tree_id(tree), commit_parent_from_array, &data, false); } static const git_oid *commit_parent_for_amend(size_t curr, void *payload) { const git_commit *commit_to_amend = payload; if (curr >= git_array_size(commit_to_amend->parent_ids)) return NULL; return git_array_get(commit_to_amend->parent_ids, curr); } int git_commit_amend( git_oid *id, const git_commit *commit_to_amend, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree) { git_repository *repo; git_oid tree_id; git_reference *ref; int error; GIT_ASSERT_ARG(id); GIT_ASSERT_ARG(commit_to_amend); repo = git_commit_owner(commit_to_amend); if (!author) author = git_commit_author(commit_to_amend); if (!committer) committer = git_commit_committer(commit_to_amend); if (!message_encoding) message_encoding = git_commit_message_encoding(commit_to_amend); if (!message) message = git_commit_message(commit_to_amend); if (!tree) { git_tree *old_tree; GIT_ERROR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) ); git_oid_cpy(&tree_id, git_tree_id(old_tree)); git_tree_free(old_tree); } else { GIT_ASSERT_ARG(git_tree_owner(tree) == repo); git_oid_cpy(&tree_id, git_tree_id(tree)); } if (update_ref) { if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0) return error; if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) { git_reference_free(ref); git_error_set(GIT_ERROR_REFERENCE, "commit to amend is not the tip of the given branch"); return -1; } } error = git_commit__create_internal( id, repo, NULL, author, committer, message_encoding, message, &tree_id, commit_parent_for_amend, (void *)commit_to_amend, false); if (!error && update_ref) { error = git_reference__update_for_commit( repo, ref, NULL, id, "commit"); git_reference_free(ref); } return error; } static int commit_parse(git_commit *commit, const char *data, size_t size, unsigned int flags) { const char *buffer_start = data, *buffer; const char *buffer_end = buffer_start + size; git_oid parent_id; size_t header_len; git_signature dummy_sig; GIT_ASSERT_ARG(commit); GIT_ASSERT_ARG(data); buffer = buffer_start; /* Allocate for one, which will allow not to realloc 90% of the time */ git_array_init_to_size(commit->parent_ids, 1); GIT_ERROR_CHECK_ARRAY(commit->parent_ids); /* The tree is always the first field */ if (!(flags & GIT_COMMIT_PARSE_QUICK)) { if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0) goto bad_buffer; } else { size_t tree_len = strlen("tree ") + GIT_OID_HEXSZ + 1; if (buffer + tree_len > buffer_end) goto bad_buffer; buffer += tree_len; } /* * TODO: commit grafts! */ while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) { git_oid *new_id = git_array_alloc(commit->parent_ids); GIT_ERROR_CHECK_ALLOC(new_id); git_oid_cpy(new_id, &parent_id); } if (!(flags & GIT_COMMIT_PARSE_QUICK)) { commit->author = git__malloc(sizeof(git_signature)); GIT_ERROR_CHECK_ALLOC(commit->author); if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0) return -1; } /* Some tools create multiple author fields, ignore the extra ones */ while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) { if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0) return -1; git__free(dummy_sig.name); git__free(dummy_sig.email); } /* Always parse the committer; we need the commit time */ commit->committer = git__malloc(sizeof(git_signature)); GIT_ERROR_CHECK_ALLOC(commit->committer); if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0) return -1; if (flags & GIT_COMMIT_PARSE_QUICK) return 0; /* Parse add'l header entries */ while (buffer < buffer_end) { const char *eoln = buffer; if (buffer[-1] == '\n' && buffer[0] == '\n') break; while (eoln < buffer_end && *eoln != '\n') ++eoln; if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) { buffer += strlen("encoding "); commit->message_encoding = git__strndup(buffer, eoln - buffer); GIT_ERROR_CHECK_ALLOC(commit->message_encoding); } if (eoln < buffer_end && *eoln == '\n') ++eoln; buffer = eoln; } header_len = buffer - buffer_start; commit->raw_header = git__strndup(buffer_start, header_len); GIT_ERROR_CHECK_ALLOC(commit->raw_header); /* point "buffer" to data after header, +1 for the final LF */ buffer = buffer_start + header_len + 1; /* extract commit message */ if (buffer <= buffer_end) commit->raw_message = git__strndup(buffer, buffer_end - buffer); else commit->raw_message = git__strdup(""); GIT_ERROR_CHECK_ALLOC(commit->raw_message); return 0; bad_buffer: git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object"); return -1; } int git_commit__parse_raw(void *commit, const char *data, size_t size) { return commit_parse(commit, data, size, 0); } int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags) { return commit_parse(commit, git_odb_object_data(odb_obj), git_odb_object_size(odb_obj), flags); } int git_commit__parse(void *_commit, git_odb_object *odb_obj) { return git_commit__parse_ext(_commit, odb_obj, 0); } #define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \ _rvalue git_commit_##_name(const git_commit *commit) \ {\ GIT_ASSERT_ARG_WITH_RETVAL(commit, _invalid); \ return _return; \ } GIT_COMMIT_GETTER(const git_signature *, author, commit->author, NULL) GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer, NULL) GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message, NULL) GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding, NULL) GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header, NULL) GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time, INT64_MIN) GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset, -1) GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids), 0) GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id, NULL) const char *git_commit_message(const git_commit *commit) { const char *message; GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL); message = commit->raw_message; /* trim leading newlines from raw message */ while (*message && *message == '\n') ++message; return message; } const char *git_commit_summary(git_commit *commit) { git_buf summary = GIT_BUF_INIT; const char *msg, *space; bool space_contains_newline = false; GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL); if (!commit->summary) { for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) { char next_character = msg[0]; /* stop processing at the end of the first paragraph */ if (next_character == '\n' && (!msg[1] || msg[1] == '\n')) break; /* record the beginning of contiguous whitespace runs */ else if (git__isspace(next_character)) { if(space == NULL) { space = msg; space_contains_newline = false; } space_contains_newline |= next_character == '\n'; } /* the next character is non-space */ else { /* process any recorded whitespace */ if (space) { if(space_contains_newline) git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */ else git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */ space = NULL; } /* copy the next character */ git_buf_putc(&summary, next_character); } } commit->summary = git_buf_detach(&summary); if (!commit->summary) commit->summary = git__strdup(""); } return commit->summary; } const char *git_commit_body(git_commit *commit) { const char *msg, *end; GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL); if (!commit->body) { /* search for end of summary */ for (msg = git_commit_message(commit); *msg; ++msg) if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n')) break; /* trim leading and trailing whitespace */ for (; *msg; ++msg) if (!git__isspace(*msg)) break; for (end = msg + strlen(msg) - 1; msg <= end; --end) if (!git__isspace(*end)) break; if (*msg) commit->body = git__strndup(msg, end - msg + 1); } return commit->body; } int git_commit_tree(git_tree **tree_out, const git_commit *commit) { GIT_ASSERT_ARG(commit); return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id); } const git_oid *git_commit_parent_id( const git_commit *commit, unsigned int n) { GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL); return git_array_get(commit->parent_ids, n); } int git_commit_parent( git_commit **parent, const git_commit *commit, unsigned int n) { const git_oid *parent_id; GIT_ASSERT_ARG(commit); parent_id = git_commit_parent_id(commit, n); if (parent_id == NULL) { git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n); return GIT_ENOTFOUND; } return git_commit_lookup(parent, commit->object.repo, parent_id); } int git_commit_nth_gen_ancestor( git_commit **ancestor, const git_commit *commit, unsigned int n) { git_commit *current, *parent = NULL; int error; GIT_ASSERT_ARG(ancestor); GIT_ASSERT_ARG(commit); if (git_commit_dup(¤t, (git_commit *)commit) < 0) return -1; if (n == 0) { *ancestor = current; return 0; } while (n--) { error = git_commit_parent(&parent, current, 0); git_commit_free(current); if (error < 0) return error; current = parent; } *ancestor = parent; return 0; } int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field) { const char *eol, *buf = commit->raw_header; git_buf_clear(out); while ((eol = strchr(buf, '\n'))) { /* We can skip continuations here */ if (buf[0] == ' ') { buf = eol + 1; continue; } /* Skip until we find the field we're after */ if (git__prefixcmp(buf, field)) { buf = eol + 1; continue; } buf += strlen(field); /* Check that we're not matching a prefix but the field itself */ if (buf[0] != ' ') { buf = eol + 1; continue; } buf++; /* skip the SP */ git_buf_put(out, buf, eol - buf); if (git_buf_oom(out)) goto oom; /* If the next line starts with SP, it's multi-line, we must continue */ while (eol[1] == ' ') { git_buf_putc(out, '\n'); buf = eol + 2; eol = strchr(buf, '\n'); if (!eol) goto malformed; git_buf_put(out, buf, eol - buf); } if (git_buf_oom(out)) goto oom; return 0; } git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field); return GIT_ENOTFOUND; malformed: git_error_set(GIT_ERROR_OBJECT, "malformed header"); return -1; oom: git_error_set_oom(); return -1; } int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field) { git_odb_object *obj; git_odb *odb; const char *buf; const char *h, *eol; int error; git_buf_clear(signature); git_buf_clear(signed_data); if (!field) field = "gpgsig"; if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) return error; if ((error = git_odb_read(&obj, odb, commit_id)) < 0) return error; if (obj->cached.type != GIT_OBJECT_COMMIT) { git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB"); error = GIT_ENOTFOUND; goto cleanup; } buf = git_odb_object_data(obj); while ((h = strchr(buf, '\n')) && h[1] != '\0') { h++; if (git__prefixcmp(buf, field)) { if (git_buf_put(signed_data, buf, h - buf) < 0) return -1; buf = h; continue; } h = buf; h += strlen(field); eol = strchr(h, '\n'); if (h[0] != ' ') { buf = h; continue; } if (!eol) goto malformed; h++; /* skip the SP */ git_buf_put(signature, h, eol - h); if (git_buf_oom(signature)) goto oom; /* If the next line starts with SP, it's multi-line, we must continue */ while (eol[1] == ' ') { git_buf_putc(signature, '\n'); h = eol + 2; eol = strchr(h, '\n'); if (!eol) goto malformed; git_buf_put(signature, h, eol - h); } if (git_buf_oom(signature)) goto oom; error = git_buf_puts(signed_data, eol+1); git_odb_object_free(obj); return error; } git_error_set(GIT_ERROR_OBJECT, "this commit is not signed"); error = GIT_ENOTFOUND; goto cleanup; malformed: git_error_set(GIT_ERROR_OBJECT, "malformed header"); error = -1; goto cleanup; oom: git_error_set_oom(); error = -1; goto cleanup; cleanup: git_odb_object_free(obj); git_buf_clear(signature); git_buf_clear(signed_data); return error; } int git_commit_create_buffer(git_buf *out, git_repository *repo, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, const git_commit *parents[]) { int error; commit_parent_data data = { parent_count, parents, repo }; git_array_oid_t parents_arr = GIT_ARRAY_INIT; const git_oid *tree_id; GIT_ASSERT_ARG(tree); GIT_ASSERT_ARG(git_tree_owner(tree) == repo); tree_id = git_tree_id(tree); if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0) return error; error = git_commit__create_buffer_internal( out, author, committer, message_encoding, message, tree_id, &parents_arr); git_array_clear(parents_arr); return error; } /** * Append to 'out' properly marking continuations when there's a newline in 'content' */ static int format_header_field(git_buf *out, const char *field, const char *content) { const char *lf; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(field); GIT_ASSERT_ARG(content); git_buf_puts(out, field); git_buf_putc(out, ' '); while ((lf = strchr(content, '\n')) != NULL) { git_buf_put(out, content, lf - content); git_buf_puts(out, "\n "); content = lf + 1; } git_buf_puts(out, content); git_buf_putc(out, '\n'); return git_buf_oom(out) ? -1 : 0; } static const git_oid *commit_parent_from_commit(size_t n, void *payload) { const git_commit *commit = (const git_commit *) payload; return git_array_get(commit->parent_ids, n); } int git_commit_create_with_signature( git_oid *out, git_repository *repo, const char *commit_content, const char *signature, const char *signature_field) { git_odb *odb; int error = 0; const char *field; const char *header_end; git_buf commit = GIT_BUF_INIT; git_commit *parsed; git_array_oid_t parents = GIT_ARRAY_INIT; /* The first step is to verify that all the tree and parents exist */ parsed = git__calloc(1, sizeof(git_commit)); GIT_ERROR_CHECK_ALLOC(parsed); if ((error = commit_parse(parsed, commit_content, strlen(commit_content), 0)) < 0) goto cleanup; if ((error = validate_tree_and_parents(&parents, repo, &parsed->tree_id, commit_parent_from_commit, parsed, NULL, true)) < 0) goto cleanup; git_array_clear(parents); /* Then we start appending by identifying the end of the commit header */ header_end = strstr(commit_content, "\n\n"); if (!header_end) { git_error_set(GIT_ERROR_INVALID, "malformed commit contents"); error = -1; goto cleanup; } /* The header ends after the first LF */ header_end++; git_buf_put(&commit, commit_content, header_end - commit_content); if (signature != NULL) { field = signature_field ? signature_field : "gpgsig"; if ((error = format_header_field(&commit, field, signature)) < 0) goto cleanup; } git_buf_puts(&commit, header_end); if (git_buf_oom(&commit)) return -1; if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) goto cleanup; if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0) goto cleanup; cleanup: git_commit__free(parsed); git_buf_dispose(&commit); return error; } int git_commit_committer_with_mailmap( git_signature **out, const git_commit *commit, const git_mailmap *mailmap) { return git_mailmap_resolve_signature(out, mailmap, commit->committer); } int git_commit_author_with_mailmap( git_signature **out, const git_commit *commit, const git_mailmap *mailmap) { return git_mailmap_resolve_signature(out, mailmap, commit->author); } git2r/src/libgit2/src/refdb_fs.c0000644000175000017500000015427214125111754016277 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "refs.h" #include "hash.h" #include "repository.h" #include "futils.h" #include "filebuf.h" #include "pack.h" #include "parse.h" #include "reflog.h" #include "refdb.h" #include "iterator.h" #include "sortedcache.h" #include "signature.h" #include "wildmatch.h" #include #include #include #include #include #include #include #define DEFAULT_NESTING_LEVEL 5 #define MAX_NESTING_LEVEL 10 enum { PACKREF_HAS_PEEL = 1, PACKREF_WAS_LOOSE = 2, PACKREF_CANNOT_PEEL = 4, PACKREF_SHADOWED = 8, }; enum { PEELING_NONE = 0, PEELING_STANDARD, PEELING_FULL }; struct packref { git_oid oid; git_oid peel; char flags; char name[GIT_FLEX_ARRAY]; }; typedef struct refdb_fs_backend { git_refdb_backend parent; git_repository *repo; /* path to git directory */ char *gitpath; /* path to common objects' directory */ char *commonpath; git_sortedcache *refcache; int peeling_mode; git_iterator_flag_t iterator_flags; uint32_t direach_flags; int fsync; } refdb_fs_backend; static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name); GIT_INLINE(int) loose_path( git_buf *out, const char *base, const char *refname) { if (git_buf_joinpath(out, base, refname) < 0) return -1; return git_path_validate_filesystem_with_suffix(out->ptr, out->size, CONST_STRLEN(".lock")); } GIT_INLINE(int) reflog_path( git_buf *out, git_repository *repo, const char *refname) { const char *base; int error; base = (strcmp(refname, GIT_HEAD_FILE) == 0) ? repo->gitdir : repo->commondir; if ((error = git_buf_joinpath(out, base, GIT_REFLOG_DIR)) < 0) return error; return loose_path(out, out->ptr, refname); } static int packref_cmp(const void *a_, const void *b_) { const struct packref *a = a_, *b = b_; return strcmp(a->name, b->name); } static int packed_reload(refdb_fs_backend *backend) { int error; git_buf packedrefs = GIT_BUF_INIT; char *scan, *eof, *eol; if (!backend->gitpath) return 0; error = git_sortedcache_lockandload(backend->refcache, &packedrefs); /* * If we can't find the packed-refs, clear table and return. * Any other error just gets passed through. * If no error, and file wasn't changed, just return. * Anything else means we need to refresh the packed refs. */ if (error <= 0) { if (error == GIT_ENOTFOUND) { GIT_UNUSED(git_sortedcache_clear(backend->refcache, true)); git_error_clear(); error = 0; } return error; } /* At this point, refresh the packed refs from the loaded buffer. */ GIT_UNUSED(git_sortedcache_clear(backend->refcache, false)); scan = (char *)packedrefs.ptr; eof = scan + packedrefs.size; backend->peeling_mode = PEELING_NONE; if (*scan == '#') { static const char *traits_header = "# pack-refs with: "; if (git__prefixcmp(scan, traits_header) == 0) { scan += strlen(traits_header); eol = strchr(scan, '\n'); if (!eol) goto parse_failed; *eol = '\0'; if (strstr(scan, " fully-peeled ") != NULL) { backend->peeling_mode = PEELING_FULL; } else if (strstr(scan, " peeled ") != NULL) { backend->peeling_mode = PEELING_STANDARD; } scan = eol + 1; } } while (scan < eof && *scan == '#') { if (!(eol = strchr(scan, '\n'))) goto parse_failed; scan = eol + 1; } while (scan < eof) { struct packref *ref; git_oid oid; /* parse " \n" */ if (git_oid_fromstr(&oid, scan) < 0) goto parse_failed; scan += GIT_OID_HEXSZ; if (*scan++ != ' ') goto parse_failed; if (!(eol = strchr(scan, '\n'))) goto parse_failed; *eol = '\0'; if (eol[-1] == '\r') eol[-1] = '\0'; if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0) goto parse_failed; scan = eol + 1; git_oid_cpy(&ref->oid, &oid); /* look for optional "^\n" */ if (*scan == '^') { if (git_oid_fromstr(&oid, scan + 1) < 0) goto parse_failed; scan += GIT_OID_HEXSZ + 1; if (scan < eof) { if (!(eol = strchr(scan, '\n'))) goto parse_failed; scan = eol + 1; } git_oid_cpy(&ref->peel, &oid); ref->flags |= PACKREF_HAS_PEEL; } else if (backend->peeling_mode == PEELING_FULL || (backend->peeling_mode == PEELING_STANDARD && git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) ref->flags |= PACKREF_CANNOT_PEEL; } git_sortedcache_wunlock(backend->refcache); git_buf_dispose(&packedrefs); return 0; parse_failed: git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file"); GIT_UNUSED(git_sortedcache_clear(backend->refcache, false)); git_sortedcache_wunlock(backend->refcache); git_buf_dispose(&packedrefs); return -1; } static int loose_parse_oid( git_oid *oid, const char *filename, git_buf *file_content) { const char *str = git_buf_cstr(file_content); if (git_buf_len(file_content) < GIT_OID_HEXSZ) goto corrupted; /* we need to get 40 OID characters from the file */ if (git_oid_fromstr(oid, str) < 0) goto corrupted; /* If the file is longer than 40 chars, the 41st must be a space */ str += GIT_OID_HEXSZ; if (*str == '\0' || git__isspace(*str)) return 0; corrupted: git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file: %s", filename); return -1; } static int loose_readbuffer(git_buf *buf, const char *base, const char *path) { int error; if ((error = loose_path(buf, base, path)) < 0 || (error = git_futils_readbuffer(buf, buf->ptr)) < 0) git_buf_dispose(buf); return error; } static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name) { int error = 0; git_buf ref_file = GIT_BUF_INIT; struct packref *ref = NULL; git_oid oid; /* if we fail to load the loose reference, assume someone changed * the filesystem under us and skip it... */ if (loose_readbuffer(&ref_file, backend->gitpath, name) < 0) { git_error_clear(); goto done; } /* skip symbolic refs */ if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF)) goto done; /* parse OID from file */ if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0) goto done; if ((error = git_sortedcache_wlock(backend->refcache)) < 0) goto done; if (!(error = git_sortedcache_upsert( (void **)&ref, backend->refcache, name))) { git_oid_cpy(&ref->oid, &oid); ref->flags = PACKREF_WAS_LOOSE; } git_sortedcache_wunlock(backend->refcache); done: git_buf_dispose(&ref_file); return error; } static int _dirent_loose_load(void *payload, git_buf *full_path) { refdb_fs_backend *backend = payload; const char *file_path; if (git__suffixcmp(full_path->ptr, ".lock") == 0) return 0; if (git_path_isdir(full_path->ptr)) { int error = git_path_direach( full_path, backend->direach_flags, _dirent_loose_load, backend); /* Race with the filesystem, ignore it */ if (error == GIT_ENOTFOUND) { git_error_clear(); return 0; } return error; } file_path = full_path->ptr + strlen(backend->gitpath); return loose_lookup_to_packfile(backend, file_path); } /* * Load all the loose references from the repository * into the in-memory Packfile, and build a vector with * all the references so it can be written back to * disk. */ static int packed_loadloose(refdb_fs_backend *backend) { int error; git_buf refs_path = GIT_BUF_INIT; if (git_buf_joinpath(&refs_path, backend->gitpath, GIT_REFS_DIR) < 0) return -1; /* * Load all the loose files from disk into the Packfile table. * This will overwrite any old packed entries with their * updated loose versions */ error = git_path_direach( &refs_path, backend->direach_flags, _dirent_loose_load, backend); git_buf_dispose(&refs_path); return error; } static int refdb_fs_backend__exists( int *exists, git_refdb_backend *_backend, const char *ref_name) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); git_buf ref_path = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(backend); *exists = 0; if ((error = loose_path(&ref_path, backend->gitpath, ref_name)) < 0) goto out; if (git_path_isfile(ref_path.ptr)) { *exists = 1; goto out; } if ((error = packed_reload(backend)) < 0) goto out; if (git_sortedcache_lookup(backend->refcache, ref_name) != NULL) { *exists = 1; goto out; } out: git_buf_dispose(&ref_path); return error; } static const char *loose_parse_symbolic(git_buf *file_content) { const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF); const char *refname_start; refname_start = (const char *)file_content->ptr; if (git_buf_len(file_content) < header_len + 1) { git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file"); return NULL; } /* * Assume we have already checked for the header * before calling this function */ refname_start += header_len; return refname_start; } /* * Returns whether a reference is stored per worktree or not. * Per-worktree references are: * * - all pseudorefs, e.g. HEAD and MERGE_HEAD * - all references stored inside of "refs/bisect/" */ static bool is_per_worktree_ref(const char *ref_name) { return git__prefixcmp(ref_name, "refs/") != 0 || git__prefixcmp(ref_name, "refs/bisect/") == 0; } static int loose_lookup( git_reference **out, refdb_fs_backend *backend, const char *ref_name) { git_buf ref_file = GIT_BUF_INIT; int error = 0; const char *ref_dir; if (out) *out = NULL; if (is_per_worktree_ref(ref_name)) ref_dir = backend->gitpath; else ref_dir = backend->commonpath; if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0) /* cannot read loose ref file - gah */; else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) { const char *target; git_buf_rtrim(&ref_file); if (!(target = loose_parse_symbolic(&ref_file))) error = -1; else if (out != NULL) *out = git_reference__alloc_symbolic(ref_name, target); } else { git_oid oid; if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) && out != NULL) *out = git_reference__alloc(ref_name, &oid, NULL); } git_buf_dispose(&ref_file); return error; } static int ref_error_notfound(const char *name) { git_error_set(GIT_ERROR_REFERENCE, "reference '%s' not found", name); return GIT_ENOTFOUND; } static int packed_lookup( git_reference **out, refdb_fs_backend *backend, const char *ref_name) { int error = 0; struct packref *entry; if ((error = packed_reload(backend)) < 0) return error; if (git_sortedcache_rlock(backend->refcache) < 0) return -1; entry = git_sortedcache_lookup(backend->refcache, ref_name); if (!entry) { error = ref_error_notfound(ref_name); } else { *out = git_reference__alloc(ref_name, &entry->oid, &entry->peel); if (!*out) error = -1; } git_sortedcache_runlock(backend->refcache); return error; } static int refdb_fs_backend__lookup( git_reference **out, git_refdb_backend *_backend, const char *ref_name) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); int error; GIT_ASSERT_ARG(backend); if (!(error = loose_lookup(out, backend, ref_name))) return 0; /* only try to lookup this reference on the packfile if it * wasn't found on the loose refs; not if there was a critical error */ if (error == GIT_ENOTFOUND) { git_error_clear(); error = packed_lookup(out, backend, ref_name); } return error; } typedef struct { git_reference_iterator parent; char *glob; git_pool pool; git_vector loose; git_sortedcache *cache; size_t loose_pos; size_t packed_pos; } refdb_fs_iter; static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) { refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent); git_vector_free(&iter->loose); git_pool_clear(&iter->pool); git_sortedcache_free(iter->cache); git__free(iter); } static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) { int error = 0; git_buf path = GIT_BUF_INIT; git_iterator *fsit = NULL; git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry = NULL; const char *ref_prefix = GIT_REFS_DIR; size_t ref_prefix_len = strlen(ref_prefix); if (!backend->commonpath) /* do nothing if no commonpath for loose refs */ return 0; fsit_opts.flags = backend->iterator_flags; if (iter->glob) { const char *last_sep = NULL; const char *pos; for (pos = iter->glob; *pos; ++pos) { switch (*pos) { case '?': case '*': case '[': case '\\': break; case '/': last_sep = pos; /* FALLTHROUGH */ default: continue; } break; } if (last_sep) { ref_prefix = iter->glob; ref_prefix_len = (last_sep - ref_prefix) + 1; } } if ((error = git_buf_puts(&path, backend->commonpath)) < 0 || (error = git_buf_put(&path, ref_prefix, ref_prefix_len)) < 0) { git_buf_dispose(&path); return error; } if ((error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) { git_buf_dispose(&path); return (iter->glob && error == GIT_ENOTFOUND)? 0 : error; } error = git_buf_sets(&path, ref_prefix); while (!error && !git_iterator_advance(&entry, fsit)) { const char *ref_name; char *ref_dup; git_buf_truncate(&path, ref_prefix_len); git_buf_puts(&path, entry->path); ref_name = git_buf_cstr(&path); if (git__suffixcmp(ref_name, ".lock") == 0 || (iter->glob && wildmatch(iter->glob, ref_name, 0) != 0)) continue; ref_dup = git_pool_strdup(&iter->pool, ref_name); if (!ref_dup) error = -1; else error = git_vector_insert(&iter->loose, ref_dup); } git_iterator_free(fsit); git_buf_dispose(&path); return error; } static int refdb_fs_backend__iterator_next( git_reference **out, git_reference_iterator *_iter) { int error = GIT_ITEROVER; refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent); refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent); struct packref *ref; while (iter->loose_pos < iter->loose.length) { const char *path = git_vector_get(&iter->loose, iter->loose_pos++); if (loose_lookup(out, backend, path) == 0) { ref = git_sortedcache_lookup(iter->cache, path); if (ref) ref->flags |= PACKREF_SHADOWED; return 0; } git_error_clear(); } error = GIT_ITEROVER; while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) { ref = git_sortedcache_entry(iter->cache, iter->packed_pos++); if (!ref) /* stop now if another thread deleted refs and we past end */ break; if (ref->flags & PACKREF_SHADOWED) continue; if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0) continue; *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel); error = (*out != NULL) ? 0 : -1; break; } return error; } static int refdb_fs_backend__iterator_next_name( const char **out, git_reference_iterator *_iter) { int error = GIT_ITEROVER; refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent); refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent); struct packref *ref; while (iter->loose_pos < iter->loose.length) { const char *path = git_vector_get(&iter->loose, iter->loose_pos++); struct packref *ref; if (loose_lookup(NULL, backend, path) == 0) { ref = git_sortedcache_lookup(iter->cache, path); if (ref) ref->flags |= PACKREF_SHADOWED; *out = path; return 0; } git_error_clear(); } error = GIT_ITEROVER; while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) { ref = git_sortedcache_entry(iter->cache, iter->packed_pos++); if (!ref) /* stop now if another thread deleted refs and we past end */ break; if (ref->flags & PACKREF_SHADOWED) continue; if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0) continue; *out = ref->name; error = 0; break; } return error; } static int refdb_fs_backend__iterator( git_reference_iterator **out, git_refdb_backend *_backend, const char *glob) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); refdb_fs_iter *iter = NULL; int error; GIT_ASSERT_ARG(backend); iter = git__calloc(1, sizeof(refdb_fs_iter)); GIT_ERROR_CHECK_ALLOC(iter); if ((error = git_pool_init(&iter->pool, 1)) < 0) goto out; if ((error = git_vector_init(&iter->loose, 8, NULL)) < 0) goto out; if (glob != NULL && (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL) { error = GIT_ERROR_NOMEMORY; goto out; } if ((error = iter_load_loose_paths(backend, iter)) < 0) goto out; if ((error = packed_reload(backend)) < 0) goto out; if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0) goto out; iter->parent.next = refdb_fs_backend__iterator_next; iter->parent.next_name = refdb_fs_backend__iterator_next_name; iter->parent.free = refdb_fs_backend__iterator_free; *out = (git_reference_iterator *)iter; out: if (error) refdb_fs_backend__iterator_free((git_reference_iterator *)iter); return error; } static bool ref_is_available( const char *old_ref, const char *new_ref, const char *this_ref) { if (old_ref == NULL || strcmp(old_ref, this_ref)) { size_t reflen = strlen(this_ref); size_t newlen = strlen(new_ref); size_t cmplen = reflen < newlen ? reflen : newlen; const char *lead = reflen < newlen ? new_ref : this_ref; if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') { return false; } } return true; } static int reference_path_available( refdb_fs_backend *backend, const char *new_ref, const char *old_ref, int force) { size_t i; int error; if ((error = packed_reload(backend)) < 0) return error; if (!force) { int exists; if ((error = refdb_fs_backend__exists( &exists, (git_refdb_backend *)backend, new_ref)) < 0) { return error; } if (exists) { git_error_set(GIT_ERROR_REFERENCE, "failed to write reference '%s': a reference with " "that name already exists.", new_ref); return GIT_EEXISTS; } } if ((error = git_sortedcache_rlock(backend->refcache)) < 0) return error; for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) { struct packref *ref = git_sortedcache_entry(backend->refcache, i); if (ref && !ref_is_available(old_ref, new_ref, ref->name)) { git_sortedcache_runlock(backend->refcache); git_error_set(GIT_ERROR_REFERENCE, "path to reference '%s' collides with existing one", new_ref); return -1; } } git_sortedcache_runlock(backend->refcache); return 0; } static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name) { int error, filebuf_flags; git_buf ref_path = GIT_BUF_INIT; const char *basedir; GIT_ASSERT_ARG(file); GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(name); if (!git_path_validate(backend->repo, name, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", name); return GIT_EINVALIDSPEC; } if (is_per_worktree_ref(name)) basedir = backend->gitpath; else basedir = backend->commonpath; /* Remove a possibly existing empty directory hierarchy * which name would collide with the reference name */ if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0) return error; if ((error = loose_path(&ref_path, basedir, name)) < 0) return error; filebuf_flags = GIT_FILEBUF_CREATE_LEADING_DIRS; if (backend->fsync) filebuf_flags |= GIT_FILEBUF_FSYNC; error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE); if (error == GIT_EDIRECTORY) git_error_set(GIT_ERROR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name); git_buf_dispose(&ref_path); return error; } static int loose_commit(git_filebuf *file, const git_reference *ref) { GIT_ASSERT_ARG(file); GIT_ASSERT_ARG(ref); if (ref->type == GIT_REFERENCE_DIRECT) { char oid[GIT_OID_HEXSZ + 1]; git_oid_nfmt(oid, sizeof(oid), &ref->target.oid); git_filebuf_printf(file, "%s\n", oid); } else if (ref->type == GIT_REFERENCE_SYMBOLIC) { git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic); } else { GIT_ASSERT(0); } return git_filebuf_commit(file); } static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname) { int error; git_filebuf *lock; refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); lock = git__calloc(1, sizeof(git_filebuf)); GIT_ERROR_CHECK_ALLOC(lock); if ((error = loose_lock(lock, backend, refname)) < 0) { git__free(lock); return error; } *out = lock; return 0; } static int refdb_fs_backend__write_tail( git_refdb_backend *_backend, const git_reference *ref, git_filebuf *file, int update_reflog, const git_oid *old_id, const char *old_target, const git_signature *who, const char *message); static int refdb_fs_backend__delete_tail( git_refdb_backend *_backend, git_filebuf *file, const char *ref_name, const git_oid *old_id, const char *old_target); static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message) { git_filebuf *lock = (git_filebuf *) payload; int error = 0; if (success == 2) error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL); else if (success) error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, NULL, NULL, sig, message); else git_filebuf_cleanup(lock); git__free(lock); return error; } /* * Find out what object this reference resolves to. * * For references that point to a 'big' tag (e.g. an * actual tag object on the repository), we need to * cache on the packfile the OID of the object to * which that 'big tag' is pointing to. */ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref) { git_object *object; if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL) return 0; /* * Find the tagged object in the repository */ if (git_object_lookup(&object, backend->repo, &ref->oid, GIT_OBJECT_ANY) < 0) return -1; /* * If the tagged object is a Tag object, we need to resolve it; * if the ref is actually a 'weak' ref, we don't need to resolve * anything. */ if (git_object_type(object) == GIT_OBJECT_TAG) { git_tag *tag = (git_tag *)object; /* * Find the object pointed at by this tag */ git_oid_cpy(&ref->peel, git_tag_target_id(tag)); ref->flags |= PACKREF_HAS_PEEL; /* * The reference has now cached the resolved OID, and is * marked at such. When written to the packfile, it'll be * accompanied by this resolved oid */ } git_object_free(object); return 0; } /* * Write a single reference into a packfile */ static int packed_write_ref(struct packref *ref, git_filebuf *file) { char oid[GIT_OID_HEXSZ + 1]; git_oid_nfmt(oid, sizeof(oid), &ref->oid); /* * For references that peel to an object in the repo, we must * write the resulting peel on a separate line, e.g. * * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4 * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100 * * This obviously only applies to tags. * The required peels have already been loaded into `ref->peel_target`. */ if (ref->flags & PACKREF_HAS_PEEL) { char peel[GIT_OID_HEXSZ + 1]; git_oid_nfmt(peel, sizeof(peel), &ref->peel); if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0) return -1; } else { if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0) return -1; } return 0; } /* * Remove all loose references * * Once we have successfully written a packfile, * all the loose references that were packed must be * removed from disk. * * This is a dangerous method; make sure the packfile * is well-written, because we are destructing references * here otherwise. */ static int packed_remove_loose(refdb_fs_backend *backend) { size_t i; git_filebuf lock = GIT_FILEBUF_INIT; git_buf ref_content = GIT_BUF_INIT; int error = 0; /* backend->refcache is already locked when this is called */ for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) { struct packref *ref = git_sortedcache_entry(backend->refcache, i); git_oid current_id; if (!ref || !(ref->flags & PACKREF_WAS_LOOSE)) continue; git_filebuf_cleanup(&lock); /* We need to stop anybody from updating the ref while we try to do a safe delete */ error = loose_lock(&lock, backend, ref->name); /* If someone else is updating it, let them do it */ if (error == GIT_EEXISTS || error == GIT_ENOTFOUND) continue; if (error < 0) { git_buf_dispose(&ref_content); git_error_set(GIT_ERROR_REFERENCE, "failed to lock loose reference '%s'", ref->name); return error; } error = git_futils_readbuffer(&ref_content, lock.path_original); /* Someone else beat us to cleaning up the ref, let's simply continue */ if (error == GIT_ENOTFOUND) continue; /* This became a symref between us packing and trying to delete it, so ignore it */ if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF)) continue; /* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */ if (loose_parse_oid(¤t_id, lock.path_original, &ref_content) < 0) continue; /* If the ref moved since we packed it, we must not delete it */ if (!git_oid_equal(¤t_id, &ref->oid)) continue; /* * if we fail to remove a single file, this is *not* good, * but we should keep going and remove as many as possible. * If we fail to remove, the ref is still in the old state, so * we haven't lost information. */ p_unlink(lock.path_original); } git_buf_dispose(&ref_content); git_filebuf_cleanup(&lock); return 0; } /* * Write all the contents in the in-memory packfile to disk. */ static int packed_write(refdb_fs_backend *backend) { git_sortedcache *refcache = backend->refcache; git_filebuf pack_file = GIT_FILEBUF_INIT; int error, open_flags = 0; size_t i; /* lock the cache to updates while we do this */ if ((error = git_sortedcache_wlock(refcache)) < 0) return error; if (backend->fsync) open_flags = GIT_FILEBUF_FSYNC; /* Open the file! */ if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), open_flags, GIT_PACKEDREFS_FILE_MODE)) < 0) goto fail; /* Packfiles have a header... apparently * This is in fact not required, but we might as well print it * just for kicks */ if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0) goto fail; for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) { struct packref *ref = git_sortedcache_entry(refcache, i); GIT_ASSERT(ref); if ((error = packed_find_peel(backend, ref)) < 0) goto fail; if ((error = packed_write_ref(ref, &pack_file)) < 0) goto fail; } /* if we've written all the references properly, we can commit * the packfile to make the changes effective */ if ((error = git_filebuf_commit(&pack_file)) < 0) goto fail; /* when and only when the packfile has been properly written, * we can go ahead and remove the loose refs */ if ((error = packed_remove_loose(backend)) < 0) goto fail; git_sortedcache_updated(refcache); git_sortedcache_wunlock(refcache); /* we're good now */ return 0; fail: git_filebuf_cleanup(&pack_file); git_sortedcache_wunlock(refcache); return error; } static int packed_delete(refdb_fs_backend *backend, const char *ref_name) { size_t pack_pos; int error, found = 0; if ((error = packed_reload(backend)) < 0) goto cleanup; if ((error = git_sortedcache_wlock(backend->refcache)) < 0) goto cleanup; /* If a packed reference exists, remove it from the packfile and repack if necessary */ error = git_sortedcache_lookup_index(&pack_pos, backend->refcache, ref_name); if (error == 0) { error = git_sortedcache_remove(backend->refcache, pack_pos); found = 1; } if (error == GIT_ENOTFOUND) error = 0; git_sortedcache_wunlock(backend->refcache); if (found) error = packed_write(backend); cleanup: return error; } static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message); static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name, const git_oid *old_id, const char *old_target) { int error = 0; git_reference *old_ref = NULL; *cmp = 0; /* It "matches" if there is no old value to compare against */ if (!old_id && !old_target) return 0; if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0) { if (error == GIT_ENOTFOUND && old_id && git_oid_is_zero(old_id)) return 0; goto out; } /* If the types don't match, there's no way the values do */ if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) { *cmp = -1; goto out; } if (old_target && old_ref->type != GIT_REFERENCE_SYMBOLIC) { *cmp = 1; goto out; } if (old_id && old_ref->type == GIT_REFERENCE_DIRECT) *cmp = git_oid_cmp(old_id, &old_ref->target.oid); if (old_target && old_ref->type == GIT_REFERENCE_SYMBOLIC) *cmp = git__strcmp(old_target, old_ref->target.symbolic); out: git_reference_free(old_ref); return error; } /* * The git.git comment regarding this, for your viewing pleasure: * * Special hack: If a branch is updated directly and HEAD * points to it (may happen on the remote side of a push * for example) then logically the HEAD reflog should be * updated too. * A generic solution implies reverse symref information, * but finding all symrefs pointing to the given branch * would be rather costly for this rare event (the direct * update of a branch) to be worth it. So let's cheat and * check with HEAD only which should cover 99% of all usage * scenarios (even 100% of the default ones). */ static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message) { git_reference *head = NULL; git_refdb *refdb = NULL; int error, write_reflog; git_oid old_id; if ((error = git_repository_refdb(&refdb, backend->repo)) < 0 || (error = git_refdb_should_write_head_reflog(&write_reflog, refdb, ref)) < 0) goto out; if (!write_reflog) goto out; /* if we can't resolve, we use {0}*40 as old id */ if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0) memset(&old_id, 0, sizeof(old_id)); if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0 || (error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message)) < 0) goto out; out: git_reference_free(head); git_refdb_free(refdb); return error; } static int refdb_fs_backend__write( git_refdb_backend *_backend, const git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); git_filebuf file = GIT_FILEBUF_INIT; int error = 0; GIT_ASSERT_ARG(backend); if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0) return error; /* We need to perform the reflog append and old value check under the ref's lock */ if ((error = loose_lock(&file, backend, ref->name)) < 0) return error; return refdb_fs_backend__write_tail(_backend, ref, &file, true, old_id, old_target, who, message); } static int refdb_fs_backend__write_tail( git_refdb_backend *_backend, const git_reference *ref, git_filebuf *file, int update_reflog, const git_oid *old_id, const char *old_target, const git_signature *who, const char *message) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); int error = 0, cmp = 0, should_write; const char *new_target = NULL; const git_oid *new_id = NULL; if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0) goto on_error; if (cmp) { git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match"); error = GIT_EMODIFIED; goto on_error; } if (ref->type == GIT_REFERENCE_SYMBOLIC) new_target = ref->target.symbolic; else new_id = &ref->target.oid; error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target); if (error < 0 && error != GIT_ENOTFOUND) goto on_error; /* Don't update if we have the same value */ if (!error && !cmp) { error = 0; goto on_error; /* not really error */ } if (update_reflog) { git_refdb *refdb; if ((error = git_repository_refdb__weakptr(&refdb, backend->repo)) < 0 || (error = git_refdb_should_write_reflog(&should_write, refdb, ref)) < 0) goto on_error; if (should_write) { if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0) goto on_error; if ((error = maybe_append_head(backend, ref, who, message)) < 0) goto on_error; } } return loose_commit(file, ref); on_error: git_filebuf_cleanup(file); return error; } static int refdb_fs_backend__prune_refs( refdb_fs_backend *backend, const char *ref_name, const char *prefix) { git_buf relative_path = GIT_BUF_INIT; git_buf base_path = GIT_BUF_INIT; size_t commonlen; int error; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(ref_name); if ((error = git_buf_sets(&relative_path, ref_name)) < 0) goto cleanup; git_path_squash_slashes(&relative_path); if ((commonlen = git_path_common_dirlen("refs/heads/", git_buf_cstr(&relative_path))) == strlen("refs/heads/") || (commonlen = git_path_common_dirlen("refs/tags/", git_buf_cstr(&relative_path))) == strlen("refs/tags/") || (commonlen = git_path_common_dirlen("refs/remotes/", git_buf_cstr(&relative_path))) == strlen("refs/remotes/")) { git_buf_truncate(&relative_path, commonlen); if (prefix) error = git_buf_join3(&base_path, '/', backend->commonpath, prefix, git_buf_cstr(&relative_path)); else error = git_buf_joinpath(&base_path, backend->commonpath, git_buf_cstr(&relative_path)); if (!error) error = git_path_validate_filesystem(base_path.ptr, base_path.size); if (error < 0) goto cleanup; error = git_futils_rmdir_r(ref_name + commonlen, git_buf_cstr(&base_path), GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT); if (error == GIT_ENOTFOUND) error = 0; } cleanup: git_buf_dispose(&relative_path); git_buf_dispose(&base_path); return error; } static int refdb_fs_backend__delete( git_refdb_backend *_backend, const char *ref_name, const git_oid *old_id, const char *old_target) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); git_filebuf file = GIT_FILEBUF_INIT; int error = 0; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(ref_name); if ((error = loose_lock(&file, backend, ref_name)) < 0) return error; if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) { git_filebuf_cleanup(&file); return error; } return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target); } static int loose_delete(refdb_fs_backend *backend, const char *ref_name) { git_buf path = GIT_BUF_INIT; int error = 0; if ((error = loose_path(&path, backend->commonpath, ref_name)) < 0) return error; error = p_unlink(path.ptr); if (error < 0 && errno == ENOENT) error = GIT_ENOTFOUND; else if (error != 0) error = -1; git_buf_dispose(&path); return error; } static int refdb_fs_backend__delete_tail( git_refdb_backend *_backend, git_filebuf *file, const char *ref_name, const git_oid *old_id, const char *old_target) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); int error = 0, cmp = 0; bool packed_deleted = 0; error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target); if (error < 0) goto cleanup; if (cmp) { git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match"); error = GIT_EMODIFIED; goto cleanup; } /* * To ensure that an external observer will see either the current ref value * (because the loose ref still exists), or a missing ref (after the packed-file is * unlocked, there will be nothing left), we must ensure things happen in the * following order: * * - the packed-ref file is locked and loaded, as well as a loose one, if it exists * - we optimistically delete a packed ref, keeping track of whether it existed * - we delete the loose ref, note that we have its .lock * - the loose ref is "unlocked", then the packed-ref file is rewritten and unlocked * - we should prune the path components if a loose ref was deleted * * Note that, because our packed backend doesn't expose its filesystem lock, * we might not be able to guarantee that this is what actually happens (ie. * as our current code never write packed-refs.lock, nothing stops observers * from grabbing a "stale" value from there). */ if ((error = packed_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND) goto cleanup; if (error == 0) packed_deleted = 1; if ((error = loose_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND) goto cleanup; if (error == GIT_ENOTFOUND) { error = packed_deleted ? 0 : ref_error_notfound(ref_name); goto cleanup; } cleanup: git_filebuf_cleanup(file); if (error == 0) error = refdb_fs_backend__prune_refs(backend, ref_name, ""); return error; } static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name); static int refdb_fs_backend__rename( git_reference **out, git_refdb_backend *_backend, const char *old_name, const char *new_name, int force, const git_signature *who, const char *message) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); git_reference *old, *new = NULL; git_filebuf file = GIT_FILEBUF_INIT; int error; GIT_ASSERT_ARG(backend); if ((error = reference_path_available( backend, new_name, old_name, force)) < 0 || (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0) return error; if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) { git_reference_free(old); return error; } new = git_reference__realloc(&old, new_name); if (!new) { git_reference_free(old); return -1; } if ((error = loose_lock(&file, backend, new->name)) < 0) { git_reference_free(new); return error; } /* Try to rename the refog; it's ok if the old doesn't exist */ error = refdb_reflog_fs__rename(_backend, old_name, new_name); if (((error == 0) || (error == GIT_ENOTFOUND)) && ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) { git_reference_free(new); git_filebuf_cleanup(&file); return error; } if (error < 0) { git_reference_free(new); git_filebuf_cleanup(&file); return error; } if ((error = loose_commit(&file, new)) < 0 || out == NULL) { git_reference_free(new); return error; } *out = new; return 0; } static int refdb_fs_backend__compress(git_refdb_backend *_backend) { int error; refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); GIT_ASSERT_ARG(backend); if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */ (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */ (error = packed_write(backend)) < 0) /* write back to disk */ return error; return 0; } static void refdb_fs_backend__free(git_refdb_backend *_backend) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); if (!backend) return; git_sortedcache_free(backend->refcache); git__free(backend->gitpath); git__free(backend->commonpath); git__free(backend); } static char *setup_namespace(git_repository *repo, const char *in) { git_buf path = GIT_BUF_INIT; char *parts, *start, *end, *out = NULL; if (!in) goto done; git_buf_puts(&path, in); /* if the repo is not namespaced, nothing else to do */ if (repo->namespace == NULL) { out = git_buf_detach(&path); goto done; } parts = end = git__strdup(repo->namespace); if (parts == NULL) goto done; /* * From `man gitnamespaces`: * namespaces which include a / will expand to a hierarchy * of namespaces; for example, GIT_NAMESPACE=foo/bar will store * refs under refs/namespaces/foo/refs/namespaces/bar/ */ while ((start = git__strsep(&end, "/")) != NULL) git_buf_printf(&path, "refs/namespaces/%s/", start); git_buf_printf(&path, "refs/namespaces/%s/refs", end); git__free(parts); /* Make sure that the folder with the namespace exists */ if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777, GIT_MKDIR_PATH, NULL) < 0) goto done; /* Return root of the namespaced gitpath, i.e. without the trailing 'refs' */ git_buf_rtruncate_at_char(&path, '/'); git_buf_putc(&path, '/'); out = git_buf_detach(&path); done: git_buf_dispose(&path); return out; } static int reflog_alloc(git_reflog **reflog, const char *name) { git_reflog *log; *reflog = NULL; log = git__calloc(1, sizeof(git_reflog)); GIT_ERROR_CHECK_ALLOC(log); log->ref_name = git__strdup(name); GIT_ERROR_CHECK_ALLOC(log->ref_name); if (git_vector_init(&log->entries, 0, NULL) < 0) { git__free(log->ref_name); git__free(log); return -1; } *reflog = log; return 0; } static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) { git_parse_ctx parser = GIT_PARSE_CTX_INIT; if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0) return -1; for (; parser.remain_len; git_parse_advance_line(&parser)) { git_reflog_entry *entry; const char *sig; char c; entry = git__calloc(1, sizeof(*entry)); GIT_ERROR_CHECK_ALLOC(entry); entry->committer = git__calloc(1, sizeof(*entry->committer)); GIT_ERROR_CHECK_ALLOC(entry->committer); if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 || git_parse_advance_expected(&parser, " ", 1) < 0 || git_parse_advance_oid(&entry->oid_cur, &parser) < 0) goto next; sig = parser.line; while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n') git_parse_advance_chars(&parser, 1); if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0) goto next; if (c == '\t') { size_t len; git_parse_advance_chars(&parser, 1); len = parser.line_len; if (parser.line[len - 1] == '\n') len--; entry->msg = git__strndup(parser.line, len); GIT_ERROR_CHECK_ALLOC(entry->msg); } if ((git_vector_insert(&log->entries, entry)) < 0) { git_reflog_entry__free(entry); return -1; } continue; next: git_reflog_entry__free(entry); } return 0; } static int create_new_reflog_file(const char *filepath) { int fd, error; if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0) return error; if ((fd = p_open(filepath, O_WRONLY | O_CREAT, GIT_REFLOG_FILE_MODE)) < 0) return -1; return p_close(fd); } static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name) { refdb_fs_backend *backend; git_repository *repo; git_buf path = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(_backend && name); backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); repo = backend->repo; if ((error = reflog_path(&path, repo, name)) < 0) return error; error = create_new_reflog_file(git_buf_cstr(&path)); git_buf_dispose(&path); return error; } static int has_reflog(git_repository *repo, const char *name) { int ret = 0; git_buf path = GIT_BUF_INIT; if (reflog_path(&path, repo, name) < 0) goto cleanup; ret = git_path_isfile(git_buf_cstr(&path)); cleanup: git_buf_dispose(&path); return ret; } static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name) { refdb_fs_backend *backend; GIT_ASSERT_ARG(_backend); GIT_ASSERT_ARG(name); backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); return has_reflog(backend->repo, name); } static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name) { int error = -1; git_buf log_path = GIT_BUF_INIT; git_buf log_file = GIT_BUF_INIT; git_reflog *log = NULL; git_repository *repo; refdb_fs_backend *backend; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(_backend); GIT_ASSERT_ARG(name); backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); repo = backend->repo; if (reflog_alloc(&log, name) < 0) return -1; if (reflog_path(&log_path, repo, name) < 0) goto cleanup; error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path)); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; if ((error == GIT_ENOTFOUND) && ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0)) goto cleanup; if ((error = reflog_parse(log, git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0) goto cleanup; *out = log; goto success; cleanup: git_reflog_free(log); success: git_buf_dispose(&log_file); git_buf_dispose(&log_path); return error; } static int serialize_reflog_entry( git_buf *buf, const git_oid *oid_old, const git_oid *oid_new, const git_signature *committer, const char *msg) { char raw_old[GIT_OID_HEXSZ+1]; char raw_new[GIT_OID_HEXSZ+1]; git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old); git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new); git_buf_clear(buf); git_buf_puts(buf, raw_old); git_buf_putc(buf, ' '); git_buf_puts(buf, raw_new); git_signature__writebuf(buf, " ", committer); /* drop trailing LF */ git_buf_rtrim(buf); if (msg) { size_t i; git_buf_putc(buf, '\t'); git_buf_puts(buf, msg); for (i = 0; i < buf->size - 2; i++) if (buf->ptr[i] == '\n') buf->ptr[i] = ' '; git_buf_rtrim(buf); } git_buf_putc(buf, '\n'); return git_buf_oom(buf); } static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname) { git_repository *repo; git_buf log_path = GIT_BUF_INIT; int error; repo = backend->repo; if (!git_path_validate(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname); return GIT_EINVALIDSPEC; } if (reflog_path(&log_path, repo, refname) < 0) return -1; if (!git_path_isfile(git_buf_cstr(&log_path))) { git_error_set(GIT_ERROR_INVALID, "log file for reference '%s' doesn't exist", refname); error = -1; goto cleanup; } error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE); cleanup: git_buf_dispose(&log_path); return error; } static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog) { int error = -1; unsigned int i; git_reflog_entry *entry; refdb_fs_backend *backend; git_buf log = GIT_BUF_INIT; git_filebuf fbuf = GIT_FILEBUF_INIT; GIT_ASSERT_ARG(_backend); GIT_ASSERT_ARG(reflog); backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0) return -1; git_vector_foreach(&reflog->entries, i, entry) { if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0) goto cleanup; if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0) goto cleanup; } error = git_filebuf_commit(&fbuf); goto success; cleanup: git_filebuf_cleanup(&fbuf); success: git_buf_dispose(&log); return error; } /* Append to the reflog, must be called under reference lock */ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message) { int error, is_symbolic, open_flags; git_oid old_id = {{0}}, new_id = {{0}}; git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; git_repository *repo = backend->repo; is_symbolic = ref->type == GIT_REFERENCE_SYMBOLIC; /* "normal" symbolic updates do not write */ if (is_symbolic && strcmp(ref->name, GIT_HEAD_FILE) && !(old && new)) return 0; /* From here on is_symbolic also means that it's HEAD */ if (old) { git_oid_cpy(&old_id, old); } else { error = git_reference_name_to_id(&old_id, repo, ref->name); if (error < 0 && error != GIT_ENOTFOUND) return error; } if (new) { git_oid_cpy(&new_id, new); } else { if (!is_symbolic) { git_oid_cpy(&new_id, git_reference_target(ref)); } else { error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref)); if (error < 0 && error != GIT_ENOTFOUND) return error; /* detaching HEAD does not create an entry */ if (error == GIT_ENOTFOUND) return 0; git_error_clear(); } } if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0) goto cleanup; if ((error = reflog_path(&path, repo, ref->name)) < 0) goto cleanup; if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) && (error != GIT_EEXISTS)) { goto cleanup; } /* If the new branch matches part of the namespace of a previously deleted branch, * there maybe an obsolete/unused directory (or directory hierarchy) in the way. */ if (git_path_isdir(git_buf_cstr(&path))) { if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) { if (error == GIT_ENOTFOUND) error = 0; } else if (git_path_isdir(git_buf_cstr(&path))) { git_error_set(GIT_ERROR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder", ref->name); error = GIT_EDIRECTORY; } if (error != 0) goto cleanup; } open_flags = O_WRONLY | O_CREAT | O_APPEND; if (backend->fsync) open_flags |= O_FSYNC; error = git_futils_writebuffer(&buf, git_buf_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE); cleanup: git_buf_dispose(&buf); git_buf_dispose(&path); return error; } static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name) { int error = 0, fd; git_buf old_path = GIT_BUF_INIT; git_buf new_path = GIT_BUF_INIT; git_buf temp_path = GIT_BUF_INIT; git_buf normalized = GIT_BUF_INIT; git_repository *repo; refdb_fs_backend *backend; GIT_ASSERT_ARG(_backend); GIT_ASSERT_ARG(old_name); GIT_ASSERT_ARG(new_name); backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); repo = backend->repo; if ((error = git_reference__normalize_name( &normalized, new_name, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL)) < 0) return error; if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0) return -1; if ((error = loose_path(&old_path, git_buf_cstr(&temp_path), old_name)) < 0) return error; if ((error = loose_path(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized))) < 0) return error; if (!git_path_exists(git_buf_cstr(&old_path))) { error = GIT_ENOTFOUND; goto cleanup; } /* * Move the reflog to a temporary place. This two-phase renaming is required * in order to cope with funny renaming use cases when one tries to move a reference * to a partially colliding namespace: * - a/b -> a/b/c * - a/b/c/d -> a/b/c */ if ((error = loose_path(&temp_path, git_buf_cstr(&temp_path), "temp_reflog")) < 0) return error; if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) { error = -1; goto cleanup; } p_close(fd); if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) { git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name); error = -1; goto cleanup; } if (git_path_isdir(git_buf_cstr(&new_path)) && (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) { error = -1; goto cleanup; } if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) { error = -1; goto cleanup; } if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) { git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name); error = -1; } cleanup: git_buf_dispose(&temp_path); git_buf_dispose(&old_path); git_buf_dispose(&new_path); git_buf_dispose(&normalized); return error; } static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name) { refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent); git_buf path = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(_backend); GIT_ASSERT_ARG(name); if ((error = reflog_path(&path, backend->repo, name)) < 0) goto out; if (!git_path_exists(path.ptr)) goto out; if ((error = p_unlink(path.ptr)) < 0) goto out; error = refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR); out: git_buf_dispose(&path); return error; } int git_refdb_backend_fs( git_refdb_backend **backend_out, git_repository *repository) { int t = 0; git_buf gitpath = GIT_BUF_INIT; refdb_fs_backend *backend; backend = git__calloc(1, sizeof(refdb_fs_backend)); GIT_ERROR_CHECK_ALLOC(backend); if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0) goto fail; backend->repo = repository; if (repository->gitdir) { backend->gitpath = setup_namespace(repository, repository->gitdir); if (backend->gitpath == NULL) goto fail; } if (repository->commondir) { backend->commonpath = setup_namespace(repository, repository->commondir); if (backend->commonpath == NULL) goto fail; } if (git_buf_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 || git_sortedcache_new( &backend->refcache, offsetof(struct packref, name), NULL, NULL, packref_cmp, git_buf_cstr(&gitpath)) < 0) goto fail; git_buf_dispose(&gitpath); if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_IGNORECASE) && t) { backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE; backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE; } if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_PRECOMPOSE) && t) { backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE; backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE; } if ((!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) || git_repository__fsync_gitdir) backend->fsync = 1; backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS; backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; backend->parent.iterator = &refdb_fs_backend__iterator; backend->parent.write = &refdb_fs_backend__write; backend->parent.del = &refdb_fs_backend__delete; backend->parent.rename = &refdb_fs_backend__rename; backend->parent.compress = &refdb_fs_backend__compress; backend->parent.lock = &refdb_fs_backend__lock; backend->parent.unlock = &refdb_fs_backend__unlock; backend->parent.has_log = &refdb_reflog_fs__has_log; backend->parent.ensure_log = &refdb_reflog_fs__ensure_log; backend->parent.free = &refdb_fs_backend__free; backend->parent.reflog_read = &refdb_reflog_fs__read; backend->parent.reflog_write = &refdb_reflog_fs__write; backend->parent.reflog_rename = &refdb_reflog_fs__rename; backend->parent.reflog_delete = &refdb_reflog_fs__delete; *backend_out = (git_refdb_backend *)backend; return 0; fail: git_buf_dispose(&gitpath); git__free(backend->gitpath); git__free(backend->commonpath); git__free(backend); return -1; } git2r/src/libgit2/src/zstream.c0000644000175000017500000001152014125111754016176 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "zstream.h" #include #include "buffer.h" #define ZSTREAM_BUFFER_SIZE (1024 * 1024) #define ZSTREAM_BUFFER_MIN_EXTRA 8 GIT_INLINE(int) zstream_seterr(git_zstream *zs) { switch (zs->zerr) { case Z_OK: case Z_STREAM_END: case Z_BUF_ERROR: /* not fatal; we retry with a larger buffer */ return 0; case Z_MEM_ERROR: git_error_set_oom(); break; default: if (zs->z.msg) git_error_set_str(GIT_ERROR_ZLIB, zs->z.msg); else git_error_set(GIT_ERROR_ZLIB, "unknown compression error"); } return -1; } int git_zstream_init(git_zstream *zstream, git_zstream_t type) { zstream->type = type; if (zstream->type == GIT_ZSTREAM_INFLATE) zstream->zerr = inflateInit(&zstream->z); else zstream->zerr = deflateInit(&zstream->z, Z_DEFAULT_COMPRESSION); return zstream_seterr(zstream); } void git_zstream_free(git_zstream *zstream) { if (zstream->type == GIT_ZSTREAM_INFLATE) inflateEnd(&zstream->z); else deflateEnd(&zstream->z); } void git_zstream_reset(git_zstream *zstream) { if (zstream->type == GIT_ZSTREAM_INFLATE) inflateReset(&zstream->z); else deflateReset(&zstream->z); zstream->in = NULL; zstream->in_len = 0; zstream->zerr = Z_STREAM_END; } int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len) { zstream->in = in; zstream->in_len = in_len; zstream->zerr = Z_OK; return 0; } bool git_zstream_done(git_zstream *zstream) { return (!zstream->in_len && zstream->zerr == Z_STREAM_END); } bool git_zstream_eos(git_zstream *zstream) { return zstream->zerr == Z_STREAM_END; } size_t git_zstream_suggest_output_len(git_zstream *zstream) { if (zstream->in_len > ZSTREAM_BUFFER_SIZE) return ZSTREAM_BUFFER_SIZE; else if (zstream->in_len > ZSTREAM_BUFFER_MIN_EXTRA) return zstream->in_len; else return ZSTREAM_BUFFER_MIN_EXTRA; } int git_zstream_get_output_chunk( void *out, size_t *out_len, git_zstream *zstream) { size_t in_queued, in_used, out_queued; /* set up input data */ zstream->z.next_in = (Bytef *)zstream->in; /* feed as much data to zlib as it can consume, at most UINT_MAX */ if (zstream->in_len > UINT_MAX) { zstream->z.avail_in = UINT_MAX; zstream->flush = Z_NO_FLUSH; } else { zstream->z.avail_in = (uInt)zstream->in_len; zstream->flush = Z_FINISH; } in_queued = (size_t)zstream->z.avail_in; /* set up output data */ zstream->z.next_out = out; zstream->z.avail_out = (uInt)*out_len; if ((size_t)zstream->z.avail_out != *out_len) zstream->z.avail_out = UINT_MAX; out_queued = (size_t)zstream->z.avail_out; /* compress next chunk */ if (zstream->type == GIT_ZSTREAM_INFLATE) zstream->zerr = inflate(&zstream->z, zstream->flush); else zstream->zerr = deflate(&zstream->z, zstream->flush); if (zstream_seterr(zstream)) return -1; in_used = (in_queued - zstream->z.avail_in); zstream->in_len -= in_used; zstream->in += in_used; *out_len = (out_queued - zstream->z.avail_out); return 0; } int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream) { size_t out_remain = *out_len; if (zstream->in_len && zstream->zerr == Z_STREAM_END) { git_error_set(GIT_ERROR_ZLIB, "zlib input had trailing garbage"); return -1; } while (out_remain > 0 && zstream->zerr != Z_STREAM_END) { size_t out_written = out_remain; if (git_zstream_get_output_chunk(out, &out_written, zstream) < 0) return -1; out_remain -= out_written; out = ((char *)out) + out_written; } /* either we finished the input or we did not flush the data */ GIT_ASSERT(zstream->in_len > 0 || zstream->flush == Z_FINISH); /* set out_size to number of bytes actually written to output */ *out_len = *out_len - out_remain; return 0; } static int zstream_buf(git_buf *out, const void *in, size_t in_len, git_zstream_t type) { git_zstream zs = GIT_ZSTREAM_INIT; int error = 0; if ((error = git_zstream_init(&zs, type)) < 0) return error; if ((error = git_zstream_set_input(&zs, in, in_len)) < 0) goto done; while (!git_zstream_done(&zs)) { size_t step = git_zstream_suggest_output_len(&zs), written; if ((error = git_buf_grow_by(out, step)) < 0) goto done; written = out->asize - out->size; if ((error = git_zstream_get_output( out->ptr + out->size, &written, &zs)) < 0) goto done; out->size += written; } /* NULL terminate for consistency if possible */ if (out->size < out->asize) out->ptr[out->size] = '\0'; done: git_zstream_free(&zs); return error; } int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len) { return zstream_buf(out, in, in_len, GIT_ZSTREAM_DEFLATE); } int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len) { return zstream_buf(out, in, in_len, GIT_ZSTREAM_INFLATE); } git2r/src/libgit2/src/branch.h0000644000175000017500000000066414125111754015762 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_branch_h__ #define INCLUDE_branch_h__ #include "common.h" #include "buffer.h" int git_branch_upstream__name( git_buf *tracking_name, git_repository *repo, const char *canonical_branch_name); #endif git2r/src/libgit2/src/remote.c0000644000175000017500000020475714125111754016024 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "remote.h" #include "git2/config.h" #include "git2/types.h" #include "git2/oid.h" #include "git2/net.h" #include "config.h" #include "repository.h" #include "fetch.h" #include "refs.h" #include "refspec.h" #include "fetchhead.h" #include "push.h" #define CONFIG_URL_FMT "remote.%s.url" #define CONFIG_PUSHURL_FMT "remote.%s.pushurl" #define CONFIG_FETCH_FMT "remote.%s.fetch" #define CONFIG_PUSH_FMT "remote.%s.push" #define CONFIG_TAGOPT_FMT "remote.%s.tagopt" static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name); char *apply_insteadof(git_config *config, const char *url, int direction); static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch) { git_refspec *spec; spec = git__calloc(1, sizeof(git_refspec)); GIT_ERROR_CHECK_ALLOC(spec); if (git_refspec__parse(spec, string, is_fetch) < 0) { git__free(spec); return -1; } spec->push = !is_fetch; if (git_vector_insert(vector, spec) < 0) { git_refspec__dispose(spec); git__free(spec); return -1; } return 0; } static int add_refspec(git_remote *remote, const char *string, bool is_fetch) { return add_refspec_to(&remote->refspecs, string, is_fetch); } static int download_tags_value(git_remote *remote, git_config *cfg) { git_config_entry *ce; git_buf buf = GIT_BUF_INIT; int error; if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) return -1; error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); git_buf_dispose(&buf); if (!error && ce && ce->value) { if (!strcmp(ce->value, "--no-tags")) remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; else if (!strcmp(ce->value, "--tags")) remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; } git_config_entry_free(ce); return error; } static int ensure_remote_name_is_valid(const char *name) { int valid, error; error = git_remote_name_is_valid(&valid, name); if (!error && !valid) { git_error_set( GIT_ERROR_CONFIG, "'%s' is not a valid remote name.", name ? name : "(null)"); error = GIT_EINVALIDSPEC; } return error; } static int write_add_refspec(git_repository *repo, const char *name, const char *refspec, bool fetch) { git_config *cfg; git_buf var = GIT_BUF_INIT; git_refspec spec; const char *fmt; int error; if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; fmt = fetch ? CONFIG_FETCH_FMT : CONFIG_PUSH_FMT; if ((error = ensure_remote_name_is_valid(name)) < 0) return error; if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0) return error; git_refspec__dispose(&spec); if ((error = git_buf_printf(&var, fmt, name)) < 0) return error; /* * "$^" is an unmatchable regexp: it will not match anything at all, so * all values will be considered new and we will not replace any * present value. */ if ((error = git_config_set_multivar(cfg, var.ptr, "$^", refspec)) < 0) { goto cleanup; } cleanup: git_buf_dispose(&var); return 0; } static int canonicalize_url(git_buf *out, const char *in) { if (in == NULL || strlen(in) == 0) { git_error_set(GIT_ERROR_INVALID, "cannot set empty URL"); return GIT_EINVALIDSPEC; } #ifdef GIT_WIN32 /* Given a UNC path like \\server\path, we need to convert this * to //server/path for compatibility with core git. */ if (in[0] == '\\' && in[1] == '\\' && (git__isalpha(in[2]) || git__isdigit(in[2]))) { const char *c; for (c = in; *c; c++) git_buf_putc(out, *c == '\\' ? '/' : *c); return git_buf_oom(out) ? -1 : 0; } #endif return git_buf_puts(out, in); } static int default_fetchspec_for_name(git_buf *buf, const char *name) { if (git_buf_printf(buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) return -1; return 0; } static int ensure_remote_doesnot_exist(git_repository *repo, const char *name) { int error; git_remote *remote; error = git_remote_lookup(&remote, repo, name); if (error == GIT_ENOTFOUND) return 0; if (error < 0) return error; git_remote_free(remote); git_error_set(GIT_ERROR_CONFIG, "remote '%s' already exists", name); return GIT_EEXISTS; } int git_remote_create_options_init(git_remote_create_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_remote_create_options, GIT_REMOTE_CREATE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_remote_create_init_options(git_remote_create_options *opts, unsigned int version) { return git_remote_create_options_init(opts, version); } #endif int git_remote_create_with_opts(git_remote **out, const char *url, const git_remote_create_options *opts) { git_remote *remote = NULL; git_config *config_ro = NULL, *config_rw; git_buf canonical_url = GIT_BUF_INIT; git_buf var = GIT_BUF_INIT; git_buf specbuf = GIT_BUF_INIT; const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT; int error = -1; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(url); if (!opts) { opts = &dummy_opts; } GIT_ERROR_CHECK_VERSION(opts, GIT_REMOTE_CREATE_OPTIONS_VERSION, "git_remote_create_options"); if (opts->name != NULL) { if ((error = ensure_remote_name_is_valid(opts->name)) < 0) return error; if (opts->repository && (error = ensure_remote_doesnot_exist(opts->repository, opts->name)) < 0) return error; } if (opts->repository) { if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0) goto on_error; } remote = git__calloc(1, sizeof(git_remote)); GIT_ERROR_CHECK_ALLOC(remote); remote->repo = opts->repository; if ((error = git_vector_init(&remote->refs, 8, NULL)) < 0 || (error = canonicalize_url(&canonical_url, url)) < 0) goto on_error; if (opts->repository && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) { remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH); } else { remote->url = git__strdup(canonical_url.ptr); } GIT_ERROR_CHECK_ALLOC(remote->url); if (opts->name != NULL) { remote->name = git__strdup(opts->name); GIT_ERROR_CHECK_ALLOC(remote->name); if (opts->repository && ((error = git_buf_printf(&var, CONFIG_URL_FMT, opts->name)) < 0 || (error = git_repository_config__weakptr(&config_rw, opts->repository)) < 0 || (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)) goto on_error; } if (opts->fetchspec != NULL || (opts->name && !(opts->flags & GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC))) { const char *fetch = NULL; if (opts->fetchspec) { fetch = opts->fetchspec; } else { if ((error = default_fetchspec_for_name(&specbuf, opts->name)) < 0) goto on_error; fetch = git_buf_cstr(&specbuf); } if ((error = add_refspec(remote, fetch, true)) < 0) goto on_error; /* only write for named remotes with a repository */ if (opts->repository && opts->name && ((error = write_add_refspec(opts->repository, opts->name, fetch, true)) < 0 || (error = lookup_remote_prune_config(remote, config_ro, opts->name)) < 0)) goto on_error; /* Move the data over to where the matching functions can find them */ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0) goto on_error; } /* A remote without a name doesn't download tags */ if (!opts->name) remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; else remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; git_buf_dispose(&var); *out = remote; error = 0; on_error: if (error) git_remote_free(remote); git_config_free(config_ro); git_buf_dispose(&specbuf); git_buf_dispose(&canonical_url); git_buf_dispose(&var); return error; } int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url) { git_buf buf = GIT_BUF_INIT; int error; git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT; /* Those 2 tests are duplicated here because of backward-compatibility */ if ((error = ensure_remote_name_is_valid(name)) < 0) return error; if (canonicalize_url(&buf, url) < 0) return GIT_ERROR; git_buf_clear(&buf); opts.repository = repo; opts.name = name; error = git_remote_create_with_opts(out, url, &opts); git_buf_dispose(&buf); return error; } int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) { int error; git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT; if ((error = ensure_remote_name_is_valid(name)) < 0) return error; opts.repository = repo; opts.name = name; opts.fetchspec = fetch; opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC; return git_remote_create_with_opts(out, url, &opts); } int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url) { git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT; opts.repository = repo; return git_remote_create_with_opts(out, url, &opts); } int git_remote_create_detached(git_remote **out, const char *url) { return git_remote_create_with_opts(out, url, NULL); } int git_remote_dup(git_remote **dest, git_remote *source) { size_t i; int error = 0; git_refspec *spec; git_remote *remote = git__calloc(1, sizeof(git_remote)); GIT_ERROR_CHECK_ALLOC(remote); if (source->name != NULL) { remote->name = git__strdup(source->name); GIT_ERROR_CHECK_ALLOC(remote->name); } if (source->url != NULL) { remote->url = git__strdup(source->url); GIT_ERROR_CHECK_ALLOC(remote->url); } if (source->pushurl != NULL) { remote->pushurl = git__strdup(source->pushurl); GIT_ERROR_CHECK_ALLOC(remote->pushurl); } remote->repo = source->repo; remote->download_tags = source->download_tags; remote->prune_refs = source->prune_refs; if (git_vector_init(&remote->refs, 32, NULL) < 0 || git_vector_init(&remote->refspecs, 2, NULL) < 0 || git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { error = -1; goto cleanup; } git_vector_foreach(&source->refspecs, i, spec) { if ((error = add_refspec(remote, spec->string, !spec->push)) < 0) goto cleanup; } *dest = remote; cleanup: if (error < 0) git__free(remote); return error; } struct refspec_cb_data { git_remote *remote; int fetch; }; static int refspec_cb(const git_config_entry *entry, void *payload) { struct refspec_cb_data *data = (struct refspec_cb_data *)payload; return add_refspec(data->remote, entry->value, data->fetch); } static int get_optional_config( bool *found, git_config *config, git_buf *buf, git_config_foreach_cb cb, void *payload) { int error = 0; const char *key = git_buf_cstr(buf); if (git_buf_oom(buf)) return -1; if (cb != NULL) error = git_config_get_multivar_foreach(config, key, NULL, cb, payload); else error = git_config_get_string(payload, config, key); if (found) *found = !error; if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } return error; } int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) { git_remote *remote = NULL; git_buf buf = GIT_BUF_INIT; const char *val; int error = 0; git_config *config; struct refspec_cb_data data = { NULL }; bool optional_setting_found = false, found; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if ((error = ensure_remote_name_is_valid(name)) < 0) return error; if ((error = git_repository_config_snapshot(&config, repo)) < 0) return error; remote = git__calloc(1, sizeof(git_remote)); GIT_ERROR_CHECK_ALLOC(remote); remote->name = git__strdup(name); GIT_ERROR_CHECK_ALLOC(remote->name); if (git_vector_init(&remote->refs, 32, NULL) < 0 || git_vector_init(&remote->refspecs, 2, NULL) < 0 || git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 || git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { error = -1; goto cleanup; } if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0) goto cleanup; if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) goto cleanup; optional_setting_found |= found; remote->repo = repo; remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; if (found && strlen(val) > 0) { remote->url = apply_insteadof(config, val, GIT_DIRECTION_FETCH); GIT_ERROR_CHECK_ALLOC(remote->url); } val = NULL; git_buf_clear(&buf); git_buf_printf(&buf, "remote.%s.pushurl", name); if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) goto cleanup; optional_setting_found |= found; if (!optional_setting_found) { error = GIT_ENOTFOUND; git_error_set(GIT_ERROR_CONFIG, "remote '%s' does not exist", name); goto cleanup; } if (found && strlen(val) > 0) { remote->pushurl = apply_insteadof(config, val, GIT_DIRECTION_PUSH); GIT_ERROR_CHECK_ALLOC(remote->pushurl); } data.remote = remote; data.fetch = true; git_buf_clear(&buf); git_buf_printf(&buf, "remote.%s.fetch", name); if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) goto cleanup; data.fetch = false; git_buf_clear(&buf); git_buf_printf(&buf, "remote.%s.push", name); if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) goto cleanup; if ((error = download_tags_value(remote, config)) < 0) goto cleanup; if ((error = lookup_remote_prune_config(remote, config, name)) < 0) goto cleanup; /* Move the data over to where the matching functions can find them */ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0) goto cleanup; *out = remote; cleanup: git_config_free(config); git_buf_dispose(&buf); if (error < 0) git_remote_free(remote); return error; } static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name) { git_buf buf = GIT_BUF_INIT; int error = 0; git_buf_printf(&buf, "remote.%s.prune", name); if ((error = git_config_get_bool(&remote->prune_refs, config, git_buf_cstr(&buf))) < 0) { if (error == GIT_ENOTFOUND) { git_error_clear(); if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) { if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } } } } git_buf_dispose(&buf); return error; } const char *git_remote_name(const git_remote *remote) { GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL); return remote->name; } git_repository *git_remote_owner(const git_remote *remote) { GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL); return remote->repo; } const char *git_remote_url(const git_remote *remote) { GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL); return remote->url; } int git_remote_set_instance_url(git_remote *remote, const char *url) { char *tmp; GIT_ASSERT_ARG(remote); GIT_ASSERT_ARG(url); if ((tmp = git__strdup(url)) == NULL) return -1; git__free(remote->url); remote->url = tmp; return 0; } static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url) { git_config *cfg; git_buf buf = GIT_BUF_INIT, canonical_url = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(remote); if ((error = ensure_remote_name_is_valid(remote)) < 0) return error; if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; if ((error = git_buf_printf(&buf, pattern, remote)) < 0) return error; if (url) { if ((error = canonicalize_url(&canonical_url, url)) < 0) goto cleanup; error = git_config_set_string(cfg, buf.ptr, url); } else { error = git_config_delete_entry(cfg, buf.ptr); } cleanup: git_buf_dispose(&canonical_url); git_buf_dispose(&buf); return error; } int git_remote_set_url(git_repository *repo, const char *remote, const char *url) { return set_url(repo, remote, CONFIG_URL_FMT, url); } const char *git_remote_pushurl(const git_remote *remote) { GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL); return remote->pushurl; } int git_remote_set_instance_pushurl(git_remote *remote, const char *url) { char *tmp; GIT_ASSERT_ARG(remote); GIT_ASSERT_ARG(url); if ((tmp = git__strdup(url)) == NULL) return -1; git__free(remote->pushurl); remote->pushurl = tmp; return 0; } int git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url) { return set_url(repo, remote, CONFIG_PUSHURL_FMT, url); } static int resolve_url( git_buf *resolved_url, const char *url, int direction, const git_remote_callbacks *callbacks) { #ifdef GIT_DEPRECATE_HARD GIT_UNUSED(direction); GIT_UNUSED(callbacks); #else int status, error; if (callbacks && callbacks->resolve_url) { git_buf_clear(resolved_url); status = callbacks->resolve_url(resolved_url, url, direction, callbacks->payload); if (status != GIT_PASSTHROUGH) { git_error_set_after_callback_function(status, "git_resolve_url_cb"); if ((error = git_buf_sanitize(resolved_url)) < 0) return error; return status; } } #endif return git_buf_sets(resolved_url, url); } int git_remote__urlfordirection( git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks) { const char *url = NULL; GIT_ASSERT_ARG(remote); GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH); if (callbacks && callbacks->remote_ready) { int status = callbacks->remote_ready(remote, direction, callbacks->payload); if (status != 0 && status != GIT_PASSTHROUGH) { git_error_set_after_callback_function(status, "git_remote_ready_cb"); return status; } } if (direction == GIT_DIRECTION_FETCH) url = remote->url; else if (direction == GIT_DIRECTION_PUSH) url = remote->pushurl ? remote->pushurl : remote->url; if (!url) { git_error_set(GIT_ERROR_INVALID, "malformed remote '%s' - missing %s URL", remote->name ? remote->name : "(anonymous)", direction == GIT_DIRECTION_FETCH ? "fetch" : "push"); return GIT_EINVALID; } return resolve_url(url_out, url, direction, callbacks); } static int remote_transport_set_callbacks(git_transport *t, const git_remote_callbacks *cbs) { if (!t->set_callbacks || !cbs) return 0; return t->set_callbacks(t, cbs->sideband_progress, NULL, cbs->certificate_check, cbs->payload); } static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers) { if (!t->set_custom_headers) return 0; return t->set_custom_headers(t, custom_headers); } int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn) { git_transport *t; git_buf url = GIT_BUF_INIT; int flags = GIT_TRANSPORTFLAGS_NONE; int error; void *payload = NULL; git_credential_acquire_cb credentials = NULL; git_transport_cb transport = NULL; GIT_ASSERT_ARG(remote); if (callbacks) { GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); credentials = callbacks->credentials; transport = callbacks->transport; payload = callbacks->payload; } if (conn->proxy) GIT_ERROR_CHECK_VERSION(conn->proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); t = remote->transport; if ((error = git_remote__urlfordirection(&url, remote, direction, callbacks)) < 0) goto on_error; /* If we don't have a transport object yet, and the caller specified a * custom transport factory, use that */ if (!t && transport && (error = transport(&t, remote, payload)) < 0) goto on_error; /* If we still don't have a transport, then use the global * transport registrations which map URI schemes to transport factories */ if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0) goto on_error; if ((error = set_transport_custom_headers(t, conn->custom_headers)) != 0) goto on_error; if ((error = remote_transport_set_callbacks(t, callbacks)) < 0 || (error = t->connect(t, url.ptr, credentials, payload, conn->proxy, direction, flags)) != 0) goto on_error; remote->transport = t; git_buf_dispose(&url); return 0; on_error: if (t) t->free(t); git_buf_dispose(&url); if (t == remote->transport) remote->transport = NULL; return error; } int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers) { git_remote_connection_opts conn; conn.proxy = proxy; conn.custom_headers = custom_headers; return git_remote__connect(remote, direction, callbacks, &conn); } int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote) { GIT_ASSERT_ARG(remote); if (!remote->transport) { git_error_set(GIT_ERROR_NET, "this remote has never connected"); return -1; } return remote->transport->ls(out, size, remote->transport); } static int lookup_config(char **out, git_config *cfg, const char *name) { git_config_entry *ce = NULL; int error; if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0) return error; if (ce && ce->value) { *out = git__strdup(ce->value); GIT_ERROR_CHECK_ALLOC(*out); } else { error = GIT_ENOTFOUND; } git_config_entry_free(ce); return error; } static void url_config_trim(git_net_url *url) { size_t len = strlen(url->path); if (url->path[len - 1] == '/') { len--; } else { while (len && url->path[len - 1] != '/') len--; } url->path[len] = '\0'; } static int http_proxy_config(char **out, git_remote *remote, git_net_url *url) { git_config *cfg = NULL; git_buf buf = GIT_BUF_INIT; git_net_url lookup_url = GIT_NET_URL_INIT; int error; if ((error = git_net_url_dup(&lookup_url, url)) < 0) goto done; if (remote->repo) { if ((error = git_repository_config(&cfg, remote->repo)) < 0) goto done; } else { if ((error = git_config_open_default(&cfg)) < 0) goto done; } /* remote..proxy config setting */ if (remote->name && remote->name[0]) { git_buf_clear(&buf); if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0 || (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND) goto done; } while (true) { git_buf_clear(&buf); if ((error = git_buf_puts(&buf, "http.")) < 0 || (error = git_net_url_fmt(&buf, &lookup_url)) < 0 || (error = git_buf_puts(&buf, ".proxy")) < 0 || (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND) goto done; if (! lookup_url.path[0]) break; url_config_trim(&lookup_url); } git_buf_clear(&buf); error = lookup_config(out, cfg, "http.proxy"); done: git_config_free(cfg); git_buf_dispose(&buf); git_net_url_dispose(&lookup_url); return error; } static int http_proxy_env(char **out, git_remote *remote, git_net_url *url) { git_buf proxy_env = GIT_BUF_INIT, no_proxy_env = GIT_BUF_INIT; bool use_ssl = (strcmp(url->scheme, "https") == 0); int error; GIT_UNUSED(remote); /* http_proxy / https_proxy environment variables */ error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy"); /* try uppercase environment variables */ if (error == GIT_ENOTFOUND) error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); if (error) goto done; /* no_proxy/NO_PROXY environment variables */ error = git__getenv(&no_proxy_env, "no_proxy"); if (error == GIT_ENOTFOUND) error = git__getenv(&no_proxy_env, "NO_PROXY"); if (error && error != GIT_ENOTFOUND) goto done; if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr)) *out = git_buf_detach(&proxy_env); else error = GIT_ENOTFOUND; done: git_buf_dispose(&proxy_env); git_buf_dispose(&no_proxy_env); return error; } int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(remote); *out = NULL; /* * Go through the possible sources for proxy configuration, * Examine the various git config options first, then * consult environment variables. */ if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND || (error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND) return error; return 0; } /* DWIM `refspecs` based on `refs` and append the output to `out` */ static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs) { size_t i; git_refspec *spec; git_vector_foreach(refspecs, i, spec) { if (git_refspec__dwim_one(out, spec, refs) < 0) return -1; } return 0; } static void free_refspecs(git_vector *vec) { size_t i; git_refspec *spec; git_vector_foreach(vec, i, spec) { git_refspec__dispose(spec); git__free(spec); } git_vector_clear(vec); } static int remote_head_cmp(const void *_a, const void *_b) { const git_remote_head *a = (git_remote_head *) _a; const git_remote_head *b = (git_remote_head *) _b; return git__strcmp_cb(a->name, b->name); } static int ls_to_vector(git_vector *out, git_remote *remote) { git_remote_head **heads; size_t heads_len, i; if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) return -1; if (git_vector_init(out, heads_len, remote_head_cmp) < 0) return -1; for (i = 0; i < heads_len; i++) { if (git_vector_insert(out, heads[i]) < 0) return -1; } return 0; } int git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts) { int error = -1; size_t i; git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT; const git_remote_callbacks *cbs = NULL; const git_strarray *custom_headers = NULL; const git_proxy_options *proxy = NULL; GIT_ASSERT_ARG(remote); if (!remote->repo) { git_error_set(GIT_ERROR_INVALID, "cannot download detached remote"); return -1; } if (opts) { GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; custom_headers = &opts->custom_headers; GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); proxy = &opts->proxy_opts; } if (!git_remote_connected(remote) && (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0) goto on_error; if (ls_to_vector(&refs, remote) < 0) return -1; if ((git_vector_init(&specs, 0, NULL)) < 0) goto on_error; remote->passed_refspecs = 0; if (!refspecs || !refspecs->count) { to_active = &remote->refspecs; } else { for (i = 0; i < refspecs->count; i++) { if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0) goto on_error; } to_active = &specs; remote->passed_refspecs = 1; } free_refspecs(&remote->passive_refspecs); if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0) goto on_error; free_refspecs(&remote->active_refspecs); error = dwim_refspecs(&remote->active_refspecs, to_active, &refs); git_vector_free(&refs); free_refspecs(&specs); git_vector_free(&specs); if (error < 0) return error; if (remote->push) { git_push_free(remote->push); remote->push = NULL; } if ((error = git_fetch_negotiate(remote, opts)) < 0) return error; return git_fetch_download_pack(remote, cbs); on_error: git_vector_free(&refs); free_refspecs(&specs); git_vector_free(&specs); return error; } int git_remote_fetch( git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts, const char *reflog_message) { int error, update_fetchhead = 1; git_remote_autotag_option_t tagopt = remote->download_tags; bool prune = false; git_buf reflog_msg_buf = GIT_BUF_INIT; const git_remote_callbacks *cbs = NULL; git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT; if (opts) { GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; conn.custom_headers = &opts->custom_headers; update_fetchhead = opts->update_fetchhead; tagopt = opts->download_tags; GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); conn.proxy = &opts->proxy_opts; } /* Connect and download everything */ if ((error = git_remote__connect(remote, GIT_DIRECTION_FETCH, cbs, &conn)) != 0) return error; error = git_remote_download(remote, refspecs, opts); /* We don't need to be connected anymore */ git_remote_disconnect(remote); /* If the download failed, return the error */ if (error != 0) return error; /* Default reflog message */ if (reflog_message) git_buf_sets(&reflog_msg_buf, reflog_message); else { git_buf_printf(&reflog_msg_buf, "fetch %s", remote->name ? remote->name : remote->url); } /* Create "remote/foo" branches for all remote branches */ error = git_remote_update_tips(remote, cbs, update_fetchhead, tagopt, git_buf_cstr(&reflog_msg_buf)); git_buf_dispose(&reflog_msg_buf); if (error < 0) return error; if (opts && opts->prune == GIT_FETCH_PRUNE) prune = true; else if (opts && opts->prune == GIT_FETCH_PRUNE_UNSPECIFIED && remote->prune_refs) prune = true; else if (opts && opts->prune == GIT_FETCH_NO_PRUNE) prune = false; else prune = remote->prune_refs; if (prune) error = git_remote_prune(remote, cbs); return error; } static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src) { unsigned int i; git_remote_head *remote_ref; GIT_ASSERT_ARG(update_heads); GIT_ASSERT_ARG(fetchspec_src); *out = NULL; git_vector_foreach(update_heads, i, remote_ref) { if (strcmp(remote_ref->name, fetchspec_src) == 0) { *out = remote_ref; break; } } return 0; } static int ref_to_update(int *update, git_buf *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name) { int error = 0; git_repository *repo; git_buf upstream_remote = GIT_BUF_INIT; git_buf upstream_name = GIT_BUF_INIT; repo = git_remote_owner(remote); if ((!git_reference__is_branch(ref_name)) || !git_remote_name(remote) || (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name) < 0) || git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) || (error = git_refspec_rtransform(remote_name, spec, upstream_name.ptr)) < 0) { /* Not an error if there is no upstream */ if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } *update = 0; } else { *update = 1; } git_buf_dispose(&upstream_remote); git_buf_dispose(&upstream_name); return error; } static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref) { git_reference *resolved_ref = NULL; git_buf remote_name = GIT_BUF_INIT; git_config *config = NULL; const char *ref_name; int error = 0, update; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(spec); GIT_ASSERT_ARG(ref); *out = NULL; error = git_reference_resolve(&resolved_ref, ref); /* If we're in an unborn branch, let's pretend nothing happened */ if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { ref_name = git_reference_symbolic_target(ref); error = 0; } else { ref_name = git_reference_name(resolved_ref); } /* * The ref name may be unresolvable - perhaps it's pointing to * something invalid. In this case, there is no remote head for * this ref. */ if (!ref_name) { error = 0; goto cleanup; } if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0) goto cleanup; if (update) error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); cleanup: git_buf_dispose(&remote_name); git_reference_free(resolved_ref); git_config_free(config); return error; } static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads) { git_reference *head_ref = NULL; git_fetchhead_ref *fetchhead_ref; git_remote_head *remote_ref, *merge_remote_ref; git_vector fetchhead_refs; bool include_all_fetchheads; unsigned int i = 0; int error = 0; GIT_ASSERT_ARG(remote); /* no heads, nothing to do */ if (update_heads->length == 0) return 0; if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0) return -1; /* Iff refspec is * (but not subdir slash star), include tags */ include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0); /* Determine what to merge: if refspec was a wildcard, just use HEAD */ if (git_refspec_is_wildcard(spec)) { if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 || (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0) goto cleanup; } else { /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */ if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0) goto cleanup; } /* Create the FETCH_HEAD file */ git_vector_foreach(update_heads, i, remote_ref) { int merge_this_fetchhead = (merge_remote_ref == remote_ref); if (!include_all_fetchheads && !git_refspec_src_matches(spec, remote_ref->name) && !merge_this_fetchhead) continue; if (git_fetchhead_ref_create(&fetchhead_ref, &remote_ref->oid, merge_this_fetchhead, remote_ref->name, git_remote_url(remote)) < 0) goto cleanup; if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0) goto cleanup; } git_fetchhead_write(remote->repo, &fetchhead_refs); cleanup: for (i = 0; i < fetchhead_refs.length; ++i) git_fetchhead_ref_free(fetchhead_refs.contents[i]); git_vector_free(&fetchhead_refs); git_reference_free(head_ref); return error; } /** * Generate a list of candidates for pruning by getting a list of * references which match the rhs of an active refspec. */ static int prune_candidates(git_vector *candidates, git_remote *remote) { git_strarray arr = { 0 }; size_t i; int error; if ((error = git_reference_list(&arr, remote->repo)) < 0) return error; for (i = 0; i < arr.count; i++) { const char *refname = arr.strings[i]; char *refname_dup; if (!git_remote__matching_dst_refspec(remote, refname)) continue; refname_dup = git__strdup(refname); GIT_ERROR_CHECK_ALLOC(refname_dup); if ((error = git_vector_insert(candidates, refname_dup)) < 0) goto out; } out: git_strarray_dispose(&arr); return error; } static int find_head(const void *_a, const void *_b) { git_remote_head *a = (git_remote_head *) _a; git_remote_head *b = (git_remote_head *) _b; return strcmp(a->name, b->name); } int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks) { size_t i, j; git_vector remote_refs = GIT_VECTOR_INIT; git_vector candidates = GIT_VECTOR_INIT; const git_refspec *spec; const char *refname; int error; git_oid zero_id = {{ 0 }}; if (callbacks) GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); if ((error = ls_to_vector(&remote_refs, remote)) < 0) goto cleanup; git_vector_set_cmp(&remote_refs, find_head); if ((error = prune_candidates(&candidates, remote)) < 0) goto cleanup; /* * Remove those entries from the candidate list for which we * can find a remote reference in at least one refspec. */ git_vector_foreach(&candidates, i, refname) { git_vector_foreach(&remote->active_refspecs, j, spec) { git_buf buf = GIT_BUF_INIT; size_t pos; char *src_name; git_remote_head key = {0}; if (!git_refspec_dst_matches(spec, refname)) continue; if ((error = git_refspec_rtransform(&buf, spec, refname)) < 0) goto cleanup; key.name = (char *) git_buf_cstr(&buf); error = git_vector_bsearch(&pos, &remote_refs, &key); git_buf_dispose(&buf); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; if (error == GIT_ENOTFOUND) continue; /* If we did find a source, remove it from the candidates. */ if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0) goto cleanup; git__free(src_name); break; } } /* * For those candidates still left in the list, we need to * remove them. We do not remove symrefs, as those are for * stuff like origin/HEAD which will never match, but we do * not want to remove them. */ git_vector_foreach(&candidates, i, refname) { git_reference *ref; git_oid id; if (refname == NULL) continue; error = git_reference_lookup(&ref, remote->repo, refname); /* as we want it gone, let's not consider this an error */ if (error == GIT_ENOTFOUND) continue; if (error < 0) goto cleanup; if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { git_reference_free(ref); continue; } git_oid_cpy(&id, git_reference_target(ref)); error = git_reference_delete(ref); git_reference_free(ref); if (error < 0) goto cleanup; if (callbacks && callbacks->update_tips) error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload); if (error < 0) goto cleanup; } cleanup: git_vector_free(&remote_refs); git_vector_free_deep(&candidates); return error; } static int update_tips_for_spec( git_remote *remote, const git_remote_callbacks *callbacks, int update_fetchhead, git_remote_autotag_option_t tagopt, git_refspec *spec, git_vector *refs, const char *log_message) { int error = 0, autotag, valid; unsigned int i = 0; git_buf refname = GIT_BUF_INIT; git_oid old; git_odb *odb; git_remote_head *head; git_reference *ref; git_refspec tagspec; git_vector update_heads; GIT_ASSERT_ARG(remote); if (git_repository_odb__weakptr(&odb, remote->repo) < 0) return -1; if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) return -1; /* Make a copy of the transport's refs */ if (git_vector_init(&update_heads, 16, NULL) < 0) return -1; for (; i < refs->length; ++i) { head = git_vector_get(refs, i); autotag = 0; git_buf_clear(&refname); /* Ignore malformed ref names (which also saves us from tag^{} */ if (git_reference_name_is_valid(&valid, head->name) < 0) goto on_error; if (!valid) continue; /* If we have a tag, see if the auto-follow rules say to update it */ if (git_refspec_src_matches(&tagspec, head->name)) { if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO) autotag = 1; git_buf_clear(&refname); if (git_buf_puts(&refname, head->name) < 0) goto on_error; } } /* If we didn't want to auto-follow the tag, check if the refspec matches */ if (!autotag && git_refspec_src_matches(spec, head->name)) { if (spec->dst) { if (git_refspec_transform(&refname, spec, head->name) < 0) goto on_error; } else { /* * no rhs mans store it in FETCH_HEAD, even if we don't update anything else. */ if ((error = git_vector_insert(&update_heads, head)) < 0) goto on_error; continue; } } /* If we still don't have a refname, we don't want it */ if (git_buf_len(&refname) == 0) { continue; } /* In autotag mode, only create tags for objects already in db */ if (autotag && !git_odb_exists(odb, &head->oid)) continue; if (!autotag && git_vector_insert(&update_heads, head) < 0) goto on_error; error = git_reference_name_to_id(&old, remote->repo, refname.ptr); if (error < 0 && error != GIT_ENOTFOUND) goto on_error; if (!(error || error == GIT_ENOTFOUND) && !spec->force && !git_graph_descendant_of(remote->repo, &head->oid, &old)) continue; if (error == GIT_ENOTFOUND) { memset(&old, 0, GIT_OID_RAWSZ); if (autotag && git_vector_insert(&update_heads, head) < 0) goto on_error; } if (!git_oid__cmp(&old, &head->oid)) continue; /* In autotag mode, don't overwrite any locally-existing tags */ error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, log_message); if (error == GIT_EEXISTS) continue; if (error < 0) goto on_error; git_reference_free(ref); if (callbacks && callbacks->update_tips != NULL) { if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0) goto on_error; } } if (update_fetchhead && (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0) goto on_error; git_vector_free(&update_heads); git_refspec__dispose(&tagspec); git_buf_dispose(&refname); return 0; on_error: git_vector_free(&update_heads); git_refspec__dispose(&tagspec); git_buf_dispose(&refname); return -1; } /** * Iteration over the three vectors, with a pause whenever we find a match * * On each stop, we store the iteration stat in the inout i,j,k * parameters, and return the currently matching passive refspec as * well as the head which we matched. */ static int next_head(const git_remote *remote, git_vector *refs, git_refspec **out_spec, git_remote_head **out_head, size_t *out_i, size_t *out_j, size_t *out_k) { const git_vector *active, *passive; git_remote_head *head; git_refspec *spec, *passive_spec; size_t i, j, k; int valid; active = &remote->active_refspecs; passive = &remote->passive_refspecs; i = *out_i; j = *out_j; k = *out_k; for (; i < refs->length; i++) { head = git_vector_get(refs, i); if (git_reference_name_is_valid(&valid, head->name) < 0) return -1; if (!valid) continue; for (; j < active->length; j++) { spec = git_vector_get(active, j); if (!git_refspec_src_matches(spec, head->name)) continue; for (; k < passive->length; k++) { passive_spec = git_vector_get(passive, k); if (!git_refspec_src_matches(passive_spec, head->name)) continue; *out_spec = passive_spec; *out_head = head; *out_i = i; *out_j = j; *out_k = k + 1; return 0; } k = 0; } j = 0; } return GIT_ITEROVER; } static int opportunistic_updates(const git_remote *remote, const git_remote_callbacks *callbacks, git_vector *refs, const char *msg) { size_t i, j, k; git_refspec *spec; git_remote_head *head; git_reference *ref; git_buf refname = GIT_BUF_INIT; int error = 0; i = j = k = 0; while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) { git_oid old = {{ 0 }}; /* * If we got here, there is a refspec which was used * for fetching which matches the source of one of the * passive refspecs, so we should update that * remote-tracking branch, but not add it to * FETCH_HEAD */ git_buf_clear(&refname); if ((error = git_refspec_transform(&refname, spec, head->name)) < 0) goto cleanup; error = git_reference_name_to_id(&old, remote->repo, refname.ptr); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; if (!git_oid_cmp(&old, &head->oid)) continue; /* If we did find a current reference, make sure we haven't lost a race */ if (error) error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, msg); else error = git_reference_create_matching(&ref, remote->repo, refname.ptr, &head->oid, true, &old, msg); git_reference_free(ref); if (error < 0) goto cleanup; if (callbacks && callbacks->update_tips != NULL) { if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0) goto cleanup; } } if (error == GIT_ITEROVER) error = 0; cleanup: git_buf_dispose(&refname); return error; } static int truncate_fetch_head(const char *gitdir) { git_buf path = GIT_BUF_INIT; int error; if ((error = git_buf_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0) return error; error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE); git_buf_dispose(&path); return error; } int git_remote_update_tips( git_remote *remote, const git_remote_callbacks *callbacks, int update_fetchhead, git_remote_autotag_option_t download_tags, const char *reflog_message) { git_refspec *spec, tagspec; git_vector refs = GIT_VECTOR_INIT; git_remote_autotag_option_t tagopt; int error; size_t i; /* push has its own logic hidden away in the push object */ if (remote->push) { return git_push_update_tips(remote->push, callbacks); } if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) return -1; if ((error = ls_to_vector(&refs, remote)) < 0) goto out; if (download_tags == GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED) tagopt = remote->download_tags; else tagopt = download_tags; if ((error = truncate_fetch_head(git_repository_path(remote->repo))) < 0) goto out; if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0) goto out; } git_vector_foreach(&remote->active_refspecs, i, spec) { if (spec->push) continue; if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0) goto out; } /* Only try to do opportunistic updates if the refpec lists differ. */ if (remote->passed_refspecs) error = opportunistic_updates(remote, callbacks, &refs, reflog_message); out: git_vector_free(&refs); git_refspec__dispose(&tagspec); return error; } int git_remote_connected(const git_remote *remote) { GIT_ASSERT_ARG(remote); if (!remote->transport || !remote->transport->is_connected) return 0; /* Ask the transport if it's connected. */ return remote->transport->is_connected(remote->transport); } int git_remote_stop(git_remote *remote) { GIT_ASSERT_ARG(remote); if (remote->transport && remote->transport->cancel) remote->transport->cancel(remote->transport); return 0; } int git_remote_disconnect(git_remote *remote) { GIT_ASSERT_ARG(remote); if (git_remote_connected(remote)) remote->transport->close(remote->transport); return 0; } void git_remote_free(git_remote *remote) { if (remote == NULL) return; if (remote->transport != NULL) { git_remote_disconnect(remote); remote->transport->free(remote->transport); remote->transport = NULL; } git_vector_free(&remote->refs); free_refspecs(&remote->refspecs); git_vector_free(&remote->refspecs); free_refspecs(&remote->active_refspecs); git_vector_free(&remote->active_refspecs); free_refspecs(&remote->passive_refspecs); git_vector_free(&remote->passive_refspecs); git_push_free(remote->push); git__free(remote->url); git__free(remote->pushurl); git__free(remote->name); git__free(remote); } static int remote_list_cb(const git_config_entry *entry, void *payload) { git_vector *list = payload; const char *name = entry->name + strlen("remote."); size_t namelen = strlen(name); char *remote_name; /* we know name matches "remote..(push)?url" */ if (!strcmp(&name[namelen - 4], ".url")) remote_name = git__strndup(name, namelen - 4); /* strip ".url" */ else remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */ GIT_ERROR_CHECK_ALLOC(remote_name); return git_vector_insert(list, remote_name); } int git_remote_list(git_strarray *remotes_list, git_repository *repo) { int error; git_config *cfg; git_vector list = GIT_VECTOR_INIT; if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0) return error; error = git_config_foreach_match( cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); if (error < 0) { git_vector_free_deep(&list); return error; } git_vector_uniq(&list, git__free); remotes_list->strings = (char **)git_vector_detach(&remotes_list->count, NULL, &list); return 0; } const git_indexer_progress *git_remote_stats(git_remote *remote) { GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL); return &remote->stats; } git_remote_autotag_option_t git_remote_autotag(const git_remote *remote) { return remote->download_tags; } int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value) { git_buf var = GIT_BUF_INIT; git_config *config; int error; GIT_ASSERT_ARG(repo && remote); if ((error = ensure_remote_name_is_valid(remote)) < 0) return error; if ((error = git_repository_config__weakptr(&config, repo)) < 0) return error; if ((error = git_buf_printf(&var, CONFIG_TAGOPT_FMT, remote))) return error; switch (value) { case GIT_REMOTE_DOWNLOAD_TAGS_NONE: error = git_config_set_string(config, var.ptr, "--no-tags"); break; case GIT_REMOTE_DOWNLOAD_TAGS_ALL: error = git_config_set_string(config, var.ptr, "--tags"); break; case GIT_REMOTE_DOWNLOAD_TAGS_AUTO: error = git_config_delete_entry(config, var.ptr); if (error == GIT_ENOTFOUND) error = 0; break; default: git_error_set(GIT_ERROR_INVALID, "invalid value for the tagopt setting"); error = -1; } git_buf_dispose(&var); return error; } int git_remote_prune_refs(const git_remote *remote) { return remote->prune_refs; } static int rename_remote_config_section( git_repository *repo, const char *old_name, const char *new_name) { git_buf old_section_name = GIT_BUF_INIT, new_section_name = GIT_BUF_INIT; int error = -1; if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0) goto cleanup; if (new_name && (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)) goto cleanup; error = git_config_rename_section( repo, git_buf_cstr(&old_section_name), new_name ? git_buf_cstr(&new_section_name) : NULL); cleanup: git_buf_dispose(&old_section_name); git_buf_dispose(&new_section_name); return error; } struct update_data { git_config *config; const char *old_remote_name; const char *new_remote_name; }; static int update_config_entries_cb( const git_config_entry *entry, void *payload) { struct update_data *data = (struct update_data *)payload; if (strcmp(entry->value, data->old_remote_name)) return 0; return git_config_set_string( data->config, entry->name, data->new_remote_name); } static int update_branch_remote_config_entry( git_repository *repo, const char *old_name, const char *new_name) { int error; struct update_data data = { NULL }; if ((error = git_repository_config__weakptr(&data.config, repo)) < 0) return error; data.old_remote_name = old_name; data.new_remote_name = new_name; return git_config_foreach_match( data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); } static int rename_one_remote_reference( git_reference *reference_in, const char *old_remote_name, const char *new_remote_name) { int error; git_reference *ref = NULL, *dummy = NULL; git_buf namespace = GIT_BUF_INIT, old_namespace = GIT_BUF_INIT; git_buf new_name = GIT_BUF_INIT; git_buf log_message = GIT_BUF_INIT; size_t pfx_len; const char *target; if ((error = git_buf_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0) return error; pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1; git_buf_puts(&new_name, namespace.ptr); if ((error = git_buf_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0) goto cleanup; if ((error = git_buf_printf(&log_message, "renamed remote %s to %s", old_remote_name, new_remote_name)) < 0) goto cleanup; if ((error = git_reference_rename(&ref, reference_in, git_buf_cstr(&new_name), 1, git_buf_cstr(&log_message))) < 0) goto cleanup; if (git_reference_type(ref) != GIT_REFERENCE_SYMBOLIC) goto cleanup; /* Handle refs like origin/HEAD -> origin/master */ target = git_reference_symbolic_target(ref); if ((error = git_buf_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0) goto cleanup; if (git__prefixcmp(target, old_namespace.ptr)) goto cleanup; git_buf_clear(&new_name); git_buf_puts(&new_name, namespace.ptr); if ((error = git_buf_puts(&new_name, target + pfx_len)) < 0) goto cleanup; error = git_reference_symbolic_set_target(&dummy, ref, git_buf_cstr(&new_name), git_buf_cstr(&log_message)); git_reference_free(dummy); cleanup: git_reference_free(reference_in); git_reference_free(ref); git_buf_dispose(&namespace); git_buf_dispose(&old_namespace); git_buf_dispose(&new_name); git_buf_dispose(&log_message); return error; } static int rename_remote_references( git_repository *repo, const char *old_name, const char *new_name) { int error; git_buf buf = GIT_BUF_INIT; git_reference *ref; git_reference_iterator *iter; if ((error = git_buf_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0) return error; error = git_reference_iterator_glob_new(&iter, repo, git_buf_cstr(&buf)); git_buf_dispose(&buf); if (error < 0) return error; while ((error = git_reference_next(&ref, iter)) == 0) { if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) break; } git_reference_iterator_free(iter); return (error == GIT_ITEROVER) ? 0 : error; } static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name) { git_config *config; git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; const git_refspec *spec; size_t i; int error = 0; if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) return error; if ((error = git_vector_init(problems, 1, NULL)) < 0) return error; if ((error = default_fetchspec_for_name(&base, remote->name)) < 0) return error; git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push) continue; /* Does the dst part of the refspec follow the expected format? */ if (strcmp(git_buf_cstr(&base), spec->string)) { char *dup; dup = git__strdup(spec->string); GIT_ERROR_CHECK_ALLOC(dup); if ((error = git_vector_insert(problems, dup)) < 0) break; continue; } /* If we do want to move it to the new section */ git_buf_clear(&val); git_buf_clear(&var); if (default_fetchspec_for_name(&val, new_name) < 0 || git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) { error = -1; break; } if ((error = git_config_set_string( config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) break; } git_buf_dispose(&base); git_buf_dispose(&var); git_buf_dispose(&val); if (error < 0) { char *str; git_vector_foreach(problems, i, str) git__free(str); git_vector_free(problems); } return error; } int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name) { int error; git_vector problem_refspecs = GIT_VECTOR_INIT; git_remote *remote = NULL; GIT_ASSERT_ARG(out && repo && name && new_name); if ((error = git_remote_lookup(&remote, repo, name)) < 0) return error; if ((error = ensure_remote_name_is_valid(new_name)) < 0) goto cleanup; if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0) goto cleanup; if ((error = rename_remote_config_section(repo, name, new_name)) < 0) goto cleanup; if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0) goto cleanup; if ((error = rename_remote_references(repo, name, new_name)) < 0) goto cleanup; if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0) goto cleanup; out->count = problem_refspecs.length; out->strings = (char **) problem_refspecs.contents; cleanup: if (error < 0) git_vector_free(&problem_refspecs); git_remote_free(remote); return error; } int git_remote_name_is_valid(int *valid, const char *remote_name) { git_buf buf = GIT_BUF_INIT; git_refspec refspec = {0}; int error; GIT_ASSERT(valid); *valid = 0; if (!remote_name || *remote_name == '\0') return 0; if ((error = git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name)) < 0) goto done; error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true); if (!error) *valid = 1; else if (error == GIT_EINVALIDSPEC) error = 0; done: git_buf_dispose(&buf); git_refspec__dispose(&refspec); return error; } git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname) { git_refspec *spec; size_t i; git_vector_foreach(&remote->active_refspecs, i, spec) { if (spec->push) continue; if (git_refspec_src_matches(spec, refname)) return spec; } return NULL; } git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname) { git_refspec *spec; size_t i; git_vector_foreach(&remote->active_refspecs, i, spec) { if (spec->push) continue; if (git_refspec_dst_matches(spec, refname)) return spec; } return NULL; } int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec) { return write_add_refspec(repo, remote, refspec, true); } int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec) { return write_add_refspec(repo, remote, refspec, false); } static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push) { size_t i; git_vector refspecs; git_refspec *spec; char *dup; if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0) return -1; git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push != push) continue; if ((dup = git__strdup(spec->string)) == NULL) goto on_error; if (git_vector_insert(&refspecs, dup) < 0) { git__free(dup); goto on_error; } } array->strings = (char **)refspecs.contents; array->count = refspecs.length; return 0; on_error: git_vector_free_deep(&refspecs); return -1; } int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote) { return copy_refspecs(array, remote, false); } int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote) { return copy_refspecs(array, remote, true); } size_t git_remote_refspec_count(const git_remote *remote) { return remote->refspecs.length; } const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n) { return git_vector_get(&remote->refspecs, n); } int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT); return 0; } /* asserts a branch..remote format */ static const char *name_offset(size_t *len_out, const char *name) { size_t prefix_len; const char *dot; prefix_len = strlen("remote."); dot = strchr(name + prefix_len, '.'); GIT_ASSERT_ARG_WITH_RETVAL(dot, NULL); *len_out = dot - name - prefix_len; return name + prefix_len; } static int remove_branch_config_related_entries( git_repository *repo, const char *remote_name) { int error; git_config *config; git_config_entry *entry; git_config_iterator *iter; git_buf buf = GIT_BUF_INIT; if ((error = git_repository_config__weakptr(&config, repo)) < 0) return error; if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0) return error; /* find any branches with us as upstream and remove that config */ while ((error = git_config_next(&entry, iter)) == 0) { const char *branch; size_t branch_len; if (strcmp(remote_name, entry->value)) continue; if ((branch = name_offset(&branch_len, entry->name)) == NULL) { error = -1; break; } git_buf_clear(&buf); if ((error = git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch)) < 0) break; if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) { if (error != GIT_ENOTFOUND) break; git_error_clear(); } git_buf_clear(&buf); if ((error = git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch)) < 0) break; if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) { if (error != GIT_ENOTFOUND) break; git_error_clear(); } } if (error == GIT_ITEROVER) error = 0; git_buf_dispose(&buf); git_config_iterator_free(iter); return error; } static int remove_refs(git_repository *repo, const git_refspec *spec) { git_reference_iterator *iter = NULL; git_vector refs; const char *name; char *dup; int error; size_t i; if ((error = git_vector_init(&refs, 8, NULL)) < 0) return error; if ((error = git_reference_iterator_new(&iter, repo)) < 0) goto cleanup; while ((error = git_reference_next_name(&name, iter)) == 0) { if (!git_refspec_dst_matches(spec, name)) continue; dup = git__strdup(name); if (!dup) { error = -1; goto cleanup; } if ((error = git_vector_insert(&refs, dup)) < 0) goto cleanup; } if (error == GIT_ITEROVER) error = 0; if (error < 0) goto cleanup; git_vector_foreach(&refs, i, name) { if ((error = git_reference_remove(repo, name)) < 0) break; } cleanup: git_reference_iterator_free(iter); git_vector_foreach(&refs, i, dup) { git__free(dup); } git_vector_free(&refs); return error; } static int remove_remote_tracking(git_repository *repo, const char *remote_name) { git_remote *remote; int error; size_t i, count; /* we want to use what's on the config, regardless of changes to the instance in memory */ if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0) return error; count = git_remote_refspec_count(remote); for (i = 0; i < count; i++) { const git_refspec *refspec = git_remote_get_refspec(remote, i); /* shouldn't ever actually happen */ if (refspec == NULL) continue; if ((error = remove_refs(repo, refspec)) < 0) break; } git_remote_free(remote); return error; } int git_remote_delete(git_repository *repo, const char *name) { int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if ((error = remove_branch_config_related_entries(repo, name)) < 0 || (error = remove_remote_tracking(repo, name)) < 0 || (error = rename_remote_config_section(repo, name, NULL)) < 0) return error; return 0; } int git_remote_default_branch(git_buf *out, git_remote *remote) { const git_remote_head **heads; const git_remote_head *guess = NULL; const git_oid *head_id; size_t heads_len, i; git_buf local_default = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(out); if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0) goto done; if (heads_len == 0 || strcmp(heads[0]->name, GIT_HEAD_FILE)) { error = GIT_ENOTFOUND; goto done; } if ((error = git_buf_sanitize(out)) < 0) return error; /* the first one must be HEAD so if that has the symref info, we're done */ if (heads[0]->symref_target) { error = git_buf_puts(out, heads[0]->symref_target); goto done; } /* * If there's no symref information, we have to look over them * and guess. We return the first match unless the default * branch is a candidate. Then we return the default branch. */ if ((error = git_repository_initialbranch(&local_default, remote->repo)) < 0) goto done; head_id = &heads[0]->oid; for (i = 1; i < heads_len; i++) { if (git_oid_cmp(head_id, &heads[i]->oid)) continue; if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR)) continue; if (!guess) { guess = heads[i]; continue; } if (!git__strcmp(local_default.ptr, heads[i]->name)) { guess = heads[i]; break; } } if (!guess) { error = GIT_ENOTFOUND; goto done; } error = git_buf_puts(out, guess->name); done: git_buf_dispose(&local_default); return error; } int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) { size_t i; int error; git_push *push; git_refspec *spec; const git_remote_callbacks *cbs = NULL; git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT; GIT_ASSERT_ARG(remote); if (!remote->repo) { git_error_set(GIT_ERROR_INVALID, "cannot download detached remote"); return -1; } if (opts) { cbs = &opts->callbacks; conn.custom_headers = &opts->custom_headers; conn.proxy = &opts->proxy_opts; } if (!git_remote_connected(remote) && (error = git_remote__connect(remote, GIT_DIRECTION_PUSH, cbs, &conn)) < 0) goto cleanup; free_refspecs(&remote->active_refspecs); if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0) goto cleanup; if (remote->push) { git_push_free(remote->push); remote->push = NULL; } if ((error = git_push_new(&remote->push, remote)) < 0) return error; push = remote->push; if (opts && (error = git_push_set_options(push, opts)) < 0) goto cleanup; if (refspecs && refspecs->count > 0) { for (i = 0; i < refspecs->count; i++) { if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0) goto cleanup; } } else { git_vector_foreach(&remote->refspecs, i, spec) { if (!spec->push) continue; if ((error = git_push_add_refspec(push, spec->string)) < 0) goto cleanup; } } if ((error = git_push_finish(push, cbs)) < 0) goto cleanup; if (cbs && cbs->push_update_reference && (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0) goto cleanup; cleanup: return error; } int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) { int error; const git_remote_callbacks *cbs = NULL; const git_strarray *custom_headers = NULL; const git_proxy_options *proxy = NULL; GIT_ASSERT_ARG(remote); if (!remote->repo) { git_error_set(GIT_ERROR_INVALID, "cannot download detached remote"); return -1; } if (opts) { GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; custom_headers = &opts->custom_headers; GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); proxy = &opts->proxy_opts; } if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0) return error; if ((error = git_remote_upload(remote, refspecs, opts)) < 0) return error; error = git_remote_update_tips(remote, cbs, 0, 0, NULL); git_remote_disconnect(remote); return error; } #define PREFIX "url" #define SUFFIX_FETCH "insteadof" #define SUFFIX_PUSH "pushinsteadof" char *apply_insteadof(git_config *config, const char *url, int direction) { size_t match_length, prefix_length, suffix_length; char *replacement = NULL; const char *regexp; git_buf result = GIT_BUF_INIT; git_config_entry *entry; git_config_iterator *iter; GIT_ASSERT_ARG_WITH_RETVAL(config, NULL); GIT_ASSERT_ARG_WITH_RETVAL(url, NULL); GIT_ASSERT_ARG_WITH_RETVAL(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH, NULL); /* Add 1 to prefix/suffix length due to the additional escaped dot */ prefix_length = strlen(PREFIX) + 1; if (direction == GIT_DIRECTION_FETCH) { regexp = PREFIX "\\..*\\." SUFFIX_FETCH; suffix_length = strlen(SUFFIX_FETCH) + 1; } else { regexp = PREFIX "\\..*\\." SUFFIX_PUSH; suffix_length = strlen(SUFFIX_PUSH) + 1; } if (git_config_iterator_glob_new(&iter, config, regexp) < 0) return NULL; match_length = 0; while (git_config_next(&entry, iter) == 0) { size_t n, replacement_length; /* Check if entry value is a prefix of URL */ if (git__prefixcmp(url, entry->value)) continue; /* Check if entry value is longer than previous * prefixes */ if ((n = strlen(entry->value)) <= match_length) continue; git__free(replacement); match_length = n; /* Cut off prefix and suffix of the value */ replacement_length = strlen(entry->name) - (prefix_length + suffix_length); replacement = git__strndup(entry->name + prefix_length, replacement_length); } git_config_iterator_free(iter); if (match_length == 0) return git__strdup(url); git_buf_printf(&result, "%s%s", replacement, url + match_length); git__free(replacement); return result.ptr; } /* Deprecated functions */ #ifndef GIT_DEPRECATE_HARD int git_remote_is_valid_name(const char *remote_name) { int valid = 0; git_remote_name_is_valid(&valid, remote_name); return valid; } #endif git2r/src/libgit2/src/push.h0000644000175000017500000000616314125111754015504 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_push_h__ #define INCLUDE_push_h__ #include "common.h" #include "git2.h" #include "refspec.h" #include "remote.h" typedef struct push_spec { struct git_refspec refspec; git_oid loid; git_oid roid; } push_spec; typedef struct push_status { bool ok; char *ref; char *msg; } push_status; struct git_push { git_repository *repo; git_packbuilder *pb; git_remote *remote; git_vector specs; git_vector updates; bool report_status; /* report-status */ bool unpack_ok; git_vector status; /* options */ unsigned pb_parallelism; git_remote_connection_opts connection; }; /** * Free the given push status object * * @param status The push status object */ void git_push_status_free(push_status *status); /** * Create a new push object * * @param out New push object * @param remote Remote instance * * @return 0 or an error code */ int git_push_new(git_push **out, git_remote *remote); /** * Set options on a push object * * @param push The push object * @param opts The options to set on the push object * * @return 0 or an error code */ int git_push_set_options( git_push *push, const git_push_options *opts); /** * Add a refspec to be pushed * * @param push The push object * @param refspec Refspec string * * @return 0 or an error code */ int git_push_add_refspec(git_push *push, const char *refspec); /** * Update remote tips after a push * * @param push The push object * @param callbacks the callbacks to use for this connection * * @return 0 or an error code */ int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks); /** * Perform the push * * This function will return an error in case of a protocol error or * the server being unable to unpack the data we sent. * * The return value does not reflect whether the server accepted or * refused any reference updates. Use `git_push_status_foreach()` in * order to find out which updates were accepted or rejected. * * @param push The push object * @param callbacks the callbacks to use for this connection * * @return 0 or an error code */ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks); /** * Invoke callback `cb' on each status entry * * For each of the updated references, we receive a status report in the * form of `ok refs/heads/master` or `ng refs/heads/master `. * `msg != NULL` means the reference has not been updated for the given * reason. * * Return a non-zero value from the callback to stop the loop. * * @param push The push object * @param cb The callback to call on each object * @param data The payload passed to the callback * * @return 0 on success, non-zero callback return value, or error code */ int git_push_status_foreach(git_push *push, int (*cb)(const char *ref, const char *msg, void *data), void *data); /** * Free the given push object * * @param push The push object */ void git_push_free(git_push *push); #endif git2r/src/libgit2/src/notes.h0000644000175000017500000000123614125111754015651 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_note_h__ #define INCLUDE_note_h__ #include "common.h" #include "git2/oid.h" #include "git2/types.h" #define GIT_NOTES_DEFAULT_REF "refs/notes/commits" #define GIT_NOTES_DEFAULT_MSG_ADD \ "Notes added by 'git_note_create' from libgit2" #define GIT_NOTES_DEFAULT_MSG_RM \ "Notes removed by 'git_note_remove' from libgit2" struct git_note { git_oid id; git_signature *author; git_signature *committer; char *message; }; #endif git2r/src/libgit2/src/tree-cache.h0000644000175000017500000000224614125111754016523 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_tree_cache_h__ #define INCLUDE_tree_cache_h__ #include "common.h" #include "pool.h" #include "buffer.h" #include "git2/oid.h" typedef struct git_tree_cache { struct git_tree_cache **children; size_t children_count; ssize_t entry_count; git_oid oid; size_t namelen; char name[GIT_FLEX_ARRAY]; } git_tree_cache; int git_tree_cache_write(git_buf *out, git_tree_cache *tree); int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool); void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path); const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path); int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool); /** * Read a tree as the root of the tree cache (like for `git read-tree`) */ int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool); void git_tree_cache_free(git_tree_cache *tree); #endif git2r/src/libgit2/src/worktree.c0000644000175000017500000003411014125111754016353 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "worktree.h" #include "git2/branch.h" #include "git2/commit.h" #include "git2/worktree.h" #include "repository.h" static bool is_worktree_dir(const char *dir) { git_buf buf = GIT_BUF_INIT; int error; if (git_buf_sets(&buf, dir) < 0) return -1; error = git_path_contains_file(&buf, "commondir") && git_path_contains_file(&buf, "gitdir") && git_path_contains_file(&buf, "HEAD"); git_buf_dispose(&buf); return error; } int git_worktree_list(git_strarray *wts, git_repository *repo) { git_vector worktrees = GIT_VECTOR_INIT; git_buf path = GIT_BUF_INIT; char *worktree; size_t i, len; int error; GIT_ASSERT_ARG(wts); GIT_ASSERT_ARG(repo); wts->count = 0; wts->strings = NULL; if ((error = git_buf_joinpath(&path, repo->commondir, "worktrees/")) < 0) goto exit; if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr)) goto exit; if ((error = git_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0) goto exit; len = path.size; git_vector_foreach(&worktrees, i, worktree) { git_buf_truncate(&path, len); git_buf_puts(&path, worktree); if (!is_worktree_dir(path.ptr)) { git_vector_remove(&worktrees, i); git__free(worktree); } } wts->strings = (char **)git_vector_detach(&wts->count, NULL, &worktrees); exit: git_buf_dispose(&path); return error; } char *git_worktree__read_link(const char *base, const char *file) { git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT; GIT_ASSERT_ARG_WITH_RETVAL(base, NULL); GIT_ASSERT_ARG_WITH_RETVAL(file, NULL); if (git_buf_joinpath(&path, base, file) < 0) goto err; if (git_futils_readbuffer(&buf, path.ptr) < 0) goto err; git_buf_dispose(&path); git_buf_rtrim(&buf); if (!git_path_is_relative(buf.ptr)) return git_buf_detach(&buf); if (git_buf_sets(&path, base) < 0) goto err; if (git_path_apply_relative(&path, buf.ptr) < 0) goto err; git_buf_dispose(&buf); return git_buf_detach(&path); err: git_buf_dispose(&buf); git_buf_dispose(&path); return NULL; } static int write_wtfile(const char *base, const char *file, const git_buf *buf) { git_buf path = GIT_BUF_INIT; int err; GIT_ASSERT_ARG(base); GIT_ASSERT_ARG(file); GIT_ASSERT_ARG(buf); if ((err = git_buf_joinpath(&path, base, file)) < 0) goto out; if ((err = git_futils_writebuffer(buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) goto out; out: git_buf_dispose(&path); return err; } static int open_worktree_dir(git_worktree **out, const char *parent, const char *dir, const char *name) { git_buf gitdir = GIT_BUF_INIT; git_worktree *wt = NULL; int error = 0; if (!is_worktree_dir(dir)) { error = -1; goto out; } if ((error = git_path_validate_workdir(NULL, dir)) < 0) goto out; if ((wt = git__calloc(1, sizeof(*wt))) == NULL) { error = -1; goto out; } if ((wt->name = git__strdup(name)) == NULL || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL || (parent && (wt->parent_path = git__strdup(parent)) == NULL) || (wt->worktree_path = git_path_dirname(wt->gitlink_path)) == NULL) { error = -1; goto out; } if ((error = git_path_prettify_dir(&gitdir, dir, NULL)) < 0) goto out; wt->gitdir_path = git_buf_detach(&gitdir); if ((error = git_worktree_is_locked(NULL, wt)) < 0) goto out; wt->locked = !!error; error = 0; *out = wt; out: if (error) git_worktree_free(wt); git_buf_dispose(&gitdir); return error; } int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name) { git_buf path = GIT_BUF_INIT; git_worktree *wt = NULL; int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); *out = NULL; if ((error = git_buf_join3(&path, '/', repo->commondir, "worktrees", name)) < 0) goto out; if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0) goto out; out: git_buf_dispose(&path); if (error) git_worktree_free(wt); return error; } int git_worktree_open_from_repository(git_worktree **out, git_repository *repo) { git_buf parent = GIT_BUF_INIT; const char *gitdir, *commondir; char *name = NULL; int error = 0; if (!git_repository_is_worktree(repo)) { git_error_set(GIT_ERROR_WORKTREE, "cannot open worktree of a non-worktree repo"); error = -1; goto out; } gitdir = git_repository_path(repo); commondir = git_repository_commondir(repo); if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0) goto out; /* The name is defined by the last component in '.git/worktree/%s' */ name = git_path_basename(gitdir); if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0) goto out; out: git__free(name); git_buf_dispose(&parent); return error; } void git_worktree_free(git_worktree *wt) { if (!wt) return; git__free(wt->commondir_path); git__free(wt->worktree_path); git__free(wt->gitlink_path); git__free(wt->gitdir_path); git__free(wt->parent_path); git__free(wt->name); git__free(wt); } int git_worktree_validate(const git_worktree *wt) { GIT_ASSERT_ARG(wt); if (!is_worktree_dir(wt->gitdir_path)) { git_error_set(GIT_ERROR_WORKTREE, "worktree gitdir ('%s') is not valid", wt->gitlink_path); return GIT_ERROR; } if (wt->parent_path && !git_path_exists(wt->parent_path)) { git_error_set(GIT_ERROR_WORKTREE, "worktree parent directory ('%s') does not exist ", wt->parent_path); return GIT_ERROR; } if (!git_path_exists(wt->commondir_path)) { git_error_set(GIT_ERROR_WORKTREE, "worktree common directory ('%s') does not exist ", wt->commondir_path); return GIT_ERROR; } if (!git_path_exists(wt->worktree_path)) { git_error_set(GIT_ERROR_WORKTREE, "worktree directory '%s' does not exist", wt->worktree_path); return GIT_ERROR; } return 0; } int git_worktree_add_options_init(git_worktree_add_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, git_worktree_add_options, GIT_WORKTREE_ADD_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_worktree_add_init_options(git_worktree_add_options *opts, unsigned int version) { return git_worktree_add_options_init(opts, version); } #endif int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *worktree, const git_worktree_add_options *opts) { git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT; git_reference *ref = NULL, *head = NULL; git_commit *commit = NULL; git_repository *wt = NULL; git_checkout_options coopts = GIT_CHECKOUT_OPTIONS_INIT; git_worktree_add_options wtopts = GIT_WORKTREE_ADD_OPTIONS_INIT; int err; GIT_ERROR_CHECK_VERSION( opts, GIT_WORKTREE_ADD_OPTIONS_VERSION, "git_worktree_add_options"); if (opts) memcpy(&wtopts, opts, sizeof(wtopts)); GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(worktree); *out = NULL; if (wtopts.ref) { if (!git_reference_is_branch(wtopts.ref)) { git_error_set(GIT_ERROR_WORKTREE, "reference is not a branch"); err = -1; goto out; } if (git_branch_is_checked_out(wtopts.ref)) { git_error_set(GIT_ERROR_WORKTREE, "reference is already checked out"); err = -1; goto out; } } /* Create gitdir directory ".git/worktrees/" */ if ((err = git_buf_joinpath(&gitdir, repo->commondir, "worktrees")) < 0) goto out; if (!git_path_exists(gitdir.ptr)) if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) goto out; if ((err = git_buf_joinpath(&gitdir, gitdir.ptr, name)) < 0) goto out; if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) goto out; if ((err = git_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0) goto out; /* Create worktree work dir */ if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0) goto out; if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0) goto out; if (wtopts.lock) { int fd; if ((err = git_buf_joinpath(&buf, gitdir.ptr, "locked")) < 0) goto out; if ((fd = p_creat(buf.ptr, 0644)) < 0) { err = fd; goto out; } p_close(fd); git_buf_clear(&buf); } /* Create worktree .git file */ if ((err = git_buf_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0) goto out; if ((err = write_wtfile(wddir.ptr, ".git", &buf)) < 0) goto out; /* Create gitdir files */ if ((err = git_path_prettify_dir(&buf, repo->commondir, NULL) < 0) || (err = git_buf_putc(&buf, '\n')) < 0 || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0) goto out; if ((err = git_buf_joinpath(&buf, wddir.ptr, ".git")) < 0 || (err = git_buf_putc(&buf, '\n')) < 0 || (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0) goto out; /* Set up worktree reference */ if (wtopts.ref) { if ((err = git_reference_dup(&ref, wtopts.ref)) < 0) goto out; } else { if ((err = git_repository_head(&head, repo)) < 0) goto out; if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0) goto out; if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0) goto out; } /* Set worktree's HEAD */ if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0) goto out; if ((err = git_repository_open(&wt, wddir.ptr)) < 0) goto out; /* Checkout worktree's HEAD */ coopts.checkout_strategy = GIT_CHECKOUT_FORCE; if ((err = git_checkout_head(wt, &coopts)) < 0) goto out; /* Load result */ if ((err = git_worktree_lookup(out, repo, name)) < 0) goto out; out: git_buf_dispose(&gitdir); git_buf_dispose(&wddir); git_buf_dispose(&buf); git_reference_free(ref); git_reference_free(head); git_commit_free(commit); git_repository_free(wt); return err; } int git_worktree_lock(git_worktree *wt, const char *reason) { git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(wt); if ((error = git_worktree_is_locked(NULL, wt)) < 0) goto out; if (error) { error = GIT_ELOCKED; goto out; } if ((error = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) goto out; if (reason) git_buf_attach_notowned(&buf, reason, strlen(reason)); if ((error = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) goto out; wt->locked = 1; out: git_buf_dispose(&path); return error; } int git_worktree_unlock(git_worktree *wt) { git_buf path = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(wt); if ((error = git_worktree_is_locked(NULL, wt)) < 0) return error; if (!error) return 1; if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0) return -1; if (p_unlink(path.ptr) != 0) { git_buf_dispose(&path); return -1; } wt->locked = 0; git_buf_dispose(&path); return 0; } int git_worktree_is_locked(git_buf *reason, const git_worktree *wt) { git_buf path = GIT_BUF_INIT; int error, locked; GIT_ASSERT_ARG(wt); if (reason) git_buf_clear(reason); if ((error = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) goto out; locked = git_path_exists(path.ptr); if (locked && reason && (error = git_futils_readbuffer(reason, path.ptr)) < 0) goto out; error = locked; out: git_buf_dispose(&path); return error; } const char *git_worktree_name(const git_worktree *wt) { GIT_ASSERT_ARG_WITH_RETVAL(wt, NULL); return wt->name; } const char *git_worktree_path(const git_worktree *wt) { GIT_ASSERT_ARG_WITH_RETVAL(wt, NULL); return wt->worktree_path; } int git_worktree_prune_options_init( git_worktree_prune_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, git_worktree_prune_options, GIT_WORKTREE_PRUNE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_worktree_prune_init_options(git_worktree_prune_options *opts, unsigned int version) { return git_worktree_prune_options_init(opts, version); } #endif int git_worktree_is_prunable(git_worktree *wt, git_worktree_prune_options *opts) { git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; GIT_ERROR_CHECK_VERSION( opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, "git_worktree_prune_options"); if (opts) memcpy(&popts, opts, sizeof(popts)); if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0) { git_buf reason = GIT_BUF_INIT; int error; if ((error = git_worktree_is_locked(&reason, wt)) < 0) return error; if (error) { if (!reason.size) git_buf_attach_notowned(&reason, "no reason given", 15); git_error_set(GIT_ERROR_WORKTREE, "not pruning locked working tree: '%s'", reason.ptr); git_buf_dispose(&reason); return 0; } } if ((popts.flags & GIT_WORKTREE_PRUNE_VALID) == 0 && git_worktree_validate(wt) == 0) { git_error_set(GIT_ERROR_WORKTREE, "not pruning valid working tree"); return 0; } return 1; } int git_worktree_prune(git_worktree *wt, git_worktree_prune_options *opts) { git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; git_buf path = GIT_BUF_INIT; char *wtpath; int err; GIT_ERROR_CHECK_VERSION( opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, "git_worktree_prune_options"); if (opts) memcpy(&popts, opts, sizeof(popts)); if (!git_worktree_is_prunable(wt, &popts)) { err = -1; goto out; } /* Delete gitdir in parent repository */ if ((err = git_buf_join3(&path, '/', wt->commondir_path, "worktrees", wt->name)) < 0) goto out; if (!git_path_exists(path.ptr)) { git_error_set(GIT_ERROR_WORKTREE, "worktree gitdir '%s' does not exist", path.ptr); err = -1; goto out; } if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0) goto out; /* Skip deletion of the actual working tree if it does * not exist or deletion was not requested */ if ((popts.flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 || !git_path_exists(wt->gitlink_path)) { goto out; } if ((wtpath = git_path_dirname(wt->gitlink_path)) == NULL) goto out; git_buf_attach(&path, wtpath, 0); if (!git_path_exists(path.ptr)) { git_error_set(GIT_ERROR_WORKTREE, "working tree '%s' does not exist", path.ptr); err = -1; goto out; } if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0) goto out; out: git_buf_dispose(&path); return err; } git2r/src/libgit2/src/diff_generate.c0000644000175000017500000013530114125111754017277 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "diff_generate.h" #include "diff.h" #include "patch_generate.h" #include "futils.h" #include "config.h" #include "attr_file.h" #include "filter.h" #include "pathspec.h" #include "index.h" #include "odb.h" #include "submodule.h" #define DIFF_FLAG_IS_SET(DIFF,FLAG) \ (((DIFF)->base.opts.flags & (FLAG)) != 0) #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \ (((DIFF)->base.opts.flags & (FLAG)) == 0) #define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->base.opts.flags = \ (VAL) ? ((DIFF)->base.opts.flags | (FLAG)) : \ ((DIFF)->base.opts.flags & ~(FLAG)) typedef struct { struct git_diff base; git_vector pathspec; uint32_t diffcaps; bool index_updated; } git_diff_generated; static git_diff_delta *diff_delta__alloc( git_diff_generated *diff, git_delta_t status, const char *path) { git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta)); if (!delta) return NULL; delta->old_file.path = git_pool_strdup(&diff->base.pool, path); if (delta->old_file.path == NULL) { git__free(delta); return NULL; } delta->new_file.path = delta->old_file.path; if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { switch (status) { case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break; case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break; default: break; /* leave other status values alone */ } } delta->status = status; return delta; } static int diff_insert_delta( git_diff_generated *diff, git_diff_delta *delta, const char *matched_pathspec) { int error = 0; if (diff->base.opts.notify_cb) { error = diff->base.opts.notify_cb( &diff->base, delta, matched_pathspec, diff->base.opts.payload); if (error) { git__free(delta); if (error > 0) /* positive value means to skip this delta */ return 0; else /* negative value means to cancel diff */ return git_error_set_after_callback_function(error, "git_diff"); } } if ((error = git_vector_insert(&diff->base.deltas, delta)) < 0) git__free(delta); return error; } static bool diff_pathspec_match( const char **matched_pathspec, git_diff_generated *diff, const git_index_entry *entry) { bool disable_pathspec_match = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH); /* If we're disabling fnmatch, then the iterator has already applied * the filters to the files for us and we don't have to do anything. * However, this only applies to *files* - the iterator will include * directories that we need to recurse into when not autoexpanding, * so we still need to apply the pathspec match to directories. */ if ((S_ISLNK(entry->mode) || S_ISREG(entry->mode)) && disable_pathspec_match) { *matched_pathspec = entry->path; return true; } return git_pathspec__match( &diff->pathspec, entry->path, disable_pathspec_match, DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE), matched_pathspec, NULL); } static int diff_delta__from_one( git_diff_generated *diff, git_delta_t status, const git_index_entry *oitem, const git_index_entry *nitem) { const git_index_entry *entry = nitem; bool has_old = false; git_diff_delta *delta; const char *matched_pathspec; GIT_ASSERT_ARG((oitem != NULL) ^ (nitem != NULL)); if (oitem) { entry = oitem; has_old = true; } if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) has_old = !has_old; if ((entry->flags & GIT_INDEX_ENTRY_VALID) != 0) return 0; if (status == GIT_DELTA_IGNORED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) return 0; if (status == GIT_DELTA_UNTRACKED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED)) return 0; if (status == GIT_DELTA_UNREADABLE && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE)) return 0; if (!diff_pathspec_match(&matched_pathspec, diff, entry)) return 0; delta = diff_delta__alloc(diff, status, entry->path); GIT_ERROR_CHECK_ALLOC(delta); /* This fn is just for single-sided diffs */ GIT_ASSERT(status != GIT_DELTA_MODIFIED); delta->nfiles = 1; if (has_old) { delta->old_file.mode = entry->mode; delta->old_file.size = entry->file_size; delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS; git_oid_cpy(&delta->old_file.id, &entry->id); delta->old_file.id_abbrev = GIT_OID_HEXSZ; } else /* ADDED, IGNORED, UNTRACKED */ { delta->new_file.mode = entry->mode; delta->new_file.size = entry->file_size; delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS; git_oid_cpy(&delta->new_file.id, &entry->id); delta->new_file.id_abbrev = GIT_OID_HEXSZ; } delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; if (has_old || !git_oid_is_zero(&delta->new_file.id)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; return diff_insert_delta(diff, delta, matched_pathspec); } static int diff_delta__from_two( git_diff_generated *diff, git_delta_t status, const git_index_entry *old_entry, uint32_t old_mode, const git_index_entry *new_entry, uint32_t new_mode, const git_oid *new_id, const char *matched_pathspec) { const git_oid *old_id = &old_entry->id; git_diff_delta *delta; const char *canonical_path = old_entry->path; if (status == GIT_DELTA_UNMODIFIED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED)) return 0; if (!new_id) new_id = &new_entry->id; if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { uint32_t temp_mode = old_mode; const git_index_entry *temp_entry = old_entry; const git_oid *temp_id = old_id; old_entry = new_entry; new_entry = temp_entry; old_mode = new_mode; new_mode = temp_mode; old_id = new_id; new_id = temp_id; } delta = diff_delta__alloc(diff, status, canonical_path); GIT_ERROR_CHECK_ALLOC(delta); delta->nfiles = 2; if (!git_index_entry_is_conflict(old_entry)) { delta->old_file.size = old_entry->file_size; delta->old_file.mode = old_mode; git_oid_cpy(&delta->old_file.id, old_id); delta->old_file.id_abbrev = GIT_OID_HEXSZ; delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID | GIT_DIFF_FLAG_EXISTS; } if (!git_index_entry_is_conflict(new_entry)) { git_oid_cpy(&delta->new_file.id, new_id); delta->new_file.id_abbrev = GIT_OID_HEXSZ; delta->new_file.size = new_entry->file_size; delta->new_file.mode = new_mode; delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS; delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS; if (!git_oid_is_zero(&new_entry->id)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; } return diff_insert_delta(diff, delta, matched_pathspec); } static git_diff_delta *diff_delta__last_for_item( git_diff_generated *diff, const git_index_entry *item) { git_diff_delta *delta = git_vector_last(&diff->base.deltas); if (!delta) return NULL; switch (delta->status) { case GIT_DELTA_UNMODIFIED: case GIT_DELTA_DELETED: if (git_oid__cmp(&delta->old_file.id, &item->id) == 0) return delta; break; case GIT_DELTA_ADDED: if (git_oid__cmp(&delta->new_file.id, &item->id) == 0) return delta; break; case GIT_DELTA_UNREADABLE: case GIT_DELTA_UNTRACKED: if (diff->base.strcomp(delta->new_file.path, item->path) == 0 && git_oid__cmp(&delta->new_file.id, &item->id) == 0) return delta; break; case GIT_DELTA_MODIFIED: if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 || (delta->new_file.mode == item->mode && git_oid__cmp(&delta->new_file.id, &item->id) == 0)) return delta; break; default: break; } return NULL; } static char *diff_strdup_prefix(git_pool *pool, const char *prefix) { size_t len = strlen(prefix); /* append '/' at end if needed */ if (len > 0 && prefix[len - 1] != '/') return git_pool_strcat(pool, prefix, "/"); else return git_pool_strndup(pool, prefix, len + 1); } GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta) { return delta->old_file.path ? delta->old_file.path : delta->new_file.path; } static int diff_delta_i2w_cmp(const void *a, const void *b) { const git_diff_delta *da = a, *db = b; int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db)); return val ? val : ((int)da->status - (int)db->status); } static int diff_delta_i2w_casecmp(const void *a, const void *b) { const git_diff_delta *da = a, *db = b; int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db)); return val ? val : ((int)da->status - (int)db->status); } bool git_diff_delta__should_skip( const git_diff_options *opts, const git_diff_delta *delta) { uint32_t flags = opts ? opts->flags : 0; if (delta->status == GIT_DELTA_UNMODIFIED && (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) return true; if (delta->status == GIT_DELTA_IGNORED && (flags & GIT_DIFF_INCLUDE_IGNORED) == 0) return true; if (delta->status == GIT_DELTA_UNTRACKED && (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) return true; if (delta->status == GIT_DELTA_UNREADABLE && (flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0) return true; return false; } static const char *diff_mnemonic_prefix( git_iterator_t type, bool left_side) { const char *pfx = ""; switch (type) { case GIT_ITERATOR_EMPTY: pfx = "c"; break; case GIT_ITERATOR_TREE: pfx = "c"; break; case GIT_ITERATOR_INDEX: pfx = "i"; break; case GIT_ITERATOR_WORKDIR: pfx = "w"; break; case GIT_ITERATOR_FS: pfx = left_side ? "1" : "2"; break; default: break; } /* note: without a deeper look at pathspecs, there is no easy way * to get the (o)bject / (w)ork tree mnemonics working... */ return pfx; } static void diff_set_ignore_case(git_diff *diff, bool ignore_case) { if (!ignore_case) { diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE; diff->strcomp = git__strcmp; diff->strncomp = git__strncmp; diff->pfxcomp = git__prefixcmp; diff->entrycomp = git_diff__entry_cmp; git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp); } else { diff->opts.flags |= GIT_DIFF_IGNORE_CASE; diff->strcomp = git__strcasecmp; diff->strncomp = git__strncasecmp; diff->pfxcomp = git__prefixcmp_icase; diff->entrycomp = git_diff__entry_icmp; git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp); } git_vector_sort(&diff->deltas); } static void diff_generated_free(git_diff *d) { git_diff_generated *diff = (git_diff_generated *)d; git_attr_session__free(&diff->base.attrsession); git_vector_free_deep(&diff->base.deltas); git_pathspec__vfree(&diff->pathspec); git_pool_clear(&diff->base.pool); git__memzero(diff, sizeof(*diff)); git__free(diff); } static git_diff_generated *diff_generated_alloc( git_repository *repo, git_iterator *old_iter, git_iterator *new_iter) { git_diff_generated *diff; git_diff_options dflt = GIT_DIFF_OPTIONS_INIT; GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL); GIT_ASSERT_ARG_WITH_RETVAL(old_iter, NULL); GIT_ASSERT_ARG_WITH_RETVAL(new_iter, NULL); if ((diff = git__calloc(1, sizeof(git_diff_generated))) == NULL) return NULL; GIT_REFCOUNT_INC(&diff->base); diff->base.type = GIT_DIFF_TYPE_GENERATED; diff->base.repo = repo; diff->base.old_src = old_iter->type; diff->base.new_src = new_iter->type; diff->base.patch_fn = git_patch_generated_from_diff; diff->base.free_fn = diff_generated_free; git_attr_session__init(&diff->base.attrsession, repo); memcpy(&diff->base.opts, &dflt, sizeof(git_diff_options)); if (git_pool_init(&diff->base.pool, 1) < 0 || git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) { git_diff_free(&diff->base); return NULL; } /* Use case-insensitive compare if either iterator has * the ignore_case bit set */ diff_set_ignore_case( &diff->base, git_iterator_ignore_case(old_iter) || git_iterator_ignore_case(new_iter)); return diff; } static int diff_generated_apply_options( git_diff_generated *diff, const git_diff_options *opts) { git_config *cfg = NULL; git_repository *repo = diff->base.repo; git_pool *pool = &diff->base.pool; int val; if (opts) { /* copy user options (except case sensitivity info from iterators) */ bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE); memcpy(&diff->base.opts, opts, sizeof(diff->base.opts)); DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase); /* initialize pathspec from options */ if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0) return -1; } /* flag INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)) diff->base.opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE; /* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT)) diff->base.opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; /* load config values that affect diff behavior */ if ((val = git_repository_config_snapshot(&cfg, repo)) < 0) return val; if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_SYMLINKS) && val) diff->diffcaps |= GIT_DIFFCAPS_HAS_SYMLINKS; if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_IGNORESTAT) && val) diff->diffcaps |= GIT_DIFFCAPS_IGNORE_STAT; if ((diff->base.opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 && !git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_FILEMODE) && val) diff->diffcaps |= GIT_DIFFCAPS_TRUST_MODE_BITS; if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_TRUSTCTIME) && val) diff->diffcaps |= GIT_DIFFCAPS_TRUST_CTIME; /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { int context = git_config__get_int_force(cfg, "diff.context", 3); diff->base.opts.context_lines = context >= 0 ? (uint32_t)context : 3; /* add other defaults here */ } /* Reverse src info if diff is reversed */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { git_iterator_t tmp_src = diff->base.old_src; diff->base.old_src = diff->base.new_src; diff->base.new_src = tmp_src; } /* Unset UPDATE_INDEX unless diffing workdir and index */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && (!(diff->base.old_src == GIT_ITERATOR_WORKDIR || diff->base.new_src == GIT_ITERATOR_WORKDIR) || !(diff->base.old_src == GIT_ITERATOR_INDEX || diff->base.new_src == GIT_ITERATOR_INDEX))) diff->base.opts.flags &= ~GIT_DIFF_UPDATE_INDEX; /* if ignore_submodules not explicitly set, check diff config */ if (diff->base.opts.ignore_submodules <= 0) { git_config_entry *entry; git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true); if (entry && git_submodule_parse_ignore( &diff->base.opts.ignore_submodules, entry->value) < 0) git_error_clear(); git_config_entry_free(entry); } /* if either prefix is not set, figure out appropriate value */ if (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) { const char *use_old = DIFF_OLD_PREFIX_DEFAULT; const char *use_new = DIFF_NEW_PREFIX_DEFAULT; if (git_config__get_bool_force(cfg, "diff.noprefix", 0)) use_old = use_new = ""; else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) { use_old = diff_mnemonic_prefix(diff->base.old_src, true); use_new = diff_mnemonic_prefix(diff->base.new_src, false); } if (!diff->base.opts.old_prefix) diff->base.opts.old_prefix = use_old; if (!diff->base.opts.new_prefix) diff->base.opts.new_prefix = use_new; } /* strdup prefix from pool so we're not dependent on external data */ diff->base.opts.old_prefix = diff_strdup_prefix(pool, diff->base.opts.old_prefix); diff->base.opts.new_prefix = diff_strdup_prefix(pool, diff->base.opts.new_prefix); if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { const char *tmp_prefix = diff->base.opts.old_prefix; diff->base.opts.old_prefix = diff->base.opts.new_prefix; diff->base.opts.new_prefix = tmp_prefix; } git_config_free(cfg); /* check strdup results for error */ return (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) ? -1 : 0; } int git_diff__oid_for_file( git_oid *out, git_diff *diff, const char *path, uint16_t mode, git_object_size_t size) { git_index_entry entry; if (size > UINT32_MAX) { git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'", path); return -1; } memset(&entry, 0, sizeof(entry)); entry.mode = mode; entry.file_size = (uint32_t)size; entry.path = (char *)path; return git_diff__oid_for_entry(out, diff, &entry, mode, NULL); } int git_diff__oid_for_entry( git_oid *out, git_diff *d, const git_index_entry *src, uint16_t mode, const git_oid *update_match) { git_diff_generated *diff; git_buf full_path = GIT_BUF_INIT; git_index_entry entry = *src; git_filter_list *fl = NULL; int error = 0; GIT_ASSERT(d->type == GIT_DIFF_TYPE_GENERATED); diff = (git_diff_generated *)d; memset(out, 0, sizeof(*out)); if (git_repository_workdir_path(&full_path, diff->base.repo, entry.path) < 0) return -1; if (!mode) { struct stat st; diff->base.perf.stat_calls++; if (p_stat(full_path.ptr, &st) < 0) { error = git_path_set_error(errno, entry.path, "stat"); git_buf_dispose(&full_path); return error; } git_index_entry__init_from_stat(&entry, &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0); } /* calculate OID for file if possible */ if (S_ISGITLINK(mode)) { git_submodule *sm; if (!git_submodule_lookup(&sm, diff->base.repo, entry.path)) { const git_oid *sm_oid = git_submodule_wd_id(sm); if (sm_oid) git_oid_cpy(out, sm_oid); git_submodule_free(sm); } else { /* if submodule lookup failed probably just in an intermediate * state where some init hasn't happened, so ignore the error */ git_error_clear(); } } else if (S_ISLNK(mode)) { error = git_odb__hashlink(out, full_path.ptr); diff->base.perf.oid_calculations++; } else if (!git__is_sizet(entry.file_size)) { git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'", entry.path); error = -1; } else if (!(error = git_filter_list_load(&fl, diff->base.repo, NULL, entry.path, GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE))) { int fd = git_futils_open_ro(full_path.ptr); if (fd < 0) error = fd; else { error = git_odb__hashfd_filtered( out, fd, (size_t)entry.file_size, GIT_OBJECT_BLOB, fl); p_close(fd); diff->base.perf.oid_calculations++; } git_filter_list_free(fl); } /* update index for entry if requested */ if (!error && update_match && git_oid_equal(out, update_match)) { git_index *idx; git_index_entry updated_entry; memcpy(&updated_entry, &entry, sizeof(git_index_entry)); updated_entry.mode = mode; git_oid_cpy(&updated_entry.id, out); if (!(error = git_repository_index__weakptr(&idx, diff->base.repo))) { error = git_index_add(idx, &updated_entry); diff->index_updated = true; } } git_buf_dispose(&full_path); return error; } typedef struct { git_repository *repo; git_iterator *old_iter; git_iterator *new_iter; const git_index_entry *oitem; const git_index_entry *nitem; git_strmap *submodule_cache; bool submodule_cache_initialized; } diff_in_progress; #define MODE_BITS_MASK 0000777 static int maybe_modified_submodule( git_delta_t *status, git_oid *found_oid, git_diff_generated *diff, diff_in_progress *info) { int error = 0; git_submodule *sub; unsigned int sm_status = 0; git_submodule_ignore_t ign = diff->base.opts.ignore_submodules; git_strmap *submodule_cache = NULL; *status = GIT_DELTA_UNMODIFIED; if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) || ign == GIT_SUBMODULE_IGNORE_ALL) return 0; if (diff->base.repo->submodule_cache != NULL) { submodule_cache = diff->base.repo->submodule_cache; } else { if (!info->submodule_cache_initialized) { info->submodule_cache_initialized = true; /* * Try to cache the submodule information to avoid having to parse it for * every submodule. It is okay if it fails, the cache will still be NULL * and the submodules will be attempted to be looked up individually. */ git_submodule_cache_init(&info->submodule_cache, diff->base.repo); } submodule_cache = info->submodule_cache; } if ((error = git_submodule__lookup_with_cache( &sub, diff->base.repo, info->nitem->path, submodule_cache)) < 0) { /* GIT_EEXISTS means dir with .git in it was found - ignore it */ if (error == GIT_EEXISTS) { git_error_clear(); error = 0; } return error; } if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL) /* ignore it */; else if ((error = git_submodule__status( &sm_status, NULL, NULL, found_oid, sub, ign)) < 0) /* return error below */; /* check IS_WD_UNMODIFIED because this case is only used * when the new side of the diff is the working directory */ else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)) *status = GIT_DELTA_MODIFIED; /* now that we have a HEAD OID, check if HEAD moved */ else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 && !git_oid_equal(&info->oitem->id, found_oid)) *status = GIT_DELTA_MODIFIED; git_submodule_free(sub); return error; } static int maybe_modified( git_diff_generated *diff, diff_in_progress *info) { git_oid noid; git_delta_t status = GIT_DELTA_MODIFIED; const git_index_entry *oitem = info->oitem; const git_index_entry *nitem = info->nitem; unsigned int omode = oitem->mode; unsigned int nmode = nitem->mode; bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_WORKDIR); bool modified_uncertain = false; const char *matched_pathspec; int error = 0; if (!diff_pathspec_match(&matched_pathspec, diff, oitem)) return 0; memset(&noid, 0, sizeof(noid)); /* on platforms with no symlinks, preserve mode of existing symlinks */ if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir && !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS)) nmode = omode; /* on platforms with no execmode, just preserve old mode */ if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) && (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) && new_is_workdir) nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK); /* if one side is a conflict, mark the whole delta as conflicted */ if (git_index_entry_is_conflict(oitem) || git_index_entry_is_conflict(nitem)) { status = GIT_DELTA_CONFLICTED; /* support "assume unchanged" (poorly, b/c we still stat everything) */ } else if ((oitem->flags & GIT_INDEX_ENTRY_VALID) != 0) { status = GIT_DELTA_UNMODIFIED; /* support "skip worktree" index bit */ } else if ((oitem->flags_extended & GIT_INDEX_ENTRY_SKIP_WORKTREE) != 0) { status = GIT_DELTA_UNMODIFIED; /* if basic type of file changed, then split into delete and add */ } else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) { status = GIT_DELTA_TYPECHANGE; } else if (nmode == GIT_FILEMODE_UNREADABLE) { if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL))) error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, NULL, nitem); return error; } else { if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL))) error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem); return error; } /* if oids and modes match (and are valid), then file is unmodified */ } else if (git_oid_equal(&oitem->id, &nitem->id) && omode == nmode && !git_oid_is_zero(&oitem->id)) { status = GIT_DELTA_UNMODIFIED; /* if we have an unknown OID and a workdir iterator, then check some * circumstances that can accelerate things or need special handling */ } else if (git_oid_is_zero(&nitem->id) && new_is_workdir) { bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0); git_index *index = git_iterator_index(info->new_iter); status = GIT_DELTA_UNMODIFIED; if (S_ISGITLINK(nmode)) { if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0) return error; } /* if the stat data looks different, then mark modified - this just * means that the OID will be recalculated below to confirm change */ else if (omode != nmode || oitem->file_size != nitem->file_size) { status = GIT_DELTA_MODIFIED; modified_uncertain = (oitem->file_size <= 0 && nitem->file_size > 0); } else if (!git_index_time_eq(&oitem->mtime, &nitem->mtime) || (use_ctime && !git_index_time_eq(&oitem->ctime, &nitem->ctime)) || oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid || git_index_entry_newer_than_index(nitem, index)) { status = GIT_DELTA_MODIFIED; modified_uncertain = true; } /* if mode is GITLINK and submodules are ignored, then skip */ } else if (S_ISGITLINK(nmode) && DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) { status = GIT_DELTA_UNMODIFIED; } /* if we got here and decided that the files are modified, but we * haven't calculated the OID of the new item, then calculate it now */ if (modified_uncertain && git_oid_is_zero(&nitem->id)) { const git_oid *update_check = DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ? &oitem->id : NULL; if ((error = git_diff__oid_for_entry( &noid, &diff->base, nitem, nmode, update_check)) < 0) return error; /* if oid matches, then mark unmodified (except submodules, where * the filesystem content may be modified even if the oid still * matches between the index and the workdir HEAD) */ if (omode == nmode && !S_ISGITLINK(omode) && git_oid_equal(&oitem->id, &noid)) status = GIT_DELTA_UNMODIFIED; } /* If we want case changes, then break this into a delete of the old * and an add of the new so that consumers can act accordingly (eg, * checkout will update the case on disk.) */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE) && DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) && strcmp(oitem->path, nitem->path) != 0) { if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL))) error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem); return error; } return diff_delta__from_two( diff, status, oitem, omode, nitem, nmode, git_oid_is_zero(&noid) ? NULL : &noid, matched_pathspec); } static bool entry_is_prefixed( git_diff_generated *diff, const git_index_entry *item, const git_index_entry *prefix_item) { size_t pathlen; if (!item || diff->base.pfxcomp(item->path, prefix_item->path) != 0) return false; pathlen = strlen(prefix_item->path); return (prefix_item->path[pathlen - 1] == '/' || item->path[pathlen] == '\0' || item->path[pathlen] == '/'); } static int iterator_current( const git_index_entry **entry, git_iterator *iterator) { int error; if ((error = git_iterator_current(entry, iterator)) == GIT_ITEROVER) { *entry = NULL; error = 0; } return error; } static int iterator_advance( const git_index_entry **entry, git_iterator *iterator) { const git_index_entry *prev_entry = *entry; int cmp, error; /* if we're looking for conflicts, we only want to report * one conflict for each file, instead of all three sides. * so if this entry is a conflict for this file, and the * previous one was a conflict for the same file, skip it. */ while ((error = git_iterator_advance(entry, iterator)) == 0) { if (!(iterator->flags & GIT_ITERATOR_INCLUDE_CONFLICTS) || !git_index_entry_is_conflict(prev_entry) || !git_index_entry_is_conflict(*entry)) break; cmp = (iterator->flags & GIT_ITERATOR_IGNORE_CASE) ? strcasecmp(prev_entry->path, (*entry)->path) : strcmp(prev_entry->path, (*entry)->path); if (cmp) break; } if (error == GIT_ITEROVER) { *entry = NULL; error = 0; } return error; } static int iterator_advance_into( const git_index_entry **entry, git_iterator *iterator) { int error; if ((error = git_iterator_advance_into(entry, iterator)) == GIT_ITEROVER) { *entry = NULL; error = 0; } return error; } static int iterator_advance_over( const git_index_entry **entry, git_iterator_status_t *status, git_iterator *iterator) { int error = git_iterator_advance_over(entry, status, iterator); if (error == GIT_ITEROVER) { *entry = NULL; error = 0; } return error; } static int handle_unmatched_new_item( git_diff_generated *diff, diff_in_progress *info) { int error = 0; const git_index_entry *nitem = info->nitem; git_delta_t delta_type = GIT_DELTA_UNTRACKED; bool contains_oitem; /* check if this is a prefix of the other side */ contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); /* update delta_type if this item is conflicted */ if (git_index_entry_is_conflict(nitem)) delta_type = GIT_DELTA_CONFLICTED; /* update delta_type if this item is ignored */ else if (git_iterator_current_is_ignored(info->new_iter)) delta_type = GIT_DELTA_IGNORED; if (nitem->mode == GIT_FILEMODE_TREE) { bool recurse_into_dir = contains_oitem; /* check if user requests recursion into this type of dir */ recurse_into_dir = contains_oitem || (delta_type == GIT_DELTA_UNTRACKED && DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) || (delta_type == GIT_DELTA_IGNORED && DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)); /* do not advance into directories that contain a .git file */ if (recurse_into_dir && !contains_oitem) { git_buf *full = NULL; if (git_iterator_current_workdir_path(&full, info->new_iter) < 0) return -1; if (full && git_path_contains(full, DOT_GIT)) { /* TODO: warning if not a valid git repository */ recurse_into_dir = false; } } /* still have to look into untracked directories to match core git - * with no untracked files, directory is treated as ignored */ if (!recurse_into_dir && delta_type == GIT_DELTA_UNTRACKED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS)) { git_diff_delta *last; git_iterator_status_t untracked_state; /* attempt to insert record for this directory */ if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0) return error; /* if delta wasn't created (because of rules), just skip ahead */ last = diff_delta__last_for_item(diff, nitem); if (!last) return iterator_advance(&info->nitem, info->new_iter); /* iterate into dir looking for an actual untracked file */ if ((error = iterator_advance_over( &info->nitem, &untracked_state, info->new_iter)) < 0) return error; /* if we found nothing that matched our pathlist filter, exclude */ if (untracked_state == GIT_ITERATOR_STATUS_FILTERED) { git_vector_pop(&diff->base.deltas); git__free(last); } /* if we found nothing or just ignored items, update the record */ if (untracked_state == GIT_ITERATOR_STATUS_IGNORED || untracked_state == GIT_ITERATOR_STATUS_EMPTY) { last->status = GIT_DELTA_IGNORED; /* remove the record if we don't want ignored records */ if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) { git_vector_pop(&diff->base.deltas); git__free(last); } } return 0; } /* try to advance into directory if necessary */ if (recurse_into_dir) { error = iterator_advance_into(&info->nitem, info->new_iter); /* if directory is empty, can't advance into it, so skip it */ if (error == GIT_ENOTFOUND) { git_error_clear(); error = iterator_advance(&info->nitem, info->new_iter); } return error; } } else if (delta_type == GIT_DELTA_IGNORED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) && git_iterator_current_tree_is_ignored(info->new_iter)) /* item contained in ignored directory, so skip over it */ return iterator_advance(&info->nitem, info->new_iter); else if (info->new_iter->type != GIT_ITERATOR_WORKDIR) { if (delta_type != GIT_DELTA_CONFLICTED) delta_type = GIT_DELTA_ADDED; } else if (nitem->mode == GIT_FILEMODE_COMMIT) { /* ignore things that are not actual submodules */ if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) { git_error_clear(); delta_type = GIT_DELTA_IGNORED; /* if this contains a tracked item, treat as normal TREE */ if (contains_oitem) { error = iterator_advance_into(&info->nitem, info->new_iter); if (error != GIT_ENOTFOUND) return error; git_error_clear(); return iterator_advance(&info->nitem, info->new_iter); } } } else if (nitem->mode == GIT_FILEMODE_UNREADABLE) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED)) delta_type = GIT_DELTA_UNTRACKED; else delta_type = GIT_DELTA_UNREADABLE; } /* Actually create the record for this item if necessary */ if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0) return error; /* If user requested TYPECHANGE records, then check for that instead of * just generating an ADDED/UNTRACKED record */ if (delta_type != GIT_DELTA_IGNORED && DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) && contains_oitem) { /* this entry was prefixed with a tree - make TYPECHANGE */ git_diff_delta *last = diff_delta__last_for_item(diff, nitem); if (last) { last->status = GIT_DELTA_TYPECHANGE; last->old_file.mode = GIT_FILEMODE_TREE; } } return iterator_advance(&info->nitem, info->new_iter); } static int handle_unmatched_old_item( git_diff_generated *diff, diff_in_progress *info) { git_delta_t delta_type = GIT_DELTA_DELETED; int error; /* update delta_type if this item is conflicted */ if (git_index_entry_is_conflict(info->oitem)) delta_type = GIT_DELTA_CONFLICTED; if ((error = diff_delta__from_one(diff, delta_type, info->oitem, NULL)) < 0) return error; /* if we are generating TYPECHANGE records then check for that * instead of just generating a DELETE record */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) && entry_is_prefixed(diff, info->nitem, info->oitem)) { /* this entry has become a tree! convert to TYPECHANGE */ git_diff_delta *last = diff_delta__last_for_item(diff, info->oitem); if (last) { last->status = GIT_DELTA_TYPECHANGE; last->new_file.mode = GIT_FILEMODE_TREE; } /* If new_iter is a workdir iterator, then this situation * will certainly be followed by a series of untracked items. * Unless RECURSE_UNTRACKED_DIRS is set, skip over them... */ if (S_ISDIR(info->nitem->mode) && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) return iterator_advance(&info->nitem, info->new_iter); } return iterator_advance(&info->oitem, info->old_iter); } static int handle_matched_item( git_diff_generated *diff, diff_in_progress *info) { int error = 0; if ((error = maybe_modified(diff, info)) < 0) return error; if (!(error = iterator_advance(&info->oitem, info->old_iter))) error = iterator_advance(&info->nitem, info->new_iter); return error; } int git_diff__from_iterators( git_diff **out, git_repository *repo, git_iterator *old_iter, git_iterator *new_iter, const git_diff_options *opts) { git_diff_generated *diff; diff_in_progress info = {0}; int error = 0; *out = NULL; diff = diff_generated_alloc(repo, old_iter, new_iter); GIT_ERROR_CHECK_ALLOC(diff); info.repo = repo; info.old_iter = old_iter; info.new_iter = new_iter; /* make iterators have matching icase behavior */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) { if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 || (error = git_iterator_set_ignore_case(new_iter, true)) < 0) goto cleanup; } /* finish initialization */ if ((error = diff_generated_apply_options(diff, opts)) < 0) goto cleanup; if ((error = iterator_current(&info.oitem, old_iter)) < 0 || (error = iterator_current(&info.nitem, new_iter)) < 0) goto cleanup; /* run iterators building diffs */ while (!error && (info.oitem || info.nitem)) { int cmp; /* report progress */ if (opts && opts->progress_cb) { if ((error = opts->progress_cb(&diff->base, info.oitem ? info.oitem->path : NULL, info.nitem ? info.nitem->path : NULL, opts->payload))) break; } cmp = info.oitem ? (info.nitem ? diff->base.entrycomp(info.oitem, info.nitem) : -1) : 1; /* create DELETED records for old items not matched in new */ if (cmp < 0) error = handle_unmatched_old_item(diff, &info); /* create ADDED, TRACKED, or IGNORED records for new items not * matched in old (and/or descend into directories as needed) */ else if (cmp > 0) error = handle_unmatched_new_item(diff, &info); /* otherwise item paths match, so create MODIFIED record * (or ADDED and DELETED pair if type changed) */ else error = handle_matched_item(diff, &info); } diff->base.perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls; cleanup: if (!error) *out = &diff->base; else git_diff_free(&diff->base); if (info.submodule_cache) git_submodule_cache_free(info.submodule_cache); return error; } static int diff_prepare_iterator_opts(char **prefix, git_iterator_options *a, int aflags, git_iterator_options *b, int bflags, const git_diff_options *opts) { GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); *prefix = NULL; if (opts && (opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) { a->pathlist.strings = opts->pathspec.strings; a->pathlist.count = opts->pathspec.count; b->pathlist.strings = opts->pathspec.strings; b->pathlist.count = opts->pathspec.count; } else if (opts) { *prefix = git_pathspec_prefix(&opts->pathspec); GIT_ERROR_CHECK_ALLOC(prefix); } a->flags = aflags; b->flags = bflags; a->start = b->start = *prefix; a->end = b->end = *prefix; return 0; } int git_diff_tree_to_tree( git_diff **out, git_repository *repo, git_tree *old_tree, git_tree *new_tree, const git_diff_options *opts) { git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE; git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, b_opts = GIT_ITERATOR_OPTIONS_INIT; git_iterator *a = NULL, *b = NULL; git_diff *diff = NULL; char *prefix = NULL; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; /* for tree to tree diff, be case sensitive even if the index is * currently case insensitive, unless the user explicitly asked * for case insensitivity */ if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0) iflag = GIT_ITERATOR_IGNORE_CASE; if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, iflag, &b_opts, iflag, opts)) < 0 || (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 || (error = git_iterator_for_tree(&b, new_tree, &b_opts)) < 0 || (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0) goto out; *out = diff; diff = NULL; out: git_iterator_free(a); git_iterator_free(b); git_diff_free(diff); git__free(prefix); return error; } static int diff_load_index(git_index **index, git_repository *repo) { int error = git_repository_index__weakptr(index, repo); /* reload the repository index when user did not pass one in */ if (!error && git_index_read(*index, false) < 0) git_error_clear(); return error; } int git_diff_tree_to_index( git_diff **out, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts) { git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_CONFLICTS; git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, b_opts = GIT_ITERATOR_OPTIONS_INIT; git_iterator *a = NULL, *b = NULL; git_diff *diff = NULL; char *prefix = NULL; bool index_ignore_case = false; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; if (!index && (error = diff_load_index(&index, repo)) < 0) return error; index_ignore_case = index->ignore_case; if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, iflag, &b_opts, iflag, opts)) < 0 || (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 || (error = git_iterator_for_index(&b, repo, index, &b_opts)) < 0 || (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0) goto out; /* if index is in case-insensitive order, re-sort deltas to match */ if (index_ignore_case) diff_set_ignore_case(diff, true); *out = diff; diff = NULL; out: git_iterator_free(a); git_iterator_free(b); git_diff_free(diff); git__free(prefix); return error; } int git_diff_index_to_workdir( git_diff **out, git_repository *repo, git_index *index, const git_diff_options *opts) { git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, b_opts = GIT_ITERATOR_OPTIONS_INIT; git_iterator *a = NULL, *b = NULL; git_diff *diff = NULL; char *prefix = NULL; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; if (!index && (error = diff_load_index(&index, repo)) < 0) return error; if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, GIT_ITERATOR_INCLUDE_CONFLICTS, &b_opts, GIT_ITERATOR_DONT_AUTOEXPAND, opts)) < 0 || (error = git_iterator_for_index(&a, repo, index, &a_opts)) < 0 || (error = git_iterator_for_workdir(&b, repo, index, NULL, &b_opts)) < 0 || (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0) goto out; if ((diff->opts.flags & GIT_DIFF_UPDATE_INDEX) && ((git_diff_generated *)diff)->index_updated) if ((error = git_index_write(index)) < 0) goto out; *out = diff; diff = NULL; out: git_iterator_free(a); git_iterator_free(b); git_diff_free(diff); git__free(prefix); return error; } int git_diff_tree_to_workdir( git_diff **out, git_repository *repo, git_tree *old_tree, const git_diff_options *opts) { git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, b_opts = GIT_ITERATOR_OPTIONS_INIT; git_iterator *a = NULL, *b = NULL; git_diff *diff = NULL; char *prefix = NULL; git_index *index; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, 0, &b_opts, GIT_ITERATOR_DONT_AUTOEXPAND, opts) < 0) || (error = git_repository_index__weakptr(&index, repo)) < 0 || (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 || (error = git_iterator_for_workdir(&b, repo, index, old_tree, &b_opts)) < 0 || (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0) goto out; *out = diff; diff = NULL; out: git_iterator_free(a); git_iterator_free(b); git_diff_free(diff); git__free(prefix); return error; } int git_diff_tree_to_workdir_with_index( git_diff **out, git_repository *repo, git_tree *tree, const git_diff_options *opts) { git_diff *d1 = NULL, *d2 = NULL; git_index *index = NULL; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; if ((error = diff_load_index(&index, repo)) < 0) return error; if (!(error = git_diff_tree_to_index(&d1, repo, tree, index, opts)) && !(error = git_diff_index_to_workdir(&d2, repo, index, opts))) error = git_diff_merge(d1, d2); git_diff_free(d2); if (error) { git_diff_free(d1); d1 = NULL; } *out = d1; return error; } int git_diff_index_to_index( git_diff **out, git_repository *repo, git_index *old_index, git_index *new_index, const git_diff_options *opts) { git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, b_opts = GIT_ITERATOR_OPTIONS_INIT; git_iterator *a = NULL, *b = NULL; git_diff *diff = NULL; char *prefix = NULL; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(old_index); GIT_ASSERT_ARG(new_index); *out = NULL; if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, GIT_ITERATOR_DONT_IGNORE_CASE, &b_opts, GIT_ITERATOR_DONT_IGNORE_CASE, opts) < 0) || (error = git_iterator_for_index(&a, repo, old_index, &a_opts)) < 0 || (error = git_iterator_for_index(&b, repo, new_index, &b_opts)) < 0 || (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0) goto out; /* if index is in case-insensitive order, re-sort deltas to match */ if (old_index->ignore_case || new_index->ignore_case) diff_set_ignore_case(diff, true); *out = diff; diff = NULL; out: git_iterator_free(a); git_iterator_free(b); git_diff_free(diff); git__free(prefix); return error; } int git_diff__paired_foreach( git_diff *head2idx, git_diff *idx2wd, int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload), void *payload) { int cmp, error = 0; git_diff_delta *h2i, *i2w; size_t i, j, i_max, j_max; int (*strcomp)(const char *, const char *) = git__strcmp; bool h2i_icase, i2w_icase, icase_mismatch; i_max = head2idx ? head2idx->deltas.length : 0; j_max = idx2wd ? idx2wd->deltas.length : 0; if (!i_max && !j_max) return 0; /* At some point, tree-to-index diffs will probably never ignore case, * even if that isn't true now. Index-to-workdir diffs may or may not * ignore case, but the index filename for the idx2wd diff should * still be using the canonical case-preserving name. * * Therefore the main thing we need to do here is make sure the diffs * are traversed in a compatible order. To do this, we temporarily * resort a mismatched diff to get the order correct. * * In order to traverse renames in the index->workdir, we need to * ensure that we compare the index name on both sides, so we * always sort by the old name in the i2w list. */ h2i_icase = head2idx != NULL && git_diff_is_sorted_icase(head2idx); i2w_icase = idx2wd != NULL && git_diff_is_sorted_icase(idx2wd); icase_mismatch = (head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase); if (icase_mismatch && h2i_icase) { git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp); git_vector_sort(&head2idx->deltas); } if (i2w_icase && !icase_mismatch) { strcomp = git__strcasecmp; git_vector_set_cmp(&idx2wd->deltas, diff_delta_i2w_casecmp); git_vector_sort(&idx2wd->deltas); } else if (idx2wd != NULL) { git_vector_set_cmp(&idx2wd->deltas, diff_delta_i2w_cmp); git_vector_sort(&idx2wd->deltas); } for (i = 0, j = 0; i < i_max || j < j_max; ) { h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL; i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL; cmp = !i2w ? -1 : !h2i ? 1 : strcomp(h2i->new_file.path, i2w->old_file.path); if (cmp < 0) { i++; i2w = NULL; } else if (cmp > 0) { j++; h2i = NULL; } else { i++; j++; } if ((error = cb(h2i, i2w, payload)) != 0) { git_error_set_after_callback(error); break; } } /* restore case-insensitive delta sort */ if (icase_mismatch && h2i_icase) { git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp); git_vector_sort(&head2idx->deltas); } /* restore idx2wd sort by new path */ if (idx2wd != NULL) { git_vector_set_cmp(&idx2wd->deltas, i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp); git_vector_sort(&idx2wd->deltas); } return error; } int git_diff__commit( git_diff **out, git_repository *repo, const git_commit *commit, const git_diff_options *opts) { git_commit *parent = NULL; git_diff *commit_diff = NULL; git_tree *old_tree = NULL, *new_tree = NULL; size_t parents; int error = 0; *out = NULL; if ((parents = git_commit_parentcount(commit)) > 1) { char commit_oidstr[GIT_OID_HEXSZ + 1]; error = -1; git_error_set(GIT_ERROR_INVALID, "commit %s is a merge commit", git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); goto on_error; } if (parents > 0) if ((error = git_commit_parent(&parent, commit, 0)) < 0 || (error = git_commit_tree(&old_tree, parent)) < 0) goto on_error; if ((error = git_commit_tree(&new_tree, commit)) < 0 || (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0) goto on_error; *out = commit_diff; on_error: git_tree_free(new_tree); git_tree_free(old_tree); git_commit_free(parent); return error; } git2r/src/libgit2/src/alloc.c0000644000175000017500000000245014125111754015605 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "alloc.h" #include "runtime.h" #include "allocators/failalloc.h" #include "allocators/stdalloc.h" #include "allocators/win32_leakcheck.h" /* Fail any allocation until git_libgit2_init is called. */ git_allocator git__allocator = { git_failalloc_malloc, git_failalloc_calloc, git_failalloc_strdup, git_failalloc_strndup, git_failalloc_substrdup, git_failalloc_realloc, git_failalloc_reallocarray, git_failalloc_mallocarray, git_failalloc_free }; static int setup_default_allocator(void) { #if defined(GIT_WIN32_LEAKCHECK) return git_win32_leakcheck_init_allocator(&git__allocator); #else return git_stdalloc_init_allocator(&git__allocator); #endif } int git_allocator_global_init(void) { /* * We don't want to overwrite any allocator which has been set * before the init function is called. */ if (git__allocator.gmalloc != git_failalloc_malloc) return 0; return setup_default_allocator(); } int git_allocator_setup(git_allocator *allocator) { if (!allocator) return setup_default_allocator(); memcpy(&git__allocator, allocator, sizeof(*allocator)); return 0; } git2r/src/libgit2/src/hashsig.c0000644000175000017500000002024514125111754016143 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/sys/hashsig.h" #include "futils.h" #include "util.h" typedef uint32_t hashsig_t; typedef uint64_t hashsig_state; #define HASHSIG_SCALE 100 #define HASHSIG_MAX_RUN 80 #define HASHSIG_HASH_START INT64_C(0x012345678ABCDEF0) #define HASHSIG_HASH_SHIFT 5 #define HASHSIG_HASH_MIX(S,CH) \ (S) = ((S) << HASHSIG_HASH_SHIFT) - (S) + (hashsig_state)(CH) #define HASHSIG_HEAP_SIZE ((1 << 7) - 1) #define HASHSIG_HEAP_MIN_SIZE 4 typedef int (*hashsig_cmp)(const void *a, const void *b, void *); typedef struct { int size, asize; hashsig_cmp cmp; hashsig_t values[HASHSIG_HEAP_SIZE]; } hashsig_heap; struct git_hashsig { hashsig_heap mins; hashsig_heap maxs; size_t lines; git_hashsig_option_t opt; }; #define HEAP_LCHILD_OF(I) (((I)<<1)+1) #define HEAP_RCHILD_OF(I) (((I)<<1)+2) #define HEAP_PARENT_OF(I) (((I)-1)>>1) static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp) { h->size = 0; h->asize = HASHSIG_HEAP_SIZE; h->cmp = cmp; } static int hashsig_cmp_max(const void *a, const void *b, void *payload) { hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b; GIT_UNUSED(payload); return (av < bv) ? -1 : (av > bv) ? 1 : 0; } static int hashsig_cmp_min(const void *a, const void *b, void *payload) { hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b; GIT_UNUSED(payload); return (av > bv) ? -1 : (av < bv) ? 1 : 0; } static void hashsig_heap_up(hashsig_heap *h, int el) { int parent_el = HEAP_PARENT_OF(el); while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el], NULL) > 0) { hashsig_t t = h->values[el]; h->values[el] = h->values[parent_el]; h->values[parent_el] = t; el = parent_el; parent_el = HEAP_PARENT_OF(el); } } static void hashsig_heap_down(hashsig_heap *h, int el) { hashsig_t v, lv, rv; /* 'el < h->size / 2' tests if el is bottom row of heap */ while (el < h->size / 2) { int lel = HEAP_LCHILD_OF(el), rel = HEAP_RCHILD_OF(el), swapel; v = h->values[el]; lv = h->values[lel]; rv = h->values[rel]; if (h->cmp(&v, &lv, NULL) < 0 && h->cmp(&v, &rv, NULL) < 0) break; swapel = (h->cmp(&lv, &rv, NULL) < 0) ? lel : rel; h->values[el] = h->values[swapel]; h->values[swapel] = v; el = swapel; } } static void hashsig_heap_sort(hashsig_heap *h) { /* only need to do this at the end for signature comparison */ git__qsort_r(h->values, h->size, sizeof(hashsig_t), h->cmp, NULL); } static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val) { /* if heap is not full, insert new element */ if (h->size < h->asize) { h->values[h->size++] = val; hashsig_heap_up(h, h->size - 1); } /* if heap is full, pop top if new element should replace it */ else if (h->cmp(&val, &h->values[0], NULL) > 0) { h->size--; h->values[0] = h->values[h->size]; hashsig_heap_down(h, 0); } } typedef struct { int use_ignores; uint8_t ignore_ch[256]; } hashsig_in_progress; static int hashsig_in_progress_init( hashsig_in_progress *prog, git_hashsig *sig) { int i; /* no more than one can be set */ GIT_ASSERT(!(sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) || !(sig->opt & GIT_HASHSIG_SMART_WHITESPACE)); if (sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) { for (i = 0; i < 256; ++i) prog->ignore_ch[i] = git__isspace_nonlf(i); prog->use_ignores = 1; } else if (sig->opt & GIT_HASHSIG_SMART_WHITESPACE) { for (i = 0; i < 256; ++i) prog->ignore_ch[i] = git__isspace(i); prog->use_ignores = 1; } else { memset(prog, 0, sizeof(*prog)); } return 0; } static int hashsig_add_hashes( git_hashsig *sig, const uint8_t *data, size_t size, hashsig_in_progress *prog) { const uint8_t *scan = data, *end = data + size; hashsig_state state = HASHSIG_HASH_START; int use_ignores = prog->use_ignores, len; uint8_t ch; while (scan < end) { state = HASHSIG_HASH_START; for (len = 0; scan < end && len < HASHSIG_MAX_RUN; ) { ch = *scan; if (use_ignores) for (; scan < end && git__isspace_nonlf(ch); ch = *scan) ++scan; else if (sig->opt & (GIT_HASHSIG_IGNORE_WHITESPACE | GIT_HASHSIG_SMART_WHITESPACE)) for (; scan < end && ch == '\r'; ch = *scan) ++scan; /* peek at next character to decide what to do next */ if (sig->opt & GIT_HASHSIG_SMART_WHITESPACE) use_ignores = (ch == '\n'); if (scan >= end) break; ++scan; /* check run terminator */ if (ch == '\n' || ch == '\0') { sig->lines++; break; } ++len; HASHSIG_HASH_MIX(state, ch); } if (len > 0) { hashsig_heap_insert(&sig->mins, (hashsig_t)state); hashsig_heap_insert(&sig->maxs, (hashsig_t)state); while (scan < end && (*scan == '\n' || !*scan)) ++scan; } } prog->use_ignores = use_ignores; return 0; } static int hashsig_finalize_hashes(git_hashsig *sig) { if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE && !(sig->opt & GIT_HASHSIG_ALLOW_SMALL_FILES)) { git_error_set(GIT_ERROR_INVALID, "file too small for similarity signature calculation"); return GIT_EBUFS; } hashsig_heap_sort(&sig->mins); hashsig_heap_sort(&sig->maxs); return 0; } static git_hashsig *hashsig_alloc(git_hashsig_option_t opts) { git_hashsig *sig = git__calloc(1, sizeof(git_hashsig)); if (!sig) return NULL; hashsig_heap_init(&sig->mins, hashsig_cmp_min); hashsig_heap_init(&sig->maxs, hashsig_cmp_max); sig->opt = opts; return sig; } int git_hashsig_create( git_hashsig **out, const char *buf, size_t buflen, git_hashsig_option_t opts) { int error; hashsig_in_progress prog; git_hashsig *sig = hashsig_alloc(opts); GIT_ERROR_CHECK_ALLOC(sig); if ((error = hashsig_in_progress_init(&prog, sig)) < 0) return error; error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog); if (!error) error = hashsig_finalize_hashes(sig); if (!error) *out = sig; else git_hashsig_free(sig); return error; } int git_hashsig_create_fromfile( git_hashsig **out, const char *path, git_hashsig_option_t opts) { uint8_t buf[0x1000]; ssize_t buflen = 0; int error = 0, fd; hashsig_in_progress prog; git_hashsig *sig = hashsig_alloc(opts); GIT_ERROR_CHECK_ALLOC(sig); if ((fd = git_futils_open_ro(path)) < 0) { git__free(sig); return fd; } if ((error = hashsig_in_progress_init(&prog, sig)) < 0) { p_close(fd); return error; } while (!error) { if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) { if ((error = (int)buflen) < 0) git_error_set(GIT_ERROR_OS, "read error on '%s' calculating similarity hashes", path); break; } error = hashsig_add_hashes(sig, buf, buflen, &prog); } p_close(fd); if (!error) error = hashsig_finalize_hashes(sig); if (!error) *out = sig; else git_hashsig_free(sig); return error; } void git_hashsig_free(git_hashsig *sig) { git__free(sig); } static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b) { int matches = 0, i, j, cmp; GIT_ASSERT_WITH_RETVAL(a->cmp == b->cmp, 0); /* hash heaps are sorted - just look for overlap vs total */ for (i = 0, j = 0; i < a->size && j < b->size; ) { cmp = a->cmp(&a->values[i], &b->values[j], NULL); if (cmp < 0) ++i; else if (cmp > 0) ++j; else { ++i; ++j; ++matches; } } return HASHSIG_SCALE * (matches * 2) / (a->size + b->size); } int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b) { /* if we have no elements in either file then each file is either * empty or blank. if we're ignoring whitespace then the files are * similar, otherwise they're dissimilar. */ if (a->mins.size == 0 && b->mins.size == 0) { if ((!a->lines && !b->lines) || (a->opt & GIT_HASHSIG_IGNORE_WHITESPACE)) return HASHSIG_SCALE; else return 0; } /* if we have fewer than the maximum number of elements, then just use * one array since the two arrays will be the same */ if (a->mins.size < HASHSIG_HEAP_SIZE) { return hashsig_heap_compare(&a->mins, &b->mins); } else { int mins, maxs; if ((mins = hashsig_heap_compare(&a->mins, &b->mins)) < 0) return mins; if ((maxs = hashsig_heap_compare(&a->maxs, &b->maxs)) < 0) return maxs; return (mins + maxs) / 2; } } git2r/src/libgit2/src/describe.c0000644000175000017500000005102414125111754016274 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/describe.h" #include "git2/strarray.h" #include "git2/diff.h" #include "git2/status.h" #include "commit.h" #include "commit_list.h" #include "oidmap.h" #include "refs.h" #include "repository.h" #include "revwalk.h" #include "tag.h" #include "vector.h" #include "wildmatch.h" /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */ struct commit_name { git_tag *tag; unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */ unsigned name_checked:1; git_oid sha1; char *path; /* Khash workaround. They original key has to still be reachable */ git_oid peeled; }; static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key) { return git_oidmap_get(map, key); } static struct commit_name *find_commit_name( git_oidmap *names, const git_oid *peeled) { return (struct commit_name *)(oidmap_value_bykey(names, peeled)); } static int replace_name( git_tag **tag, git_repository *repo, struct commit_name *e, unsigned int prio, const git_oid *sha1) { git_time_t e_time = 0, t_time = 0; if (!e || e->prio < prio) return 1; if (e->prio == 2 && prio == 2) { /* Multiple annotated tags point to the same commit. * Select one to keep based upon their tagger date. */ git_tag *t = NULL; if (!e->tag) { if (git_tag_lookup(&t, repo, &e->sha1) < 0) return 1; e->tag = t; } if (git_tag_lookup(&t, repo, sha1) < 0) return 0; *tag = t; if (e->tag->tagger) e_time = e->tag->tagger->when.time; if (t->tagger) t_time = t->tagger->when.time; if (e_time < t_time) return 1; } return 0; } static int add_to_known_names( git_repository *repo, git_oidmap *names, const char *path, const git_oid *peeled, unsigned int prio, const git_oid *sha1) { struct commit_name *e = find_commit_name(names, peeled); bool found = (e != NULL); git_tag *tag = NULL; if (replace_name(&tag, repo, e, prio, sha1)) { if (!found) { e = git__malloc(sizeof(struct commit_name)); GIT_ERROR_CHECK_ALLOC(e); e->path = NULL; e->tag = NULL; } if (e->tag) git_tag_free(e->tag); e->tag = tag; e->prio = prio; e->name_checked = 0; git_oid_cpy(&e->sha1, sha1); git__free(e->path); e->path = git__strdup(path); git_oid_cpy(&e->peeled, peeled); if (!found && git_oidmap_set(names, &e->peeled, e) < 0) return -1; } else git_tag_free(tag); return 0; } static int retrieve_peeled_tag_or_object_oid( git_oid *peeled_out, git_oid *ref_target_out, git_repository *repo, const char *refname) { git_reference *ref; git_object *peeled = NULL; int error; if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0) return error; if ((error = git_reference_peel(&peeled, ref, GIT_OBJECT_ANY)) < 0) goto cleanup; git_oid_cpy(ref_target_out, git_reference_target(ref)); git_oid_cpy(peeled_out, git_object_id(peeled)); if (git_oid_cmp(ref_target_out, peeled_out) != 0) error = 1; /* The reference was pointing to a annotated tag */ else error = 0; /* Any other object */ cleanup: git_reference_free(ref); git_object_free(peeled); return error; } struct git_describe_result { int dirty; int exact_match; int fallback_to_id; git_oid commit_id; git_repository *repo; struct commit_name *name; struct possible_tag *tag; }; struct get_name_data { git_describe_options *opts; git_repository *repo; git_oidmap *names; git_describe_result *result; }; static int commit_name_dup(struct commit_name **out, struct commit_name *in) { struct commit_name *name; name = git__malloc(sizeof(struct commit_name)); GIT_ERROR_CHECK_ALLOC(name); memcpy(name, in, sizeof(struct commit_name)); name->tag = NULL; name->path = NULL; if (in->tag && git_tag_dup(&name->tag, in->tag) < 0) return -1; name->path = git__strdup(in->path); GIT_ERROR_CHECK_ALLOC(name->path); *out = name; return 0; } static int get_name(const char *refname, void *payload) { struct get_name_data *data; bool is_tag, is_annotated, all; git_oid peeled, sha1; unsigned int prio; int error = 0; data = (struct get_name_data *)payload; is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR); all = data->opts->describe_strategy == GIT_DESCRIBE_ALL; /* Reject anything outside refs/tags/ unless --all */ if (!all && !is_tag) return 0; /* Accept only tags that match the pattern, if given */ if (data->opts->pattern && (!is_tag || wildmatch(data->opts->pattern, refname + strlen(GIT_REFS_TAGS_DIR), 0))) return 0; /* Is it annotated? */ if ((error = retrieve_peeled_tag_or_object_oid( &peeled, &sha1, data->repo, refname)) < 0) return error; is_annotated = error; /* * By default, we only use annotated tags, but with --tags * we fall back to lightweight ones (even without --tags, * we still remember lightweight ones, only to give hints * in an error message). --all allows any refs to be used. */ if (is_annotated) prio = 2; else if (is_tag) prio = 1; else prio = 0; add_to_known_names(data->repo, data->names, all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR), &peeled, prio, &sha1); return 0; } struct possible_tag { struct commit_name *name; int depth; int found_order; unsigned flag_within; }; static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in) { struct possible_tag *tag; int error; tag = git__malloc(sizeof(struct possible_tag)); GIT_ERROR_CHECK_ALLOC(tag); memcpy(tag, in, sizeof(struct possible_tag)); tag->name = NULL; if ((error = commit_name_dup(&tag->name, in->name)) < 0) { git__free(tag); *out = NULL; return error; } *out = tag; return 0; } static int compare_pt(const void *a_, const void *b_) { struct possible_tag *a = (struct possible_tag *)a_; struct possible_tag *b = (struct possible_tag *)b_; if (a->depth != b->depth) return a->depth - b->depth; if (a->found_order != b->found_order) return a->found_order - b->found_order; return 0; } #define SEEN (1u << 0) static unsigned long finish_depth_computation( git_pqueue *list, git_revwalk *walk, struct possible_tag *best) { unsigned long seen_commits = 0; int error, i; while (git_pqueue_size(list) > 0) { git_commit_list_node *c = git_pqueue_pop(list); seen_commits++; if (c->flags & best->flag_within) { size_t index = 0; while (git_pqueue_size(list) > index) { git_commit_list_node *i = git_pqueue_get(list, index); if (!(i->flags & best->flag_within)) break; index++; } if (index > git_pqueue_size(list)) break; } else best->depth++; for (i = 0; i < c->out_degree; i++) { git_commit_list_node *p = c->parents[i]; if ((error = git_commit_list_parse(walk, p)) < 0) return error; if (!(p->flags & SEEN)) if ((error = git_pqueue_insert(list, p)) < 0) return error; p->flags |= c->flags; } } return seen_commits; } static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n) { if (n->prio == 2 && !n->tag) { if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) { git_error_set(GIT_ERROR_TAG, "annotated tag '%s' not available", n->path); return -1; } } if (n->tag && !n->name_checked) { if (!git_tag_name(n->tag)) { git_error_set(GIT_ERROR_TAG, "annotated tag '%s' has no embedded name", n->path); return -1; } /* TODO: Cope with warnings if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path); */ n->name_checked = 1; } if (n->tag) git_buf_printf(buf, "%s", git_tag_name(n->tag)); else git_buf_printf(buf, "%s", n->path); return 0; } static int find_unique_abbrev_size( int *out, git_repository *repo, const git_oid *oid_in, unsigned int abbreviated_size) { size_t size = abbreviated_size; git_odb *odb; git_oid dummy; int error; if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) return error; while (size < GIT_OID_HEXSZ) { if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) { *out = (int) size; return 0; } /* If the error wasn't that it's not unique, then it's a proper error */ if (error != GIT_EAMBIGUOUS) return error; /* Try again with a larger size */ size++; } /* If we didn't find any shorter prefix, we have to do the whole thing */ *out = GIT_OID_HEXSZ; return 0; } static int show_suffix( git_buf *buf, int depth, git_repository *repo, const git_oid *id, unsigned int abbrev_size) { int error, size = 0; char hex_oid[GIT_OID_HEXSZ]; if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0) return error; git_oid_fmt(hex_oid, id); git_buf_printf(buf, "-%d-g", depth); git_buf_put(buf, hex_oid, size); return git_buf_oom(buf) ? -1 : 0; } #define MAX_CANDIDATES_TAGS FLAG_BITS - 1 static int describe_not_found(const git_oid *oid, const char *message_format) { char oid_str[GIT_OID_HEXSZ + 1]; git_oid_tostr(oid_str, sizeof(oid_str), oid); git_error_set(GIT_ERROR_DESCRIBE, message_format, oid_str); return GIT_ENOTFOUND; } static int describe( struct get_name_data *data, git_commit *commit) { struct commit_name *n; struct possible_tag *best; bool all, tags; git_revwalk *walk = NULL; git_pqueue list; git_commit_list_node *cmit, *gave_up_on = NULL; git_vector all_matches = GIT_VECTOR_INIT; unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; unsigned long seen_commits = 0; /* TODO: Check long */ unsigned int unannotated_cnt = 0; int error; if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0) return -1; if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0) goto cleanup; all = data->opts->describe_strategy == GIT_DESCRIBE_ALL; tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS; git_oid_cpy(&data->result->commit_id, git_commit_id(commit)); n = find_commit_name(data->names, git_commit_id(commit)); if (n && (tags || all || n->prio == 2)) { /* * Exact match to an existing ref. */ data->result->exact_match = 1; if ((error = commit_name_dup(&data->result->name, n)) < 0) goto cleanup; goto cleanup; } if (!data->opts->max_candidates_tags) { error = describe_not_found( git_commit_id(commit), "cannot describe - no tag exactly matches '%s'"); goto cleanup; } if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0) goto cleanup; if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL) goto cleanup; if ((error = git_commit_list_parse(walk, cmit)) < 0) goto cleanup; cmit->flags = SEEN; if ((error = git_pqueue_insert(&list, cmit)) < 0) goto cleanup; while (git_pqueue_size(&list) > 0) { int i; git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list); seen_commits++; n = find_commit_name(data->names, &c->oid); if (n) { if (!tags && !all && n->prio < 2) { unannotated_cnt++; } else if (match_cnt < data->opts->max_candidates_tags) { struct possible_tag *t = git__malloc(sizeof(struct commit_name)); GIT_ERROR_CHECK_ALLOC(t); if ((error = git_vector_insert(&all_matches, t)) < 0) goto cleanup; match_cnt++; t->name = n; t->depth = seen_commits - 1; t->flag_within = 1u << match_cnt; t->found_order = match_cnt; c->flags |= t->flag_within; if (n->prio == 2) annotated_cnt++; } else { gave_up_on = c; break; } } for (cur_match = 0; cur_match < match_cnt; cur_match++) { struct possible_tag *t = git_vector_get(&all_matches, cur_match); if (!(c->flags & t->flag_within)) t->depth++; } if (annotated_cnt && (git_pqueue_size(&list) == 0)) { /* if (debug) { char oid_str[GIT_OID_HEXSZ + 1]; git_oid_tostr(oid_str, sizeof(oid_str), &c->oid); fprintf(stderr, "finished search at %s\n", oid_str); } */ break; } for (i = 0; i < c->out_degree; i++) { git_commit_list_node *p = c->parents[i]; if ((error = git_commit_list_parse(walk, p)) < 0) goto cleanup; if (!(p->flags & SEEN)) if ((error = git_pqueue_insert(&list, p)) < 0) goto cleanup; p->flags |= c->flags; if (data->opts->only_follow_first_parent) break; } } if (!match_cnt) { if (data->opts->show_commit_oid_as_fallback) { data->result->fallback_to_id = 1; git_oid_cpy(&data->result->commit_id, &cmit->oid); goto cleanup; } if (unannotated_cnt) { error = describe_not_found(git_commit_id(commit), "cannot describe - " "no annotated tags can describe '%s'; " "however, there were unannotated tags."); goto cleanup; } else { error = describe_not_found(git_commit_id(commit), "cannot describe - " "no tags can describe '%s'."); goto cleanup; } } git_vector_sort(&all_matches); best = (struct possible_tag *)git_vector_get(&all_matches, 0); if (gave_up_on) { if ((error = git_pqueue_insert(&list, gave_up_on)) < 0) goto cleanup; seen_commits--; } if ((error = finish_depth_computation( &list, walk, best)) < 0) goto cleanup; seen_commits += error; if ((error = possible_tag_dup(&data->result->tag, best)) < 0) goto cleanup; /* { static const char *prio_names[] = { "head", "lightweight", "annotated", }; char oid_str[GIT_OID_HEXSZ + 1]; if (debug) { for (cur_match = 0; cur_match < match_cnt; cur_match++) { struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match); fprintf(stderr, " %-11s %8d %s\n", prio_names[t->name->prio], t->depth, t->name->path); } fprintf(stderr, "traversed %lu commits\n", seen_commits); if (gave_up_on) { git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid); fprintf(stderr, "more than %i tags found; listed %i most recent\n" "gave up search at %s\n", data->opts->max_candidates_tags, data->opts->max_candidates_tags, oid_str); } } } */ git_oid_cpy(&data->result->commit_id, &cmit->oid); cleanup: { size_t i; struct possible_tag *match; git_vector_foreach(&all_matches, i, match) { git__free(match); } } git_vector_free(&all_matches); git_pqueue_free(&list); git_revwalk_free(walk); return error; } static int normalize_options( git_describe_options *dst, const git_describe_options *src) { git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT; if (!src) src = &default_options; *dst = *src; if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS) dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS; return 0; } int git_describe_commit( git_describe_result **result, git_object *committish, git_describe_options *opts) { struct get_name_data data; struct commit_name *name; git_commit *commit; int error = -1; git_describe_options normalized; GIT_ASSERT_ARG(result); GIT_ASSERT_ARG(committish); data.result = git__calloc(1, sizeof(git_describe_result)); GIT_ERROR_CHECK_ALLOC(data.result); data.result->repo = git_object_owner(committish); data.repo = git_object_owner(committish); if ((error = normalize_options(&normalized, opts)) < 0) return error; GIT_ERROR_CHECK_VERSION( &normalized, GIT_DESCRIBE_OPTIONS_VERSION, "git_describe_options"); data.opts = &normalized; if ((error = git_oidmap_new(&data.names)) < 0) return error; /** TODO: contains to be implemented */ if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJECT_COMMIT)) < 0) goto cleanup; if ((error = git_reference_foreach_name( git_object_owner(committish), get_name, &data)) < 0) goto cleanup; if (git_oidmap_size(data.names) == 0 && !normalized.show_commit_oid_as_fallback) { git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - " "no reference found, cannot describe anything."); error = -1; goto cleanup; } if ((error = describe(&data, commit)) < 0) goto cleanup; cleanup: git_commit_free(commit); git_oidmap_foreach_value(data.names, name, { git_tag_free(name->tag); git__free(name->path); git__free(name); }); git_oidmap_free(data.names); if (error < 0) git_describe_result_free(data.result); else *result = data.result; return error; } int git_describe_workdir( git_describe_result **out, git_repository *repo, git_describe_options *opts) { int error; git_oid current_id; git_status_list *status = NULL; git_status_options status_opts = GIT_STATUS_OPTIONS_INIT; git_describe_result *result = NULL; git_object *commit; if ((error = git_reference_name_to_id(¤t_id, repo, GIT_HEAD_FILE)) < 0) return error; if ((error = git_object_lookup(&commit, repo, ¤t_id, GIT_OBJECT_COMMIT)) < 0) return error; /* The first step is to perform a describe of HEAD, so we can leverage this */ if ((error = git_describe_commit(&result, commit, opts)) < 0) goto out; if ((error = git_status_list_new(&status, repo, &status_opts)) < 0) goto out; if (git_status_list_entrycount(status) > 0) result->dirty = 1; out: git_object_free(commit); git_status_list_free(status); if (error < 0) git_describe_result_free(result); else *out = result; return error; } static int normalize_format_options( git_describe_format_options *dst, const git_describe_format_options *src) { if (!src) { git_describe_format_options_init(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION); return 0; } memcpy(dst, src, sizeof(git_describe_format_options)); return 0; } int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *given) { int error; git_repository *repo; struct commit_name *name; git_describe_format_options opts; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(result); GIT_ERROR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options"); normalize_format_options(&opts, given); if ((error = git_buf_sanitize(out)) < 0) return error; if (opts.always_use_long_format && opts.abbreviated_size == 0) { git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - " "'always_use_long_format' is incompatible with a zero" "'abbreviated_size'"); return -1; } repo = result->repo; /* If we did find an exact match, then it's the easier method */ if (result->exact_match) { name = result->name; if ((error = display_name(out, repo, name)) < 0) return error; if (opts.always_use_long_format) { const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id; if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0) return error; } if (result->dirty && opts.dirty_suffix) git_buf_puts(out, opts.dirty_suffix); return git_buf_oom(out) ? -1 : 0; } /* If we didn't find *any* tags, we fall back to the commit's id */ if (result->fallback_to_id) { char hex_oid[GIT_OID_HEXSZ + 1] = {0}; int size = 0; if ((error = find_unique_abbrev_size( &size, repo, &result->commit_id, opts.abbreviated_size)) < 0) return -1; git_oid_fmt(hex_oid, &result->commit_id); git_buf_put(out, hex_oid, size); if (result->dirty && opts.dirty_suffix) git_buf_puts(out, opts.dirty_suffix); return git_buf_oom(out) ? -1 : 0; } /* Lastly, if we found a matching tag, we show that */ name = result->tag->name; if ((error = display_name(out, repo, name)) < 0) return error; if (opts.abbreviated_size) { if ((error = show_suffix(out, result->tag->depth, repo, &result->commit_id, opts.abbreviated_size)) < 0) return error; } if (result->dirty && opts.dirty_suffix) { git_buf_puts(out, opts.dirty_suffix); } return git_buf_oom(out) ? -1 : 0; } void git_describe_result_free(git_describe_result *result) { if (result == NULL) return; if (result->name) { git_tag_free(result->name->tag); git__free(result->name->path); git__free(result->name); } if (result->tag) { git_tag_free(result->tag->name->tag); git__free(result->tag->name->path); git__free(result->tag->name); git__free(result->tag); } git__free(result); } int git_describe_options_init(git_describe_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_describe_init_options(git_describe_options *opts, unsigned int version) { return git_describe_options_init(opts, version); } #endif int git_describe_format_options_init(git_describe_format_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version) { return git_describe_format_options_init(opts, version); } #endif git2r/src/libgit2/src/ignore.h0000644000175000017500000000371114125111754016004 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_ignore_h__ #define INCLUDE_ignore_h__ #include "common.h" #include "repository.h" #include "vector.h" #include "attr_file.h" #define GIT_IGNORE_FILE ".gitignore" #define GIT_IGNORE_FILE_INREPO "exclude" #define GIT_IGNORE_FILE_XDG "ignore" /* The git_ignores structure maintains three sets of ignores: * - internal ignores * - per directory ignores * - global ignores (at lower priority than the others) * As you traverse from one directory to another, you can push and pop * directories onto git_ignores list efficiently. */ typedef struct { git_repository *repo; git_buf dir; /* current directory reflected in ign_path */ git_attr_file *ign_internal; git_vector ign_path; git_vector ign_global; size_t dir_root; /* offset in dir to repo root */ int ignore_case; int depth; } git_ignores; extern int git_ignore__for_path( git_repository *repo, const char *path, git_ignores *ign); extern int git_ignore__push_dir(git_ignores *ign, const char *dir); extern int git_ignore__pop_dir(git_ignores *ign); extern void git_ignore__free(git_ignores *ign); enum { GIT_IGNORE_UNCHECKED = -2, GIT_IGNORE_NOTFOUND = -1, GIT_IGNORE_FALSE = 0, GIT_IGNORE_TRUE = 1, }; extern int git_ignore__lookup(int *out, git_ignores *ign, const char *path, git_dir_flag dir_flag); /* command line Git sometimes generates an error message if given a * pathspec that contains an exact match to an ignored file (provided * --force isn't also given). This makes it easy to check it that has * happened. Returns GIT_EINVALIDSPEC if the pathspec contains ignored * exact matches (that are not already present in the index). */ extern int git_ignore__check_pathspec_for_exact_ignores( git_repository *repo, git_vector *pathspec, bool no_fnmatch); #endif git2r/src/libgit2/src/settings.h0000644000175000017500000000055114125111754016360 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ extern int git_settings_global_init(void); extern const char *git_libgit2__user_agent(void); extern const char *git_libgit2__ssl_ciphers(void); git2r/src/libgit2/src/repo_template.h0000644000175000017500000000404214125111754017357 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_repo_template_h__ #define INCLUDE_repo_template_h__ #define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/" #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/" #define GIT_HOOKS_DIR "hooks/" #define GIT_HOOKS_DIR_MODE 0777 #define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample" #define GIT_HOOKS_README_MODE 0777 #define GIT_HOOKS_README_CONTENT \ "#!/bin/sh\n"\ "#\n"\ "# Place appropriately named executable hook scripts into this directory\n"\ "# to intercept various actions that git takes. See `git help hooks` for\n"\ "# more information.\n" #define GIT_INFO_DIR "info/" #define GIT_INFO_DIR_MODE 0777 #define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude" #define GIT_INFO_EXCLUDE_MODE 0666 #define GIT_INFO_EXCLUDE_CONTENT \ "# File patterns to ignore; see `git help ignore` for more information.\n"\ "# Lines that start with '#' are comments.\n" #define GIT_DESC_FILE "description" #define GIT_DESC_MODE 0666 #define GIT_DESC_CONTENT \ "Unnamed repository; edit this file 'description' to name the repository.\n" typedef struct { const char *path; mode_t mode; const char *content; } repo_template_item; static repo_template_item repo_template[] = { { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */ { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */ { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/heads/' */ { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/tags/' */ { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL }, /* '/hooks/' */ { GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL }, /* '/info/' */ { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT }, { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT }, { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT }, { NULL, 0, NULL } }; #endif git2r/src/libgit2/src/sortedcache.c0000644000175000017500000002072314125111754017002 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "sortedcache.h" int git_sortedcache_new( git_sortedcache **out, size_t item_path_offset, git_sortedcache_free_item_fn free_item, void *free_item_payload, git_vector_cmp item_cmp, const char *path) { git_sortedcache *sc; size_t pathlen, alloclen; pathlen = path ? strlen(path) : 0; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_sortedcache), pathlen); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); sc = git__calloc(1, alloclen); GIT_ERROR_CHECK_ALLOC(sc); if (git_pool_init(&sc->pool, 1) < 0 || git_vector_init(&sc->items, 4, item_cmp) < 0 || git_strmap_new(&sc->map) < 0) goto fail; if (git_rwlock_init(&sc->lock)) { git_error_set(GIT_ERROR_OS, "failed to initialize lock"); goto fail; } sc->item_path_offset = item_path_offset; sc->free_item = free_item; sc->free_item_payload = free_item_payload; GIT_REFCOUNT_INC(sc); if (pathlen) memcpy(sc->path, path, pathlen); *out = sc; return 0; fail: git_strmap_free(sc->map); git_vector_free(&sc->items); git_pool_clear(&sc->pool); git__free(sc); return -1; } void git_sortedcache_incref(git_sortedcache *sc) { GIT_REFCOUNT_INC(sc); } const char *git_sortedcache_path(git_sortedcache *sc) { return sc->path; } static void sortedcache_clear(git_sortedcache *sc) { git_strmap_clear(sc->map); if (sc->free_item) { size_t i; void *item; git_vector_foreach(&sc->items, i, item) { sc->free_item(sc->free_item_payload, item); } } git_vector_clear(&sc->items); git_pool_clear(&sc->pool); } static void sortedcache_free(git_sortedcache *sc) { /* acquire write lock to make sure everyone else is done */ if (git_sortedcache_wlock(sc) < 0) return; sortedcache_clear(sc); git_vector_free(&sc->items); git_strmap_free(sc->map); git_sortedcache_wunlock(sc); git_rwlock_free(&sc->lock); git__free(sc); } void git_sortedcache_free(git_sortedcache *sc) { if (!sc) return; GIT_REFCOUNT_DEC(sc, sortedcache_free); } static int sortedcache_copy_item(void *payload, void *tgt_item, void *src_item) { git_sortedcache *sc = payload; /* path will already have been copied by upsert */ memcpy(tgt_item, src_item, sc->item_path_offset); return 0; } /* copy a sorted cache */ int git_sortedcache_copy( git_sortedcache **out, git_sortedcache *src, bool lock, int (*copy_item)(void *payload, void *tgt_item, void *src_item), void *payload) { int error = 0; git_sortedcache *tgt; size_t i; void *src_item, *tgt_item; /* just use memcpy if no special copy fn is passed in */ if (!copy_item) { copy_item = sortedcache_copy_item; payload = src; } if ((error = git_sortedcache_new( &tgt, src->item_path_offset, src->free_item, src->free_item_payload, src->items._cmp, src->path)) < 0) return error; if (lock && git_sortedcache_rlock(src) < 0) { git_sortedcache_free(tgt); return -1; } git_vector_foreach(&src->items, i, src_item) { char *path = ((char *)src_item) + src->item_path_offset; if ((error = git_sortedcache_upsert(&tgt_item, tgt, path)) < 0 || (error = copy_item(payload, tgt_item, src_item)) < 0) break; } if (lock) git_sortedcache_runlock(src); if (error) git_sortedcache_free(tgt); *out = !error ? tgt : NULL; return error; } /* lock sortedcache while making modifications */ int git_sortedcache_wlock(git_sortedcache *sc) { GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */ if (git_rwlock_wrlock(&sc->lock) < 0) { git_error_set(GIT_ERROR_OS, "unable to acquire write lock on cache"); return -1; } return 0; } /* unlock sorted cache when done with modifications */ void git_sortedcache_wunlock(git_sortedcache *sc) { git_vector_sort(&sc->items); git_rwlock_wrunlock(&sc->lock); } /* lock sortedcache for read */ int git_sortedcache_rlock(git_sortedcache *sc) { GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */ if (git_rwlock_rdlock(&sc->lock) < 0) { git_error_set(GIT_ERROR_OS, "unable to acquire read lock on cache"); return -1; } return 0; } /* unlock sorted cache when done reading */ void git_sortedcache_runlock(git_sortedcache *sc) { GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */ git_rwlock_rdunlock(&sc->lock); } /* if the file has changed, lock cache and load file contents into buf; * returns <0 on error, >0 if file has not changed */ int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf) { int error, fd; struct stat st; if ((error = git_sortedcache_wlock(sc)) < 0) return error; if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0) goto unlock; if ((fd = git_futils_open_ro(sc->path)) < 0) { error = fd; goto unlock; } if (p_fstat(fd, &st) < 0) { git_error_set(GIT_ERROR_OS, "failed to stat file"); error = -1; (void)p_close(fd); goto unlock; } if (!git__is_sizet(st.st_size)) { git_error_set(GIT_ERROR_INVALID, "unable to load file larger than size_t"); error = -1; (void)p_close(fd); goto unlock; } if (buf) error = git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size); (void)p_close(fd); if (error < 0) goto unlock; return 1; /* return 1 -> file needs reload and was successfully loaded */ unlock: git_sortedcache_wunlock(sc); return error; } void git_sortedcache_updated(git_sortedcache *sc) { /* update filestamp to latest value */ git_futils_filestamp_check(&sc->stamp, sc->path); } /* release all items in sorted cache */ int git_sortedcache_clear(git_sortedcache *sc, bool wlock) { if (wlock && git_sortedcache_wlock(sc) < 0) return -1; sortedcache_clear(sc); if (wlock) git_sortedcache_wunlock(sc); return 0; } /* find and/or insert item, returning pointer to item data */ int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key) { size_t keylen, itemlen; int error = 0; char *item_key; void *item; if ((item = git_strmap_get(sc->map, key)) != NULL) goto done; keylen = strlen(key); itemlen = sc->item_path_offset + keylen + 1; itemlen = (itemlen + 7) & ~7; if ((item = git_pool_mallocz(&sc->pool, itemlen)) == NULL) { /* don't use GIT_ERROR_CHECK_ALLOC b/c of lock */ error = -1; goto done; } /* one strange thing is that even if the vector or hash table insert * fail, there is no way to free the pool item so we just abandon it */ item_key = ((char *)item) + sc->item_path_offset; memcpy(item_key, key, keylen); if ((error = git_strmap_set(sc->map, item_key, item)) < 0) goto done; if ((error = git_vector_insert(&sc->items, item)) < 0) git_strmap_delete(sc->map, item_key); done: if (out) *out = !error ? item : NULL; return error; } /* lookup item by key */ void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key) { return git_strmap_get(sc->map, key); } /* find out how many items are in the cache */ size_t git_sortedcache_entrycount(const git_sortedcache *sc) { return git_vector_length(&sc->items); } /* lookup item by index */ void *git_sortedcache_entry(git_sortedcache *sc, size_t pos) { /* make sure the items are sorted so this gets the correct item */ if (!git_vector_is_sorted(&sc->items)) git_vector_sort(&sc->items); return git_vector_get(&sc->items, pos); } /* helper struct so bsearch callback can know offset + key value for cmp */ struct sortedcache_magic_key { size_t offset; const char *key; }; static int sortedcache_magic_cmp(const void *key, const void *value) { const struct sortedcache_magic_key *magic = key; const char *value_key = ((const char *)value) + magic->offset; return strcmp(magic->key, value_key); } /* lookup index of item by key */ int git_sortedcache_lookup_index( size_t *out, git_sortedcache *sc, const char *key) { struct sortedcache_magic_key magic; magic.offset = sc->item_path_offset; magic.key = key; return git_vector_bsearch2(out, &sc->items, sortedcache_magic_cmp, &magic); } /* remove entry from cache */ int git_sortedcache_remove(git_sortedcache *sc, size_t pos) { char *item; /* * Because of pool allocation, this can't actually remove the item, * but we can remove it from the items vector and the hash table. */ if ((item = git_vector_get(&sc->items, pos)) == NULL) { git_error_set(GIT_ERROR_INVALID, "removing item out of range"); return GIT_ENOTFOUND; } (void)git_vector_remove(&sc->items, pos); git_strmap_delete(sc->map, item + sc->item_path_offset); if (sc->free_item) sc->free_item(sc->free_item_payload, item); return 0; } git2r/src/libgit2/src/attr_file.c0000644000175000017500000006136214125111754016473 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "attr_file.h" #include "repository.h" #include "filebuf.h" #include "attrcache.h" #include "git2/blob.h" #include "git2/tree.h" #include "blob.h" #include "index.h" #include "wildmatch.h" #include static void attr_file_free(git_attr_file *file) { bool unlock = !git_mutex_lock(&file->lock); git_attr_file__clear_rules(file, false); git_pool_clear(&file->pool); if (unlock) git_mutex_unlock(&file->lock); git_mutex_free(&file->lock); git__memzero(file, sizeof(*file)); git__free(file); } int git_attr_file__new( git_attr_file **out, git_attr_file_entry *entry, git_attr_file_source *source) { git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file)); GIT_ERROR_CHECK_ALLOC(attrs); if (git_mutex_init(&attrs->lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to initialize lock"); goto on_error; } if (git_pool_init(&attrs->pool, 1) < 0) goto on_error; GIT_REFCOUNT_INC(attrs); attrs->entry = entry; memcpy(&attrs->source, source, sizeof(git_attr_file_source)); *out = attrs; return 0; on_error: git__free(attrs); return -1; } int git_attr_file__clear_rules(git_attr_file *file, bool need_lock) { unsigned int i; git_attr_rule *rule; if (need_lock && git_mutex_lock(&file->lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock attribute file"); return -1; } git_vector_foreach(&file->rules, i, rule) git_attr_rule__free(rule); git_vector_free(&file->rules); if (need_lock) git_mutex_unlock(&file->lock); return 0; } void git_attr_file__free(git_attr_file *file) { if (!file) return; GIT_REFCOUNT_DEC(file, attr_file_free); } static int attr_file_oid_from_index( git_oid *oid, git_repository *repo, const char *path) { int error; git_index *idx; size_t pos; const git_index_entry *entry; if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || (error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0) return error; if (!(entry = git_index_get_byindex(idx, pos))) return GIT_ENOTFOUND; *oid = entry->id; return 0; } int git_attr_file__load( git_attr_file **out, git_repository *repo, git_attr_session *attr_session, git_attr_file_entry *entry, git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros) { int error = 0; git_commit *commit = NULL; git_tree *tree = NULL; git_tree_entry *tree_entry = NULL; git_blob *blob = NULL; git_buf content = GIT_BUF_INIT; const char *content_str; git_attr_file *file; struct stat st; bool nonexistent = false; int bom_offset; git_buf_bom_t bom; git_oid id; git_object_size_t blobsize; *out = NULL; switch (source->type) { case GIT_ATTR_FILE_SOURCE_MEMORY: /* in-memory attribute file doesn't need data */ break; case GIT_ATTR_FILE_SOURCE_INDEX: { if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 || (error = git_blob_lookup(&blob, repo, &id)) < 0) return error; /* Do not assume that data straight from the ODB is NULL-terminated; * copy the contents of a file to a buffer to work on */ blobsize = git_blob_rawsize(blob); GIT_ERROR_CHECK_BLOBSIZE(blobsize); git_buf_put(&content, git_blob_rawcontent(blob), (size_t)blobsize); break; } case GIT_ATTR_FILE_SOURCE_FILE: { int fd = -1; /* For open or read errors, pretend that we got ENOTFOUND. */ /* TODO: issue warning when warning API is available */ if (p_stat(entry->fullpath, &st) < 0 || S_ISDIR(st.st_mode) || (fd = git_futils_open_ro(entry->fullpath)) < 0 || (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0) nonexistent = true; if (fd >= 0) p_close(fd); break; } case GIT_ATTR_FILE_SOURCE_HEAD: case GIT_ATTR_FILE_SOURCE_COMMIT: { if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT) { if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0 || (error = git_commit_tree(&tree, commit)) < 0) goto cleanup; } else { if ((error = git_repository_head_tree(&tree, repo)) < 0) goto cleanup; } if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) { /* * If the attributes file does not exist, we can * cache an empty file for this commit to prevent * needless future lookups. */ if (error == GIT_ENOTFOUND) { error = 0; break; } goto cleanup; } if ((error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0) goto cleanup; /* * Do not assume that data straight from the ODB is NULL-terminated; * copy the contents of a file to a buffer to work on. */ blobsize = git_blob_rawsize(blob); GIT_ERROR_CHECK_BLOBSIZE(blobsize); if ((error = git_buf_put(&content, git_blob_rawcontent(blob), (size_t)blobsize)) < 0) goto cleanup; break; } default: git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source->type); return -1; } if ((error = git_attr_file__new(&file, entry, source)) < 0) goto cleanup; /* advance over a UTF8 BOM */ content_str = git_buf_cstr(&content); bom_offset = git_buf_detect_bom(&bom, &content); if (bom == GIT_BUF_BOM_UTF8) content_str += bom_offset; /* store the key of the attr_reader; don't bother with cache * invalidation during the same attr reader session. */ if (attr_session) file->session_key = attr_session->key; if (parser && (error = parser(repo, file, content_str, allow_macros)) < 0) { git_attr_file__free(file); goto cleanup; } /* write cache breakers */ if (nonexistent) file->nonexistent = 1; else if (source->type == GIT_ATTR_FILE_SOURCE_INDEX) git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); else if (source->type == GIT_ATTR_FILE_SOURCE_HEAD) git_oid_cpy(&file->cache_data.oid, git_tree_id(tree)); else if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT) git_oid_cpy(&file->cache_data.oid, git_tree_id(tree)); else if (source->type == GIT_ATTR_FILE_SOURCE_FILE) git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); /* else always cacheable */ *out = file; cleanup: git_blob_free(blob); git_tree_entry_free(tree_entry); git_tree_free(tree); git_commit_free(commit); git_buf_dispose(&content); return error; } int git_attr_file__out_of_date( git_repository *repo, git_attr_session *attr_session, git_attr_file *file, git_attr_file_source *source) { if (!file) return 1; /* we are never out of date if we just created this data in the same * attr_session; otherwise, nonexistent files must be invalidated */ if (attr_session && attr_session->key == file->session_key) return 0; else if (file->nonexistent) return 1; switch (file->source.type) { case GIT_ATTR_FILE_SOURCE_MEMORY: return 0; case GIT_ATTR_FILE_SOURCE_FILE: return git_futils_filestamp_check( &file->cache_data.stamp, file->entry->fullpath); case GIT_ATTR_FILE_SOURCE_INDEX: { int error; git_oid id; if ((error = attr_file_oid_from_index( &id, repo, file->entry->path)) < 0) return error; return (git_oid__cmp(&file->cache_data.oid, &id) != 0); } case GIT_ATTR_FILE_SOURCE_HEAD: { git_tree *tree = NULL; int error = git_repository_head_tree(&tree, repo); if (error < 0) return error; error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0); git_tree_free(tree); return error; } case GIT_ATTR_FILE_SOURCE_COMMIT: { git_commit *commit = NULL; git_tree *tree = NULL; int error; if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0) return error; error = git_commit_tree(&tree, commit); git_commit_free(commit); if (error < 0) return error; error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0); git_tree_free(tree); return error; } default: git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source.type); return -1; } } static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); static void git_attr_rule__clear(git_attr_rule *rule); static bool parse_optimized_patterns( git_attr_fnmatch *spec, git_pool *pool, const char *pattern); int git_attr_file__parse_buffer( git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros) { const char *scan = data, *context = NULL; git_attr_rule *rule = NULL; int error = 0; /* If subdir file path, convert context for file paths */ if (attrs->entry && git_path_root(attrs->entry->path) < 0 && !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE)) context = attrs->entry->path; if (git_mutex_lock(&attrs->lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock attribute file"); return -1; } while (!error && *scan) { /* Allocate rule if needed, otherwise re-use previous rule */ if (!rule) { rule = git__calloc(1, sizeof(*rule)); GIT_ERROR_CHECK_ALLOC(rule); } else git_attr_rule__clear(rule); rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO; /* Parse the next "pattern attr attr attr" line */ if ((error = git_attr_fnmatch__parse(&rule->match, &attrs->pool, context, &scan)) < 0 || (error = git_attr_assignment__parse(repo, &attrs->pool, &rule->assigns, &scan)) < 0) { if (error != GIT_ENOTFOUND) goto out; error = 0; continue; } if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) { /* TODO: warning if macro found in file below repo root */ if (!allow_macros) continue; if ((error = git_attr_cache__insert_macro(repo, rule)) < 0) goto out; } else if ((error = git_vector_insert(&attrs->rules, rule)) < 0) goto out; rule = NULL; } out: git_mutex_unlock(&attrs->lock); git_attr_rule__free(rule); return error; } uint32_t git_attr_file__name_hash(const char *name) { uint32_t h = 5381; int c; GIT_ASSERT_ARG(name); while ((c = (int)*name++) != 0) h = ((h << 5) + h) + c; return h; } int git_attr_file__lookup_one( git_attr_file *file, git_attr_path *path, const char *attr, const char **value) { size_t i; git_attr_name name; git_attr_rule *rule; *value = NULL; name.name = attr; name.name_hash = git_attr_file__name_hash(attr); git_attr_file__foreach_matching_rule(file, path, i, rule) { size_t pos; if (!git_vector_bsearch(&pos, &rule->assigns, &name)) { *value = ((git_attr_assignment *) git_vector_get(&rule->assigns, pos))->value; break; } } return 0; } int git_attr_file__load_standalone(git_attr_file **out, const char *path) { git_buf content = GIT_BUF_INIT; git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE }; git_attr_file *file = NULL; int error; if ((error = git_futils_readbuffer(&content, path)) < 0) goto out; /* * Because the cache entry is allocated from the file's own pool, we * don't have to free it - freeing file+pool will free cache entry, too. */ if ((error = git_attr_file__new(&file, NULL, &source)) < 0 || (error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 || (error = git_attr_cache__alloc_file_entry(&file->entry, NULL, NULL, path, &file->pool)) < 0) goto out; *out = file; out: if (error < 0) git_attr_file__free(file); git_buf_dispose(&content); return error; } bool git_attr_fnmatch__match( git_attr_fnmatch *match, git_attr_path *path) { const char *relpath = path->path; const char *filename; int flags = 0; /* * If the rule was generated in a subdirectory, we must only * use it for paths inside that directory. We can thus return * a non-match if the prefixes don't match. */ if (match->containing_dir) { if (match->flags & GIT_ATTR_FNMATCH_ICASE) { if (git__strncasecmp(path->path, match->containing_dir, match->containing_dir_length)) return 0; } else { if (git__prefixcmp(path->path, match->containing_dir)) return 0; } relpath += match->containing_dir_length; } if (match->flags & GIT_ATTR_FNMATCH_ICASE) flags |= WM_CASEFOLD; if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) { filename = relpath; flags |= WM_PATHNAME; } else { filename = path->basename; } if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) { bool samename; /* * for attribute checks or checks at the root of this match's * containing_dir (or root of the repository if no containing_dir), * do not match. */ if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) || path->basename == relpath) return false; /* fail match if this is a file with same name as ignored folder */ samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? !strcasecmp(match->pattern, relpath) : !strcmp(match->pattern, relpath); if (samename) return false; return (wildmatch(match->pattern, relpath, flags) == WM_MATCH); } return (wildmatch(match->pattern, filename, flags) == WM_MATCH); } bool git_attr_rule__match( git_attr_rule *rule, git_attr_path *path) { bool matched = git_attr_fnmatch__match(&rule->match, path); if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) matched = !matched; return matched; } git_attr_assignment *git_attr_rule__lookup_assignment( git_attr_rule *rule, const char *name) { size_t pos; git_attr_name key; key.name = name; key.name_hash = git_attr_file__name_hash(name); if (git_vector_bsearch(&pos, &rule->assigns, &key)) return NULL; return git_vector_get(&rule->assigns, pos); } int git_attr_path__init( git_attr_path *info, const char *path, const char *base, git_dir_flag dir_flag) { ssize_t root; /* build full path as best we can */ git_buf_init(&info->full, 0); if (git_path_join_unrooted(&info->full, path, base, &root) < 0) return -1; info->path = info->full.ptr + root; /* remove trailing slashes */ while (info->full.size > 0) { if (info->full.ptr[info->full.size - 1] != '/') break; info->full.size--; } info->full.ptr[info->full.size] = '\0'; /* skip leading slashes in path */ while (*info->path == '/') info->path++; /* find trailing basename component */ info->basename = strrchr(info->path, '/'); if (info->basename) info->basename++; if (!info->basename || !*info->basename) info->basename = info->path; switch (dir_flag) { case GIT_DIR_FLAG_FALSE: info->is_dir = 0; break; case GIT_DIR_FLAG_TRUE: info->is_dir = 1; break; case GIT_DIR_FLAG_UNKNOWN: default: info->is_dir = (int)git_path_isdir(info->full.ptr); break; } return 0; } void git_attr_path__free(git_attr_path *info) { git_buf_dispose(&info->full); info->path = NULL; info->basename = NULL; } /* * From gitattributes(5): * * Patterns have the following format: * * - A blank line matches no files, so it can serve as a separator for * readability. * * - A line starting with # serves as a comment. * * - An optional prefix ! which negates the pattern; any matching file * excluded by a previous pattern will become included again. If a negated * pattern matches, this will override lower precedence patterns sources. * * - If the pattern ends with a slash, it is removed for the purpose of the * following description, but it would only find a match with a directory. In * other words, foo/ will match a directory foo and paths underneath it, but * will not match a regular file or a symbolic link foo (this is consistent * with the way how pathspec works in general in git). * * - If the pattern does not contain a slash /, git treats it as a shell glob * pattern and checks for a match against the pathname without leading * directories. * * - Otherwise, git treats the pattern as a shell glob suitable for consumption * by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will * not match a / in the pathname. For example, "Documentation/\*.html" matches * "Documentation/git.html" but not "Documentation/ppc/ppc.html". A leading * slash matches the beginning of the pathname; for example, "/\*.c" matches * "cat-file.c" but not "mozilla-sha1/sha1.c". */ /* * Determine the length of trailing spaces. Escaped spaces do not count as * trailing whitespace. */ static size_t trailing_space_length(const char *p, size_t len) { size_t n, i; for (n = len; n; n--) { if (p[n-1] != ' ' && p[n-1] != '\t') break; /* * Count escape-characters before space. In case where it's an * even number of escape characters, then the escape char itself * is escaped and the whitespace is an unescaped whitespace. * Otherwise, the last escape char is not escaped and the * whitespace in an escaped whitespace. */ i = n; while (i > 1 && p[i-2] == '\\') i--; if ((n - i) % 2) break; } return len - n; } static size_t unescape_spaces(char *str) { char *scan, *pos = str; bool escaped = false; if (!str) return 0; for (scan = str; *scan; scan++) { if (!escaped && *scan == '\\') { escaped = true; continue; } /* Only insert the escape character for escaped non-spaces */ if (escaped && !git__isspace(*scan)) *pos++ = '\\'; *pos++ = *scan; escaped = false; } if (pos != scan) *pos = '\0'; return (pos - str); } /* * This will return 0 if the spec was filled out, * GIT_ENOTFOUND if the fnmatch does not require matching, or * another error code there was an actual problem. */ int git_attr_fnmatch__parse( git_attr_fnmatch *spec, git_pool *pool, const char *context, const char **base) { const char *pattern, *scan; int slash_count, allow_space; bool escaped; GIT_ASSERT_ARG(spec); GIT_ASSERT_ARG(base && *base); if (parse_optimized_patterns(spec, pool, *base)) return 0; spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING); allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0); pattern = *base; while (!allow_space && git__isspace(*pattern)) pattern++; if (!*pattern || *pattern == '#' || *pattern == '\n' || (*pattern == '\r' && *(pattern + 1) == '\n')) { *base = git__next_line(pattern); return GIT_ENOTFOUND; } if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) { if (strncmp(pattern, "[attr]", 6) == 0) { spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO; pattern += 6; } /* else a character range like [a-e]* which is accepted */ } if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) { spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE; pattern++; } slash_count = 0; escaped = false; /* Scan until a non-escaped whitespace. */ for (scan = pattern; *scan != '\0'; ++scan) { char c = *scan; if (c == '\\' && !escaped) { escaped = true; continue; } else if (git__isspace(c) && !escaped) { if (!allow_space || (c != ' ' && c != '\t' && c != '\r')) break; } else if (c == '/') { spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH; slash_count++; if (slash_count == 1 && pattern == scan) pattern++; } else if (git__iswildcard(c) && !escaped) { /* remember if we see an unescaped wildcard in pattern */ spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD; } escaped = false; } *base = scan; if ((spec->length = scan - pattern) == 0) return GIT_ENOTFOUND; /* * Remove one trailing \r in case this is a CRLF delimited * file, in the case of Icon\r\r\n, we still leave the first * \r there to match against. */ if (pattern[spec->length - 1] == '\r') if (--spec->length == 0) return GIT_ENOTFOUND; /* Remove trailing spaces. */ spec->length -= trailing_space_length(pattern, spec->length); if (spec->length == 0) return GIT_ENOTFOUND; if (pattern[spec->length - 1] == '/') { spec->length--; spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY; if (--slash_count <= 0) spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH; } if (context) { char *slash = strrchr(context, '/'); size_t len; if (slash) { /* include the slash for easier matching */ len = slash - context + 1; spec->containing_dir = git_pool_strndup(pool, context, len); spec->containing_dir_length = len; } } spec->pattern = git_pool_strndup(pool, pattern, spec->length); if (!spec->pattern) { *base = git__next_line(pattern); return -1; } else { /* strip '\' that might have been used for internal whitespace */ spec->length = unescape_spaces(spec->pattern); } return 0; } static bool parse_optimized_patterns( git_attr_fnmatch *spec, git_pool *pool, const char *pattern) { if (!pattern[1] && (pattern[0] == '*' || pattern[0] == '.')) { spec->flags = GIT_ATTR_FNMATCH_MATCH_ALL; spec->pattern = git_pool_strndup(pool, pattern, 1); spec->length = 1; return true; } return false; } static int sort_by_hash_and_name(const void *a_raw, const void *b_raw) { const git_attr_name *a = a_raw; const git_attr_name *b = b_raw; if (b->name_hash < a->name_hash) return 1; else if (b->name_hash > a->name_hash) return -1; else return strcmp(b->name, a->name); } static void git_attr_assignment__free(git_attr_assignment *assign) { /* name and value are stored in a git_pool associated with the * git_attr_file, so they do not need to be freed here */ assign->name = NULL; assign->value = NULL; git__free(assign); } static int merge_assignments(void **old_raw, void *new_raw) { git_attr_assignment **old = (git_attr_assignment **)old_raw; git_attr_assignment *new = (git_attr_assignment *)new_raw; GIT_REFCOUNT_DEC(*old, git_attr_assignment__free); *old = new; return GIT_EEXISTS; } int git_attr_assignment__parse( git_repository *repo, git_pool *pool, git_vector *assigns, const char **base) { int error; const char *scan = *base; git_attr_assignment *assign = NULL; GIT_ASSERT_ARG(assigns && !assigns->length); git_vector_set_cmp(assigns, sort_by_hash_and_name); while (*scan && *scan != '\n') { const char *name_start, *value_start; /* skip leading blanks */ while (git__isspace(*scan) && *scan != '\n') scan++; /* allocate assign if needed */ if (!assign) { assign = git__calloc(1, sizeof(git_attr_assignment)); GIT_ERROR_CHECK_ALLOC(assign); GIT_REFCOUNT_INC(assign); } assign->name_hash = 5381; assign->value = git_attr__true; /* look for magic name prefixes */ if (*scan == '-') { assign->value = git_attr__false; scan++; } else if (*scan == '!') { assign->value = git_attr__unset; /* explicit unspecified state */ scan++; } else if (*scan == '#') /* comment rest of line */ break; /* find the name */ name_start = scan; while (*scan && !git__isspace(*scan) && *scan != '=') { assign->name_hash = ((assign->name_hash << 5) + assign->name_hash) + *scan; scan++; } if (scan == name_start) { /* must have found lone prefix (" - ") or leading = ("=foo") * or end of buffer -- advance until whitespace and continue */ while (*scan && !git__isspace(*scan)) scan++; continue; } /* allocate permanent storage for name */ assign->name = git_pool_strndup(pool, name_start, scan - name_start); GIT_ERROR_CHECK_ALLOC(assign->name); /* if there is an equals sign, find the value */ if (*scan == '=') { for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan); /* if we found a value, allocate permanent storage for it */ if (scan > value_start) { assign->value = git_pool_strndup(pool, value_start, scan - value_start); GIT_ERROR_CHECK_ALLOC(assign->value); } } /* expand macros (if given a repo with a macro cache) */ if (repo != NULL && assign->value == git_attr__true) { git_attr_rule *macro = git_attr_cache__lookup_macro(repo, assign->name); if (macro != NULL) { unsigned int i; git_attr_assignment *massign; git_vector_foreach(¯o->assigns, i, massign) { GIT_REFCOUNT_INC(massign); error = git_vector_insert_sorted( assigns, massign, &merge_assignments); if (error < 0 && error != GIT_EEXISTS) { git_attr_assignment__free(assign); return error; } } } } /* insert allocated assign into vector */ error = git_vector_insert_sorted(assigns, assign, &merge_assignments); if (error < 0 && error != GIT_EEXISTS) return error; /* clear assign since it is now "owned" by the vector */ assign = NULL; } if (assign != NULL) git_attr_assignment__free(assign); *base = git__next_line(scan); return (assigns->length == 0) ? GIT_ENOTFOUND : 0; } static void git_attr_rule__clear(git_attr_rule *rule) { unsigned int i; git_attr_assignment *assign; if (!rule) return; if (!(rule->match.flags & GIT_ATTR_FNMATCH_IGNORE)) { git_vector_foreach(&rule->assigns, i, assign) GIT_REFCOUNT_DEC(assign, git_attr_assignment__free); git_vector_free(&rule->assigns); } /* match.pattern is stored in a git_pool, so no need to free */ rule->match.pattern = NULL; rule->match.length = 0; } void git_attr_rule__free(git_attr_rule *rule) { git_attr_rule__clear(rule); git__free(rule); } int git_attr_session__init(git_attr_session *session, git_repository *repo) { GIT_ASSERT_ARG(repo); memset(session, 0, sizeof(*session)); session->key = git_atomic32_inc(&repo->attr_session_key); return 0; } void git_attr_session__free(git_attr_session *session) { if (!session) return; git_buf_dispose(&session->sysdir); git_buf_dispose(&session->tmp); memset(session, 0, sizeof(git_attr_session)); } git2r/src/libgit2/src/patch.c0000644000175000017500000001114614125111754015614 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "patch.h" #include "git2/patch.h" #include "diff.h" int git_patch__invoke_callbacks( git_patch *patch, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload) { int error = 0; uint32_t i, j; if (file_cb) error = file_cb(patch->delta, 0, payload); if (error) return error; if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) { if (binary_cb) error = binary_cb(patch->delta, &patch->binary, payload); return error; } if (!hunk_cb && !line_cb) return error; for (i = 0; !error && i < git_array_size(patch->hunks); ++i) { git_patch_hunk *h = git_array_get(patch->hunks, i); if (hunk_cb) error = hunk_cb(patch->delta, &h->hunk, payload); if (!line_cb) continue; for (j = 0; !error && j < h->line_count; ++j) { git_diff_line *l = git_array_get(patch->lines, h->line_start + j); error = line_cb(patch->delta, &h->hunk, l, payload); } } return error; } size_t git_patch_size( git_patch *patch, int include_context, int include_hunk_headers, int include_file_headers) { size_t out; GIT_ASSERT_ARG(patch); out = patch->content_size; if (!include_context) out -= patch->context_size; if (include_hunk_headers) out += patch->header_size; if (include_file_headers) { git_buf file_header = GIT_BUF_INIT; if (git_diff_delta__format_file_header( &file_header, patch->delta, NULL, NULL, 0, true) < 0) git_error_clear(); else out += git_buf_len(&file_header); git_buf_dispose(&file_header); } return out; } int git_patch_line_stats( size_t *total_ctxt, size_t *total_adds, size_t *total_dels, const git_patch *patch) { size_t totals[3], idx; memset(totals, 0, sizeof(totals)); for (idx = 0; idx < git_array_size(patch->lines); ++idx) { git_diff_line *line = git_array_get(patch->lines, idx); if (!line) continue; switch (line->origin) { case GIT_DIFF_LINE_CONTEXT: totals[0]++; break; case GIT_DIFF_LINE_ADDITION: totals[1]++; break; case GIT_DIFF_LINE_DELETION: totals[2]++; break; default: /* diff --stat and --numstat don't count EOFNL marks because * they will always be paired with a ADDITION or DELETION line. */ break; } } if (total_ctxt) *total_ctxt = totals[0]; if (total_adds) *total_adds = totals[1]; if (total_dels) *total_dels = totals[2]; return 0; } const git_diff_delta *git_patch_get_delta(const git_patch *patch) { GIT_ASSERT_ARG_WITH_RETVAL(patch, NULL); return patch->delta; } size_t git_patch_num_hunks(const git_patch *patch) { GIT_ASSERT_ARG(patch); return git_array_size(patch->hunks); } static int patch_error_outofrange(const char *thing) { git_error_set(GIT_ERROR_INVALID, "patch %s index out of range", thing); return GIT_ENOTFOUND; } int git_patch_get_hunk( const git_diff_hunk **out, size_t *lines_in_hunk, git_patch *patch, size_t hunk_idx) { git_patch_hunk *hunk; GIT_ASSERT_ARG(patch); hunk = git_array_get(patch->hunks, hunk_idx); if (!hunk) { if (out) *out = NULL; if (lines_in_hunk) *lines_in_hunk = 0; return patch_error_outofrange("hunk"); } if (out) *out = &hunk->hunk; if (lines_in_hunk) *lines_in_hunk = hunk->line_count; return 0; } int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx) { git_patch_hunk *hunk; GIT_ASSERT_ARG(patch); if (!(hunk = git_array_get(patch->hunks, hunk_idx))) return patch_error_outofrange("hunk"); return (int)hunk->line_count; } int git_patch_get_line_in_hunk( const git_diff_line **out, git_patch *patch, size_t hunk_idx, size_t line_of_hunk) { git_patch_hunk *hunk; git_diff_line *line; GIT_ASSERT_ARG(patch); if (!(hunk = git_array_get(patch->hunks, hunk_idx))) { if (out) *out = NULL; return patch_error_outofrange("hunk"); } if (line_of_hunk >= hunk->line_count || !(line = git_array_get( patch->lines, hunk->line_start + line_of_hunk))) { if (out) *out = NULL; return patch_error_outofrange("line"); } if (out) *out = line; return 0; } git_repository *git_patch_owner(const git_patch *patch) { return patch->repo; } int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(diff); GIT_ASSERT_ARG(diff->patch_fn); return diff->patch_fn(out, diff, idx); } static void git_patch__free(git_patch *patch) { if (patch->free_fn) patch->free_fn(patch); } void git_patch_free(git_patch *patch) { if (patch) GIT_REFCOUNT_DEC(patch, git_patch__free); } git2r/src/libgit2/src/path.c0000644000175000017500000013633014125111754015454 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "path.h" #include "posix.h" #include "repository.h" #ifdef GIT_WIN32 #include "win32/posix.h" #include "win32/w32_buffer.h" #include "win32/w32_util.h" #include "win32/version.h" #include #else #include #endif #include #include static int dos_drive_prefix_length(const char *path) { int i; /* * Does it start with an ASCII letter (i.e. highest bit not set), * followed by a colon? */ if (!(0x80 & (unsigned char)*path)) return *path && path[1] == ':' ? 2 : 0; /* * While drive letters must be letters of the English alphabet, it is * possible to assign virtually _any_ Unicode character via `subst` as * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff * like this: * * subst ֍: %USERPROFILE%\Desktop */ for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++) ; /* skip first UTF-8 character */ return path[i] == ':' ? i + 1 : 0; } #ifdef GIT_WIN32 static bool looks_like_network_computer_name(const char *path, int pos) { if (pos < 3) return false; if (path[0] != '/' || path[1] != '/') return false; while (pos-- > 2) { if (path[pos] == '/') return false; } return true; } #endif /* * Based on the Android implementation, BSD licensed. * http://android.git.kernel.org/ * * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ int git_path_basename_r(git_buf *buffer, const char *path) { const char *endp, *startp; int len, result; /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { startp = "."; len = 1; goto Exit; } /* Strip trailing slashes */ endp = path + strlen(path) - 1; while (endp > path && *endp == '/') endp--; /* All slashes becomes "/" */ if (endp == path && *endp == '/') { startp = "/"; len = 1; goto Exit; } /* Find the start of the base */ startp = endp; while (startp > path && *(startp - 1) != '/') startp--; /* Cast is safe because max path < max int */ len = (int)(endp - startp + 1); Exit: result = len; if (buffer != NULL && git_buf_set(buffer, startp, len) < 0) return -1; return result; } /* * Determine if the path is a Windows prefix and, if so, returns * its actual lentgh. If it is not a prefix, returns -1. */ static int win32_prefix_length(const char *path, int len) { #ifndef GIT_WIN32 GIT_UNUSED(path); GIT_UNUSED(len); #else /* * Mimic unix behavior where '/.git' returns '/': 'C:/.git' * will return 'C:/' here */ if (dos_drive_prefix_length(path) == len) return len; /* * Similarly checks if we're dealing with a network computer name * '//computername/.git' will return '//computername/' */ if (looks_like_network_computer_name(path, len)) return len; #endif return -1; } /* * Based on the Android implementation, BSD licensed. * Check http://android.git.kernel.org/ */ int git_path_dirname_r(git_buf *buffer, const char *path) { const char *endp; int is_prefix = 0, len; /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { path = "."; len = 1; goto Exit; } /* Strip trailing slashes */ endp = path + strlen(path) - 1; while (endp > path && *endp == '/') endp--; if (endp - path + 1 > INT_MAX) { git_error_set(GIT_ERROR_INVALID, "path too long"); len = -1; goto Exit; } if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { is_prefix = 1; goto Exit; } /* Find the start of the dir */ while (endp > path && *endp != '/') endp--; /* Either the dir is "/" or there are no slashes */ if (endp == path) { path = (*endp == '/') ? "/" : "."; len = 1; goto Exit; } do { endp--; } while (endp > path && *endp == '/'); if (endp - path + 1 > INT_MAX) { git_error_set(GIT_ERROR_INVALID, "path too long"); len = -1; goto Exit; } if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { is_prefix = 1; goto Exit; } /* Cast is safe because max path < max int */ len = (int)(endp - path + 1); Exit: if (buffer) { if (git_buf_set(buffer, path, len) < 0) return -1; if (is_prefix && git_buf_putc(buffer, '/') < 0) return -1; } return len; } char *git_path_dirname(const char *path) { git_buf buf = GIT_BUF_INIT; char *dirname; git_path_dirname_r(&buf, path); dirname = git_buf_detach(&buf); git_buf_dispose(&buf); /* avoid memleak if error occurs */ return dirname; } char *git_path_basename(const char *path) { git_buf buf = GIT_BUF_INIT; char *basename; git_path_basename_r(&buf, path); basename = git_buf_detach(&buf); git_buf_dispose(&buf); /* avoid memleak if error occurs */ return basename; } size_t git_path_basename_offset(git_buf *buffer) { ssize_t slash; if (!buffer || buffer->size <= 0) return 0; slash = git_buf_rfind_next(buffer, '/'); if (slash >= 0 && buffer->ptr[slash] == '/') return (size_t)(slash + 1); return 0; } int git_path_root(const char *path) { int offset = 0, prefix_len; /* Does the root of the path look like a windows drive ? */ if ((prefix_len = dos_drive_prefix_length(path))) offset += prefix_len; #ifdef GIT_WIN32 /* Are we dealing with a windows network path? */ else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') || (path[0] == '\\' && path[1] == '\\' && path[2] != '\\')) { offset += 2; /* Skip the computer name segment */ while (path[offset] && path[offset] != '/' && path[offset] != '\\') offset++; } if (path[offset] == '\\') return offset; #endif if (path[offset] == '/') return offset; return -1; /* Not a real error - signals that path is not rooted */ } static void path_trim_slashes(git_buf *path) { int ceiling = git_path_root(path->ptr) + 1; if (ceiling < 0) return; while (path->size > (size_t)ceiling) { if (path->ptr[path->size-1] != '/') break; path->ptr[path->size-1] = '\0'; path->size--; } } int git_path_join_unrooted( git_buf *path_out, const char *path, const char *base, ssize_t *root_at) { ssize_t root; GIT_ASSERT_ARG(path_out); GIT_ASSERT_ARG(path); root = (ssize_t)git_path_root(path); if (base != NULL && root < 0) { if (git_buf_joinpath(path_out, base, path) < 0) return -1; root = (ssize_t)strlen(base); } else { if (git_buf_sets(path_out, path) < 0) return -1; if (root < 0) root = 0; else if (base) git_path_equal_or_prefixed(base, path, &root); } if (root_at) *root_at = root; return 0; } void git_path_squash_slashes(git_buf *path) { char *p, *q; if (path->size == 0) return; for (p = path->ptr, q = path->ptr; *q; p++, q++) { *p = *q; while (*q == '/' && *(q+1) == '/') { path->size--; q++; } } *p = '\0'; } int git_path_prettify(git_buf *path_out, const char *path, const char *base) { char buf[GIT_PATH_MAX]; GIT_ASSERT_ARG(path_out); GIT_ASSERT_ARG(path); /* construct path if needed */ if (base != NULL && git_path_root(path) < 0) { if (git_buf_joinpath(path_out, base, path) < 0) return -1; path = path_out->ptr; } if (p_realpath(path, buf) == NULL) { /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */ int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1; git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path); git_buf_clear(path_out); return error; } return git_buf_sets(path_out, buf); } int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base) { int error = git_path_prettify(path_out, path, base); return (error < 0) ? error : git_path_to_dir(path_out); } int git_path_to_dir(git_buf *path) { if (path->asize > 0 && git_buf_len(path) > 0 && path->ptr[git_buf_len(path) - 1] != '/') git_buf_putc(path, '/'); return git_buf_oom(path) ? -1 : 0; } void git_path_string_to_dir(char *path, size_t size) { size_t end = strlen(path); if (end && path[end - 1] != '/' && end < size) { path[end] = '/'; path[end + 1] = '\0'; } } int git__percent_decode(git_buf *decoded_out, const char *input) { int len, hi, lo, i; GIT_ASSERT_ARG(decoded_out); GIT_ASSERT_ARG(input); len = (int)strlen(input); git_buf_clear(decoded_out); for(i = 0; i < len; i++) { char c = input[i]; if (c != '%') goto append; if (i >= len - 2) goto append; hi = git__fromhex(input[i + 1]); lo = git__fromhex(input[i + 2]); if (hi < 0 || lo < 0) goto append; c = (char)(hi << 4 | lo); i += 2; append: if (git_buf_putc(decoded_out, c) < 0) return -1; } return 0; } static int error_invalid_local_file_uri(const char *uri) { git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri); return -1; } static int local_file_url_prefixlen(const char *file_url) { int len = -1; if (git__prefixcmp(file_url, "file://") == 0) { if (file_url[7] == '/') len = 8; else if (git__prefixcmp(file_url + 7, "localhost/") == 0) len = 17; } return len; } bool git_path_is_local_file_url(const char *file_url) { return (local_file_url_prefixlen(file_url) > 0); } int git_path_fromurl(git_buf *local_path_out, const char *file_url) { int offset; GIT_ASSERT_ARG(local_path_out); GIT_ASSERT_ARG(file_url); if ((offset = local_file_url_prefixlen(file_url)) < 0 || file_url[offset] == '\0' || file_url[offset] == '/') return error_invalid_local_file_uri(file_url); #ifndef GIT_WIN32 offset--; /* A *nix absolute path starts with a forward slash */ #endif git_buf_clear(local_path_out); return git__percent_decode(local_path_out, file_url + offset); } int git_path_walk_up( git_buf *path, const char *ceiling, int (*cb)(void *data, const char *), void *data) { int error = 0; git_buf iter; ssize_t stop = 0, scan; char oldc = '\0'; GIT_ASSERT_ARG(path); GIT_ASSERT_ARG(cb); if (ceiling != NULL) { if (git__prefixcmp(path->ptr, ceiling) == 0) stop = (ssize_t)strlen(ceiling); else stop = git_buf_len(path); } scan = git_buf_len(path); /* empty path: yield only once */ if (!scan) { error = cb(data, ""); if (error) git_error_set_after_callback(error); return error; } iter.ptr = path->ptr; iter.size = git_buf_len(path); iter.asize = path->asize; while (scan >= stop) { error = cb(data, iter.ptr); iter.ptr[scan] = oldc; if (error) { git_error_set_after_callback(error); break; } scan = git_buf_rfind_next(&iter, '/'); if (scan >= 0) { scan++; oldc = iter.ptr[scan]; iter.size = scan; iter.ptr[scan] = '\0'; } } if (scan >= 0) iter.ptr[scan] = oldc; /* relative path: yield for the last component */ if (!error && stop == 0 && iter.ptr[0] != '/') { error = cb(data, ""); if (error) git_error_set_after_callback(error); } return error; } bool git_path_exists(const char *path) { GIT_ASSERT_ARG_WITH_RETVAL(path, false); return p_access(path, F_OK) == 0; } bool git_path_isdir(const char *path) { struct stat st; if (p_stat(path, &st) < 0) return false; return S_ISDIR(st.st_mode) != 0; } bool git_path_isfile(const char *path) { struct stat st; GIT_ASSERT_ARG_WITH_RETVAL(path, false); if (p_stat(path, &st) < 0) return false; return S_ISREG(st.st_mode) != 0; } bool git_path_islink(const char *path) { struct stat st; GIT_ASSERT_ARG_WITH_RETVAL(path, false); if (p_lstat(path, &st) < 0) return false; return S_ISLNK(st.st_mode) != 0; } #ifdef GIT_WIN32 bool git_path_is_empty_dir(const char *path) { git_win32_path filter_w; bool empty = false; if (git_win32__findfirstfile_filter(filter_w, path)) { WIN32_FIND_DATAW findData; HANDLE hFind = FindFirstFileW(filter_w, &findData); /* FindFirstFile will fail if there are no children to the given * path, which can happen if the given path is a file (and obviously * has no children) or if the given path is an empty mount point. * (Most directories have at least directory entries '.' and '..', * but ridiculously another volume mounted in another drive letter's * path space do not, and thus have nothing to enumerate.) If * FindFirstFile fails, check if this is a directory-like thing * (a mount point). */ if (hFind == INVALID_HANDLE_VALUE) return git_path_isdir(path); /* If the find handle was created successfully, then it's a directory */ empty = true; do { /* Allow the enumeration to return . and .. and still be considered * empty. In the special case of drive roots (i.e. C:\) where . and * .. do not occur, we can still consider the path to be an empty * directory if there's nothing there. */ if (!git_path_is_dot_or_dotdotW(findData.cFileName)) { empty = false; break; } } while (FindNextFileW(hFind, &findData)); FindClose(hFind); } return empty; } #else static int path_found_entry(void *payload, git_buf *path) { GIT_UNUSED(payload); return !git_path_is_dot_or_dotdot(path->ptr); } bool git_path_is_empty_dir(const char *path) { int error; git_buf dir = GIT_BUF_INIT; if (!git_path_isdir(path)) return false; if ((error = git_buf_sets(&dir, path)) != 0) git_error_clear(); else error = git_path_direach(&dir, 0, path_found_entry, NULL); git_buf_dispose(&dir); return !error; } #endif int git_path_set_error(int errno_value, const char *path, const char *action) { switch (errno_value) { case ENOENT: case ENOTDIR: git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action); return GIT_ENOTFOUND; case EINVAL: case ENAMETOOLONG: git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path); return GIT_EINVALIDSPEC; case EEXIST: git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path); return GIT_EEXISTS; case EACCES: git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path); return GIT_ELOCKED; default: git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path); return -1; } } int git_path_lstat(const char *path, struct stat *st) { if (p_lstat(path, st) == 0) return 0; return git_path_set_error(errno, path, "stat"); } static bool _check_dir_contents( git_buf *dir, const char *sub, bool (*predicate)(const char *)) { bool result; size_t dir_size = git_buf_len(dir); size_t sub_size = strlen(sub); size_t alloc_size; /* leave base valid even if we could not make space for subdir */ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) || GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) || git_buf_try_grow(dir, alloc_size, false) < 0) return false; /* save excursion */ if (git_buf_joinpath(dir, dir->ptr, sub) < 0) return false; result = predicate(dir->ptr); /* restore path */ git_buf_truncate(dir, dir_size); return result; } bool git_path_contains(git_buf *dir, const char *item) { return _check_dir_contents(dir, item, &git_path_exists); } bool git_path_contains_dir(git_buf *base, const char *subdir) { return _check_dir_contents(base, subdir, &git_path_isdir); } bool git_path_contains_file(git_buf *base, const char *file) { return _check_dir_contents(base, file, &git_path_isfile); } int git_path_find_dir(git_buf *dir) { int error = 0; char buf[GIT_PATH_MAX]; if (p_realpath(dir->ptr, buf) != NULL) error = git_buf_sets(dir, buf); /* call dirname if this is not a directory */ if (!error) /* && git_path_isdir(dir->ptr) == false) */ error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0; if (!error) error = git_path_to_dir(dir); return error; } int git_path_resolve_relative(git_buf *path, size_t ceiling) { char *base, *to, *from, *next; size_t len; GIT_ERROR_CHECK_ALLOC_BUF(path); if (ceiling > path->size) ceiling = path->size; /* recognize drive prefixes, etc. that should not be backed over */ if (ceiling == 0) ceiling = git_path_root(path->ptr) + 1; /* recognize URL prefixes that should not be backed over */ if (ceiling == 0) { for (next = path->ptr; *next && git__isalpha(*next); ++next); if (next[0] == ':' && next[1] == '/' && next[2] == '/') ceiling = (next + 3) - path->ptr; } base = to = from = path->ptr + ceiling; while (*from) { for (next = from; *next && *next != '/'; ++next); len = next - from; if (len == 1 && from[0] == '.') /* do nothing with singleton dot */; else if (len == 2 && from[0] == '.' && from[1] == '.') { /* error out if trying to up one from a hard base */ if (to == base && ceiling != 0) { git_error_set(GIT_ERROR_INVALID, "cannot strip root component off url"); return -1; } /* no more path segments to strip, * use '../' as a new base path */ if (to == base) { if (*next == '/') len++; if (to != from) memmove(to, from, len); to += len; /* this is now the base, can't back up from a * relative prefix */ base = to; } else { /* back up a path segment */ while (to > base && to[-1] == '/') to--; while (to > base && to[-1] != '/') to--; } } else { if (*next == '/' && *from != '/') len++; if (to != from) memmove(to, from, len); to += len; } from += len; while (*from == '/') from++; } *to = '\0'; path->size = to - path->ptr; return 0; } int git_path_apply_relative(git_buf *target, const char *relpath) { return git_buf_joinpath(target, git_buf_cstr(target), relpath) || git_path_resolve_relative(target, 0); } int git_path_cmp( const char *name1, size_t len1, int isdir1, const char *name2, size_t len2, int isdir2, int (*compare)(const char *, const char *, size_t)) { unsigned char c1, c2; size_t len = len1 < len2 ? len1 : len2; int cmp; cmp = compare(name1, name2, len); if (cmp) return cmp; c1 = name1[len]; c2 = name2[len]; if (c1 == '\0' && isdir1) c1 = '/'; if (c2 == '\0' && isdir2) c2 = '/'; return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; } size_t git_path_common_dirlen(const char *one, const char *two) { const char *p, *q, *dirsep = NULL; for (p = one, q = two; *p && *q; p++, q++) { if (*p == '/' && *q == '/') dirsep = p; else if (*p != *q) break; } return dirsep ? (dirsep - one) + 1 : 0; } int git_path_make_relative(git_buf *path, const char *parent) { const char *p, *q, *p_dirsep, *q_dirsep; size_t plen = path->size, newlen, alloclen, depth = 1, i, offset; for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) { if (*p == '/' && *q == '/') { p_dirsep = p; q_dirsep = q; } else if (*p != *q) break; } /* need at least 1 common path segment */ if ((p_dirsep == path->ptr || q_dirsep == parent) && (*p_dirsep != '/' || *q_dirsep != '/')) { git_error_set(GIT_ERROR_INVALID, "%s is not a parent of %s", parent, path->ptr); return GIT_ENOTFOUND; } if (*p == '/' && !*q) p++; else if (!*p && *q == '/') q++; else if (!*p && !*q) return git_buf_clear(path), 0; else { p = p_dirsep + 1; q = q_dirsep + 1; } plen -= (p - path->ptr); if (!*q) return git_buf_set(path, p, plen); for (; (q = strchr(q, '/')) && *(q + 1); q++) depth++; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3); GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1); /* save the offset as we might realllocate the pointer */ offset = p - path->ptr; if (git_buf_try_grow(path, alloclen, 1) < 0) return -1; p = path->ptr + offset; memmove(path->ptr + (depth * 3), p, plen + 1); for (i = 0; i < depth; i++) memcpy(path->ptr + (i * 3), "../", 3); path->size = newlen; return 0; } bool git_path_has_non_ascii(const char *path, size_t pathlen) { const uint8_t *scan = (const uint8_t *)path, *end; for (end = scan + pathlen; scan < end; ++scan) if (*scan & 0x80) return true; return false; } #ifdef GIT_USE_ICONV int git_path_iconv_init_precompose(git_path_iconv_t *ic) { git_buf_init(&ic->buf, 0); ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING); return 0; } void git_path_iconv_clear(git_path_iconv_t *ic) { if (ic) { if (ic->map != (iconv_t)-1) iconv_close(ic->map); git_buf_dispose(&ic->buf); } } int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen) { char *nfd = (char*)*in, *nfc; size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv; int retry = 1; if (!ic || ic->map == (iconv_t)-1 || !git_path_has_non_ascii(*in, *inlen)) return 0; git_buf_clear(&ic->buf); while (1) { GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1); if (git_buf_grow(&ic->buf, alloclen) < 0) return -1; nfc = ic->buf.ptr + ic->buf.size; nfclen = ic->buf.asize - ic->buf.size; rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen); ic->buf.size = (nfc - ic->buf.ptr); if (rv != (size_t)-1) break; /* if we cannot convert the data (probably because iconv thinks * it is not valid UTF-8 source data), then use original data */ if (errno != E2BIG) return 0; /* make space for 2x the remaining data to be converted * (with per retry overhead to avoid infinite loops) */ wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4); if (retry++ > 4) goto fail; } ic->buf.ptr[ic->buf.size] = '\0'; *in = ic->buf.ptr; *inlen = ic->buf.size; return 0; fail: git_error_set(GIT_ERROR_OS, "unable to convert unicode path data"); return -1; } static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; /* Check if the platform is decomposing unicode data for us. We will * emulate core Git and prefer to use precomposed unicode data internally * on these platforms, composing the decomposed unicode on the fly. * * This mainly happens on the Mac where HDFS stores filenames as * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will * return decomposed unicode from readdir() even when the actual * filesystem is storing precomposed unicode. */ bool git_path_does_fs_decompose_unicode(const char *root) { git_buf path = GIT_BUF_INIT; int fd; bool found_decomposed = false; char tmp[6]; /* Create a file using a precomposed path and then try to find it * using the decomposed name. If the lookup fails, then we will mark * that we should precompose unicode for this repository. */ if (git_buf_joinpath(&path, root, nfc_file) < 0 || (fd = p_mkstemp(path.ptr)) < 0) goto done; p_close(fd); /* record trailing digits generated by mkstemp */ memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); /* try to look up as NFD path */ if (git_buf_joinpath(&path, root, nfd_file) < 0) goto done; memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); found_decomposed = git_path_exists(path.ptr); /* remove temporary file (using original precomposed path) */ if (git_buf_joinpath(&path, root, nfc_file) < 0) goto done; memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); (void)p_unlink(path.ptr); done: git_buf_dispose(&path); return found_decomposed; } #else bool git_path_does_fs_decompose_unicode(const char *root) { GIT_UNUSED(root); return false; } #endif #if defined(__sun) || defined(__GNU__) typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1]; #else typedef struct dirent path_dirent_data; #endif int git_path_direach( git_buf *path, uint32_t flags, int (*fn)(void *, git_buf *), void *arg) { int error = 0; ssize_t wd_len; DIR *dir; struct dirent *de; #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; #endif GIT_UNUSED(flags); if (git_path_to_dir(path) < 0) return -1; wd_len = git_buf_len(path); if ((dir = opendir(path->ptr)) == NULL) { git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr); if (errno == ENOENT) return GIT_ENOTFOUND; return -1; } #ifdef GIT_USE_ICONV if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0) (void)git_path_iconv_init_precompose(&ic); #endif while ((de = readdir(dir)) != NULL) { const char *de_path = de->d_name; size_t de_len = strlen(de_path); if (git_path_is_dot_or_dotdot(de_path)) continue; #ifdef GIT_USE_ICONV if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0) break; #endif if ((error = git_buf_put(path, de_path, de_len)) < 0) break; git_error_clear(); error = fn(arg, path); git_buf_truncate(path, wd_len); /* restore path */ /* Only set our own error if the callback did not set one already */ if (error != 0) { if (!git_error_last()) git_error_set_after_callback(error); break; } } closedir(dir); #ifdef GIT_USE_ICONV git_path_iconv_clear(&ic); #endif return error; } #if defined(GIT_WIN32) && !defined(__MINGW32__) /* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7 * and better. */ #ifndef FIND_FIRST_EX_LARGE_FETCH # define FIND_FIRST_EX_LARGE_FETCH 2 #endif int git_path_diriter_init( git_path_diriter *diriter, const char *path, unsigned int flags) { git_win32_path path_filter; static int is_win7_or_later = -1; if (is_win7_or_later < 0) is_win7_or_later = git_has_win32_version(6, 1, 0); GIT_ASSERT_ARG(diriter); GIT_ASSERT_ARG(path); memset(diriter, 0, sizeof(git_path_diriter)); diriter->handle = INVALID_HANDLE_VALUE; if (git_buf_puts(&diriter->path_utf8, path) < 0) return -1; path_trim_slashes(&diriter->path_utf8); if (diriter->path_utf8.size == 0) { git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); return -1; } if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 || !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) { git_error_set(GIT_ERROR_OS, "could not parse the directory path '%s'", path); return -1; } diriter->handle = FindFirstFileExW( path_filter, is_win7_or_later ? FindExInfoBasic : FindExInfoStandard, &diriter->current, FindExSearchNameMatch, NULL, is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0); if (diriter->handle == INVALID_HANDLE_VALUE) { git_error_set(GIT_ERROR_OS, "could not open directory '%s'", path); return -1; } diriter->parent_utf8_len = diriter->path_utf8.size; diriter->flags = flags; return 0; } static int diriter_update_paths(git_path_diriter *diriter) { size_t filename_len, path_len; filename_len = wcslen(diriter->current.cFileName); if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) || GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2)) return -1; if (path_len > GIT_WIN_PATH_UTF16) { git_error_set(GIT_ERROR_FILESYSTEM, "invalid path '%.*ls\\%ls' (path too long)", diriter->parent_len, diriter->path, diriter->current.cFileName); return -1; } diriter->path[diriter->parent_len] = L'\\'; memcpy(&diriter->path[diriter->parent_len+1], diriter->current.cFileName, filename_len * sizeof(wchar_t)); diriter->path[path_len-1] = L'\0'; git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len); if (diriter->parent_utf8_len > 0 && diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/') git_buf_putc(&diriter->path_utf8, '/'); git_buf_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len); if (git_buf_oom(&diriter->path_utf8)) return -1; return 0; } int git_path_diriter_next(git_path_diriter *diriter) { bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); do { /* Our first time through, we already have the data from * FindFirstFileW. Use it, otherwise get the next file. */ if (!diriter->needs_next) diriter->needs_next = 1; else if (!FindNextFileW(diriter->handle, &diriter->current)) return GIT_ITEROVER; } while (skip_dot && git_path_is_dot_or_dotdotW(diriter->current.cFileName)); if (diriter_update_paths(diriter) < 0) return -1; return 0; } int git_path_diriter_filename( const char **out, size_t *out_len, git_path_diriter *diriter) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(out_len); GIT_ASSERT_ARG(diriter); GIT_ASSERT(diriter->path_utf8.size > diriter->parent_utf8_len); *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1]; *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1; return 0; } int git_path_diriter_fullpath( const char **out, size_t *out_len, git_path_diriter *diriter) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(out_len); GIT_ASSERT_ARG(diriter); *out = diriter->path_utf8.ptr; *out_len = diriter->path_utf8.size; return 0; } int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(diriter); return git_win32__file_attribute_to_stat(out, (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current, diriter->path); } void git_path_diriter_free(git_path_diriter *diriter) { if (diriter == NULL) return; git_buf_dispose(&diriter->path_utf8); if (diriter->handle != INVALID_HANDLE_VALUE) { FindClose(diriter->handle); diriter->handle = INVALID_HANDLE_VALUE; } } #else int git_path_diriter_init( git_path_diriter *diriter, const char *path, unsigned int flags) { GIT_ASSERT_ARG(diriter); GIT_ASSERT_ARG(path); memset(diriter, 0, sizeof(git_path_diriter)); if (git_buf_puts(&diriter->path, path) < 0) return -1; path_trim_slashes(&diriter->path); if (diriter->path.size == 0) { git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); return -1; } if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) { git_buf_dispose(&diriter->path); git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path); return -1; } #ifdef GIT_USE_ICONV if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0) (void)git_path_iconv_init_precompose(&diriter->ic); #endif diriter->parent_len = diriter->path.size; diriter->flags = flags; return 0; } int git_path_diriter_next(git_path_diriter *diriter) { struct dirent *de; const char *filename; size_t filename_len; bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); int error = 0; GIT_ASSERT_ARG(diriter); errno = 0; do { if ((de = readdir(diriter->dir)) == NULL) { if (!errno) return GIT_ITEROVER; git_error_set(GIT_ERROR_OS, "could not read directory '%s'", diriter->path.ptr); return -1; } } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name)); filename = de->d_name; filename_len = strlen(filename); #ifdef GIT_USE_ICONV if ((diriter->flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0 && (error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0) return error; #endif git_buf_truncate(&diriter->path, diriter->parent_len); if (diriter->parent_len > 0 && diriter->path.ptr[diriter->parent_len-1] != '/') git_buf_putc(&diriter->path, '/'); git_buf_put(&diriter->path, filename, filename_len); if (git_buf_oom(&diriter->path)) return -1; return error; } int git_path_diriter_filename( const char **out, size_t *out_len, git_path_diriter *diriter) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(out_len); GIT_ASSERT_ARG(diriter); GIT_ASSERT(diriter->path.size > diriter->parent_len); *out = &diriter->path.ptr[diriter->parent_len+1]; *out_len = diriter->path.size - diriter->parent_len - 1; return 0; } int git_path_diriter_fullpath( const char **out, size_t *out_len, git_path_diriter *diriter) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(out_len); GIT_ASSERT_ARG(diriter); *out = diriter->path.ptr; *out_len = diriter->path.size; return 0; } int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(diriter); return git_path_lstat(diriter->path.ptr, out); } void git_path_diriter_free(git_path_diriter *diriter) { if (diriter == NULL) return; if (diriter->dir) { closedir(diriter->dir); diriter->dir = NULL; } #ifdef GIT_USE_ICONV git_path_iconv_clear(&diriter->ic); #endif git_buf_dispose(&diriter->path); } #endif int git_path_dirload( git_vector *contents, const char *path, size_t prefix_len, uint32_t flags) { git_path_diriter iter = GIT_PATH_DIRITER_INIT; const char *name; size_t name_len; char *dup; int error; GIT_ASSERT_ARG(contents); GIT_ASSERT_ARG(path); if ((error = git_path_diriter_init(&iter, path, flags)) < 0) return error; while ((error = git_path_diriter_next(&iter)) == 0) { if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0) break; GIT_ASSERT(name_len > prefix_len); dup = git__strndup(name + prefix_len, name_len - prefix_len); GIT_ERROR_CHECK_ALLOC(dup); if ((error = git_vector_insert(contents, dup)) < 0) break; } if (error == GIT_ITEROVER) error = 0; git_path_diriter_free(&iter); return error; } int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path) { if (git_path_is_local_file_url(url_or_path)) return git_path_fromurl(local_path_out, url_or_path); else return git_buf_sets(local_path_out, url_or_path); } /* Reject paths like AUX or COM1, or those versions that end in a dot or * colon. ("AUX." or "AUX:") */ GIT_INLINE(bool) verify_dospath( const char *component, size_t len, const char dospath[3], bool trailing_num) { size_t last = trailing_num ? 4 : 3; if (len < last || git__strncasecmp(component, dospath, 3) != 0) return true; if (trailing_num && (component[3] < '1' || component[3] > '9')) return true; return (len > last && component[last] != '.' && component[last] != ':'); } static int32_t next_hfs_char(const char **in, size_t *len) { while (*len) { uint32_t codepoint; int cp_len = git_utf8_iterate(&codepoint, *in, *len); if (cp_len < 0) return -1; (*in) += cp_len; (*len) -= cp_len; /* these code points are ignored completely */ switch (codepoint) { case 0x200c: /* ZERO WIDTH NON-JOINER */ case 0x200d: /* ZERO WIDTH JOINER */ case 0x200e: /* LEFT-TO-RIGHT MARK */ case 0x200f: /* RIGHT-TO-LEFT MARK */ case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */ case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */ case 0x202c: /* POP DIRECTIONAL FORMATTING */ case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */ case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */ case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */ case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */ case 0x206c: /* INHIBIT ARABIC FORM SHAPING */ case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */ case 0x206e: /* NATIONAL DIGIT SHAPES */ case 0x206f: /* NOMINAL DIGIT SHAPES */ case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */ continue; } /* fold into lowercase -- this will only fold characters in * the ASCII range, which is perfectly fine, because the * git folder name can only be composed of ascii characters */ return git__tolower((int)codepoint); } return 0; /* NULL byte -- end of string */ } static bool verify_dotgit_hfs_generic(const char *path, size_t len, const char *needle, size_t needle_len) { size_t i; char c; if (next_hfs_char(&path, &len) != '.') return true; for (i = 0; i < needle_len; i++) { c = next_hfs_char(&path, &len); if (c != needle[i]) return true; } if (next_hfs_char(&path, &len) != '\0') return true; return false; } static bool verify_dotgit_hfs(const char *path, size_t len) { return verify_dotgit_hfs_generic(path, len, "git", CONST_STRLEN("git")); } GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len) { git_buf *reserved = git_repository__reserved_names_win32; size_t reserved_len = git_repository__reserved_names_win32_len; size_t start = 0, i; if (repo) git_repository__reserved_names(&reserved, &reserved_len, repo, true); for (i = 0; i < reserved_len; i++) { git_buf *r = &reserved[i]; if (len >= r->size && strncasecmp(path, r->ptr, r->size) == 0) { start = r->size; break; } } if (!start) return true; /* * Reject paths that start with Windows-style directory separators * (".git\") or NTFS alternate streams (".git:") and could be used * to write to the ".git" directory on Windows platforms. */ if (path[start] == '\\' || path[start] == ':') return false; /* Reject paths like '.git ' or '.git.' */ for (i = start; i < len; i++) { if (path[i] != ' ' && path[i] != '.') return true; } return false; } /* * Windows paths that end with spaces and/or dots are elided to the * path without them for backward compatibility. That is to say * that opening file "foo ", "foo." or even "foo . . ." will all * map to a filename of "foo". This function identifies spaces and * dots at the end of a filename, whether the proper end of the * filename (end of string) or a colon (which would indicate a * Windows alternate data stream.) */ GIT_INLINE(bool) ntfs_end_of_filename(const char *path) { const char *c = path; for (;; c++) { if (*c == '\0' || *c == ':') return true; if (*c != ' ' && *c != '.') return false; } return true; } GIT_INLINE(bool) verify_dotgit_ntfs_generic(const char *name, size_t len, const char *dotgit_name, size_t dotgit_len, const char *shortname_pfix) { int i, saw_tilde; if (name[0] == '.' && len >= dotgit_len && !strncasecmp(name + 1, dotgit_name, dotgit_len)) { return !ntfs_end_of_filename(name + dotgit_len + 1); } /* Detect the basic NTFS shortname with the first six chars */ if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' && name[7] >= '1' && name[7] <= '4') return !ntfs_end_of_filename(name + 8); /* Catch fallback names */ for (i = 0, saw_tilde = 0; i < 8; i++) { if (name[i] == '\0') { return true; } else if (saw_tilde) { if (name[i] < '0' || name[i] > '9') return true; } else if (name[i] == '~') { if (name[i+1] < '1' || name[i+1] > '9') return true; saw_tilde = 1; } else if (i >= 6) { return true; } else if ((unsigned char)name[i] > 127) { return true; } else if (git__tolower(name[i]) != shortname_pfix[i]) { return true; } } return !ntfs_end_of_filename(name + i); } GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags) { if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\') return false; if ((flags & GIT_PATH_REJECT_SLASH) && c == '/') return false; if (flags & GIT_PATH_REJECT_NT_CHARS) { if (c < 32) return false; switch (c) { case '<': case '>': case ':': case '"': case '|': case '?': case '*': return false; } } return true; } /* * Return the length of the common prefix between str and prefix, comparing them * case-insensitively (must be ASCII to match). */ GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char *prefix) { size_t count = 0; while (len >0 && tolower(*str) == tolower(*prefix)) { count++; str++; prefix++; len--; } return count; } /* * We fundamentally don't like some paths when dealing with user-inputted * strings (in checkout or ref names): we don't want dot or dot-dot * anywhere, we want to avoid writing weird paths on Windows that can't * be handled by tools that use the non-\\?\ APIs, we don't want slashes * or double slashes at the end of paths that can make them ambiguous. * * For checkout, we don't want to recurse into ".git" either. */ static bool verify_component( git_repository *repo, const char *component, size_t len, uint16_t mode, unsigned int flags) { if (len == 0) return false; if ((flags & GIT_PATH_REJECT_TRAVERSAL) && len == 1 && component[0] == '.') return false; if ((flags & GIT_PATH_REJECT_TRAVERSAL) && len == 2 && component[0] == '.' && component[1] == '.') return false; if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.') return false; if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ') return false; if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':') return false; if (flags & GIT_PATH_REJECT_DOS_PATHS) { if (!verify_dospath(component, len, "CON", false) || !verify_dospath(component, len, "PRN", false) || !verify_dospath(component, len, "AUX", false) || !verify_dospath(component, len, "NUL", false) || !verify_dospath(component, len, "COM", true) || !verify_dospath(component, len, "LPT", true)) return false; } if (flags & GIT_PATH_REJECT_DOT_GIT_HFS) { if (!verify_dotgit_hfs(component, len)) return false; if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_HFS)) return false; } if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) { if (!verify_dotgit_ntfs(repo, component, len)) return false; if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_NTFS)) return false; } /* don't bother rerunning the `.git` test if we ran the HFS or NTFS * specific tests, they would have already rejected `.git`. */ if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 && (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 && (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL)) { if (len >= 4 && component[0] == '.' && (component[1] == 'g' || component[1] == 'G') && (component[2] == 'i' || component[2] == 'I') && (component[3] == 't' || component[3] == 'T')) { if (len == 4) return false; if (S_ISLNK(mode) && common_prefix_icase(component, len, ".gitmodules") == len) return false; } } return true; } GIT_INLINE(unsigned int) dotgit_flags( git_repository *repo, unsigned int flags) { int protectHFS = 0, protectNTFS = 1; int error = 0; flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL; #ifdef __APPLE__ protectHFS = 1; #endif if (repo && !protectHFS) error = git_repository__configmap_lookup(&protectHFS, repo, GIT_CONFIGMAP_PROTECTHFS); if (!error && protectHFS) flags |= GIT_PATH_REJECT_DOT_GIT_HFS; if (repo) error = git_repository__configmap_lookup(&protectNTFS, repo, GIT_CONFIGMAP_PROTECTNTFS); if (!error && protectNTFS) flags |= GIT_PATH_REJECT_DOT_GIT_NTFS; return flags; } bool git_path_validate( git_repository *repo, const char *path, uint16_t mode, unsigned int flags) { const char *start, *c; /* Upgrade the ".git" checks based on platform */ if ((flags & GIT_PATH_REJECT_DOT_GIT)) flags = dotgit_flags(repo, flags); for (start = c = path; *c; c++) { if (!verify_char(*c, flags)) return false; if (*c == '/') { if (!verify_component(repo, start, (c - start), mode, flags)) return false; start = c+1; } } return verify_component(repo, start, (c - start), mode, flags); } #ifdef GIT_WIN32 GIT_INLINE(bool) should_validate_longpaths(git_repository *repo) { int longpaths = 0; if (repo && git_repository__configmap_lookup(&longpaths, repo, GIT_CONFIGMAP_LONGPATHS) < 0) longpaths = 0; return (longpaths == 0); } #else GIT_INLINE(bool) should_validate_longpaths(git_repository *repo) { GIT_UNUSED(repo); return false; } #endif int git_path_validate_workdir(git_repository *repo, const char *path) { if (should_validate_longpaths(repo)) return git_path_validate_filesystem(path, strlen(path)); return 0; } int git_path_validate_workdir_with_len( git_repository *repo, const char *path, size_t path_len) { if (should_validate_longpaths(repo)) return git_path_validate_filesystem(path, path_len); return 0; } int git_path_validate_workdir_buf(git_repository *repo, git_buf *path) { return git_path_validate_workdir_with_len(repo, path->ptr, path->size); } int git_path_normalize_slashes(git_buf *out, const char *path) { int error; char *p; if ((error = git_buf_puts(out, path)) < 0) return error; for (p = out->ptr; *p; p++) { if (*p == '\\') *p = '/'; } return 0; } static const struct { const char *file; const char *hash; size_t filelen; } gitfiles[] = { { "gitignore", "gi250a", CONST_STRLEN("gitignore") }, { "gitmodules", "gi7eba", CONST_STRLEN("gitmodules") }, { "gitattributes", "gi7d29", CONST_STRLEN("gitattributes") } }; extern int git_path_is_gitfile(const char *path, size_t pathlen, git_path_gitfile gitfile, git_path_fs fs) { const char *file, *hash; size_t filelen; if (!(gitfile >= GIT_PATH_GITFILE_GITIGNORE && gitfile < ARRAY_SIZE(gitfiles))) { git_error_set(GIT_ERROR_OS, "invalid gitfile for path validation"); return -1; } file = gitfiles[gitfile].file; filelen = gitfiles[gitfile].filelen; hash = gitfiles[gitfile].hash; switch (fs) { case GIT_PATH_FS_GENERIC: return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash) || !verify_dotgit_hfs_generic(path, pathlen, file, filelen); case GIT_PATH_FS_NTFS: return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash); case GIT_PATH_FS_HFS: return !verify_dotgit_hfs_generic(path, pathlen, file, filelen); default: git_error_set(GIT_ERROR_OS, "invalid filesystem for path validation"); return -1; } } bool git_path_supports_symlinks(const char *dir) { git_buf path = GIT_BUF_INIT; bool supported = false; struct stat st; int fd; if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 || p_close(fd) < 0 || p_unlink(path.ptr) < 0 || p_symlink("testing", path.ptr) < 0 || p_lstat(path.ptr, &st) < 0) goto done; supported = (S_ISLNK(st.st_mode) != 0); done: if (path.size) (void)p_unlink(path.ptr); git_buf_dispose(&path); return supported; } int git_path_validate_system_file_ownership(const char *path) { #ifndef GIT_WIN32 GIT_UNUSED(path); return GIT_OK; #else git_win32_path buf; PSID owner_sid; PSECURITY_DESCRIPTOR descriptor = NULL; HANDLE token; TOKEN_USER *info = NULL; DWORD err, len; int ret; if (git_win32_path_from_utf8(buf, path) < 0) return -1; err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, &owner_sid, NULL, NULL, NULL, &descriptor); if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { ret = GIT_ENOTFOUND; goto cleanup; } if (err != ERROR_SUCCESS) { git_error_set(GIT_ERROR_OS, "failed to get security information"); ret = GIT_ERROR; goto cleanup; } if (!IsValidSid(owner_sid)) { git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); ret = GIT_ERROR; goto cleanup; } if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || IsWellKnownSid(owner_sid, WinLocalSystemSid)) { ret = GIT_OK; goto cleanup; } /* Obtain current user's SID */ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) && !GetTokenInformation(token, TokenUser, NULL, 0, &len)) { info = git__malloc(len); GIT_ERROR_CHECK_ALLOC(info); if (!GetTokenInformation(token, TokenUser, info, len, &len)) { git__free(info); info = NULL; } } /* * If the file is owned by the same account that is running the current * process, it's okay to read from that file. */ if (info && EqualSid(owner_sid, info->User.Sid)) ret = GIT_OK; else { git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid"); ret = GIT_ERROR; } git__free(info); cleanup: if (descriptor) LocalFree(descriptor); return ret; #endif } git2r/src/libgit2/src/odb_loose.c0000644000175000017500000007052714125111754016472 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include #include "git2/object.h" #include "git2/sys/odb_backend.h" #include "futils.h" #include "hash.h" #include "odb.h" #include "delta.h" #include "filebuf.h" #include "object.h" #include "zstream.h" #include "git2/odb_backend.h" #include "git2/types.h" /* maximum possible header length */ #define MAX_HEADER_LEN 64 typedef struct { /* object header data */ git_object_t type; /* object type */ size_t size; /* object size */ } obj_hdr; typedef struct { git_odb_stream stream; git_filebuf fbuf; } loose_writestream; typedef struct { git_odb_stream stream; git_map map; char start[MAX_HEADER_LEN]; size_t start_len; size_t start_read; git_zstream zstream; } loose_readstream; typedef struct loose_backend { git_odb_backend parent; int object_zlib_level; /** loose object zlib compression level. */ int fsync_object_files; /** loose object file fsync flag. */ mode_t object_file_mode; mode_t object_dir_mode; size_t objects_dirlen; char objects_dir[GIT_FLEX_ARRAY]; } loose_backend; /* State structure for exploring directories, * in order to locate objects matching a short oid. */ typedef struct { size_t dir_len; unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */ size_t short_oid_len; int found; /* number of matching * objects already found */ unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of * the object found */ } loose_locate_object_state; /*********************************************************** * * MISCELLANEOUS HELPER FUNCTIONS * ***********************************************************/ static int object_file_name( git_buf *name, const loose_backend *be, const git_oid *id) { size_t alloclen; /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3); if (git_buf_grow(name, alloclen) < 0) return -1; git_buf_set(name, be->objects_dir, be->objects_dirlen); git_path_to_dir(name); /* loose object filename: aa/aaa... (41 bytes) */ git_oid_pathfmt(name->ptr + name->size, id); name->size += GIT_OID_HEXSZ + 1; name->ptr[name->size] = '\0'; return 0; } static int object_mkdir(const git_buf *name, const loose_backend *be) { return git_futils_mkdir_relative( name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL); } static int parse_header_packlike( obj_hdr *out, size_t *out_len, const unsigned char *data, size_t len) { unsigned long c; size_t shift, size, used = 0; if (len == 0) goto on_error; c = data[used++]; out->type = (c >> 4) & 7; size = c & 15; shift = 4; while (c & 0x80) { if (len <= used) goto on_error; if (sizeof(size_t) * 8 <= shift) goto on_error; c = data[used++]; size += (c & 0x7f) << shift; shift += 7; } out->size = size; if (out_len) *out_len = used; return 0; on_error: git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header"); return -1; } static int parse_header( obj_hdr *out, size_t *out_len, const unsigned char *_data, size_t data_len) { const char *data = (char *)_data; size_t i, typename_len, size_idx, size_len; int64_t size; *out_len = 0; /* find the object type name */ for (i = 0, typename_len = 0; i < data_len; i++, typename_len++) { if (data[i] == ' ') break; } if (typename_len == data_len) goto on_error; out->type = git_object_stringn2type(data, typename_len); size_idx = typename_len + 1; for (i = size_idx, size_len = 0; i < data_len; i++, size_len++) { if (data[i] == '\0') break; } if (i == data_len) goto on_error; if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 || size < 0) goto on_error; if ((uint64_t)size > SIZE_MAX) { git_error_set(GIT_ERROR_OBJECT, "object is larger than available memory"); return -1; } out->size = (size_t)size; if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1)) goto on_error; return 0; on_error: git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header"); return -1; } static int is_zlib_compressed_data(unsigned char *data, size_t data_len) { unsigned int w; if (data_len < 2) return 0; w = ((unsigned int)(data[0]) << 8) + data[1]; return (data[0] & 0x8F) == 0x08 && !(w % 31); } /*********************************************************** * * ODB OBJECT READING & WRITING * * Backend for the public API; read headers and full objects * from the ODB. Write raw data to the ODB. * ***********************************************************/ /* * At one point, there was a loose object format that was intended to * mimic the format used in pack-files. This was to allow easy copying * of loose object data into packs. This format is no longer used, but * we must still read it. */ static int read_loose_packlike(git_rawobj *out, git_buf *obj) { git_buf body = GIT_BUF_INIT; const unsigned char *obj_data; obj_hdr hdr; size_t obj_len, head_len, alloc_size; int error; obj_data = (unsigned char *)obj->ptr; obj_len = obj->size; /* * read the object header, which is an (uncompressed) * binary encoding of the object type and size. */ if ((error = parse_header_packlike(&hdr, &head_len, obj_data, obj_len)) < 0) goto done; if (!git_object_typeisloose(hdr.type) || head_len > obj_len) { git_error_set(GIT_ERROR_ODB, "failed to inflate loose object"); error = -1; goto done; } obj_data += head_len; obj_len -= head_len; /* * allocate a buffer and inflate the data into it */ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) || git_buf_init(&body, alloc_size) < 0) { error = -1; goto done; } if ((error = git_zstream_inflatebuf(&body, obj_data, obj_len)) < 0) goto done; out->len = hdr.size; out->type = hdr.type; out->data = git_buf_detach(&body); done: git_buf_dispose(&body); return error; } static int read_loose_standard(git_rawobj *out, git_buf *obj) { git_zstream zstream = GIT_ZSTREAM_INIT; unsigned char head[MAX_HEADER_LEN], *body = NULL; size_t decompressed, head_len, body_len, alloc_size; obj_hdr hdr; int error; if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 || (error = git_zstream_set_input(&zstream, git_buf_cstr(obj), git_buf_len(obj))) < 0) goto done; decompressed = sizeof(head); /* * inflate the initial part of the compressed buffer in order to * parse the header; read the largest header possible, then push the * remainder into the body buffer. */ if ((error = git_zstream_get_output(head, &decompressed, &zstream)) < 0 || (error = parse_header(&hdr, &head_len, head, decompressed)) < 0) goto done; if (!git_object_typeisloose(hdr.type)) { git_error_set(GIT_ERROR_ODB, "failed to inflate disk object"); error = -1; goto done; } /* * allocate a buffer and inflate the object data into it * (including the initial sequence in the head buffer). */ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) || (body = git__calloc(1, alloc_size)) == NULL) { error = -1; goto done; } GIT_ASSERT(decompressed >= head_len); body_len = decompressed - head_len; if (body_len) memcpy(body, head + head_len, body_len); decompressed = hdr.size - body_len; if ((error = git_zstream_get_output(body + body_len, &decompressed, &zstream)) < 0) goto done; if (!git_zstream_done(&zstream)) { git_error_set(GIT_ERROR_ZLIB, "failed to finish zlib inflation: stream aborted prematurely"); error = -1; goto done; } body[hdr.size] = '\0'; out->data = body; out->len = hdr.size; out->type = hdr.type; done: if (error < 0) git__free(body); git_zstream_free(&zstream); return error; } static int read_loose(git_rawobj *out, git_buf *loc) { int error; git_buf obj = GIT_BUF_INIT; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(loc); if (git_buf_oom(loc)) return -1; out->data = NULL; out->len = 0; out->type = GIT_OBJECT_INVALID; if ((error = git_futils_readbuffer(&obj, loc->ptr)) < 0) goto done; if (!is_zlib_compressed_data((unsigned char *)obj.ptr, obj.size)) error = read_loose_packlike(out, &obj); else error = read_loose_standard(out, &obj); done: git_buf_dispose(&obj); return error; } static int read_header_loose_packlike( git_rawobj *out, const unsigned char *data, size_t len) { obj_hdr hdr; size_t header_len; int error; if ((error = parse_header_packlike(&hdr, &header_len, data, len)) < 0) return error; out->len = hdr.size; out->type = hdr.type; return error; } static int read_header_loose_standard( git_rawobj *out, const unsigned char *data, size_t len) { git_zstream zs = GIT_ZSTREAM_INIT; obj_hdr hdr = {0}; unsigned char inflated[MAX_HEADER_LEN] = {0}; size_t header_len, inflated_len = sizeof(inflated); int error; if ((error = git_zstream_init(&zs, GIT_ZSTREAM_INFLATE)) < 0 || (error = git_zstream_set_input(&zs, data, len)) < 0 || (error = git_zstream_get_output_chunk(inflated, &inflated_len, &zs)) < 0 || (error = parse_header(&hdr, &header_len, inflated, inflated_len)) < 0) goto done; out->len = hdr.size; out->type = hdr.type; done: git_zstream_free(&zs); return error; } static int read_header_loose(git_rawobj *out, git_buf *loc) { unsigned char obj[1024]; ssize_t obj_len; int fd, error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(loc); if (git_buf_oom(loc)) return -1; out->data = NULL; if ((error = fd = git_futils_open_ro(loc->ptr)) < 0) goto done; if ((obj_len = p_read(fd, obj, sizeof(obj))) < 0) { error = (int)obj_len; goto done; } if (!is_zlib_compressed_data(obj, (size_t)obj_len)) error = read_header_loose_packlike(out, obj, (size_t)obj_len); else error = read_header_loose_standard(out, obj, (size_t)obj_len); if (!error && !git_object_typeisloose(out->type)) { git_error_set(GIT_ERROR_ZLIB, "failed to read loose object header"); error = -1; goto done; } done: if (fd >= 0) p_close(fd); return error; } static int locate_object( git_buf *object_location, loose_backend *backend, const git_oid *oid) { int error = object_file_name(object_location, backend, oid); if (!error && !git_path_exists(object_location->ptr)) return GIT_ENOTFOUND; return error; } /* Explore an entry of a directory and see if it matches a short oid */ static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) { loose_locate_object_state *sstate = (loose_locate_object_state *)state; if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) { /* Entry cannot be an object. Continue to next entry */ return 0; } if (git_path_isdir(pathbuf->ptr) == false) { /* We are already in the directory matching the 2 first hex characters, * compare the first ncmp characters of the oids */ if (!memcmp(sstate->short_oid + 2, (unsigned char *)pathbuf->ptr + sstate->dir_len, sstate->short_oid_len - 2)) { if (!sstate->found) { sstate->res_oid[0] = sstate->short_oid[0]; sstate->res_oid[1] = sstate->short_oid[1]; memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2); } sstate->found++; } } if (sstate->found > 1) return GIT_EAMBIGUOUS; return 0; } /* Locate an object matching a given short oid */ static int locate_object_short_oid( git_buf *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, size_t len) { char *objects_dir = backend->objects_dir; size_t dir_len = strlen(objects_dir), alloc_len; loose_locate_object_state state; int error; /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3); if (git_buf_grow(object_location, alloc_len) < 0) return -1; git_buf_set(object_location, objects_dir, dir_len); git_path_to_dir(object_location); /* save adjusted position at end of dir so it can be restored later */ dir_len = git_buf_len(object_location); /* Convert raw oid to hex formatted oid */ git_oid_fmt((char *)state.short_oid, short_oid); /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0) return -1; object_location->ptr[object_location->size - 1] = '/'; /* Check that directory exists */ if (git_path_isdir(object_location->ptr) == false) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); state.dir_len = git_buf_len(object_location); state.short_oid_len = len; state.found = 0; /* Explore directory to find a unique object matching short_oid */ error = git_path_direach( object_location, 0, fn_locate_object_short_oid, &state); if (error < 0 && error != GIT_EAMBIGUOUS) return error; if (!state.found) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); if (state.found > 1) return git_odb__error_ambiguous("multiple matches in loose objects"); /* Convert obtained hex formatted oid to raw */ error = git_oid_fromstr(res_oid, (char *)state.res_oid); if (error) return error; /* Update the location according to the oid obtained */ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); git_buf_truncate(object_location, dir_len); if (git_buf_grow(object_location, alloc_len) < 0) return -1; git_oid_pathfmt(object_location->ptr + dir_len, res_oid); object_location->size += GIT_OID_HEXSZ + 1; object_location->ptr[object_location->size] = '\0'; return 0; } /*********************************************************** * * LOOSE BACKEND PUBLIC API * * Implement the git_odb_backend API calls * ***********************************************************/ static int loose_backend__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid) { git_buf object_path = GIT_BUF_INIT; git_rawobj raw; int error; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(oid); raw.len = 0; raw.type = GIT_OBJECT_INVALID; if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) { error = git_odb__error_notfound("no matching loose object", oid, GIT_OID_HEXSZ); } else if ((error = read_header_loose(&raw, &object_path)) == 0) { *len_p = raw.len; *type_p = raw.type; } git_buf_dispose(&object_path); return error; } static int loose_backend__read(void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid) { git_buf object_path = GIT_BUF_INIT; git_rawobj raw; int error = 0; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(oid); if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) { error = git_odb__error_notfound("no matching loose object", oid, GIT_OID_HEXSZ); } else if ((error = read_loose(&raw, &object_path)) == 0) { *buffer_p = raw.data; *len_p = raw.len; *type_p = raw.type; } git_buf_dispose(&object_path); return error; } static int loose_backend__read_prefix( git_oid *out_oid, void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *short_oid, size_t len) { int error = 0; GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ); if (len == GIT_OID_HEXSZ) { /* We can fall back to regular read method */ error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); if (!error) git_oid_cpy(out_oid, short_oid); } else { git_buf object_path = GIT_BUF_INIT; git_rawobj raw; GIT_ASSERT_ARG(backend && short_oid); if ((error = locate_object_short_oid(&object_path, out_oid, (loose_backend *)backend, short_oid, len)) == 0 && (error = read_loose(&raw, &object_path)) == 0) { *buffer_p = raw.data; *len_p = raw.len; *type_p = raw.type; } git_buf_dispose(&object_path); } return error; } static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) { git_buf object_path = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(oid); error = locate_object(&object_path, (loose_backend *)backend, oid); git_buf_dispose(&object_path); return !error; } static int loose_backend__exists_prefix( git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len) { git_buf object_path = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(short_id); GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN); error = locate_object_short_oid( &object_path, out, (loose_backend *)backend, short_id, len); git_buf_dispose(&object_path); return error; } struct foreach_state { size_t dir_len; git_odb_foreach_cb cb; void *data; }; GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) { int v, i = 0; if (strlen(ptr) != GIT_OID_HEXSZ+1) return -1; if (ptr[2] != '/') { return -1; } v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]); if (v < 0) return -1; oid->id[0] = (unsigned char) v; ptr += 3; for (i = 0; i < 38; i += 2) { v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]); if (v < 0) return -1; oid->id[1 + i/2] = (unsigned char) v; } return 0; } static int foreach_object_dir_cb(void *_state, git_buf *path) { git_oid oid; struct foreach_state *state = (struct foreach_state *) _state; if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) return 0; return git_error_set_after_callback_function( state->cb(&oid, state->data), "git_odb_foreach"); } static int foreach_cb(void *_state, git_buf *path) { struct foreach_state *state = (struct foreach_state *) _state; /* non-dir is some stray file, ignore it */ if (!git_path_isdir(git_buf_cstr(path))) return 0; return git_path_direach(path, 0, foreach_object_dir_cb, state); } static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) { char *objects_dir; int error; git_buf buf = GIT_BUF_INIT; struct foreach_state state; loose_backend *backend = (loose_backend *) _backend; GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(cb); objects_dir = backend->objects_dir; git_buf_sets(&buf, objects_dir); git_path_to_dir(&buf); if (git_buf_oom(&buf)) return -1; memset(&state, 0, sizeof(state)); state.cb = cb; state.data = data; state.dir_len = git_buf_len(&buf); error = git_path_direach(&buf, 0, foreach_cb, &state); git_buf_dispose(&buf); return error; } static int loose_backend__writestream_finalize(git_odb_stream *_stream, const git_oid *oid) { loose_writestream *stream = (loose_writestream *)_stream; loose_backend *backend = (loose_backend *)_stream->backend; git_buf final_path = GIT_BUF_INIT; int error = 0; if (object_file_name(&final_path, backend, oid) < 0 || object_mkdir(&final_path, backend) < 0) error = -1; else error = git_filebuf_commit_at( &stream->fbuf, final_path.ptr); git_buf_dispose(&final_path); return error; } static int loose_backend__writestream_write(git_odb_stream *_stream, const char *data, size_t len) { loose_writestream *stream = (loose_writestream *)_stream; return git_filebuf_write(&stream->fbuf, data, len); } static void loose_backend__writestream_free(git_odb_stream *_stream) { loose_writestream *stream = (loose_writestream *)_stream; git_filebuf_cleanup(&stream->fbuf); git__free(stream); } static int filebuf_flags(loose_backend *backend) { int flags = GIT_FILEBUF_TEMPORARY | (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT); if (backend->fsync_object_files || git_repository__fsync_gitdir) flags |= GIT_FILEBUF_FSYNC; return flags; } static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_object_size_t length, git_object_t type) { loose_backend *backend; loose_writestream *stream = NULL; char hdr[MAX_HEADER_LEN]; git_buf tmp_path = GIT_BUF_INIT; size_t hdrlen; int error; GIT_ASSERT_ARG(_backend); backend = (loose_backend *)_backend; *stream_out = NULL; if ((error = git_odb__format_object_header(&hdrlen, hdr, sizeof(hdr), length, type)) < 0) return error; stream = git__calloc(1, sizeof(loose_writestream)); GIT_ERROR_CHECK_ALLOC(stream); stream->stream.backend = _backend; stream->stream.read = NULL; /* read only */ stream->stream.write = &loose_backend__writestream_write; stream->stream.finalize_write = &loose_backend__writestream_finalize; stream->stream.free = &loose_backend__writestream_free; stream->stream.mode = GIT_STREAM_WRONLY; if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 || git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend), backend->object_file_mode) < 0 || stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0) { git_filebuf_cleanup(&stream->fbuf); git__free(stream); stream = NULL; } git_buf_dispose(&tmp_path); *stream_out = (git_odb_stream *)stream; return !stream ? -1 : 0; } static int loose_backend__readstream_read( git_odb_stream *_stream, char *buffer, size_t buffer_len) { loose_readstream *stream = (loose_readstream *)_stream; size_t start_remain = stream->start_len - stream->start_read; int total = 0, error; buffer_len = min(buffer_len, INT_MAX); /* * if we read more than just the header in the initial read, play * that back for the caller. */ if (start_remain && buffer_len) { size_t chunk = min(start_remain, buffer_len); memcpy(buffer, stream->start + stream->start_read, chunk); buffer += chunk; stream->start_read += chunk; total += (int)chunk; buffer_len -= chunk; } if (buffer_len) { size_t chunk = buffer_len; if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0) return error; total += (int)chunk; } return (int)total; } static void loose_backend__readstream_free(git_odb_stream *_stream) { loose_readstream *stream = (loose_readstream *)_stream; git_futils_mmap_free(&stream->map); git_zstream_free(&stream->zstream); git__free(stream); } static int loose_backend__readstream_packlike( obj_hdr *hdr, loose_readstream *stream) { const unsigned char *data; size_t data_len, head_len; int error; data = stream->map.data; data_len = stream->map.len; /* * read the object header, which is an (uncompressed) * binary encoding of the object type and size. */ if ((error = parse_header_packlike(hdr, &head_len, data, data_len)) < 0) return error; if (!git_object_typeisloose(hdr->type)) { git_error_set(GIT_ERROR_ODB, "failed to inflate loose object"); return -1; } return git_zstream_set_input(&stream->zstream, data + head_len, data_len - head_len); } static int loose_backend__readstream_standard( obj_hdr *hdr, loose_readstream *stream) { unsigned char head[MAX_HEADER_LEN]; size_t init, head_len; int error; if ((error = git_zstream_set_input(&stream->zstream, stream->map.data, stream->map.len)) < 0) return error; init = sizeof(head); /* * inflate the initial part of the compressed buffer in order to * parse the header; read the largest header possible, then store * it in the `start` field of the stream object. */ if ((error = git_zstream_get_output(head, &init, &stream->zstream)) < 0 || (error = parse_header(hdr, &head_len, head, init)) < 0) return error; if (!git_object_typeisloose(hdr->type)) { git_error_set(GIT_ERROR_ODB, "failed to inflate disk object"); return -1; } if (init > head_len) { stream->start_len = init - head_len; memcpy(stream->start, head + head_len, init - head_len); } return 0; } static int loose_backend__readstream( git_odb_stream **stream_out, size_t *len_out, git_object_t *type_out, git_odb_backend *_backend, const git_oid *oid) { loose_backend *backend; loose_readstream *stream = NULL; git_hash_ctx *hash_ctx = NULL; git_buf object_path = GIT_BUF_INIT; obj_hdr hdr; int error = 0; GIT_ASSERT_ARG(stream_out); GIT_ASSERT_ARG(len_out); GIT_ASSERT_ARG(type_out); GIT_ASSERT_ARG(_backend); GIT_ASSERT_ARG(oid); backend = (loose_backend *)_backend; *stream_out = NULL; *len_out = 0; *type_out = GIT_OBJECT_INVALID; if (locate_object(&object_path, backend, oid) < 0) { error = git_odb__error_notfound("no matching loose object", oid, GIT_OID_HEXSZ); goto done; } stream = git__calloc(1, sizeof(loose_readstream)); GIT_ERROR_CHECK_ALLOC(stream); hash_ctx = git__malloc(sizeof(git_hash_ctx)); GIT_ERROR_CHECK_ALLOC(hash_ctx); if ((error = git_hash_ctx_init(hash_ctx)) < 0 || (error = git_futils_mmap_ro_file(&stream->map, object_path.ptr)) < 0 || (error = git_zstream_init(&stream->zstream, GIT_ZSTREAM_INFLATE)) < 0) goto done; /* check for a packlike loose object */ if (!is_zlib_compressed_data(stream->map.data, stream->map.len)) error = loose_backend__readstream_packlike(&hdr, stream); else error = loose_backend__readstream_standard(&hdr, stream); if (error < 0) goto done; stream->stream.backend = _backend; stream->stream.hash_ctx = hash_ctx; stream->stream.read = &loose_backend__readstream_read; stream->stream.free = &loose_backend__readstream_free; *stream_out = (git_odb_stream *)stream; *len_out = hdr.size; *type_out = hdr.type; done: if (error < 0) { if (stream) { git_futils_mmap_free(&stream->map); git_zstream_free(&stream->zstream); git__free(stream); } if (hash_ctx) { git_hash_ctx_cleanup(hash_ctx); git__free(hash_ctx); } } git_buf_dispose(&object_path); return error; } static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type) { int error = 0; git_buf final_path = GIT_BUF_INIT; char header[MAX_HEADER_LEN]; size_t header_len; git_filebuf fbuf = GIT_FILEBUF_INIT; loose_backend *backend; backend = (loose_backend *)_backend; /* prepare the header for the file */ if ((error = git_odb__format_object_header(&header_len, header, sizeof(header), len, type)) < 0) goto cleanup; if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 || git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend), backend->object_file_mode) < 0) { error = -1; goto cleanup; } git_filebuf_write(&fbuf, header, header_len); git_filebuf_write(&fbuf, data, len); if (object_file_name(&final_path, backend, oid) < 0 || object_mkdir(&final_path, backend) < 0 || git_filebuf_commit_at(&fbuf, final_path.ptr) < 0) error = -1; cleanup: if (error < 0) git_filebuf_cleanup(&fbuf); git_buf_dispose(&final_path); return error; } static int loose_backend__freshen( git_odb_backend *_backend, const git_oid *oid) { loose_backend *backend = (loose_backend *)_backend; git_buf path = GIT_BUF_INIT; int error; if (object_file_name(&path, backend, oid) < 0) return -1; error = git_futils_touch(path.ptr, NULL); git_buf_dispose(&path); return error; } static void loose_backend__free(git_odb_backend *_backend) { git__free(_backend); } int git_odb_backend_loose( git_odb_backend **backend_out, const char *objects_dir, int compression_level, int do_fsync, unsigned int dir_mode, unsigned int file_mode) { loose_backend *backend; size_t objects_dirlen, alloclen; GIT_ASSERT_ARG(backend_out); GIT_ASSERT_ARG(objects_dir); objects_dirlen = strlen(objects_dir); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2); backend = git__calloc(1, alloclen); GIT_ERROR_CHECK_ALLOC(backend); backend->parent.version = GIT_ODB_BACKEND_VERSION; backend->objects_dirlen = objects_dirlen; memcpy(backend->objects_dir, objects_dir, objects_dirlen); if (backend->objects_dir[backend->objects_dirlen - 1] != '/') backend->objects_dir[backend->objects_dirlen++] = '/'; if (compression_level < 0) compression_level = Z_BEST_SPEED; if (dir_mode == 0) dir_mode = GIT_OBJECT_DIR_MODE; if (file_mode == 0) file_mode = GIT_OBJECT_FILE_MODE; backend->object_zlib_level = compression_level; backend->fsync_object_files = do_fsync; backend->object_dir_mode = dir_mode; backend->object_file_mode = file_mode; backend->parent.read = &loose_backend__read; backend->parent.write = &loose_backend__write; backend->parent.read_prefix = &loose_backend__read_prefix; backend->parent.read_header = &loose_backend__read_header; backend->parent.writestream = &loose_backend__writestream; backend->parent.readstream = &loose_backend__readstream; backend->parent.exists = &loose_backend__exists; backend->parent.exists_prefix = &loose_backend__exists_prefix; backend->parent.foreach = &loose_backend__foreach; backend->parent.freshen = &loose_backend__freshen; backend->parent.free = &loose_backend__free; *backend_out = (git_odb_backend *)backend; return 0; } git2r/src/libgit2/src/cache.h0000644000175000017500000000325314125111754015565 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_cache_h__ #define INCLUDE_cache_h__ #include "common.h" #include "git2/common.h" #include "git2/oid.h" #include "git2/odb.h" #include "thread.h" #include "oidmap.h" enum { GIT_CACHE_STORE_ANY = 0, GIT_CACHE_STORE_RAW = 1, GIT_CACHE_STORE_PARSED = 2 }; typedef struct { git_oid oid; int16_t type; /* git_object_t value */ uint16_t flags; /* GIT_CACHE_STORE value */ size_t size; git_atomic32 refcount; } git_cached_obj; typedef struct { git_oidmap *map; git_rwlock lock; ssize_t used_memory; } git_cache; extern bool git_cache__enabled; extern ssize_t git_cache__max_storage; extern git_atomic_ssize git_cache__current_storage; int git_cache_set_max_object_size(git_object_t type, size_t size); int git_cache_init(git_cache *cache); void git_cache_dispose(git_cache *cache); void git_cache_clear(git_cache *cache); void *git_cache_store_raw(git_cache *cache, git_odb_object *entry); void *git_cache_store_parsed(git_cache *cache, git_object *entry); git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid); git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid); void *git_cache_get_any(git_cache *cache, const git_oid *oid); GIT_INLINE(size_t) git_cache_size(git_cache *cache) { return (size_t)git_oidmap_size(cache->map); } GIT_INLINE(void) git_cached_obj_incref(void *_obj) { git_cached_obj *obj = _obj; git_atomic32_inc(&obj->refcount); } void git_cached_obj_decref(void *_obj); #endif git2r/src/libgit2/src/posix.h0000644000175000017500000001066514125111754015671 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_posix_h__ #define INCLUDE_posix_h__ #include "common.h" #include #include /* stat: file mode type testing macros */ #ifndef S_IFGITLINK #define S_IFGITLINK 0160000 #define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK) #endif #ifndef S_IFLNK #define S_IFLNK 0120000 #undef _S_IFLNK #define _S_IFLNK S_IFLNK #endif #ifndef S_IWUSR #define S_IWUSR 00200 #endif #ifndef S_IXUSR #define S_IXUSR 00100 #endif #ifndef S_ISLNK #define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) #endif #ifndef S_ISDIR #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) #endif #ifndef S_ISFIFO #define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO) #endif /* if S_ISGID is not defined, then don't try to set it */ #ifndef S_ISGID #define S_ISGID 0 #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #ifndef SOCK_CLOEXEC #define SOCK_CLOEXEC 0 #endif /* access() mode parameter #defines */ #ifndef F_OK #define F_OK 0 /* existence check */ #endif #ifndef W_OK #define W_OK 2 /* write mode check */ #endif #ifndef R_OK #define R_OK 4 /* read mode check */ #endif /* Determine whether an errno value indicates that a read or write failed * because the descriptor is blocked. */ #if defined(EWOULDBLOCK) #define GIT_ISBLOCKED(e) ((e) == EAGAIN || (e) == EWOULDBLOCK) #else #define GIT_ISBLOCKED(e) ((e) == EAGAIN) #endif /* define some standard errnos that the runtime may be missing. for example, * mingw lacks EAFNOSUPPORT. */ #ifndef EAFNOSUPPORT #define EAFNOSUPPORT (INT_MAX-1) #endif /* Compiler independent macro to handle signal interrpted system calls */ #define HANDLE_EINTR(result, x) do { \ result = (x); \ } while (result == -1 && errno == EINTR); /* Provide a 64-bit size for offsets. */ #if defined(_MSC_VER) typedef __int64 off64_t; #elif defined(__HAIKU__) typedef __haiku_std_int64 off64_t; #elif defined(__APPLE__) typedef __int64_t off64_t; #else typedef int64_t off64_t; #endif typedef int git_file; /** * Standard POSIX Methods * * All the methods starting with the `p_` prefix are * direct ports of the standard POSIX methods. * * Some of the methods are slightly wrapped to provide * saner defaults. Some of these methods are emulated * in Windows platforms. * * Use your manpages to check the docs on these. */ extern ssize_t p_read(git_file fd, void *buf, size_t cnt); extern int p_write(git_file fd, const void *buf, size_t cnt); extern ssize_t p_pread(int fd, void *data, size_t size, off64_t offset); extern ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset); #define p_close(fd) close(fd) #define p_umask(m) umask(m) extern int p_open(const char *path, int flags, ...); extern int p_creat(const char *path, mode_t mode); extern int p_getcwd(char *buffer_out, size_t size); extern int p_rename(const char *from, const char *to); extern int git__page_size(size_t *page_size); extern int git__mmap_alignment(size_t *page_size); /* The number of times `p_fsync` has been called. Note that this is for * test code only; it it not necessarily thread-safe and should not be * relied upon in production. */ extern size_t p_fsync__cnt; /** * Platform-dependent methods */ #ifdef GIT_WIN32 # include "win32/posix.h" #else # include "unix/posix.h" #endif #include "strnlen.h" #ifdef NO_READDIR_R GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { GIT_UNUSED(entry); *result = readdir(dirp); return 0; } #else /* NO_READDIR_R */ # define p_readdir_r(d,e,r) readdir_r(d,e,r) #endif #ifdef NO_ADDRINFO # include struct addrinfo { struct hostent *ai_hostent; struct servent *ai_servent; struct sockaddr_in ai_addr_in; struct sockaddr *ai_addr; size_t ai_addrlen; int ai_family; int ai_socktype; int ai_protocol; long ai_port; struct addrinfo *ai_next; }; extern int p_getaddrinfo(const char *host, const char *port, struct addrinfo *hints, struct addrinfo **info); extern void p_freeaddrinfo(struct addrinfo *info); extern const char *p_gai_strerror(int ret); #else # define p_getaddrinfo(a, b, c, d) getaddrinfo(a, b, c, d) # define p_freeaddrinfo(a) freeaddrinfo(a) # define p_gai_strerror(c) gai_strerror(c) #endif /* NO_ADDRINFO */ #endif git2r/src/libgit2/src/proxy.h0000644000175000017500000000072314125111754015702 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_proxy_h__ #define INCLUDE_proxy_h__ #include "common.h" #include "git2/proxy.h" extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src); extern void git_proxy_options_clear(git_proxy_options *opts); #endif git2r/src/libgit2/src/submodule.c0000644000175000017500000016626614125111754016532 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "submodule.h" #include "git2/config.h" #include "git2/sys/config.h" #include "git2/types.h" #include "git2/index.h" #include "buffer.h" #include "vector.h" #include "posix.h" #include "config_backend.h" #include "config.h" #include "repository.h" #include "tree.h" #include "iterator.h" #include "path.h" #include "index.h" #include "worktree.h" #include "clone.h" #define GIT_MODULES_FILE ".gitmodules" static git_configmap _sm_update_map[] = { {GIT_CONFIGMAP_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT}, {GIT_CONFIGMAP_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE}, {GIT_CONFIGMAP_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE}, {GIT_CONFIGMAP_STRING, "none", GIT_SUBMODULE_UPDATE_NONE}, {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE}, {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT}, }; static git_configmap _sm_ignore_map[] = { {GIT_CONFIGMAP_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}, {GIT_CONFIGMAP_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED}, {GIT_CONFIGMAP_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY}, {GIT_CONFIGMAP_STRING, "all", GIT_SUBMODULE_IGNORE_ALL}, {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE}, {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL}, }; static git_configmap _sm_recurse_map[] = { {GIT_CONFIGMAP_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND}, {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO}, {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES}, }; enum { CACHE_OK = 0, CACHE_REFRESH = 1, CACHE_FLUSH = 2 }; enum { GITMODULES_EXISTING = 0, GITMODULES_CREATE = 1, }; static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name); static git_config_backend *open_gitmodules(git_repository *repo, int gitmod); static int gitmodules_snapshot(git_config **snap, git_repository *repo); static int get_url_base(git_buf *url, git_repository *repo); static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo); static int lookup_default_remote(git_remote **remote, git_repository *repo); static int submodule_load_each(const git_config_entry *entry, void *payload); static int submodule_read_config(git_submodule *sm, git_config *cfg); static int submodule_load_from_wd_lite(git_submodule *); static void submodule_get_index_status(unsigned int *, git_submodule *); static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie); static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id); static int submodule_cmp(const void *a, const void *b) { return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name); } static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix) { ssize_t idx = git_buf_rfind(key, '.'); git_buf_truncate(key, (size_t)(idx + 1)); return git_buf_puts(key, suffix); } /* * PUBLIC APIS */ static void submodule_set_lookup_error(int error, const char *name) { if (!error) return; git_error_set(GIT_ERROR_SUBMODULE, (error == GIT_ENOTFOUND) ? "no submodule named '%s'" : "submodule '%s' has not been added yet", name); } typedef struct { const char *path; char *name; } fbp_data; static int find_by_path(const git_config_entry *entry, void *payload) { fbp_data *data = payload; if (!strcmp(entry->value, data->path)) { const char *fdot, *ldot; fdot = strchr(entry->name, '.'); ldot = strrchr(entry->name, '.'); data->name = git__strndup(fdot + 1, ldot - fdot - 1); GIT_ERROR_CHECK_ALLOC(data->name); } return 0; } /* * Checks to see if the submodule shares its name with a file or directory that * already exists on the index. If so, the submodule cannot be added. */ static int is_path_occupied(bool *occupied, git_repository *repo, const char *path) { int error = 0; git_index *index; git_buf dir = GIT_BUF_INIT; *occupied = false; if ((error = git_repository_index__weakptr(&index, repo)) < 0) goto out; if ((error = git_index_find(NULL, index, path)) != GIT_ENOTFOUND) { if (!error) { git_error_set(GIT_ERROR_SUBMODULE, "File '%s' already exists in the index", path); *occupied = true; } goto out; } if ((error = git_buf_sets(&dir, path)) < 0) goto out; if ((error = git_path_to_dir(&dir)) < 0) goto out; if ((error = git_index_find_prefix(NULL, index, dir.ptr)) != GIT_ENOTFOUND) { if (!error) { git_error_set(GIT_ERROR_SUBMODULE, "Directory '%s' already exists in the index", path); *occupied = true; } goto out; } error = 0; out: git_buf_dispose(&dir); return error; } /** * Release the name map returned by 'load_submodule_names'. */ static void free_submodule_names(git_strmap *names) { const char *key; char *value; if (names == NULL) return; git_strmap_foreach(names, key, value, { git__free((char *) key); git__free(value); }); git_strmap_free(names); return; } /** * Map submodule paths to names. * TODO: for some use-cases, this might need case-folding on a * case-insensitive filesystem */ static int load_submodule_names(git_strmap **out, git_repository *repo, git_config *cfg) { const char *key = "submodule\\..*\\.path"; git_config_iterator *iter = NULL; git_config_entry *entry; git_buf buf = GIT_BUF_INIT; git_strmap *names; int isvalid, error; *out = NULL; if ((error = git_strmap_new(&names)) < 0) goto out; if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0) goto out; while ((error = git_config_next(&entry, iter)) == 0) { const char *fdot, *ldot; fdot = strchr(entry->name, '.'); ldot = strrchr(entry->name, '.'); if (git_strmap_exists(names, entry->value)) { git_error_set(GIT_ERROR_SUBMODULE, "duplicated submodule path '%s'", entry->value); error = -1; goto out; } git_buf_clear(&buf); git_buf_put(&buf, fdot + 1, ldot - fdot - 1); isvalid = git_submodule_name_is_valid(repo, buf.ptr, 0); if (isvalid < 0) { error = isvalid; goto out; } if (!isvalid) continue; if ((error = git_strmap_set(names, git__strdup(entry->value), git_buf_detach(&buf))) < 0) { git_error_set(GIT_ERROR_NOMEMORY, "error inserting submodule into hash table"); error = -1; goto out; } } if (error == GIT_ITEROVER) error = 0; *out = names; names = NULL; out: free_submodule_names(names); git_buf_dispose(&buf); git_config_iterator_free(iter); return error; } int git_submodule_cache_init(git_strmap **out, git_repository *repo) { int error = 0; git_strmap *cache = NULL; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); if ((error = git_strmap_new(&cache)) < 0) return error; if ((error = git_submodule__map(repo, cache)) < 0) { git_submodule_cache_free(cache); return error; } *out = cache; return error; } int git_submodule_cache_free(git_strmap *cache) { git_submodule *sm = NULL; if (cache == NULL) return 0; git_strmap_foreach_value(cache, sm, { git_submodule_free(sm); }); git_strmap_free(cache); return 0; } int git_submodule_lookup( git_submodule **out, /* NULL if user only wants to test existence */ git_repository *repo, const char *name) /* trailing slash is allowed */ { return git_submodule__lookup_with_cache(out, repo, name, repo->submodule_cache); } int git_submodule__lookup_with_cache( git_submodule **out, /* NULL if user only wants to test existence */ git_repository *repo, const char *name, /* trailing slash is allowed */ git_strmap *cache) { int error; unsigned int location; git_submodule *sm; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if (repo->is_bare) { git_error_set(GIT_ERROR_SUBMODULE, "cannot get submodules without a working tree"); return -1; } if (cache != NULL) { if ((sm = git_strmap_get(cache, name)) != NULL) { if (out) { *out = sm; GIT_REFCOUNT_INC(*out); } return 0; } } if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; if ((error = git_submodule_reload(sm, false)) < 0) { git_submodule_free(sm); return error; } if ((error = git_submodule_location(&location, sm)) < 0) { git_submodule_free(sm); return error; } /* If it's not configured or we're looking by path */ if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) { git_config_backend *mods; const char *pattern = "submodule\\..*\\.path"; git_buf path = GIT_BUF_INIT; fbp_data data = { NULL, NULL }; git_buf_puts(&path, name); while (path.ptr[path.size-1] == '/') { path.ptr[--path.size] = '\0'; } data.path = path.ptr; mods = open_gitmodules(repo, GITMODULES_EXISTING); if (mods) error = git_config_backend_foreach_match(mods, pattern, find_by_path, &data); git_config_backend_free(mods); if (error < 0) { git_submodule_free(sm); git_buf_dispose(&path); return error; } if (data.name) { git__free(sm->name); sm->name = data.name; sm->path = git_buf_detach(&path); /* Try to load again with the right name */ if ((error = git_submodule_reload(sm, false)) < 0) { git_submodule_free(sm); return error; } } git_buf_dispose(&path); } if ((error = git_submodule_location(&location, sm)) < 0) { git_submodule_free(sm); return error; } /* If we still haven't found it, do the WD check */ if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) { git_submodule_free(sm); error = GIT_ENOTFOUND; /* If it's not configured, we still check if there's a repo at the path */ if (git_repository_workdir(repo)) { git_buf path = GIT_BUF_INIT; if (git_buf_join3(&path, '/', git_repository_workdir(repo), name, DOT_GIT) < 0 || git_path_validate_workdir_buf(NULL, &path) < 0) return -1; if (git_path_exists(path.ptr)) error = GIT_EEXISTS; git_buf_dispose(&path); } submodule_set_lookup_error(error, name); return error; } if (out) *out = sm; else git_submodule_free(sm); return 0; } int git_submodule_name_is_valid(git_repository *repo, const char *name, int flags) { git_buf buf = GIT_BUF_INIT; int error, isvalid; if (flags == 0) flags = GIT_PATH_REJECT_FILESYSTEM_DEFAULTS; /* Avoid allocating a new string if we can avoid it */ if (strchr(name, '\\') != NULL) { if ((error = git_path_normalize_slashes(&buf, name)) < 0) return error; } else { git_buf_attach_notowned(&buf, name, strlen(name)); } isvalid = git_path_validate(repo, buf.ptr, 0, flags); git_buf_dispose(&buf); return isvalid; } static void submodule_free_dup(void *sm) { git_submodule_free(sm); } static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name) { git_submodule *sm = NULL; int error; if ((sm = git_strmap_get(map, name)) != NULL) goto done; /* if the submodule doesn't exist yet in the map, create it */ if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; if ((error = git_strmap_set(map, sm->name, sm)) < 0) { git_submodule_free(sm); return error; } done: GIT_REFCOUNT_INC(sm); *out = sm; return 0; } static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg) { int error; git_iterator *i = NULL; const git_index_entry *entry; git_strmap *names; if ((error = load_submodule_names(&names, git_index_owner(idx), cfg))) goto done; if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0) goto done; while (!(error = git_iterator_advance(&entry, i))) { git_submodule *sm; if ((sm = git_strmap_get(map, entry->path)) != NULL) { if (S_ISGITLINK(entry->mode)) submodule_update_from_index_entry(sm, entry); else sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { const char *name; if ((name = git_strmap_get(names, entry->path)) == NULL) name = entry->path; if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) { submodule_update_from_index_entry(sm, entry); git_submodule_free(sm); } } } if (error == GIT_ITEROVER) error = 0; done: git_iterator_free(i); free_submodule_names(names); return error; } static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg) { int error; git_iterator *i = NULL; const git_index_entry *entry; git_strmap *names; if ((error = load_submodule_names(&names, git_tree_owner(head), cfg))) goto done; if ((error = git_iterator_for_tree(&i, head, NULL)) < 0) goto done; while (!(error = git_iterator_advance(&entry, i))) { git_submodule *sm; if ((sm = git_strmap_get(map, entry->path)) != NULL) { if (S_ISGITLINK(entry->mode)) submodule_update_from_head_data(sm, entry->mode, &entry->id); else sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { const char *name; if ((name = git_strmap_get(names, entry->path)) == NULL) name = entry->path; if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) { submodule_update_from_head_data( sm, entry->mode, &entry->id); git_submodule_free(sm); } } } if (error == GIT_ITEROVER) error = 0; done: git_iterator_free(i); free_submodule_names(names); return error; } /* If have_sm is true, sm is populated, otherwise map an repo are. */ typedef struct { git_config *mods; git_strmap *map; git_repository *repo; } lfc_data; int git_submodule__map(git_repository *repo, git_strmap *map) { int error = 0; git_index *idx = NULL; git_tree *head = NULL; git_buf path = GIT_BUF_INIT; git_submodule *sm; git_config *mods = NULL; bool has_workdir; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(map); /* get sources that we will need to check */ if (git_repository_index(&idx, repo) < 0) git_error_clear(); if (git_repository_head_tree(&head, repo) < 0) git_error_clear(); has_workdir = git_repository_workdir(repo) != NULL; if (has_workdir && (error = git_repository_workdir_path(&path, repo, GIT_MODULES_FILE)) < 0) goto cleanup; /* add submodule information from .gitmodules */ if (has_workdir) { lfc_data data = { 0 }; data.map = map; data.repo = repo; if ((error = gitmodules_snapshot(&mods, repo)) < 0) { if (error == GIT_ENOTFOUND) error = 0; goto cleanup; } data.mods = mods; if ((error = git_config_foreach( mods, submodule_load_each, &data)) < 0) goto cleanup; } /* add back submodule information from index */ if (mods && idx) { if ((error = submodules_from_index(map, idx, mods)) < 0) goto cleanup; } /* add submodule information from HEAD */ if (mods && head) { if ((error = submodules_from_head(map, head, mods)) < 0) goto cleanup; } /* shallow scan submodules in work tree as needed */ if (has_workdir) { git_strmap_foreach_value(map, sm, { submodule_load_from_wd_lite(sm); }); } cleanup: git_config_free(mods); /* TODO: if we got an error, mark submodule config as invalid? */ git_index_free(idx); git_tree_free(head); git_buf_dispose(&path); return error; } int git_submodule_foreach( git_repository *repo, git_submodule_cb callback, void *payload) { git_vector snapshot = GIT_VECTOR_INIT; git_strmap *submodules; git_submodule *sm; int error; size_t i; if (repo->is_bare) { git_error_set(GIT_ERROR_SUBMODULE, "cannot get submodules without a working tree"); return -1; } if ((error = git_strmap_new(&submodules)) < 0) return error; if ((error = git_submodule__map(repo, submodules)) < 0) goto done; if (!(error = git_vector_init( &snapshot, git_strmap_size(submodules), submodule_cmp))) { git_strmap_foreach_value(submodules, sm, { if ((error = git_vector_insert(&snapshot, sm)) < 0) break; GIT_REFCOUNT_INC(sm); }); } if (error < 0) goto done; git_vector_uniq(&snapshot, submodule_free_dup); git_vector_foreach(&snapshot, i, sm) { if ((error = callback(sm, sm->name, payload)) != 0) { git_error_set_after_callback(error); break; } } done: git_vector_foreach(&snapshot, i, sm) git_submodule_free(sm); git_vector_free(&snapshot); git_strmap_foreach_value(submodules, sm, { git_submodule_free(sm); }); git_strmap_free(submodules); return error; } static int submodule_repo_init( git_repository **out, git_repository *parent_repo, const char *path, const char *url, bool use_gitlink) { int error = 0; git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT; git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository *subrepo = NULL; error = git_repository_workdir_path(&workdir, parent_repo, path); if (error < 0) goto cleanup; initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT; initopt.origin_url = url; /* init submodule repository and add origin remote as needed */ /* New style: sub-repo goes in /modules// with a * gitlink in the sub-repo workdir directory to that repository * * Old style: sub-repo goes directly into repo//.git/ */ if (use_gitlink) { error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES); if (error < 0) goto cleanup; error = git_buf_joinpath(&repodir, repodir.ptr, path); if (error < 0) goto cleanup; initopt.workdir_path = workdir.ptr; initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR | GIT_REPOSITORY_INIT_RELATIVE_GITLINK; error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt); } else error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt); cleanup: git_buf_dispose(&workdir); git_buf_dispose(&repodir); *out = subrepo; return error; } int git_submodule_add_setup( git_submodule **out, git_repository *repo, const char *url, const char *path, int use_gitlink) { int error = 0; git_config_backend *mods = NULL; git_submodule *sm = NULL; git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT; git_repository *subrepo = NULL; bool path_occupied; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(url); GIT_ASSERT_ARG(path); /* see if there is already an entry for this submodule */ if (git_submodule_lookup(NULL, repo, path) < 0) git_error_clear(); else { git_error_set(GIT_ERROR_SUBMODULE, "attempt to add submodule '%s' that already exists", path); return GIT_EEXISTS; } /* validate and normalize path */ if (git__prefixcmp(path, git_repository_workdir(repo)) == 0) path += strlen(git_repository_workdir(repo)); if (git_path_root(path) >= 0) { git_error_set(GIT_ERROR_SUBMODULE, "submodule path must be a relative path"); error = -1; goto cleanup; } if ((error = is_path_occupied(&path_occupied, repo, path)) < 0) goto cleanup; if (path_occupied) { error = GIT_EEXISTS; goto cleanup; } /* update .gitmodules */ if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) { git_error_set(GIT_ERROR_SUBMODULE, "adding submodules to a bare repository is not supported"); return -1; } if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 || (error = git_config_backend_set_string(mods, name.ptr, path)) < 0) goto cleanup; if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 || (error = git_config_backend_set_string(mods, name.ptr, url)) < 0) goto cleanup; git_buf_clear(&name); /* init submodule repository and add origin remote as needed */ error = git_repository_workdir_path(&name, repo, path); if (error < 0) goto cleanup; /* if the repo does not already exist, then init a new repo and add it. * Otherwise, just add the existing repo. */ if (!(git_path_exists(name.ptr) && git_path_contains(&name, DOT_GIT))) { /* resolve the actual URL to use */ if ((error = git_submodule_resolve_url(&real_url, repo, url)) < 0) goto cleanup; if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0) goto cleanup; } if ((error = git_submodule_lookup(&sm, repo, path)) < 0) goto cleanup; error = git_submodule_init(sm, false); cleanup: if (error && sm) { git_submodule_free(sm); sm = NULL; } if (out != NULL) *out = sm; git_config_backend_free(mods); git_repository_free(subrepo); git_buf_dispose(&real_url); git_buf_dispose(&name); return error; } int git_submodule_repo_init( git_repository **out, const git_submodule *sm, int use_gitlink) { int error; git_repository *sub_repo = NULL; const char *configured_url; git_config *cfg = NULL; git_buf buf = GIT_BUF_INIT; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(sm); /* get the configured remote url of the submodule */ if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 || (error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 || (error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 || (error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0) goto done; *out = sub_repo; done: git_config_free(cfg); git_buf_dispose(&buf); return error; } static int clone_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload) { GIT_UNUSED(url); GIT_UNUSED(payload); return git_remote_lookup(out, repo, name); } static int clone_return_repo(git_repository **out, const char *path, int bare, void *payload) { git_submodule *sm = payload; GIT_UNUSED(path); GIT_UNUSED(bare); return git_submodule_open(out, sm); } int git_submodule_clone(git_repository **out, git_submodule *submodule, const git_submodule_update_options *given_opts) { int error; git_repository *clone; git_buf rel_path = GIT_BUF_INIT; git_submodule_update_options sub_opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; git_clone_options opts = GIT_CLONE_OPTIONS_INIT; GIT_ASSERT_ARG(submodule); if (given_opts) memcpy(&sub_opts, given_opts, sizeof(sub_opts)); GIT_ERROR_CHECK_VERSION(&sub_opts, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options"); memcpy(&opts.checkout_opts, &sub_opts.checkout_opts, sizeof(sub_opts.checkout_opts)); memcpy(&opts.fetch_opts, &sub_opts.fetch_opts, sizeof(sub_opts.fetch_opts)); opts.repository_cb = clone_return_repo; opts.repository_cb_payload = submodule; opts.remote_cb = clone_return_origin; opts.remote_cb_payload = submodule; error = git_repository_workdir_path(&rel_path, git_submodule_owner(submodule), git_submodule_path(submodule)); if (error < 0) goto cleanup; error = git_clone__submodule(&clone, git_submodule_url(submodule), git_buf_cstr(&rel_path), &opts); if (error < 0) goto cleanup; if (!out) git_repository_free(clone); else *out = clone; cleanup: git_buf_dispose(&rel_path); return error; } int git_submodule_add_finalize(git_submodule *sm) { int error; git_index *index; GIT_ASSERT_ARG(sm); if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 || (error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0) return error; return git_submodule_add_to_index(sm, true); } int git_submodule_add_to_index(git_submodule *sm, int write_index) { int error; git_repository *sm_repo = NULL; git_index *index; git_buf path = GIT_BUF_INIT; git_commit *head; git_index_entry entry; struct stat st; GIT_ASSERT_ARG(sm); /* force reload of wd OID by git_submodule_open */ sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID; if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 || (error = git_repository_workdir_path(&path, sm->repo, sm->path)) < 0 || (error = git_submodule_open(&sm_repo, sm)) < 0) goto cleanup; /* read stat information for submodule working directory */ if (p_stat(path.ptr, &st) < 0) { git_error_set(GIT_ERROR_SUBMODULE, "cannot add submodule without working directory"); error = -1; goto cleanup; } memset(&entry, 0, sizeof(entry)); entry.path = sm->path; git_index_entry__init_from_stat( &entry, &st, !(git_index_caps(index) & GIT_INDEX_CAPABILITY_NO_FILEMODE)); /* calling git_submodule_open will have set sm->wd_oid if possible */ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) { git_error_set(GIT_ERROR_SUBMODULE, "cannot add submodule without HEAD to index"); error = -1; goto cleanup; } git_oid_cpy(&entry.id, &sm->wd_oid); if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0) goto cleanup; entry.ctime.seconds = (int32_t)git_commit_time(head); entry.ctime.nanoseconds = 0; entry.mtime.seconds = (int32_t)git_commit_time(head); entry.mtime.nanoseconds = 0; git_commit_free(head); /* add it */ error = git_index_add(index, &entry); /* write it, if requested */ if (!error && write_index) { error = git_index_write(index); if (!error) git_oid_cpy(&sm->index_oid, &sm->wd_oid); } cleanup: git_repository_free(sm_repo); git_buf_dispose(&path); return error; } static const char *submodule_update_to_str(git_submodule_update_t update) { int i; for (i = 0; i < (int)ARRAY_SIZE(_sm_update_map); ++i) if (_sm_update_map[i].map_value == (int)update) return _sm_update_map[i].str_match; return NULL; } git_repository *git_submodule_owner(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); return submodule->repo; } const char *git_submodule_name(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); return submodule->name; } const char *git_submodule_path(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); return submodule->path; } const char *git_submodule_url(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); return submodule->url; } int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url) { int error = 0; git_buf normalized = GIT_BUF_INIT; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(url); if ((error = git_buf_sanitize(out)) < 0) return error; /* We do this in all platforms in case someone on Windows created the .gitmodules */ if (strchr(url, '\\')) { if ((error = git_path_normalize_slashes(&normalized, url)) < 0) return error; url = normalized.ptr; } if (git_path_is_relative(url)) { if (!(error = get_url_base(out, repo))) error = git_path_apply_relative(out, url); } else if (strchr(url, ':') != NULL || url[0] == '/') { error = git_buf_sets(out, url); } else { git_error_set(GIT_ERROR_SUBMODULE, "invalid format for submodule URL"); error = -1; } git_buf_dispose(&normalized); return error; } static int write_var(git_repository *repo, const char *name, const char *var, const char *val) { git_buf key = GIT_BUF_INIT; git_config_backend *mods; int error; mods = open_gitmodules(repo, GITMODULES_CREATE); if (!mods) return -1; if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0) goto cleanup; if (val) error = git_config_backend_set_string(mods, key.ptr, val); else error = git_config_backend_delete(mods, key.ptr); git_buf_dispose(&key); cleanup: git_config_backend_free(mods); return error; } static int write_mapped_var(git_repository *repo, const char *name, git_configmap *maps, size_t nmaps, const char *var, int ival) { git_configmap_t type; const char *val; if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) { git_error_set(GIT_ERROR_SUBMODULE, "invalid value for %s", var); return -1; } if (type == GIT_CONFIGMAP_TRUE) val = "true"; return write_var(repo, name, var, val); } const char *git_submodule_branch(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); return submodule->branch; } int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); return write_var(repo, name, "branch", branch); } int git_submodule_set_url(git_repository *repo, const char *name, const char *url) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(url); return write_var(repo, name, "url", url); } const git_oid *git_submodule_index_id(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) return &submodule->index_oid; else return NULL; } const git_oid *git_submodule_head_id(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) return &submodule->head_oid; else return NULL; } const git_oid *git_submodule_wd_id(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL); /* load unless we think we have a valid oid */ if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) { git_repository *subrepo; /* calling submodule open grabs the HEAD OID if possible */ if (!git_submodule_open_bare(&subrepo, submodule)) git_repository_free(subrepo); else git_error_clear(); } if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) return &submodule->wd_oid; else return NULL; } git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_IGNORE_UNSPECIFIED); return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ? GIT_SUBMODULE_IGNORE_NONE : submodule->ignore; } int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore); } git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_UPDATE_NONE); return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ? GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update; } int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update); } git_submodule_recurse_t git_submodule_fetch_recurse_submodules( git_submodule *submodule) { GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_RECURSE_NO); return submodule->fetch_recurse; } int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse); } static int submodule_repo_create( git_repository **out, git_repository *parent_repo, const char *path) { int error = 0; git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT; git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository *subrepo = NULL; initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT | GIT_REPOSITORY_INIT_NO_DOTGIT_DIR | GIT_REPOSITORY_INIT_RELATIVE_GITLINK; /* Workdir: path to sub-repo working directory */ error = git_repository_workdir_path(&workdir, parent_repo, path); if (error < 0) goto cleanup; initopt.workdir_path = workdir.ptr; /** * Repodir: path to the sub-repo. sub-repo goes in: * /modules// with a gitlink in the * sub-repo workdir directory to that repository. */ error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES); if (error < 0) goto cleanup; error = git_buf_joinpath(&repodir, repodir.ptr, path); if (error < 0) goto cleanup; error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt); cleanup: git_buf_dispose(&workdir); git_buf_dispose(&repodir); *out = subrepo; return error; } /** * Callback to override sub-repository creation when * cloning a sub-repository. */ static int git_submodule_update_repo_init_cb( git_repository **out, const char *path, int bare, void *payload) { git_submodule *sm; GIT_UNUSED(bare); sm = payload; return submodule_repo_create(out, sm->repo, path); } int git_submodule_update_options_init(git_submodule_update_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version) { return git_submodule_update_options_init(opts, version); } #endif int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options) { int error; unsigned int submodule_status; git_config *config = NULL; const char *submodule_url; git_repository *sub_repo = NULL; git_remote *remote = NULL; git_object *target_commit = NULL; git_buf buf = GIT_BUF_INIT; git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT; GIT_ASSERT_ARG(sm); if (_update_options) memcpy(&update_options, _update_options, sizeof(git_submodule_update_options)); GIT_ERROR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options"); /* Copy over the remote callbacks */ memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options)); /* Get the status of the submodule to determine if it is already initialized */ if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0) goto done; /* * If submodule work dir is not already initialized, check to see * what we need to do (initialize, clone, return error...) */ if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) { /* * Work dir is not initialized, check to see if the submodule * info has been copied into .git/config */ if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 || (error = git_buf_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0) goto done; if ((error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) { /* * If the error is not "not found" or if it is "not found" and we are not * initializing the submodule, then return error. */ if (error != GIT_ENOTFOUND) goto done; if (!init) { git_error_set(GIT_ERROR_SUBMODULE, "submodule is not initialized"); error = GIT_ERROR; goto done; } /* The submodule has not been initialized yet - initialize it now.*/ if ((error = git_submodule_init(sm, 0)) < 0) goto done; git_config_free(config); config = NULL; if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 || (error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) goto done; } /** submodule is initialized - now clone it **/ /* override repo creation */ clone_options.repository_cb = git_submodule_update_repo_init_cb; clone_options.repository_cb_payload = sm; /* * Do not perform checkout as part of clone, instead we * will checkout the specific commit manually. */ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 || (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 || (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0) goto done; } else { const git_oid *oid; /** * Work dir is initialized - look up the commit in the parent repository's index, * update the workdir contents of the subrepository, and set the subrepository's * head to the new commit. */ if ((error = git_submodule_open(&sub_repo, sm)) < 0) goto done; if ((oid = git_submodule_index_id(sm)) == NULL) { git_error_set(GIT_ERROR_SUBMODULE, "could not get ID of submodule in index"); error = -1; goto done; } /* Look up the target commit in the submodule. */ if ((error = git_object_lookup(&target_commit, sub_repo, oid, GIT_OBJECT_COMMIT)) < 0) { /* If it isn't found then fetch and try again. */ if (error != GIT_ENOTFOUND || !update_options.allow_fetch || (error = lookup_default_remote(&remote, sub_repo)) < 0 || (error = git_remote_fetch(remote, NULL, &update_options.fetch_opts, NULL)) < 0 || (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJECT_COMMIT)) < 0) goto done; } if ((error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 || (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0) goto done; /* Invalidate the wd flags as the workdir has been updated. */ sm->flags = sm->flags & ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID | GIT_SUBMODULE_STATUS__WD_SCANNED); } done: git_buf_dispose(&buf); git_config_free(config); git_object_free(target_commit); git_remote_free(remote); git_repository_free(sub_repo); return error; } int git_submodule_init(git_submodule *sm, int overwrite) { int error; const char *val; git_buf key = GIT_BUF_INIT, effective_submodule_url = GIT_BUF_INIT; git_config *cfg = NULL; if (!sm->url) { git_error_set(GIT_ERROR_SUBMODULE, "no URL configured for submodule '%s'", sm->name); return -1; } if ((error = git_repository_config(&cfg, sm->repo)) < 0) return error; /* write "submodule.NAME.url" */ if ((error = git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 || (error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 || (error = git_config__update_entry( cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0) goto cleanup; /* write "submodule.NAME.update" if not default */ val = (sm->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ? NULL : submodule_update_to_str(sm->update); if ((error = git_buf_printf(&key, "submodule.%s.update", sm->name)) < 0 || (error = git_config__update_entry( cfg, key.ptr, val, overwrite != 0, false)) < 0) goto cleanup; /* success */ cleanup: git_config_free(cfg); git_buf_dispose(&key); git_buf_dispose(&effective_submodule_url); return error; } int git_submodule_sync(git_submodule *sm) { git_buf key = GIT_BUF_INIT, url = GIT_BUF_INIT, remote_name = GIT_BUF_INIT; git_repository *smrepo = NULL; git_config *cfg = NULL; int error = 0; if (!sm->url) { git_error_set(GIT_ERROR_SUBMODULE, "no URL configured for submodule '%s'", sm->name); return -1; } /* copy URL over to config only if it already exists */ if ((error = git_repository_config__weakptr(&cfg, sm->repo)) < 0 || (error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 || (error = git_submodule_resolve_url(&url, sm->repo, sm->url)) < 0 || (error = git_config__update_entry(cfg, key.ptr, url.ptr, true, true)) < 0) goto out; if (!(sm->flags & GIT_SUBMODULE_STATUS_IN_WD)) goto out; /* if submodule exists in the working directory, update remote url */ if ((error = git_submodule_open(&smrepo, sm)) < 0 || (error = git_repository_config__weakptr(&cfg, smrepo)) < 0) goto out; if (lookup_head_remote_key(&remote_name, smrepo) == 0) { if ((error = git_buf_join3(&key, '.', "remote", remote_name.ptr, "url")) < 0) goto out; } else if ((error = git_buf_sets(&key, "remote.origin.url")) < 0) { goto out; } if ((error = git_config__update_entry(cfg, key.ptr, url.ptr, true, false)) < 0) goto out; out: git_repository_free(smrepo); git_buf_dispose(&remote_name); git_buf_dispose(&key); git_buf_dispose(&url); return error; } static int git_submodule__open( git_repository **subrepo, git_submodule *sm, bool bare) { int error; git_buf path = GIT_BUF_INIT; unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH; const char *wd; GIT_ASSERT_ARG(sm); GIT_ASSERT_ARG(subrepo); if (git_repository__ensure_not_bare( sm->repo, "open submodule repository") < 0) return GIT_EBAREREPO; wd = git_repository_workdir(sm->repo); if (git_buf_join3(&path, '/', wd, sm->path, DOT_GIT) < 0) return -1; sm->flags = sm->flags & ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID | GIT_SUBMODULE_STATUS__WD_SCANNED); if (bare) flags |= GIT_REPOSITORY_OPEN_BARE; error = git_repository_open_ext(subrepo, path.ptr, flags, wd); /* if we opened the submodule successfully, grab HEAD OID, etc. */ if (!error) { sm->flags |= GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_SCANNED; if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE)) sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID; else git_error_clear(); } else if (git_path_exists(path.ptr)) { sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED | GIT_SUBMODULE_STATUS_IN_WD; } else { git_buf_rtruncate_at_char(&path, '/'); /* remove "/.git" */ if (git_path_isdir(path.ptr)) sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED; } git_buf_dispose(&path); return error; } int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm) { return git_submodule__open(subrepo, sm, true); } int git_submodule_open(git_repository **subrepo, git_submodule *sm) { return git_submodule__open(subrepo, sm, false); } static void submodule_update_from_index_entry( git_submodule *sm, const git_index_entry *ie) { bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0; if (!S_ISGITLINK(ie->mode)) { if (!already_found) sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; } else { if (already_found) sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; else git_oid_cpy(&sm->index_oid, &ie->id); sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS__INDEX_OID_VALID; } } static int submodule_update_index(git_submodule *sm) { git_index *index; const git_index_entry *ie; if (git_repository_index__weakptr(&index, sm->repo) < 0) return -1; sm->flags = sm->flags & ~(GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS__INDEX_OID_VALID); if (!(ie = git_index_get_bypath(index, sm->path, 0))) return 0; submodule_update_from_index_entry(sm, ie); return 0; } static void submodule_update_from_head_data( git_submodule *sm, mode_t mode, const git_oid *id) { if (!S_ISGITLINK(mode)) sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; else { git_oid_cpy(&sm->head_oid, id); sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS__HEAD_OID_VALID; } } static int submodule_update_head(git_submodule *submodule) { git_tree *head = NULL; git_tree_entry *te = NULL; submodule->flags = submodule->flags & ~(GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS__HEAD_OID_VALID); /* if we can't look up file in current head, then done */ if (git_repository_head_tree(&head, submodule->repo) < 0 || git_tree_entry_bypath(&te, head, submodule->path) < 0) git_error_clear(); else submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te)); git_tree_entry_free(te); git_tree_free(head); return 0; } int git_submodule_reload(git_submodule *sm, int force) { git_config *mods = NULL; int error; GIT_UNUSED(force); GIT_ASSERT_ARG(sm); if ((error = git_submodule_name_is_valid(sm->repo, sm->name, 0)) <= 0) /* This should come with a warning, but we've no API for that */ goto out; if (git_repository_is_bare(sm->repo)) goto out; /* refresh config data */ if ((error = gitmodules_snapshot(&mods, sm->repo)) < 0 && error != GIT_ENOTFOUND) goto out; if (mods != NULL && (error = submodule_read_config(sm, mods)) < 0) goto out; /* refresh wd data */ sm->flags &= ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID | GIT_SUBMODULE_STATUS__WD_FLAGS); if ((error = submodule_load_from_wd_lite(sm)) < 0 || (error = submodule_update_index(sm)) < 0 || (error = submodule_update_head(sm)) < 0) goto out; out: git_config_free(mods); return error; } static void submodule_copy_oid_maybe( git_oid *tgt, const git_oid *src, bool valid) { if (tgt) { if (valid) memcpy(tgt, src, sizeof(*tgt)); else memset(tgt, 0, sizeof(*tgt)); } } int git_submodule__status( unsigned int *out_status, git_oid *out_head_id, git_oid *out_index_id, git_oid *out_wd_id, git_submodule *sm, git_submodule_ignore_t ign) { unsigned int status; git_repository *smrepo = NULL; if (ign == GIT_SUBMODULE_IGNORE_UNSPECIFIED) ign = sm->ignore; /* only return location info if ignore == all */ if (ign == GIT_SUBMODULE_IGNORE_ALL) { *out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS); return 0; } /* If the user has requested caching submodule state, performing these * expensive operations (especially `submodule_update_head`, which is * bottlenecked on `git_repository_head_tree`) eliminates much of the * advantage. We will, therefore, interpret the request for caching to * apply here to and skip them. */ if (sm->repo->submodule_cache == NULL) { /* refresh the index OID */ if (submodule_update_index(sm) < 0) return -1; /* refresh the HEAD OID */ if (submodule_update_head(sm) < 0) return -1; } /* for ignore == dirty, don't scan the working directory */ if (ign == GIT_SUBMODULE_IGNORE_DIRTY) { /* git_submodule_open_bare will load WD OID data */ if (git_submodule_open_bare(&smrepo, sm) < 0) git_error_clear(); else git_repository_free(smrepo); smrepo = NULL; } else if (git_submodule_open(&smrepo, sm) < 0) { git_error_clear(); smrepo = NULL; } status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags); submodule_get_index_status(&status, sm); submodule_get_wd_status(&status, sm, smrepo, ign); git_repository_free(smrepo); *out_status = status; submodule_copy_oid_maybe(out_head_id, &sm->head_oid, (sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0); submodule_copy_oid_maybe(out_index_id, &sm->index_oid, (sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0); submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid, (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0); return 0; } int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore) { git_submodule *sm; int error; GIT_ASSERT_ARG(status); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if ((error = git_submodule_lookup(&sm, repo, name)) < 0) return error; error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore); git_submodule_free(sm); return error; } int git_submodule_location(unsigned int *location, git_submodule *sm) { GIT_ASSERT_ARG(location); GIT_ASSERT_ARG(sm); return git_submodule__status( location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL); } /* * INTERNAL FUNCTIONS */ static int submodule_alloc( git_submodule **out, git_repository *repo, const char *name) { size_t namelen; git_submodule *sm; if (!name || !(namelen = strlen(name))) { git_error_set(GIT_ERROR_SUBMODULE, "invalid submodule name"); return -1; } sm = git__calloc(1, sizeof(git_submodule)); GIT_ERROR_CHECK_ALLOC(sm); sm->name = sm->path = git__strdup(name); if (!sm->name) { git__free(sm); return -1; } GIT_REFCOUNT_INC(sm); sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE; sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT; sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO; sm->repo = repo; sm->branch = NULL; *out = sm; return 0; } static void submodule_release(git_submodule *sm) { if (!sm) return; if (sm->repo) { sm->repo = NULL; } if (sm->path != sm->name) git__free(sm->path); git__free(sm->name); git__free(sm->url); git__free(sm->branch); git__memzero(sm, sizeof(*sm)); git__free(sm); } int git_submodule_dup(git_submodule **out, git_submodule *source) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(source); GIT_REFCOUNT_INC(source); *out = source; return 0; } void git_submodule_free(git_submodule *sm) { if (!sm) return; GIT_REFCOUNT_DEC(sm, submodule_release); } static int submodule_config_error(const char *property, const char *value) { git_error_set(GIT_ERROR_INVALID, "invalid value for submodule '%s' property: '%s'", property, value); return -1; } int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value) { int val; if (git_config_lookup_map_value( &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) { *out = GIT_SUBMODULE_IGNORE_NONE; return submodule_config_error("ignore", value); } *out = (git_submodule_ignore_t)val; return 0; } int git_submodule_parse_update(git_submodule_update_t *out, const char *value) { int val; if (git_config_lookup_map_value( &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) { *out = GIT_SUBMODULE_UPDATE_CHECKOUT; return submodule_config_error("update", value); } *out = (git_submodule_update_t)val; return 0; } static int submodule_parse_recurse(git_submodule_recurse_t *out, const char *value) { int val; if (git_config_lookup_map_value( &val, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), value) < 0) { *out = GIT_SUBMODULE_RECURSE_YES; return submodule_config_error("recurse", value); } *out = (git_submodule_recurse_t)val; return 0; } static int get_value(const char **out, git_config *cfg, git_buf *buf, const char *name, const char *field) { int error; git_buf_clear(buf); if ((error = git_buf_printf(buf, "submodule.%s.%s", name, field)) < 0 || (error = git_config_get_string(out, cfg, buf->ptr)) < 0) return error; return error; } static bool looks_like_command_line_option(const char *s) { if (s && s[0] == '-') return true; return false; } static int submodule_read_config(git_submodule *sm, git_config *cfg) { git_buf key = GIT_BUF_INIT; const char *value; int error, in_config = 0; /* * TODO: Look up path in index and if it is present but not a GITLINK * then this should be deleted (at least to match git's behavior) */ if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) { in_config = 1; /* We would warn here if we had that API */ if (!looks_like_command_line_option(value)) { /* * TODO: if case insensitive filesystem, then the following strcmp * should be strcasecmp */ if (strcmp(sm->name, value) != 0) { if (sm->path != sm->name) git__free(sm->path); sm->path = git__strdup(value); GIT_ERROR_CHECK_ALLOC(sm->path); } } } else if (error != GIT_ENOTFOUND) { goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) { /* We would warn here if we had that API */ if (!looks_like_command_line_option(value)) { in_config = 1; sm->url = git__strdup(value); GIT_ERROR_CHECK_ALLOC(sm->url); } } else if (error != GIT_ENOTFOUND) { goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) { in_config = 1; sm->branch = git__strdup(value); GIT_ERROR_CHECK_ALLOC(sm->branch); } else if (error != GIT_ENOTFOUND) { goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) { in_config = 1; if ((error = git_submodule_parse_update(&sm->update, value)) < 0) goto cleanup; sm->update_default = sm->update; } else if (error != GIT_ENOTFOUND) { goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) { in_config = 1; if ((error = submodule_parse_recurse(&sm->fetch_recurse, value)) < 0) goto cleanup; sm->fetch_recurse_default = sm->fetch_recurse; } else if (error != GIT_ENOTFOUND) { goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) { in_config = 1; if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0) goto cleanup; sm->ignore_default = sm->ignore; } else if (error != GIT_ENOTFOUND) { goto cleanup; } if (in_config) sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; error = 0; cleanup: git_buf_dispose(&key); return error; } static int submodule_load_each(const git_config_entry *entry, void *payload) { lfc_data *data = payload; const char *namestart, *property; git_strmap *map = data->map; git_buf name = GIT_BUF_INIT; git_submodule *sm; int error, isvalid; if (git__prefixcmp(entry->name, "submodule.") != 0) return 0; namestart = entry->name + strlen("submodule."); property = strrchr(namestart, '.'); if (!property || (property == namestart)) return 0; property++; if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0) return error; isvalid = git_submodule_name_is_valid(data->repo, name.ptr, 0); if (isvalid <= 0) { error = isvalid; goto done; } /* * Now that we have the submodule's name, we can use that to * figure out whether it's in the map. If it's not, we create * a new submodule, load the config and insert it. If it's * already inserted, we've already loaded it, so we skip. */ if (git_strmap_exists(map, name.ptr)) { error = 0; goto done; } if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0) goto done; if ((error = submodule_read_config(sm, data->mods)) < 0) { git_submodule_free(sm); goto done; } if ((error = git_strmap_set(map, sm->name, sm)) < 0) goto done; error = 0; done: git_buf_dispose(&name); return error; } static int submodule_load_from_wd_lite(git_submodule *sm) { git_buf path = GIT_BUF_INIT; if (git_repository_workdir_path(&path, sm->repo, sm->path) < 0) return -1; if (git_path_isdir(path.ptr)) sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED; if (git_path_contains(&path, DOT_GIT)) sm->flags |= GIT_SUBMODULE_STATUS_IN_WD; git_buf_dispose(&path); return 0; } /** * Requests a snapshot of $WORK_TREE/.gitmodules. * * Returns GIT_ENOTFOUND in case no .gitmodules file exist */ static int gitmodules_snapshot(git_config **snap, git_repository *repo) { git_config *mods = NULL; git_buf path = GIT_BUF_INIT; int error; if (git_repository_workdir(repo) == NULL) return GIT_ENOTFOUND; if ((error = git_repository_workdir_path(&path, repo, GIT_MODULES_FILE)) < 0) return error; if ((error = git_config_open_ondisk(&mods, path.ptr)) < 0) goto cleanup; git_buf_dispose(&path); if ((error = git_config_snapshot(snap, mods)) < 0) goto cleanup; error = 0; cleanup: if (mods) git_config_free(mods); git_buf_dispose(&path); return error; } static git_config_backend *open_gitmodules( git_repository *repo, int okay_to_create) { git_buf path = GIT_BUF_INIT; git_config_backend *mods = NULL; if (git_repository_workdir(repo) != NULL) { if (git_repository_workdir_path(&path, repo, GIT_MODULES_FILE) != 0) return NULL; if (okay_to_create || git_path_isfile(path.ptr)) { /* git_config_backend_from_file should only fail if OOM */ if (git_config_backend_from_file(&mods, path.ptr) < 0) mods = NULL; /* open should only fail here if the file is malformed */ else if (git_config_backend_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) { git_config_backend_free(mods); mods = NULL; } } } git_buf_dispose(&path); return mods; } /* Lookup name of remote of the local tracking branch HEAD points to */ static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo) { int error; git_reference *head = NULL; git_buf upstream_name = GIT_BUF_INIT; /* lookup and dereference HEAD */ if ((error = git_repository_head(&head, repo)) < 0) return error; /** * If head does not refer to a branch, then return * GIT_ENOTFOUND to indicate that we could not find * a remote key for the local tracking branch HEAD points to. **/ if (!git_reference_is_branch(head)) { git_error_set(GIT_ERROR_INVALID, "HEAD does not refer to a branch."); error = GIT_ENOTFOUND; goto done; } /* lookup remote tracking branch of HEAD */ if ((error = git_branch_upstream_name( &upstream_name, repo, git_reference_name(head))) < 0) goto done; /* lookup remote of remote tracking branch */ if ((error = git_branch_remote_name(remote_name, repo, upstream_name.ptr)) < 0) goto done; done: git_buf_dispose(&upstream_name); git_reference_free(head); return error; } /* Lookup the remote of the local tracking branch HEAD points to */ static int lookup_head_remote(git_remote **remote, git_repository *repo) { int error; git_buf remote_name = GIT_BUF_INIT; /* lookup remote of remote tracking branch name */ if (!(error = lookup_head_remote_key(&remote_name, repo))) error = git_remote_lookup(remote, repo, remote_name.ptr); git_buf_dispose(&remote_name); return error; } /* Lookup remote, either from HEAD or fall back on origin */ static int lookup_default_remote(git_remote **remote, git_repository *repo) { int error = lookup_head_remote(remote, repo); /* if that failed, use 'origin' instead */ if (error == GIT_ENOTFOUND || error == GIT_EUNBORNBRANCH) error = git_remote_lookup(remote, repo, "origin"); if (error == GIT_ENOTFOUND) git_error_set( GIT_ERROR_SUBMODULE, "cannot get default remote for submodule - no local tracking " "branch for HEAD and origin does not exist"); return error; } static int get_url_base(git_buf *url, git_repository *repo) { int error; git_worktree *wt = NULL; git_remote *remote = NULL; if ((error = lookup_default_remote(&remote, repo)) == 0) { error = git_buf_sets(url, git_remote_url(remote)); goto out; } else if (error != GIT_ENOTFOUND) goto out; else git_error_clear(); /* if repository does not have a default remote, use workdir instead */ if (git_repository_is_worktree(repo)) { if ((error = git_worktree_open_from_repository(&wt, repo)) < 0) goto out; error = git_buf_sets(url, wt->parent_path); } else { error = git_buf_sets(url, git_repository_workdir(repo)); } out: git_remote_free(remote); git_worktree_free(wt); return error; } static void submodule_get_index_status(unsigned int *status, git_submodule *sm) { const git_oid *head_oid = git_submodule_head_id(sm); const git_oid *index_oid = git_submodule_index_id(sm); *status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS; if (!head_oid) { if (index_oid) *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED; } else if (!index_oid) *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED; else if (!git_oid_equal(head_oid, index_oid)) *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED; } static void submodule_get_wd_status( unsigned int *status, git_submodule *sm, git_repository *sm_repo, git_submodule_ignore_t ign) { const git_oid *index_oid = git_submodule_index_id(sm); const git_oid *wd_oid = (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL; git_tree *sm_head = NULL; git_index *index = NULL; git_diff_options opt = GIT_DIFF_OPTIONS_INIT; git_diff *diff; *status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS; if (!index_oid) { if (wd_oid) *status |= GIT_SUBMODULE_STATUS_WD_ADDED; } else if (!wd_oid) { if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 && (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED; else *status |= GIT_SUBMODULE_STATUS_WD_DELETED; } else if (!git_oid_equal(index_oid, wd_oid)) *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED; /* if we have no repo, then we're done */ if (!sm_repo) return; /* the diffs below could be optimized with an early termination * option to the git_diff functions, but for now this is sufficient * (and certainly no worse that what core git does). */ if (ign == GIT_SUBMODULE_IGNORE_NONE) opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED; (void)git_repository_index__weakptr(&index, sm_repo); /* if we don't have an unborn head, check diff with index */ if (git_repository_head_tree(&sm_head, sm_repo) < 0) git_error_clear(); else { /* perform head to index diff on submodule */ if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0) git_error_clear(); else { if (git_diff_num_deltas(diff) > 0) *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED; git_diff_free(diff); diff = NULL; } git_tree_free(sm_head); } /* perform index-to-workdir diff on submodule */ if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0) git_error_clear(); else { size_t untracked = git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED); if (untracked > 0) *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED; if (git_diff_num_deltas(diff) != untracked) *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED; git_diff_free(diff); diff = NULL; } } git2r/src/libgit2/src/notes.c0000644000175000017500000004227214125111754015651 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "notes.h" #include "git2.h" #include "refs.h" #include "config.h" #include "iterator.h" #include "signature.h" #include "blob.h" static int note_error_notfound(void) { git_error_set(GIT_ERROR_INVALID, "note could not be found"); return GIT_ENOTFOUND; } static int find_subtree_in_current_level( git_tree **out, git_repository *repo, git_tree *parent, const char *annotated_object_sha, int fanout) { size_t i; const git_tree_entry *entry; *out = NULL; if (parent == NULL) return note_error_notfound(); for (i = 0; i < git_tree_entrycount(parent); i++) { entry = git_tree_entry_byindex(parent, i); if (!git__ishex(git_tree_entry_name(entry))) continue; if (S_ISDIR(git_tree_entry_filemode(entry)) && strlen(git_tree_entry_name(entry)) == 2 && !strncmp(git_tree_entry_name(entry), annotated_object_sha + fanout, 2)) return git_tree_lookup(out, repo, git_tree_entry_id(entry)); /* Not a DIR, so do we have an already existing blob? */ if (!strcmp(git_tree_entry_name(entry), annotated_object_sha + fanout)) return GIT_EEXISTS; } return note_error_notfound(); } static int find_subtree_r(git_tree **out, git_tree *root, git_repository *repo, const char *target, int *fanout) { int error; git_tree *subtree = NULL; *out = NULL; error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout); if (error == GIT_EEXISTS) return git_tree_lookup(out, repo, git_tree_id(root)); if (error < 0) return error; *fanout += 2; error = find_subtree_r(out, subtree, repo, target, fanout); git_tree_free(subtree); return error; } static int find_blob(git_oid *blob, git_tree *tree, const char *target) { size_t i; const git_tree_entry *entry; for (i=0; iid, note_oid); if (git_signature_dup(¬e->author, git_commit_author(commit)) < 0 || git_signature_dup(¬e->committer, git_commit_committer(commit)) < 0) return -1; blobsize = git_blob_rawsize(blob); GIT_ERROR_CHECK_BLOBSIZE(blobsize); note->message = git__strndup(git_blob_rawcontent(blob), (size_t)blobsize); GIT_ERROR_CHECK_ALLOC(note->message); *out = note; return 0; } static int note_lookup( git_note **out, git_repository *repo, git_commit *commit, git_tree *tree, const char *target) { int error, fanout = 0; git_oid oid; git_blob *blob = NULL; git_note *note = NULL; git_tree *subtree = NULL; if ((error = find_subtree_r(&subtree, tree, repo, target, &fanout)) < 0) goto cleanup; if ((error = find_blob(&oid, subtree, target + fanout)) < 0) goto cleanup; if ((error = git_blob_lookup(&blob, repo, &oid)) < 0) goto cleanup; if ((error = note_new(¬e, &oid, commit, blob)) < 0) goto cleanup; *out = note; cleanup: git_tree_free(subtree); git_blob_free(blob); return error; } static int note_remove( git_oid *notes_commit_out, git_repository *repo, const git_signature *author, const git_signature *committer, const char *notes_ref, git_tree *tree, const char *target, git_commit **parents) { int error; git_tree *tree_after_removal = NULL; git_oid oid; if ((error = manipulate_note_in_tree_r( &tree_after_removal, repo, tree, NULL, target, 0, remove_note_in_tree_eexists_cb, remove_note_in_tree_enotfound_cb)) < 0) goto cleanup; error = git_commit_create(&oid, repo, notes_ref, author, committer, NULL, GIT_NOTES_DEFAULT_MSG_RM, tree_after_removal, *parents == NULL ? 0 : 1, (const git_commit **) parents); if (error < 0) goto cleanup; if (notes_commit_out) git_oid_cpy(notes_commit_out, &oid); cleanup: git_tree_free(tree_after_removal); return error; } static int note_get_default_ref(git_buf *out, git_repository *repo) { git_config *cfg; int error; if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; error = git_config_get_string_buf(out, cfg, "core.notesref"); if (error == GIT_ENOTFOUND) error = git_buf_puts(out, GIT_NOTES_DEFAULT_REF); return error; } static int normalize_namespace(git_buf *out, git_repository *repo, const char *notes_ref) { if (notes_ref) return git_buf_puts(out, notes_ref); return note_get_default_ref(out, repo); } static int retrieve_note_commit( git_commit **commit_out, git_buf *notes_ref_out, git_repository *repo, const char *notes_ref) { int error; git_oid oid; if ((error = normalize_namespace(notes_ref_out, repo, notes_ref)) < 0) return error; if ((error = git_reference_name_to_id(&oid, repo, notes_ref_out->ptr)) < 0) return error; if (git_commit_lookup(commit_out, repo, &oid) < 0) return error; return 0; } int git_note_commit_read( git_note **out, git_repository *repo, git_commit *notes_commit, const git_oid *oid) { int error; git_tree *tree = NULL; char target[GIT_OID_HEXSZ + 1]; git_oid_tostr(target, sizeof(target), oid); if ((error = git_commit_tree(&tree, notes_commit)) < 0) goto cleanup; error = note_lookup(out, repo, notes_commit, tree, target); cleanup: git_tree_free(tree); return error; } int git_note_read(git_note **out, git_repository *repo, const char *notes_ref_in, const git_oid *oid) { int error; git_buf notes_ref = GIT_BUF_INIT; git_commit *commit = NULL; error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in); if (error < 0) goto cleanup; error = git_note_commit_read(out, repo, commit, oid); cleanup: git_buf_dispose(¬es_ref); git_commit_free(commit); return error; } int git_note_commit_create( git_oid *notes_commit_out, git_oid *notes_blob_out, git_repository *repo, git_commit *parent, const git_signature *author, const git_signature *committer, const git_oid *oid, const char *note, int allow_note_overwrite) { int error; git_tree *tree = NULL; char target[GIT_OID_HEXSZ + 1]; git_oid_tostr(target, sizeof(target), oid); if (parent != NULL && (error = git_commit_tree(&tree, parent)) < 0) goto cleanup; error = note_write(notes_commit_out, notes_blob_out, repo, author, committer, NULL, note, tree, target, &parent, allow_note_overwrite); if (error < 0) goto cleanup; cleanup: git_tree_free(tree); return error; } int git_note_create( git_oid *out, git_repository *repo, const char *notes_ref_in, const git_signature *author, const git_signature *committer, const git_oid *oid, const char *note, int allow_note_overwrite) { int error; git_buf notes_ref = GIT_BUF_INIT; git_commit *existing_notes_commit = NULL; git_reference *ref = NULL; git_oid notes_blob_oid, notes_commit_oid; error = retrieve_note_commit(&existing_notes_commit, ¬es_ref, repo, notes_ref_in); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; error = git_note_commit_create(¬es_commit_oid, ¬es_blob_oid, repo, existing_notes_commit, author, committer, oid, note, allow_note_overwrite); if (error < 0) goto cleanup; error = git_reference_create(&ref, repo, notes_ref.ptr, ¬es_commit_oid, 1, NULL); if (out != NULL) git_oid_cpy(out, ¬es_blob_oid); cleanup: git_buf_dispose(¬es_ref); git_commit_free(existing_notes_commit); git_reference_free(ref); return error; } int git_note_commit_remove( git_oid *notes_commit_out, git_repository *repo, git_commit *notes_commit, const git_signature *author, const git_signature *committer, const git_oid *oid) { int error; git_tree *tree = NULL; char target[GIT_OID_HEXSZ + 1]; git_oid_tostr(target, sizeof(target), oid); if ((error = git_commit_tree(&tree, notes_commit)) < 0) goto cleanup; error = note_remove(notes_commit_out, repo, author, committer, NULL, tree, target, ¬es_commit); cleanup: git_tree_free(tree); return error; } int git_note_remove(git_repository *repo, const char *notes_ref_in, const git_signature *author, const git_signature *committer, const git_oid *oid) { int error; git_buf notes_ref_target = GIT_BUF_INIT; git_commit *existing_notes_commit = NULL; git_oid new_notes_commit; git_reference *notes_ref = NULL; error = retrieve_note_commit(&existing_notes_commit, ¬es_ref_target, repo, notes_ref_in); if (error < 0) goto cleanup; error = git_note_commit_remove(&new_notes_commit, repo, existing_notes_commit, author, committer, oid); if (error < 0) goto cleanup; error = git_reference_create(¬es_ref, repo, notes_ref_target.ptr, &new_notes_commit, 1, NULL); cleanup: git_buf_dispose(¬es_ref_target); git_reference_free(notes_ref); git_commit_free(existing_notes_commit); return error; } int git_note_default_ref(git_buf *out, git_repository *repo) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); if ((error = git_buf_sanitize(out)) < 0 || (error = note_get_default_ref(out, repo)) < 0) git_buf_dispose(out); return error; } const git_signature *git_note_committer(const git_note *note) { GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); return note->committer; } const git_signature *git_note_author(const git_note *note) { GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); return note->author; } const char *git_note_message(const git_note *note) { GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); return note->message; } const git_oid *git_note_id(const git_note *note) { GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); return ¬e->id; } void git_note_free(git_note *note) { if (note == NULL) return; git_signature_free(note->committer); git_signature_free(note->author); git__free(note->message); git__free(note); } static int process_entry_path( const char *entry_path, git_oid *annotated_object_id) { int error = 0; size_t i = 0, j = 0, len; git_buf buf = GIT_BUF_INIT; if ((error = git_buf_puts(&buf, entry_path)) < 0) goto cleanup; len = git_buf_len(&buf); while (i < len) { if (buf.ptr[i] == '/') { i++; continue; } if (git__fromhex(buf.ptr[i]) < 0) { /* This is not a note entry */ goto cleanup; } if (i != j) buf.ptr[j] = buf.ptr[i]; i++; j++; } buf.ptr[j] = '\0'; buf.size = j; if (j != GIT_OID_HEXSZ) { /* This is not a note entry */ goto cleanup; } error = git_oid_fromstr(annotated_object_id, buf.ptr); cleanup: git_buf_dispose(&buf); return error; } int git_note_foreach( git_repository *repo, const char *notes_ref, git_note_foreach_cb note_cb, void *payload) { int error; git_note_iterator *iter = NULL; git_oid note_id, annotated_id; if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0) return error; while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { if ((error = note_cb(¬e_id, &annotated_id, payload)) != 0) { git_error_set_after_callback(error); break; } } if (error == GIT_ITEROVER) error = 0; git_note_iterator_free(iter); return error; } void git_note_iterator_free(git_note_iterator *it) { if (it == NULL) return; git_iterator_free(it); } int git_note_commit_iterator_new( git_note_iterator **it, git_commit *notes_commit) { int error; git_tree *tree; if ((error = git_commit_tree(&tree, notes_commit)) < 0) goto cleanup; if ((error = git_iterator_for_tree(it, tree, NULL)) < 0) git_iterator_free(*it); cleanup: git_tree_free(tree); return error; } int git_note_iterator_new( git_note_iterator **it, git_repository *repo, const char *notes_ref_in) { int error; git_commit *commit = NULL; git_buf notes_ref = GIT_BUF_INIT; error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in); if (error < 0) goto cleanup; error = git_note_commit_iterator_new(it, commit); cleanup: git_buf_dispose(¬es_ref); git_commit_free(commit); return error; } int git_note_next( git_oid *note_id, git_oid *annotated_id, git_note_iterator *it) { int error; const git_index_entry *item; if ((error = git_iterator_current(&item, it)) < 0) return error; git_oid_cpy(note_id, &item->id); if ((error = process_entry_path(item->path, annotated_id)) < 0) return error; if ((error = git_iterator_advance(NULL, it)) < 0 && error != GIT_ITEROVER) return error; return 0; } git2r/src/libgit2/src/offmap.h0000644000175000017500000000755514125111754016003 0ustar nileshnilesh/* * Copyright (C) 2012 the libgit2 contributors * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_offmap_h__ #define INCLUDE_offmap_h__ #include "common.h" #include "git2/types.h" /** A map with `off64_t`s as key. */ typedef struct kh_off_s git_offmap; /** * Allocate a new `off64_t` map. * * @param out Pointer to the map that shall be allocated. * @return 0 on success, an error code if allocation has failed. */ int git_offmap_new(git_offmap **out); /** * Free memory associated with the map. * * Note that this function will _not_ free values added to this * map. * * @param map Pointer to the map that is to be free'd. May be * `NULL`. */ void git_offmap_free(git_offmap *map); /** * Clear all entries from the map. * * This function will remove all entries from the associated map. * Memory associated with it will not be released, though. * * @param map Pointer to the map that shall be cleared. May be * `NULL`. */ void git_offmap_clear(git_offmap *map); /** * Return the number of elements in the map. * * @parameter map map containing the elements * @return number of elements in the map */ size_t git_offmap_size(git_offmap *map); /** * Return value associated with the given key. * * @param map map to search key in * @param key key to search for * @return value associated with the given key or NULL if the key was not found */ void *git_offmap_get(git_offmap *map, const off64_t key); /** * Set the entry for key to value. * * If the map has no corresponding entry for the given key, a new * entry will be created with the given value. If an entry exists * already, its value will be updated to match the given value. * * @param map map to create new entry in * @param key key to set * @param value value to associate the key with; may be NULL * @return zero if the key was successfully set, a negative error * code otherwise */ int git_offmap_set(git_offmap *map, const off64_t key, void *value); /** * Delete an entry from the map. * * Delete the given key and its value from the map. If no such * key exists, this will do nothing. * * @param map map to delete key in * @param key key to delete * @return `0` if the key has been deleted, GIT_ENOTFOUND if no * such key was found, a negative code in case of an * error */ int git_offmap_delete(git_offmap *map, const off64_t key); /** * Check whether a key exists in the given map. * * @param map map to query for the key * @param key key to search for * @return 0 if the key has not been found, 1 otherwise */ int git_offmap_exists(git_offmap *map, const off64_t key); /** * Iterate over entries of the map. * * This functions allows to iterate over all key-value entries of * the map. The current position is stored in the `iter` variable * and should be initialized to `0` before the first call to this * function. * * @param map map to iterate over * @param value pointer to the variable where to store the current * value. May be NULL. * @param iter iterator storing the current position. Initialize * with zero previous to the first call. * @param key pointer to the variable where to store the current * key. May be NULL. * @return `0` if the next entry was correctly retrieved. * GIT_ITEROVER if no entries are left. A negative error * code otherwise. */ int git_offmap_iterate(void **value, git_offmap *map, size_t *iter, off64_t *key); #define git_offmap_foreach(h, kvar, vvar, code) { size_t __i = 0; \ while (git_offmap_iterate((void **) &(vvar), h, &__i, &(kvar)) == 0) { \ code; \ } } #define git_offmap_foreach_value(h, vvar, code) { size_t __i = 0; \ while (git_offmap_iterate((void **) &(vvar), h, &__i, NULL) == 0) { \ code; \ } } #endif git2r/src/libgit2/src/blame_git.h0000644000175000017500000000103614125111754016442 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_blame_git__ #define INCLUDE_blame_git__ #include "common.h" #include "blame.h" int git_blame__get_origin( git_blame__origin **out, git_blame *sb, git_commit *commit, const char *path); void git_blame__free_entry(git_blame__entry *ent); int git_blame__like_git(git_blame *sb, uint32_t flags); #endif git2r/src/libgit2/src/transaction.c0000644000175000017500000002076314125111754017047 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "transaction.h" #include "repository.h" #include "strmap.h" #include "refdb.h" #include "pool.h" #include "reflog.h" #include "signature.h" #include "config.h" #include "git2/transaction.h" #include "git2/signature.h" #include "git2/sys/refs.h" #include "git2/sys/refdb_backend.h" typedef enum { TRANSACTION_NONE, TRANSACTION_REFS, TRANSACTION_CONFIG, } transaction_t; typedef struct { const char *name; void *payload; git_reference_t ref_type; union { git_oid id; char *symbolic; } target; git_reflog *reflog; const char *message; git_signature *sig; unsigned int committed :1, remove :1; } transaction_node; struct git_transaction { transaction_t type; git_repository *repo; git_refdb *db; git_config *cfg; git_strmap *locks; git_pool pool; }; int git_transaction_config_new(git_transaction **out, git_config *cfg) { git_transaction *tx; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(cfg); tx = git__calloc(1, sizeof(git_transaction)); GIT_ERROR_CHECK_ALLOC(tx); tx->type = TRANSACTION_CONFIG; tx->cfg = cfg; *out = tx; return 0; } int git_transaction_new(git_transaction **out, git_repository *repo) { int error; git_pool pool; git_transaction *tx = NULL; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); if ((error = git_pool_init(&pool, 1)) < 0) goto on_error; tx = git_pool_mallocz(&pool, sizeof(git_transaction)); if (!tx) { error = -1; goto on_error; } if ((error = git_strmap_new(&tx->locks)) < 0) { error = -1; goto on_error; } if ((error = git_repository_refdb(&tx->db, repo)) < 0) goto on_error; tx->type = TRANSACTION_REFS; memcpy(&tx->pool, &pool, sizeof(git_pool)); tx->repo = repo; *out = tx; return 0; on_error: git_pool_clear(&pool); return error; } int git_transaction_lock_ref(git_transaction *tx, const char *refname) { int error; transaction_node *node; GIT_ASSERT_ARG(tx); GIT_ASSERT_ARG(refname); node = git_pool_mallocz(&tx->pool, sizeof(transaction_node)); GIT_ERROR_CHECK_ALLOC(node); node->name = git_pool_strdup(&tx->pool, refname); GIT_ERROR_CHECK_ALLOC(node->name); if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0) return error; if ((error = git_strmap_set(tx->locks, node->name, node)) < 0) goto cleanup; return 0; cleanup: git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); return error; } static int find_locked(transaction_node **out, git_transaction *tx, const char *refname) { transaction_node *node; if ((node = git_strmap_get(tx->locks, refname)) == NULL) { git_error_set(GIT_ERROR_REFERENCE, "the specified reference is not locked"); return GIT_ENOTFOUND; } *out = node; return 0; } static int copy_common(transaction_node *node, git_transaction *tx, const git_signature *sig, const char *msg) { if (sig && git_signature__pdup(&node->sig, sig, &tx->pool) < 0) return -1; if (!node->sig) { git_signature *tmp; int error; if (git_reference__log_signature(&tmp, tx->repo) < 0) return -1; /* make sure the sig we use is in our pool */ error = git_signature__pdup(&node->sig, tmp, &tx->pool); git_signature_free(tmp); if (error < 0) return error; } if (msg) { node->message = git_pool_strdup(&tx->pool, msg); GIT_ERROR_CHECK_ALLOC(node->message); } return 0; } int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg) { int error; transaction_node *node; GIT_ASSERT_ARG(tx); GIT_ASSERT_ARG(refname); GIT_ASSERT_ARG(target); if ((error = find_locked(&node, tx, refname)) < 0) return error; if ((error = copy_common(node, tx, sig, msg)) < 0) return error; git_oid_cpy(&node->target.id, target); node->ref_type = GIT_REFERENCE_DIRECT; return 0; } int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg) { int error; transaction_node *node; GIT_ASSERT_ARG(tx); GIT_ASSERT_ARG(refname); GIT_ASSERT_ARG(target); if ((error = find_locked(&node, tx, refname)) < 0) return error; if ((error = copy_common(node, tx, sig, msg)) < 0) return error; node->target.symbolic = git_pool_strdup(&tx->pool, target); GIT_ERROR_CHECK_ALLOC(node->target.symbolic); node->ref_type = GIT_REFERENCE_SYMBOLIC; return 0; } int git_transaction_remove(git_transaction *tx, const char *refname) { int error; transaction_node *node; if ((error = find_locked(&node, tx, refname)) < 0) return error; node->remove = true; node->ref_type = GIT_REFERENCE_DIRECT; /* the id will be ignored */ return 0; } static int dup_reflog(git_reflog **out, const git_reflog *in, git_pool *pool) { git_reflog *reflog; git_reflog_entry *entries; size_t len, i; reflog = git_pool_mallocz(pool, sizeof(git_reflog)); GIT_ERROR_CHECK_ALLOC(reflog); reflog->ref_name = git_pool_strdup(pool, in->ref_name); GIT_ERROR_CHECK_ALLOC(reflog->ref_name); len = in->entries.length; reflog->entries.length = len; reflog->entries.contents = git_pool_mallocz(pool, len * sizeof(void *)); GIT_ERROR_CHECK_ALLOC(reflog->entries.contents); entries = git_pool_mallocz(pool, len * sizeof(git_reflog_entry)); GIT_ERROR_CHECK_ALLOC(entries); for (i = 0; i < len; i++) { const git_reflog_entry *src; git_reflog_entry *tgt; tgt = &entries[i]; reflog->entries.contents[i] = tgt; src = git_vector_get(&in->entries, i); git_oid_cpy(&tgt->oid_old, &src->oid_old); git_oid_cpy(&tgt->oid_cur, &src->oid_cur); tgt->msg = git_pool_strdup(pool, src->msg); GIT_ERROR_CHECK_ALLOC(tgt->msg); if (git_signature__pdup(&tgt->committer, src->committer, pool) < 0) return -1; } *out = reflog; return 0; } int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog) { int error; transaction_node *node; GIT_ASSERT_ARG(tx); GIT_ASSERT_ARG(refname); GIT_ASSERT_ARG(reflog); if ((error = find_locked(&node, tx, refname)) < 0) return error; if ((error = dup_reflog(&node->reflog, reflog, &tx->pool)) < 0) return error; return 0; } static int update_target(git_refdb *db, transaction_node *node) { git_reference *ref; int error, update_reflog; if (node->ref_type == GIT_REFERENCE_DIRECT) { ref = git_reference__alloc(node->name, &node->target.id, NULL); } else if (node->ref_type == GIT_REFERENCE_SYMBOLIC) { ref = git_reference__alloc_symbolic(node->name, node->target.symbolic); } else { abort(); } GIT_ERROR_CHECK_ALLOC(ref); update_reflog = node->reflog == NULL; if (node->remove) { error = git_refdb_unlock(db, node->payload, 2, false, ref, NULL, NULL); } else if (node->ref_type == GIT_REFERENCE_DIRECT) { error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message); } else if (node->ref_type == GIT_REFERENCE_SYMBOLIC) { error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message); } else { abort(); } git_reference_free(ref); node->committed = true; return error; } int git_transaction_commit(git_transaction *tx) { transaction_node *node; int error = 0; GIT_ASSERT_ARG(tx); if (tx->type == TRANSACTION_CONFIG) { error = git_config_unlock(tx->cfg, true); tx->cfg = NULL; return error; } git_strmap_foreach_value(tx->locks, node, { if (node->reflog) { if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0) return error; } if (node->ref_type == GIT_REFERENCE_INVALID) { /* ref was locked but not modified */ if ((error = git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL)) < 0) { return error; } node->committed = true; } else { if ((error = update_target(tx->db, node)) < 0) return error; } }); return 0; } void git_transaction_free(git_transaction *tx) { transaction_node *node; git_pool pool; if (!tx) return; if (tx->type == TRANSACTION_CONFIG) { if (tx->cfg) { git_config_unlock(tx->cfg, false); git_config_free(tx->cfg); } git__free(tx); return; } /* start by unlocking the ones we've left hanging, if any */ git_strmap_foreach_value(tx->locks, node, { if (node->committed) continue; git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); }); git_refdb_free(tx->db); git_strmap_free(tx->locks); /* tx is inside the pool, so we need to extract the data */ memcpy(&pool, &tx->pool, sizeof(git_pool)); git_pool_clear(&pool); } git2r/src/libgit2/src/diff_parse.h0000644000175000017500000000062714125111754016626 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_diff_parse_h__ #define INCLUDE_diff_parse_h__ #include "common.h" #include "diff.h" typedef struct { struct git_diff base; git_vector patches; } git_diff_parsed; #endif git2r/src/libgit2/src/odb_pack.c0000644000175000017500000006365214125111754016270 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include #include "git2/repository.h" #include "git2/indexer.h" #include "git2/sys/odb_backend.h" #include "delta.h" #include "futils.h" #include "hash.h" #include "midx.h" #include "mwindow.h" #include "odb.h" #include "pack.h" #include "git2/odb_backend.h" /* re-freshen pack files no more than every 2 seconds */ #define FRESHEN_FREQUENCY 2 struct pack_backend { git_odb_backend parent; git_midx_file *midx; git_vector midx_packs; git_vector packs; struct git_pack_file *last_found; char *pack_folder; }; struct pack_writepack { struct git_odb_writepack parent; git_indexer *indexer; }; /** * The wonderful tale of a Packed Object lookup query * =================================================== * A riveting and epic story of epicness and ASCII * art, presented by yours truly, * Sir Vicent of Marti * * * Chapter 1: Once upon a time... * Initialization of the Pack Backend * -------------------------------------------------- * * # git_odb_backend_pack * | Creates the pack backend structure, initializes the * | callback pointers to our default read() and exist() methods, * | and tries to find the `pack` folder, if it exists. ODBs without a `pack` * | folder are ignored altogether. If there is a `pack` folder, it tries to * | preload all the known packfiles in the ODB. * | * |-# pack_backend__refresh * | The `multi-pack-index` is loaded if it exists and is valid. * | Then we run a `dirent` callback through every file in the pack folder, * | even those present in `multi-pack-index`. The unindexed packfiles are * | then sorted according to a sorting callback. * | * |-# refresh_multi_pack_index * | Detect the presence of the `multi-pack-index` file. If it needs to be * | refreshed, frees the old copy and tries to load the new one, together * | with all the packfiles it indexes. If the process fails, fall back to * | the old behavior, as if the `multi-pack-index` file was not there. * | * |-# packfile_load__cb * | | This callback is called from `dirent` with every single file * | | inside the pack folder. We find the packs by actually locating * | | their index (ends in ".idx"). From that index, we verify that * | | the corresponding packfile exists and is valid, and if so, we * | | add it to the pack list. * | | * | # git_mwindow_get_pack * | Make sure that there's a packfile to back this index, and store * | some very basic information regarding the packfile itself, * | such as the full path, the size, and the modification time. * | We don't actually open the packfile to check for internal consistency. * | * |-# packfile_sort__cb * Sort all the preloaded packs according to some specific criteria: * we prioritize the "newer" packs because it's more likely they * contain the objects we are looking for, and we prioritize local * packs over remote ones. * * * * Chapter 2: To be, or not to be... * A standard packed `exist` query for an OID * -------------------------------------------------- * * # pack_backend__exists / pack_backend__exists_prefix * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the * | packs that have been loaded for our ODB. * | * |-# pack_entry_find / pack_entry_find_prefix * | If there is a multi-pack-index present, search the SHA1 oid in that * | index first. If it is not found there, iterate through all the unindexed * | packs that have been preloaded (starting by the pack where the latest * | object was found) to try to find the OID in one of them. * | * |-# git_midx_entry_find * | Search for the SHA1 oid in the multi-pack-index. See * | * | for specifics on the multi-pack-index format and how do we find * | entries in it. * | * |-# git_pack_entry_find * | Check the index of an individual unindexed pack to see if the SHA1 * | OID can be found. If we can find the offset to that SHA1 inside of the * | index, that means the object is contained inside of the packfile and * | we can stop searching. Before returning, we verify that the * | packfile behing the index we are searching still exists on disk. * | * |-# pack_entry_find_offset * | Mmap the actual index file to disk if it hasn't been opened * | yet, and run a binary search through it to find the OID. * | See * | for specifics on the Packfile Index format and how do we find * | entries in it. * | * |-# pack_index_open * | Guess the name of the index based on the full path to the * | packfile, open it and verify its contents. Only if the index * | has not been opened already. * | * |-# pack_index_check * Mmap the index file and do a quick run through the header * to guess the index version (right now we support v1 and v2), * and to verify that the size of the index makes sense. * * * * Chapter 3: The neverending story... * A standard packed `lookup` query for an OID * -------------------------------------------------- * * # pack_backend__read / pack_backend__read_prefix * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the * | packs that have been loaded for our ODB. If it does, open the packfile and * | read from it. * | * |-# git_packfile_unpack * Armed with a packfile and the offset within it, we can finally unpack * the object pointed at by the SHA1 oid. This involves mmapping part of * the `.pack` file, and uncompressing the object within it (if it is * stored in the undelfitied representation), or finding a base object and * applying some deltas to its uncompressed representation (if it is stored * in the deltified representation). See * * for specifics on the Packfile format and how do we read from it. * */ /*********************************************************** * * FORWARD DECLARATIONS * ***********************************************************/ static int packfile_sort__cb(const void *a_, const void *b_); static int packfile_load__cb(void *_data, git_buf *path); static int packfile_byname_search_cmp(const void *path, const void *pack_entry); static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid); /* Can find the offset of an object given * a prefix of an identifier. * Sets GIT_EAMBIGUOUS if short oid is ambiguous. * This method assumes that len is between * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ. */ static int pack_entry_find_prefix( struct git_pack_entry *e, struct pack_backend *backend, const git_oid *short_oid, size_t len); /*********************************************************** * * PACK WINDOW MANAGEMENT * ***********************************************************/ static int packfile_byname_search_cmp(const void *path_, const void *p_) { const git_buf *path = (const git_buf *)path_; const struct git_pack_file *p = (const struct git_pack_file *)p_; return strncmp(p->pack_name, git_buf_cstr(path), git_buf_len(path)); } static int packfile_sort__cb(const void *a_, const void *b_) { const struct git_pack_file *a = a_; const struct git_pack_file *b = b_; int st; /* * Local packs tend to contain objects specific to our * variant of the project than remote ones. In addition, * remote ones could be on a network mounted filesystem. * Favor local ones for these reasons. */ st = a->pack_local - b->pack_local; if (st) return -st; /* * Younger packs tend to contain more recent objects, * and more recent objects tend to get accessed more * often. */ if (a->mtime < b->mtime) return 1; else if (a->mtime == b->mtime) return 0; return -1; } static int packfile_load__cb(void *data, git_buf *path) { struct pack_backend *backend = data; struct git_pack_file *pack; const char *path_str = git_buf_cstr(path); git_buf index_prefix = GIT_BUF_INIT; size_t cmp_len = git_buf_len(path); int error; if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0) return 0; /* not an index */ cmp_len -= strlen(".idx"); git_buf_attach_notowned(&index_prefix, path_str, cmp_len); if (git_vector_search2(NULL, &backend->midx_packs, packfile_byname_search_cmp, &index_prefix) == 0) return 0; if (git_vector_search2(NULL, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) return 0; error = git_mwindow_get_pack(&pack, path->ptr); /* ignore missing .pack file as git does */ if (error == GIT_ENOTFOUND) { git_error_clear(); return 0; } if (!error) error = git_vector_insert(&backend->packs, pack); return error; } static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid) { struct git_pack_file *last_found = backend->last_found, *p; git_midx_entry midx_entry; size_t i; if (backend->midx && git_midx_entry_find(&midx_entry, backend->midx, oid, GIT_OID_HEXSZ) == 0 && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) { e->offset = midx_entry.offset; git_oid_cpy(&e->sha1, &midx_entry.sha1); e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index); return 0; } if (last_found && git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0) return 0; git_vector_foreach(&backend->packs, i, p) { if (p == last_found) continue; if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) { backend->last_found = p; return 0; } } return git_odb__error_notfound( "failed to find pack entry", oid, GIT_OID_HEXSZ); } static int pack_entry_find_prefix( struct git_pack_entry *e, struct pack_backend *backend, const git_oid *short_oid, size_t len) { int error; size_t i; git_oid found_full_oid = {{0}}; bool found = false; struct git_pack_file *last_found = backend->last_found, *p; git_midx_entry midx_entry; if (backend->midx) { error = git_midx_entry_find(&midx_entry, backend->midx, short_oid, len); if (error == GIT_EAMBIGUOUS) return error; if (!error && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) { e->offset = midx_entry.offset; git_oid_cpy(&e->sha1, &midx_entry.sha1); e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index); git_oid_cpy(&found_full_oid, &e->sha1); found = true; } } if (last_found) { error = git_pack_entry_find(e, last_found, short_oid, len); if (error == GIT_EAMBIGUOUS) return error; if (!error) { if (found && git_oid_cmp(&e->sha1, &found_full_oid)) return git_odb__error_ambiguous("found multiple pack entries"); git_oid_cpy(&found_full_oid, &e->sha1); found = true; } } git_vector_foreach(&backend->packs, i, p) { if (p == last_found) continue; error = git_pack_entry_find(e, p, short_oid, len); if (error == GIT_EAMBIGUOUS) return error; if (!error) { if (found && git_oid_cmp(&e->sha1, &found_full_oid)) return git_odb__error_ambiguous("found multiple pack entries"); git_oid_cpy(&found_full_oid, &e->sha1); found = true; backend->last_found = p; } } if (!found) return git_odb__error_notfound("no matching pack entry for prefix", short_oid, len); else return 0; } /*********************************************************** * * MULTI-PACK-INDEX SUPPORT * * Functions needed to support the multi-pack-index. * ***********************************************************/ /* * Remove the multi-pack-index, and move all midx_packs to packs. */ static int remove_multi_pack_index(struct pack_backend *backend) { size_t i, j = git_vector_length(&backend->packs); struct pack_backend *p; int error = git_vector_size_hint( &backend->packs, j + git_vector_length(&backend->midx_packs)); if (error < 0) return error; git_vector_foreach(&backend->midx_packs, i, p) git_vector_set(NULL, &backend->packs, j++, p); git_vector_clear(&backend->midx_packs); git_midx_free(backend->midx); backend->midx = NULL; return 0; } /* * Loads a single .pack file referred to by the multi-pack-index. These must * match the order in which they are declared in the multi-pack-index file, * since these files are referred to by their index. */ static int process_multi_pack_index_pack( struct pack_backend *backend, size_t i, const char *packfile_name) { int error; struct git_pack_file *pack; size_t found_position; git_buf pack_path = GIT_BUF_INIT, index_prefix = GIT_BUF_INIT; error = git_buf_joinpath(&pack_path, backend->pack_folder, packfile_name); if (error < 0) return error; /* This is ensured by midx_parse_packfile_name() */ if (git_buf_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0) return git_odb__error_notfound("midx file contained a non-index", NULL, 0); git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), git_buf_len(&pack_path) - strlen(".idx")); if (git_vector_search2(&found_position, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) { /* Pack was found in the packs list. Moving it to the midx_packs list. */ git_buf_dispose(&pack_path); git_vector_set(NULL, &backend->midx_packs, i, git_vector_get(&backend->packs, found_position)); git_vector_remove(&backend->packs, found_position); return 0; } /* Pack was not found. Allocate a new one. */ error = git_mwindow_get_pack(&pack, git_buf_cstr(&pack_path)); git_buf_dispose(&pack_path); if (error < 0) return error; git_vector_set(NULL, &backend->midx_packs, i, pack); return 0; } /* * Reads the multi-pack-index. If this fails for whatever reason, the * multi-pack-index object is freed, and all the packfiles that are related to * it are moved to the unindexed packfiles vector. */ static int refresh_multi_pack_index(struct pack_backend *backend) { int error; git_buf midx_path = GIT_BUF_INIT; const char *packfile_name; size_t i; error = git_buf_joinpath(&midx_path, backend->pack_folder, "multi-pack-index"); if (error < 0) return error; /* * Check whether the multi-pack-index has changed. If it has, close any * old multi-pack-index and move all the packfiles to the unindexed * packs. This is done to prevent losing any open packfiles in case * refreshing the new multi-pack-index fails, or the file is deleted. */ if (backend->midx) { if (!git_midx_needs_refresh(backend->midx, git_buf_cstr(&midx_path))) { git_buf_dispose(&midx_path); return 0; } error = remove_multi_pack_index(backend); if (error < 0) { git_buf_dispose(&midx_path); return error; } } error = git_midx_open(&backend->midx, git_buf_cstr(&midx_path)); git_buf_dispose(&midx_path); if (error < 0) return error; git_vector_resize_to(&backend->midx_packs, git_vector_length(&backend->midx->packfile_names)); git_vector_foreach(&backend->midx->packfile_names, i, packfile_name) { error = process_multi_pack_index_pack(backend, i, packfile_name); if (error < 0) { /* * Something failed during reading multi-pack-index. * Restore the state of backend as if the * multi-pack-index was never there, and move all * packfiles that have been processed so far to the * unindexed packs. */ git_vector_resize_to(&backend->midx_packs, i); remove_multi_pack_index(backend); return error; } } return 0; } /*********************************************************** * * PACKED BACKEND PUBLIC API * * Implement the git_odb_backend API calls * ***********************************************************/ static int pack_backend__refresh(git_odb_backend *backend_) { int error; struct stat st; git_buf path = GIT_BUF_INIT; struct pack_backend *backend = (struct pack_backend *)backend_; if (backend->pack_folder == NULL) return 0; if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) return git_odb__error_notfound("failed to refresh packfiles", NULL, 0); if (refresh_multi_pack_index(backend) < 0) { /* * It is okay if this fails. We will just not use the * multi-pack-index in this case. */ git_error_clear(); } /* reload all packs */ git_buf_sets(&path, backend->pack_folder); error = git_path_direach(&path, 0, packfile_load__cb, backend); git_buf_dispose(&path); git_vector_sort(&backend->packs); return error; } static int pack_backend__read_header( size_t *len_p, git_object_t *type_p, struct git_odb_backend *backend, const git_oid *oid) { struct git_pack_entry e; int error; GIT_ASSERT_ARG(len_p); GIT_ASSERT_ARG(type_p); GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(oid); if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0) return error; return git_packfile_resolve_header(len_p, type_p, e.p, e.offset); } static int pack_backend__freshen( git_odb_backend *backend, const git_oid *oid) { struct git_pack_entry e; time_t now; int error; if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0) return error; now = time(NULL); if (e.p->last_freshen > now - FRESHEN_FREQUENCY) return 0; if ((error = git_futils_touch(e.p->pack_name, &now)) < 0) return error; e.p->last_freshen = now; return 0; } static int pack_backend__read( void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid) { struct git_pack_entry e; git_rawobj raw = {NULL}; int error; if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 || (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0) return error; *buffer_p = raw.data; *len_p = raw.len; *type_p = raw.type; return 0; } static int pack_backend__read_prefix( git_oid *out_oid, void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *short_oid, size_t len) { int error = 0; if (len < GIT_OID_MINPREFIXLEN) error = git_odb__error_ambiguous("prefix length too short"); else if (len >= GIT_OID_HEXSZ) { /* We can fall back to regular read method */ error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid); if (!error) git_oid_cpy(out_oid, short_oid); } else { struct git_pack_entry e; git_rawobj raw = {NULL}; if ((error = pack_entry_find_prefix( &e, (struct pack_backend *)backend, short_oid, len)) == 0 && (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0) { *buffer_p = raw.data; *len_p = raw.len; *type_p = raw.type; git_oid_cpy(out_oid, &e.sha1); } } return error; } static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) { struct git_pack_entry e; return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0; } static int pack_backend__exists_prefix( git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len) { int error; struct pack_backend *pb = (struct pack_backend *)backend; struct git_pack_entry e = {0}; error = pack_entry_find_prefix(&e, pb, short_id, len); git_oid_cpy(out, &e.sha1); return error; } static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) { int error; struct git_pack_file *p; struct pack_backend *backend; unsigned int i; GIT_ASSERT_ARG(_backend); GIT_ASSERT_ARG(cb); backend = (struct pack_backend *)_backend; /* Make sure we know about the packfiles */ if ((error = pack_backend__refresh(_backend)) != 0) return error; if (backend->midx && (error = git_midx_foreach_entry(backend->midx, cb, data)) != 0) return error; git_vector_foreach(&backend->packs, i, p) { if ((error = git_pack_foreach_entry(p, cb, data)) != 0) return error; } return 0; } static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_indexer_progress *stats) { struct pack_writepack *writepack = (struct pack_writepack *)_writepack; GIT_ASSERT_ARG(writepack); return git_indexer_append(writepack->indexer, data, size, stats); } static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_indexer_progress *stats) { struct pack_writepack *writepack = (struct pack_writepack *)_writepack; GIT_ASSERT_ARG(writepack); return git_indexer_commit(writepack->indexer, stats); } static void pack_backend__writepack_free(struct git_odb_writepack *_writepack) { struct pack_writepack *writepack; if (!_writepack) return; writepack = (struct pack_writepack *)_writepack; git_indexer_free(writepack->indexer); git__free(writepack); } static int pack_backend__writepack(struct git_odb_writepack **out, git_odb_backend *_backend, git_odb *odb, git_indexer_progress_cb progress_cb, void *progress_payload) { git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT; struct pack_backend *backend; struct pack_writepack *writepack; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(_backend); *out = NULL; opts.progress_cb = progress_cb; opts.progress_cb_payload = progress_payload; backend = (struct pack_backend *)_backend; writepack = git__calloc(1, sizeof(struct pack_writepack)); GIT_ERROR_CHECK_ALLOC(writepack); if (git_indexer_new(&writepack->indexer, backend->pack_folder, 0, odb, &opts) < 0) { git__free(writepack); return -1; } writepack->parent.backend = _backend; writepack->parent.append = pack_backend__writepack_append; writepack->parent.commit = pack_backend__writepack_commit; writepack->parent.free = pack_backend__writepack_free; *out = (git_odb_writepack *)writepack; return 0; } static int get_idx_path( git_buf *idx_path, struct pack_backend *backend, struct git_pack_file *p) { size_t path_len; int error; error = git_path_prettify(idx_path, p->pack_name, backend->pack_folder); if (error < 0) return error; path_len = git_buf_len(idx_path); if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_cstr(idx_path), ".pack") != 0) return git_odb__error_notfound("packfile does not end in .pack", NULL, 0); path_len -= strlen(".pack"); error = git_buf_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx")); if (error < 0) return error; return 0; } static int pack_backend__writemidx(git_odb_backend *_backend) { struct pack_backend *backend; git_midx_writer *w = NULL; struct git_pack_file *p; size_t i; int error = 0; GIT_ASSERT_ARG(_backend); backend = (struct pack_backend *)_backend; error = git_midx_writer_new(&w, backend->pack_folder); if (error < 0) return error; git_vector_foreach(&backend->midx_packs, i, p) { git_buf idx_path = GIT_BUF_INIT; error = get_idx_path(&idx_path, backend, p); if (error < 0) goto cleanup; error = git_midx_writer_add(w, git_buf_cstr(&idx_path)); git_buf_dispose(&idx_path); if (error < 0) goto cleanup; } git_vector_foreach(&backend->packs, i, p) { git_buf idx_path = GIT_BUF_INIT; error = get_idx_path(&idx_path, backend, p); if (error < 0) goto cleanup; error = git_midx_writer_add(w, git_buf_cstr(&idx_path)); git_buf_dispose(&idx_path); if (error < 0) goto cleanup; } /* * Invalidate the previous midx before writing the new one. */ error = remove_multi_pack_index(backend); if (error < 0) goto cleanup; error = git_midx_writer_commit(w); if (error < 0) goto cleanup; error = refresh_multi_pack_index(backend); cleanup: git_midx_writer_free(w); return error; } static void pack_backend__free(git_odb_backend *_backend) { struct pack_backend *backend; struct git_pack_file *p; size_t i; if (!_backend) return; backend = (struct pack_backend *)_backend; git_vector_foreach(&backend->midx_packs, i, p) git_mwindow_put_pack(p); git_vector_foreach(&backend->packs, i, p) git_mwindow_put_pack(p); git_midx_free(backend->midx); git_vector_free(&backend->midx_packs); git_vector_free(&backend->packs); git__free(backend->pack_folder); git__free(backend); } static int pack_backend__alloc(struct pack_backend **out, size_t initial_size) { struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend)); GIT_ERROR_CHECK_ALLOC(backend); if (git_vector_init(&backend->midx_packs, 0, NULL) < 0) { git__free(backend); return -1; } if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) { git_vector_free(&backend->midx_packs); git__free(backend); return -1; } backend->parent.version = GIT_ODB_BACKEND_VERSION; backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; backend->parent.exists_prefix = &pack_backend__exists_prefix; backend->parent.refresh = &pack_backend__refresh; backend->parent.foreach = &pack_backend__foreach; backend->parent.writepack = &pack_backend__writepack; backend->parent.writemidx = &pack_backend__writemidx; backend->parent.freshen = &pack_backend__freshen; backend->parent.free = &pack_backend__free; *out = backend; return 0; } int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) { struct pack_backend *backend = NULL; struct git_pack_file *packfile = NULL; if (pack_backend__alloc(&backend, 1) < 0) return -1; if (git_mwindow_get_pack(&packfile, idx) < 0 || git_vector_insert(&backend->packs, packfile) < 0) { pack_backend__free((git_odb_backend *)backend); return -1; } *backend_out = (git_odb_backend *)backend; return 0; } int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) { int error = 0; struct pack_backend *backend = NULL; git_buf path = GIT_BUF_INIT; if (pack_backend__alloc(&backend, 8) < 0) return -1; if (!(error = git_buf_joinpath(&path, objects_dir, "pack")) && git_path_isdir(git_buf_cstr(&path))) { backend->pack_folder = git_buf_detach(&path); error = pack_backend__refresh((git_odb_backend *)backend); } if (error < 0) { pack_backend__free((git_odb_backend *)backend); backend = NULL; } *backend_out = (git_odb_backend *)backend; git_buf_dispose(&path); return error; } git2r/src/libgit2/src/sortedcache.h0000644000175000017500000001407314125111754017010 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sorted_cache_h__ #define INCLUDE_sorted_cache_h__ #include "common.h" #include "util.h" #include "futils.h" #include "vector.h" #include "thread.h" #include "pool.h" #include "strmap.h" #include /* * The purpose of this data structure is to cache the parsed contents of a * file (a.k.a. the backing file) where each item in the file can be * identified by a key string and you want to both look them up by name * and traverse them in sorted order. Each item is assumed to itself end * in a GIT_FLEX_ARRAY. */ typedef void (*git_sortedcache_free_item_fn)(void *payload, void *item); typedef struct { git_refcount rc; git_rwlock lock; size_t item_path_offset; git_sortedcache_free_item_fn free_item; void *free_item_payload; git_pool pool; git_vector items; git_strmap *map; git_futils_filestamp stamp; char path[GIT_FLEX_ARRAY]; } git_sortedcache; /* Create a new sortedcache * * Even though every sortedcache stores items with a GIT_FLEX_ARRAY at * the end containing their key string, you have to provide the item_cmp * sorting function because the sorting function doesn't get a payload * and therefore can't know the offset to the item key string. :-( * * @param out The allocated git_sortedcache * @param item_path_offset Offset to the GIT_FLEX_ARRAY item key in the * struct - use offsetof(struct mine, key-field) to get this * @param free_item Optional callback to free each item * @param free_item_payload Optional payload passed to free_item callback * @param item_cmp Compare the keys of two items * @param path The path to the backing store file for this cache; this * may be NULL. The cache makes it easy to load this and check * if it has been modified since the last load and/or write. */ GIT_WARN_UNUSED_RESULT int git_sortedcache_new( git_sortedcache **out, size_t item_path_offset, /* use offsetof(struct, path-field) macro */ git_sortedcache_free_item_fn free_item, void *free_item_payload, git_vector_cmp item_cmp, const char *path); /* Copy a sorted cache * * - `copy_item` can be NULL to just use memcpy * - if `lock`, grabs read lock on `src` during copy and releases after */ GIT_WARN_UNUSED_RESULT int git_sortedcache_copy( git_sortedcache **out, git_sortedcache *src, bool lock, int (*copy_item)(void *payload, void *tgt_item, void *src_item), void *payload); /* Free sorted cache (first calling `free_item` callbacks) * * Don't call on a locked collection - it may acquire a write lock */ void git_sortedcache_free(git_sortedcache *sc); /* Increment reference count - balance with call to free */ void git_sortedcache_incref(git_sortedcache *sc); /* Get the pathname associated with this cache at creation time */ const char *git_sortedcache_path(git_sortedcache *sc); /* * CACHE WRITE FUNCTIONS * * The following functions require you to have a writer lock to make the * modification. Some of the functions take a `wlock` parameter and * will optionally lock and unlock for you if that is passed as true. * */ /* Lock sortedcache for write */ GIT_WARN_UNUSED_RESULT int git_sortedcache_wlock(git_sortedcache *sc); /* Unlock sorted cache when done with write */ void git_sortedcache_wunlock(git_sortedcache *sc); /* Lock cache and load backing file into a buffer. * * This grabs a write lock on the cache then looks at the modification * time and size of the file on disk. * * If the file appears to have changed, this loads the file contents into * the buffer and returns a positive value leaving the cache locked - the * caller should parse the file content, update the cache as needed, then * release the lock. NOTE: In this case, the caller MUST unlock the cache. * * If the file appears to be unchanged, then this automatically releases * the lock on the cache, clears the buffer, and returns 0. * * @return 0 if up-to-date, 1 if out-of-date, <0 on error */ GIT_WARN_UNUSED_RESULT int git_sortedcache_lockandload( git_sortedcache *sc, git_buf *buf); /* Refresh file timestamp after write completes * You should already be holding the write lock when you call this. */ void git_sortedcache_updated(git_sortedcache *sc); /* Release all items in sorted cache * * If `wlock` is true, grabs write lock and releases when done, otherwise * you should already be holding a write lock when you call this. */ GIT_WARN_UNUSED_RESULT int git_sortedcache_clear( git_sortedcache *sc, bool wlock); /* Find and/or insert item, returning pointer to item data. * You should already be holding the write lock when you call this. */ GIT_WARN_UNUSED_RESULT int git_sortedcache_upsert( void **out, git_sortedcache *sc, const char *key); /* Removes entry at pos from cache * You should already be holding the write lock when you call this. */ int git_sortedcache_remove(git_sortedcache *sc, size_t pos); /* * CACHE READ FUNCTIONS * * The following functions access items in the cache. To prevent the * results from being invalidated before they can be used, you should be * holding either a read lock or a write lock when using these functions. * */ /* Lock sortedcache for read */ GIT_WARN_UNUSED_RESULT int git_sortedcache_rlock(git_sortedcache *sc); /* Unlock sorted cache when done with read */ void git_sortedcache_runlock(git_sortedcache *sc); /* Lookup item by key - returns NULL if not found */ void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key); /* Get how many items are in the cache * * You can call this function without holding a lock, but be aware * that it may change before you use it. */ size_t git_sortedcache_entrycount(const git_sortedcache *sc); /* Lookup item by index - returns NULL if out of range */ void *git_sortedcache_entry(git_sortedcache *sc, size_t pos); /* Lookup index of item by key - returns GIT_ENOTFOUND if not found */ int git_sortedcache_lookup_index( size_t *out, git_sortedcache *sc, const char *key); #endif git2r/src/libgit2/src/status.c0000644000175000017500000003524014125111754016041 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "status.h" #include "git2.h" #include "futils.h" #include "hash.h" #include "vector.h" #include "tree.h" #include "git2/status.h" #include "repository.h" #include "ignore.h" #include "index.h" #include "wildmatch.h" #include "git2/diff.h" #include "diff.h" #include "diff_generate.h" static unsigned int index_delta2status(const git_diff_delta *head2idx) { git_status_t st = GIT_STATUS_CURRENT; switch (head2idx->status) { case GIT_DELTA_ADDED: case GIT_DELTA_COPIED: st = GIT_STATUS_INDEX_NEW; break; case GIT_DELTA_DELETED: st = GIT_STATUS_INDEX_DELETED; break; case GIT_DELTA_MODIFIED: st = GIT_STATUS_INDEX_MODIFIED; break; case GIT_DELTA_RENAMED: st = GIT_STATUS_INDEX_RENAMED; if (!git_oid_equal(&head2idx->old_file.id, &head2idx->new_file.id)) st |= GIT_STATUS_INDEX_MODIFIED; break; case GIT_DELTA_TYPECHANGE: st = GIT_STATUS_INDEX_TYPECHANGE; break; case GIT_DELTA_CONFLICTED: st = GIT_STATUS_CONFLICTED; break; default: break; } return st; } static unsigned int workdir_delta2status( git_diff *diff, git_diff_delta *idx2wd) { git_status_t st = GIT_STATUS_CURRENT; switch (idx2wd->status) { case GIT_DELTA_ADDED: case GIT_DELTA_COPIED: case GIT_DELTA_UNTRACKED: st = GIT_STATUS_WT_NEW; break; case GIT_DELTA_UNREADABLE: st = GIT_STATUS_WT_UNREADABLE; break; case GIT_DELTA_DELETED: st = GIT_STATUS_WT_DELETED; break; case GIT_DELTA_MODIFIED: st = GIT_STATUS_WT_MODIFIED; break; case GIT_DELTA_IGNORED: st = GIT_STATUS_IGNORED; break; case GIT_DELTA_RENAMED: st = GIT_STATUS_WT_RENAMED; if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) { /* if OIDs don't match, we might need to calculate them now to * discern between RENAMED vs RENAMED+MODIFED */ if (git_oid_is_zero(&idx2wd->old_file.id) && diff->old_src == GIT_ITERATOR_WORKDIR && !git_diff__oid_for_file( &idx2wd->old_file.id, diff, idx2wd->old_file.path, idx2wd->old_file.mode, idx2wd->old_file.size)) idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; if (git_oid_is_zero(&idx2wd->new_file.id) && diff->new_src == GIT_ITERATOR_WORKDIR && !git_diff__oid_for_file( &idx2wd->new_file.id, diff, idx2wd->new_file.path, idx2wd->new_file.mode, idx2wd->new_file.size)) idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) st |= GIT_STATUS_WT_MODIFIED; } break; case GIT_DELTA_TYPECHANGE: st = GIT_STATUS_WT_TYPECHANGE; break; case GIT_DELTA_CONFLICTED: st = GIT_STATUS_CONFLICTED; break; default: break; } return st; } static bool status_is_included( git_status_list *status, git_diff_delta *head2idx, git_diff_delta *idx2wd) { if (!(status->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES)) return 1; /* if excluding submodules and this is a submodule everywhere */ if (head2idx) { if (head2idx->status != GIT_DELTA_ADDED && head2idx->old_file.mode != GIT_FILEMODE_COMMIT) return 1; if (head2idx->status != GIT_DELTA_DELETED && head2idx->new_file.mode != GIT_FILEMODE_COMMIT) return 1; } if (idx2wd) { if (idx2wd->status != GIT_DELTA_ADDED && idx2wd->old_file.mode != GIT_FILEMODE_COMMIT) return 1; if (idx2wd->status != GIT_DELTA_DELETED && idx2wd->new_file.mode != GIT_FILEMODE_COMMIT) return 1; } /* only get here if every valid mode is GIT_FILEMODE_COMMIT */ return 0; } static git_status_t status_compute( git_status_list *status, git_diff_delta *head2idx, git_diff_delta *idx2wd) { git_status_t st = GIT_STATUS_CURRENT; if (head2idx) st |= index_delta2status(head2idx); if (idx2wd) st |= workdir_delta2status(status->idx2wd, idx2wd); return st; } static int status_collect( git_diff_delta *head2idx, git_diff_delta *idx2wd, void *payload) { git_status_list *status = payload; git_status_entry *status_entry; if (!status_is_included(status, head2idx, idx2wd)) return 0; status_entry = git__malloc(sizeof(git_status_entry)); GIT_ERROR_CHECK_ALLOC(status_entry); status_entry->status = status_compute(status, head2idx, idx2wd); status_entry->head_to_index = head2idx; status_entry->index_to_workdir = idx2wd; return git_vector_insert(&status->paired, status_entry); } GIT_INLINE(int) status_entry_cmp_base( const void *a, const void *b, int (*strcomp)(const char *a, const char *b)) { const git_status_entry *entry_a = a; const git_status_entry *entry_b = b; const git_diff_delta *delta_a, *delta_b; delta_a = entry_a->index_to_workdir ? entry_a->index_to_workdir : entry_a->head_to_index; delta_b = entry_b->index_to_workdir ? entry_b->index_to_workdir : entry_b->head_to_index; if (!delta_a && delta_b) return -1; if (delta_a && !delta_b) return 1; if (!delta_a && !delta_b) return 0; return strcomp(delta_a->new_file.path, delta_b->new_file.path); } static int status_entry_icmp(const void *a, const void *b) { return status_entry_cmp_base(a, b, git__strcasecmp); } static int status_entry_cmp(const void *a, const void *b) { return status_entry_cmp_base(a, b, git__strcmp); } static git_status_list *git_status_list_alloc(git_index *index) { git_status_list *status = NULL; int (*entrycmp)(const void *a, const void *b); if (!(status = git__calloc(1, sizeof(git_status_list)))) return NULL; entrycmp = index->ignore_case ? status_entry_icmp : status_entry_cmp; if (git_vector_init(&status->paired, 0, entrycmp) < 0) { git__free(status); return NULL; } return status; } static int status_validate_options(const git_status_options *opts) { if (!opts) return 0; GIT_ERROR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) { git_error_set(GIT_ERROR_INVALID, "unknown status 'show' option"); return -1; } if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 && (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) { git_error_set(GIT_ERROR_INVALID, "updating index from status " "is not allowed when index refresh is disabled"); return -1; } return 0; } int git_status_list_new( git_status_list **out, git_repository *repo, const git_status_options *opts) { git_index *index = NULL; git_status_list *status = NULL; git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT; git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT; git_tree *head = NULL; git_status_show_t show = opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; int error = 0; unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS; *out = NULL; if (status_validate_options(opts) < 0) return -1; if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 || (error = git_repository_index(&index, repo)) < 0) return error; if (opts != NULL && opts->baseline != NULL) { head = opts->baseline; } else { /* if there is no HEAD, that's okay - we'll make an empty iterator */ if ((error = git_repository_head_tree(&head, repo)) < 0) { if (error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH) goto done; git_error_clear(); } } /* refresh index from disk unless prevented */ if ((flags & GIT_STATUS_OPT_NO_REFRESH) == 0 && git_index_read_safely(index) < 0) git_error_clear(); status = git_status_list_alloc(index); GIT_ERROR_CHECK_ALLOC(status); if (opts) { memcpy(&status->opts, opts, sizeof(git_status_options)); memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); } diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE; findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED; if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED; if ((flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED; if ((flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED; if ((flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS; if ((flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH; if ((flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS; if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES; if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX; if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE; if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED; if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0) findopt.flags = findopt.flags | GIT_DIFF_FIND_AND_BREAK_REWRITES | GIT_DIFF_FIND_RENAMES_FROM_REWRITES | GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY; if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) { if ((error = git_diff_tree_to_index( &status->head2idx, repo, head, index, &diffopt)) < 0) goto done; if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 && (error = git_diff_find_similar(status->head2idx, &findopt)) < 0) goto done; } if (show != GIT_STATUS_SHOW_INDEX_ONLY) { if ((error = git_diff_index_to_workdir( &status->idx2wd, repo, index, &diffopt)) < 0) { goto done; } if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0) goto done; } error = git_diff__paired_foreach( status->head2idx, status->idx2wd, status_collect, status); if (error < 0) goto done; if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY) git_vector_set_cmp(&status->paired, status_entry_cmp); if (flags & GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY) git_vector_set_cmp(&status->paired, status_entry_icmp); if ((flags & (GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY | GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)) != 0) git_vector_sort(&status->paired); done: if (error < 0) { git_status_list_free(status); status = NULL; } *out = status; if (opts == NULL || opts->baseline != head) git_tree_free(head); git_index_free(index); return error; } size_t git_status_list_entrycount(git_status_list *status) { GIT_ASSERT_ARG_WITH_RETVAL(status, 0); return status->paired.length; } const git_status_entry *git_status_byindex(git_status_list *status, size_t i) { GIT_ASSERT_ARG_WITH_RETVAL(status, NULL); return git_vector_get(&status->paired, i); } void git_status_list_free(git_status_list *status) { if (status == NULL) return; git_diff_free(status->head2idx); git_diff_free(status->idx2wd); git_vector_free_deep(&status->paired); git__memzero(status, sizeof(*status)); git__free(status); } int git_status_foreach_ext( git_repository *repo, const git_status_options *opts, git_status_cb cb, void *payload) { git_status_list *status; const git_status_entry *status_entry; size_t i; int error = 0; if ((error = git_status_list_new(&status, repo, opts)) < 0) { return error; } git_vector_foreach(&status->paired, i, status_entry) { const char *path = status_entry->head_to_index ? status_entry->head_to_index->old_file.path : status_entry->index_to_workdir->old_file.path; if ((error = cb(path, status_entry->status, payload)) != 0) { git_error_set_after_callback(error); break; } } git_status_list_free(status); return error; } int git_status_foreach(git_repository *repo, git_status_cb cb, void *payload) { return git_status_foreach_ext(repo, NULL, cb, payload); } struct status_file_info { char *expected; unsigned int count; unsigned int status; int wildmatch_flags; int ambiguous; }; static int get_one_status(const char *path, unsigned int status, void *data) { struct status_file_info *sfi = data; int (*strcomp)(const char *a, const char *b); sfi->count++; sfi->status = status; strcomp = (sfi->wildmatch_flags & WM_CASEFOLD) ? git__strcasecmp : git__strcmp; if (sfi->count > 1 || (strcomp(sfi->expected, path) != 0 && wildmatch(sfi->expected, path, sfi->wildmatch_flags) != 0)) { sfi->ambiguous = true; return GIT_EAMBIGUOUS; /* git_error_set will be done by caller */ } return 0; } int git_status_file( unsigned int *status_flags, git_repository *repo, const char *path) { int error; git_status_options opts = GIT_STATUS_OPTIONS_INIT; struct status_file_info sfi = {0}; git_index *index; GIT_ASSERT_ARG(status_flags); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(path); if ((error = git_repository_index__weakptr(&index, repo)) < 0) return error; if ((sfi.expected = git__strdup(path)) == NULL) return -1; if (index->ignore_case) sfi.wildmatch_flags = WM_CASEFOLD; opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS | GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | GIT_STATUS_OPT_INCLUDE_UNMODIFIED | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH; opts.pathspec.count = 1; opts.pathspec.strings = &sfi.expected; error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi); if (error < 0 && sfi.ambiguous) { git_error_set(GIT_ERROR_INVALID, "ambiguous path '%s' given to git_status_file", sfi.expected); error = GIT_EAMBIGUOUS; } if (!error && !sfi.count) { git_error_set(GIT_ERROR_INVALID, "attempt to get status of nonexistent file '%s'", path); error = GIT_ENOTFOUND; } *status_flags = sfi.status; git__free(sfi.expected); return error; } int git_status_should_ignore( int *ignored, git_repository *repo, const char *path) { return git_ignore_path_is_ignored(ignored, repo, path); } int git_status_options_init(git_status_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_status_options, GIT_STATUS_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_status_init_options(git_status_options *opts, unsigned int version) { return git_status_options_init(opts, version); } #endif int git_status_list_get_perfdata( git_diff_perfdata *out, const git_status_list *status) { GIT_ASSERT_ARG(out); GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata"); out->stat_calls = 0; out->oid_calculations = 0; if (status->head2idx) { out->stat_calls += status->head2idx->perf.stat_calls; out->oid_calculations += status->head2idx->perf.oid_calculations; } if (status->idx2wd) { out->stat_calls += status->idx2wd->perf.stat_calls; out->oid_calculations += status->idx2wd->perf.oid_calculations; } return 0; } git2r/src/libgit2/src/diff_file.h0000644000175000017500000000320514125111754016426 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_diff_file_h__ #define INCLUDE_diff_file_h__ #include "common.h" #include "diff.h" #include "diff_driver.h" #include "map.h" /* expanded information for one side of a delta */ typedef struct { git_repository *repo; git_diff_file *file; git_diff_driver *driver; uint32_t flags; uint32_t opts_flags; git_object_size_t opts_max_size; git_iterator_t src; const git_blob *blob; git_map map; } git_diff_file_content; extern int git_diff_file_content__init_from_diff( git_diff_file_content *fc, git_diff *diff, git_diff_delta *delta, bool use_old); typedef struct { const git_blob *blob; const void *buf; size_t buflen; const char *as_path; } git_diff_file_content_src; #define GIT_DIFF_FILE_CONTENT_SRC__BLOB(BLOB,PATH) { (BLOB),NULL,0,(PATH) } #define GIT_DIFF_FILE_CONTENT_SRC__BUF(BUF,LEN,PATH) { NULL,(BUF),(LEN),(PATH) } extern int git_diff_file_content__init_from_src( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, const git_diff_file_content_src *src, git_diff_file *as_file); /* this loads the blob/file-on-disk as needed */ extern int git_diff_file_content__load( git_diff_file_content *fc, git_diff_options *diff_opts); /* this releases the blob/file-in-memory */ extern void git_diff_file_content__unload(git_diff_file_content *fc); /* this unloads and also releases any other resources */ extern void git_diff_file_content__clear(git_diff_file_content *fc); #endif git2r/src/libgit2/src/pathspec.c0000644000175000017500000004153014125111754016324 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "pathspec.h" #include "git2/pathspec.h" #include "git2/diff.h" #include "attr_file.h" #include "iterator.h" #include "repository.h" #include "index.h" #include "bitvec.h" #include "diff.h" #include "wildmatch.h" /* what is the common non-wildcard prefix for all items in the pathspec */ char *git_pathspec_prefix(const git_strarray *pathspec) { git_buf prefix = GIT_BUF_INIT; const char *scan; if (!pathspec || !pathspec->count || git_buf_common_prefix(&prefix, pathspec->strings, pathspec->count) < 0) return NULL; /* diff prefix will only be leading non-wildcards */ for (scan = prefix.ptr; *scan; ++scan) { if (git__iswildcard(*scan) && (scan == prefix.ptr || (*(scan - 1) != '\\'))) break; } git_buf_truncate(&prefix, scan - prefix.ptr); if (prefix.size <= 0) { git_buf_dispose(&prefix); return NULL; } git_buf_unescape(&prefix); return git_buf_detach(&prefix); } /* is there anything in the spec that needs to be filtered on */ bool git_pathspec_is_empty(const git_strarray *pathspec) { size_t i; if (pathspec == NULL) return true; for (i = 0; i < pathspec->count; ++i) { const char *str = pathspec->strings[i]; if (str && str[0]) return false; } return true; } /* build a vector of fnmatch patterns to evaluate efficiently */ int git_pathspec__vinit( git_vector *vspec, const git_strarray *strspec, git_pool *strpool) { size_t i; memset(vspec, 0, sizeof(*vspec)); if (git_pathspec_is_empty(strspec)) return 0; if (git_vector_init(vspec, strspec->count, NULL) < 0) return -1; for (i = 0; i < strspec->count; ++i) { int ret; const char *pattern = strspec->strings[i]; git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch)); if (!match) return -1; match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern); if (ret == GIT_ENOTFOUND) { git__free(match); continue; } else if (ret < 0) { git__free(match); return ret; } if (git_vector_insert(vspec, match) < 0) return -1; } return 0; } /* free data from the pathspec vector */ void git_pathspec__vfree(git_vector *vspec) { git_vector_free_deep(vspec); } struct pathspec_match_context { int wildmatch_flags; int (*strcomp)(const char *, const char *); int (*strncomp)(const char *, const char *, size_t); }; static void pathspec_match_context_init( struct pathspec_match_context *ctxt, bool disable_fnmatch, bool casefold) { if (disable_fnmatch) ctxt->wildmatch_flags = -1; else if (casefold) ctxt->wildmatch_flags = WM_CASEFOLD; else ctxt->wildmatch_flags = 0; if (casefold) { ctxt->strcomp = git__strcasecmp; ctxt->strncomp = git__strncasecmp; } else { ctxt->strcomp = git__strcmp; ctxt->strncomp = git__strncmp; } } static int pathspec_match_one( const git_attr_fnmatch *match, struct pathspec_match_context *ctxt, const char *path) { int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : WM_NOMATCH; if (result == WM_NOMATCH) result = ctxt->strcomp(match->pattern, path) ? WM_NOMATCH : 0; if (ctxt->wildmatch_flags >= 0 && result == WM_NOMATCH) result = wildmatch(match->pattern, path, ctxt->wildmatch_flags); /* if we didn't match, look for exact dirname prefix match */ if (result == WM_NOMATCH && (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 && ctxt->strncomp(path, match->pattern, match->length) == 0 && path[match->length] == '/') result = 0; /* if we didn't match and this is a negative match, check for exact * match of filename with leading '!' */ if (result == WM_NOMATCH && (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 && *path == '!' && ctxt->strncomp(path + 1, match->pattern, match->length) == 0 && (!path[match->length + 1] || path[match->length + 1] == '/')) return 1; if (result == 0) return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1; return -1; } static int git_pathspec__match_at( size_t *matched_at, const git_vector *vspec, struct pathspec_match_context *ctxt, const char *path0, const char *path1) { int result = GIT_ENOTFOUND; size_t i = 0; const git_attr_fnmatch *match; git_vector_foreach(vspec, i, match) { if (path0 && (result = pathspec_match_one(match, ctxt, path0)) >= 0) break; if (path1 && (result = pathspec_match_one(match, ctxt, path1)) >= 0) break; } *matched_at = i; return result; } /* match a path against the vectorized pathspec */ bool git_pathspec__match( const git_vector *vspec, const char *path, bool disable_fnmatch, bool casefold, const char **matched_pathspec, size_t *matched_at) { int result; size_t pos; struct pathspec_match_context ctxt; if (matched_pathspec) *matched_pathspec = NULL; if (matched_at) *matched_at = GIT_PATHSPEC_NOMATCH; if (!vspec || !vspec->length) return true; pathspec_match_context_init(&ctxt, disable_fnmatch, casefold); result = git_pathspec__match_at(&pos, vspec, &ctxt, path, NULL); if (result >= 0) { if (matched_pathspec) { const git_attr_fnmatch *match = git_vector_get(vspec, pos); *matched_pathspec = match->pattern; } if (matched_at) *matched_at = pos; } return (result > 0); } int git_pathspec__init(git_pathspec *ps, const git_strarray *paths) { int error = 0; memset(ps, 0, sizeof(*ps)); ps->prefix = git_pathspec_prefix(paths); if ((error = git_pool_init(&ps->pool, 1)) < 0 || (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0) git_pathspec__clear(ps); return error; } void git_pathspec__clear(git_pathspec *ps) { git__free(ps->prefix); git_pathspec__vfree(&ps->pathspec); git_pool_clear(&ps->pool); memset(ps, 0, sizeof(*ps)); } int git_pathspec_new(git_pathspec **out, const git_strarray *pathspec) { int error = 0; git_pathspec *ps = git__malloc(sizeof(git_pathspec)); GIT_ERROR_CHECK_ALLOC(ps); if ((error = git_pathspec__init(ps, pathspec)) < 0) { git__free(ps); return error; } GIT_REFCOUNT_INC(ps); *out = ps; return 0; } static void pathspec_free(git_pathspec *ps) { git_pathspec__clear(ps); git__free(ps); } void git_pathspec_free(git_pathspec *ps) { if (!ps) return; GIT_REFCOUNT_DEC(ps, pathspec_free); } int git_pathspec_matches_path( const git_pathspec *ps, uint32_t flags, const char *path) { bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0; bool casefold = (flags & GIT_PATHSPEC_IGNORE_CASE) != 0; GIT_ASSERT_ARG(ps); GIT_ASSERT_ARG(path); return (0 != git_pathspec__match( &ps->pathspec, path, no_fnmatch, casefold, NULL, NULL)); } static void pathspec_match_free(git_pathspec_match_list *m) { if (!m) return; git_pathspec_free(m->pathspec); m->pathspec = NULL; git_array_clear(m->matches); git_array_clear(m->failures); git_pool_clear(&m->pool); git__free(m); } static git_pathspec_match_list *pathspec_match_alloc( git_pathspec *ps, int datatype) { git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list)); if (!m) return NULL; if (git_pool_init(&m->pool, 1) < 0) return NULL; /* need to keep reference to pathspec and increment refcount because * failures array stores pointers to the pattern strings of the * pathspec that had no matches */ GIT_REFCOUNT_INC(ps); m->pathspec = ps; m->datatype = datatype; return m; } GIT_INLINE(size_t) pathspec_mark_pattern(git_bitvec *used, size_t pos) { if (!git_bitvec_get(used, pos)) { git_bitvec_set(used, pos, true); return 1; } return 0; } static size_t pathspec_mark_remaining( git_bitvec *used, git_vector *patterns, struct pathspec_match_context *ctxt, size_t start, const char *path0, const char *path1) { size_t count = 0; if (path1 == path0) path1 = NULL; for (; start < patterns->length; ++start) { const git_attr_fnmatch *pat = git_vector_get(patterns, start); if (git_bitvec_get(used, start)) continue; if (path0 && pathspec_match_one(pat, ctxt, path0) > 0) count += pathspec_mark_pattern(used, start); else if (path1 && pathspec_match_one(pat, ctxt, path1) > 0) count += pathspec_mark_pattern(used, start); } return count; } static int pathspec_build_failure_array( git_pathspec_string_array_t *failures, git_vector *patterns, git_bitvec *used, git_pool *pool) { size_t pos; char **failed; const git_attr_fnmatch *pat; for (pos = 0; pos < patterns->length; ++pos) { if (git_bitvec_get(used, pos)) continue; if ((failed = git_array_alloc(*failures)) == NULL) return -1; pat = git_vector_get(patterns, pos); if ((*failed = git_pool_strdup(pool, pat->pattern)) == NULL) return -1; } return 0; } static int pathspec_match_from_iterator( git_pathspec_match_list **out, git_iterator *iter, uint32_t flags, git_pathspec *ps) { int error = 0; git_pathspec_match_list *m = NULL; const git_index_entry *entry = NULL; struct pathspec_match_context ctxt; git_vector *patterns = &ps->pathspec; bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0; bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0; size_t pos, used_ct = 0, found_files = 0; git_index *index = NULL; git_bitvec used_patterns; char **file; if (git_bitvec_init(&used_patterns, patterns->length) < 0) return -1; if (out) { *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_STRINGS); GIT_ERROR_CHECK_ALLOC(m); } if ((error = git_iterator_reset_range(iter, ps->prefix, ps->prefix)) < 0) goto done; if (git_iterator_type(iter) == GIT_ITERATOR_WORKDIR && (error = git_repository_index__weakptr( &index, git_iterator_owner(iter))) < 0) goto done; pathspec_match_context_init( &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0, git_iterator_ignore_case(iter)); while (!(error = git_iterator_advance(&entry, iter))) { /* search for match with entry->path */ int result = git_pathspec__match_at( &pos, patterns, &ctxt, entry->path, NULL); /* no matches for this path */ if (result < 0) continue; /* if result was a negative pattern match, then don't list file */ if (!result) { used_ct += pathspec_mark_pattern(&used_patterns, pos); continue; } /* check if path is ignored and untracked */ if (index != NULL && git_iterator_current_is_ignored(iter) && git_index__find_pos(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0) continue; /* mark the matched pattern as used */ used_ct += pathspec_mark_pattern(&used_patterns, pos); ++found_files; /* if find_failures is on, check if any later patterns also match */ if (find_failures && used_ct < patterns->length) used_ct += pathspec_mark_remaining( &used_patterns, patterns, &ctxt, pos + 1, entry->path, NULL); /* if only looking at failures, exit early or just continue */ if (failures_only || !out) { if (used_ct == patterns->length) break; continue; } /* insert matched path into matches array */ if ((file = (char **)git_array_alloc(m->matches)) == NULL || (*file = git_pool_strdup(&m->pool, entry->path)) == NULL) { error = -1; goto done; } } if (error < 0 && error != GIT_ITEROVER) goto done; error = 0; /* insert patterns that had no matches into failures array */ if (find_failures && used_ct < patterns->length && (error = pathspec_build_failure_array( &m->failures, patterns, &used_patterns, &m->pool)) < 0) goto done; /* if every pattern failed to match, then we have failed */ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) { git_error_set(GIT_ERROR_INVALID, "no matching files were found"); error = GIT_ENOTFOUND; } done: git_bitvec_free(&used_patterns); if (error < 0) { pathspec_match_free(m); if (out) *out = NULL; } return error; } static git_iterator_flag_t pathspec_match_iter_flags(uint32_t flags) { git_iterator_flag_t f = 0; if ((flags & GIT_PATHSPEC_IGNORE_CASE) != 0) f |= GIT_ITERATOR_IGNORE_CASE; else if ((flags & GIT_PATHSPEC_USE_CASE) != 0) f |= GIT_ITERATOR_DONT_IGNORE_CASE; return f; } int git_pathspec_match_workdir( git_pathspec_match_list **out, git_repository *repo, uint32_t flags, git_pathspec *ps) { git_iterator *iter; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error = 0; GIT_ASSERT_ARG(repo); iter_opts.flags = pathspec_match_iter_flags(flags); if (!(error = git_iterator_for_workdir(&iter, repo, NULL, NULL, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); git_iterator_free(iter); } return error; } int git_pathspec_match_index( git_pathspec_match_list **out, git_index *index, uint32_t flags, git_pathspec *ps) { git_iterator *iter; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error = 0; GIT_ASSERT_ARG(index); iter_opts.flags = pathspec_match_iter_flags(flags); if (!(error = git_iterator_for_index(&iter, git_index_owner(index), index, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); git_iterator_free(iter); } return error; } int git_pathspec_match_tree( git_pathspec_match_list **out, git_tree *tree, uint32_t flags, git_pathspec *ps) { git_iterator *iter; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error = 0; GIT_ASSERT_ARG(tree); iter_opts.flags = pathspec_match_iter_flags(flags); if (!(error = git_iterator_for_tree(&iter, tree, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); git_iterator_free(iter); } return error; } int git_pathspec_match_diff( git_pathspec_match_list **out, git_diff *diff, uint32_t flags, git_pathspec *ps) { int error = 0; git_pathspec_match_list *m = NULL; struct pathspec_match_context ctxt; git_vector *patterns = &ps->pathspec; bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0; bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0; size_t i, pos, used_ct = 0, found_deltas = 0; const git_diff_delta *delta, **match; git_bitvec used_patterns; GIT_ASSERT_ARG(diff); if (git_bitvec_init(&used_patterns, patterns->length) < 0) return -1; if (out) { *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_DIFF); GIT_ERROR_CHECK_ALLOC(m); } pathspec_match_context_init( &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0, git_diff_is_sorted_icase(diff)); git_vector_foreach(&diff->deltas, i, delta) { /* search for match with delta */ int result = git_pathspec__match_at( &pos, patterns, &ctxt, delta->old_file.path, delta->new_file.path); /* no matches for this path */ if (result < 0) continue; /* mark the matched pattern as used */ used_ct += pathspec_mark_pattern(&used_patterns, pos); /* if result was a negative pattern match, then don't list file */ if (!result) continue; ++found_deltas; /* if find_failures is on, check if any later patterns also match */ if (find_failures && used_ct < patterns->length) used_ct += pathspec_mark_remaining( &used_patterns, patterns, &ctxt, pos + 1, delta->old_file.path, delta->new_file.path); /* if only looking at failures, exit early or just continue */ if (failures_only || !out) { if (used_ct == patterns->length) break; continue; } /* insert matched delta into matches array */ if (!(match = (const git_diff_delta **)git_array_alloc(m->matches))) { error = -1; goto done; } else { *match = delta; } } /* insert patterns that had no matches into failures array */ if (find_failures && used_ct < patterns->length && (error = pathspec_build_failure_array( &m->failures, patterns, &used_patterns, &m->pool)) < 0) goto done; /* if every pattern failed to match, then we have failed */ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) { git_error_set(GIT_ERROR_INVALID, "no matching deltas were found"); error = GIT_ENOTFOUND; } done: git_bitvec_free(&used_patterns); if (error < 0) { pathspec_match_free(m); if (out) *out = NULL; } return error; } void git_pathspec_match_list_free(git_pathspec_match_list *m) { if (m) pathspec_match_free(m); } size_t git_pathspec_match_list_entrycount( const git_pathspec_match_list *m) { return m ? git_array_size(m->matches) : 0; } const char *git_pathspec_match_list_entry( const git_pathspec_match_list *m, size_t pos) { if (!m || m->datatype != PATHSPEC_DATATYPE_STRINGS || !git_array_valid_index(m->matches, pos)) return NULL; return *((const char **)git_array_get(m->matches, pos)); } const git_diff_delta *git_pathspec_match_list_diff_entry( const git_pathspec_match_list *m, size_t pos) { if (!m || m->datatype != PATHSPEC_DATATYPE_DIFF || !git_array_valid_index(m->matches, pos)) return NULL; return *((const git_diff_delta **)git_array_get(m->matches, pos)); } size_t git_pathspec_match_list_failed_entrycount( const git_pathspec_match_list *m) { return m ? git_array_size(m->failures) : 0; } const char * git_pathspec_match_list_failed_entry( const git_pathspec_match_list *m, size_t pos) { char **entry = m ? git_array_get(m->failures, pos) : NULL; return entry ? *entry : NULL; } git2r/src/libgit2/src/patch_generate.h0000644000175000017500000000366014125111754017475 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_patch_generate_h__ #define INCLUDE_patch_generate_h__ #include "common.h" #include "diff.h" #include "diff_file.h" #include "patch.h" enum { GIT_PATCH_GENERATED_ALLOCATED = (1 << 0), GIT_PATCH_GENERATED_INITIALIZED = (1 << 1), GIT_PATCH_GENERATED_LOADED = (1 << 2), /* the two sides are different */ GIT_PATCH_GENERATED_DIFFABLE = (1 << 3), /* the difference between the two sides has been computed */ GIT_PATCH_GENERATED_DIFFED = (1 << 4), GIT_PATCH_GENERATED_FLATTENED = (1 << 5), }; struct git_patch_generated { struct git_patch base; git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */ size_t delta_index; git_diff_file_content ofile; git_diff_file_content nfile; uint32_t flags; git_pool flattened; }; typedef struct git_patch_generated git_patch_generated; extern git_diff_driver *git_patch_generated_driver(git_patch_generated *); extern void git_patch_generated_old_data( char **, size_t *, git_patch_generated *); extern void git_patch_generated_new_data( char **, size_t *, git_patch_generated *); extern int git_patch_generated_from_diff( git_patch **, git_diff *, size_t); typedef struct git_patch_generated_output git_patch_generated_output; struct git_patch_generated_output { /* these callbacks are issued with the diff data */ git_diff_file_cb file_cb; git_diff_binary_cb binary_cb; git_diff_hunk_cb hunk_cb; git_diff_line_cb data_cb; void *payload; /* this records the actual error in cases where it may be obscured */ int error; /* this callback is used to do the diff and drive the other callbacks. * see diff_xdiff.h for how to use this in practice for now. */ int (*diff_cb)(git_patch_generated_output *output, git_patch_generated *patch); }; #endif git2r/src/libgit2/src/config_parse.h0000644000175000017500000000310714125111754017157 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_config_parse_h__ #define INCLUDE_config_parse_h__ #include "common.h" #include "array.h" #include "futils.h" #include "oid.h" #include "parse.h" extern const char *git_config_escapes; extern const char *git_config_escaped; typedef struct { const char *path; git_parse_ctx ctx; } git_config_parser; #define GIT_CONFIG_PARSER_INIT { NULL, GIT_PARSE_CTX_INIT } typedef int (*git_config_parser_section_cb)( git_config_parser *parser, const char *current_section, const char *line, size_t line_len, void *payload); typedef int (*git_config_parser_variable_cb)( git_config_parser *parser, const char *current_section, const char *var_name, const char *var_value, const char *line, size_t line_len, void *payload); typedef int (*git_config_parser_comment_cb)( git_config_parser *parser, const char *line, size_t line_len, void *payload); typedef int (*git_config_parser_eof_cb)( git_config_parser *parser, const char *current_section, void *payload); int git_config_parser_init(git_config_parser *out, const char *path, const char *data, size_t datalen); void git_config_parser_dispose(git_config_parser *parser); int git_config_parse( git_config_parser *parser, git_config_parser_section_cb on_section, git_config_parser_variable_cb on_variable, git_config_parser_comment_cb on_comment, git_config_parser_eof_cb on_eof, void *payload); #endif git2r/src/libgit2/src/blob.c0000644000175000017500000003035114125111754015432 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "blob.h" #include "git2/common.h" #include "git2/object.h" #include "git2/repository.h" #include "git2/odb_backend.h" #include "filebuf.h" #include "filter.h" const void *git_blob_rawcontent(const git_blob *blob) { GIT_ASSERT_ARG_WITH_RETVAL(blob, NULL); if (blob->raw) return blob->data.raw.data; else return git_odb_object_data(blob->data.odb); } git_object_size_t git_blob_rawsize(const git_blob *blob) { GIT_ASSERT_ARG(blob); if (blob->raw) return blob->data.raw.size; else return (git_object_size_t)git_odb_object_size(blob->data.odb); } int git_blob__getbuf(git_buf *buffer, git_blob *blob) { git_object_size_t size = git_blob_rawsize(blob); GIT_ERROR_CHECK_BLOBSIZE(size); return git_buf_set(buffer, git_blob_rawcontent(blob), (size_t)size); } void git_blob__free(void *_blob) { git_blob *blob = (git_blob *) _blob; if (!blob->raw) git_odb_object_free(blob->data.odb); git__free(blob); } int git_blob__parse_raw(void *_blob, const char *data, size_t size) { git_blob *blob = (git_blob *) _blob; GIT_ASSERT_ARG(blob); blob->raw = 1; blob->data.raw.data = data; blob->data.raw.size = size; return 0; } int git_blob__parse(void *_blob, git_odb_object *odb_obj) { git_blob *blob = (git_blob *) _blob; GIT_ASSERT_ARG(blob); git_cached_obj_incref((git_cached_obj *)odb_obj); blob->raw = 0; blob->data.odb = odb_obj; return 0; } int git_blob_create_from_buffer( git_oid *id, git_repository *repo, const void *buffer, size_t len) { int error; git_odb *odb; git_odb_stream *stream; GIT_ASSERT_ARG(id); GIT_ASSERT_ARG(repo); if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJECT_BLOB)) < 0) return error; if ((error = git_odb_stream_write(stream, buffer, len)) == 0) error = git_odb_stream_finalize_write(id, stream); git_odb_stream_free(stream); return error; } static int write_file_stream( git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size) { int fd, error; char buffer[FILEIO_BUFSIZE]; git_odb_stream *stream = NULL; ssize_t read_len = -1; git_object_size_t written = 0; if ((error = git_odb_open_wstream( &stream, odb, file_size, GIT_OBJECT_BLOB)) < 0) return error; if ((fd = git_futils_open_ro(path)) < 0) { git_odb_stream_free(stream); return -1; } while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) { error = git_odb_stream_write(stream, buffer, read_len); written += read_len; } p_close(fd); if (written != file_size || read_len < 0) { git_error_set(GIT_ERROR_OS, "failed to read file into stream"); error = -1; } if (!error) error = git_odb_stream_finalize_write(id, stream); git_odb_stream_free(stream); return error; } static int write_file_filtered( git_oid *id, git_object_size_t *size, git_odb *odb, const char *full_path, git_filter_list *fl, git_repository* repo) { int error; git_buf tgt = GIT_BUF_INIT; error = git_filter_list_apply_to_file(&tgt, fl, repo, full_path); /* Write the file to disk if it was properly filtered */ if (!error) { *size = tgt.size; error = git_odb_write(id, odb, tgt.ptr, tgt.size, GIT_OBJECT_BLOB); } git_buf_dispose(&tgt); return error; } static int write_symlink( git_oid *id, git_odb *odb, const char *path, size_t link_size) { char *link_data; ssize_t read_len; int error; link_data = git__malloc(link_size); GIT_ERROR_CHECK_ALLOC(link_data); read_len = p_readlink(path, link_data, link_size); if (read_len != (ssize_t)link_size) { git_error_set(GIT_ERROR_OS, "failed to create blob: cannot read symlink '%s'", path); git__free(link_data); return -1; } error = git_odb_write(id, odb, (void *)link_data, link_size, GIT_OBJECT_BLOB); git__free(link_data); return error; } int git_blob__create_from_paths( git_oid *id, struct stat *out_st, git_repository *repo, const char *content_path, const char *hint_path, mode_t hint_mode, bool try_load_filters) { int error; struct stat st; git_odb *odb = NULL; git_object_size_t size; mode_t mode; git_buf path = GIT_BUF_INIT; GIT_ASSERT_ARG(hint_path || !try_load_filters); if (!content_path) { if (git_repository_workdir_path(&path, repo, hint_path) < 0) return -1; content_path = path.ptr; } if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb(&odb, repo)) < 0) goto done; if (S_ISDIR(st.st_mode)) { git_error_set(GIT_ERROR_ODB, "cannot create blob from '%s': it is a directory", content_path); error = GIT_EDIRECTORY; goto done; } if (out_st) memcpy(out_st, &st, sizeof(st)); size = st.st_size; mode = hint_mode ? hint_mode : st.st_mode; if (S_ISLNK(mode)) { error = write_symlink(id, odb, content_path, (size_t)size); } else { git_filter_list *fl = NULL; if (try_load_filters) /* Load the filters for writing this file to the ODB */ error = git_filter_list_load( &fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT); if (error < 0) /* well, that didn't work */; else if (fl == NULL) /* No filters need to be applied to the document: we can stream * directly from disk */ error = write_file_stream(id, odb, content_path, size); else { /* We need to apply one or more filters */ error = write_file_filtered(id, &size, odb, content_path, fl, repo); git_filter_list_free(fl); } /* * TODO: eventually support streaming filtered files, for files * which are bigger than a given threshold. This is not a priority * because applying a filter in streaming mode changes the final * size of the blob, and without knowing its final size, the blob * cannot be written in stream mode to the ODB. * * The plan is to do streaming writes to a tempfile on disk and then * opening streaming that file to the ODB, using * `write_file_stream`. * * CAREFULLY DESIGNED APIS YO */ } done: git_odb_free(odb); git_buf_dispose(&path); return error; } int git_blob_create_from_workdir( git_oid *id, git_repository *repo, const char *path) { return git_blob__create_from_paths(id, NULL, repo, NULL, path, 0, true); } int git_blob_create_from_disk( git_oid *id, git_repository *repo, const char *path) { int error; git_buf full_path = GIT_BUF_INIT; const char *workdir, *hintpath = NULL; if ((error = git_path_prettify(&full_path, path, NULL)) < 0) { git_buf_dispose(&full_path); return error; } workdir = git_repository_workdir(repo); if (workdir && !git__prefixcmp(full_path.ptr, workdir)) hintpath = full_path.ptr + strlen(workdir); error = git_blob__create_from_paths( id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, !!hintpath); git_buf_dispose(&full_path); return error; } typedef struct { git_writestream parent; git_filebuf fbuf; git_repository *repo; char *hintpath; } blob_writestream; static int blob_writestream_close(git_writestream *_stream) { blob_writestream *stream = (blob_writestream *) _stream; git_filebuf_cleanup(&stream->fbuf); return 0; } static void blob_writestream_free(git_writestream *_stream) { blob_writestream *stream = (blob_writestream *) _stream; git_filebuf_cleanup(&stream->fbuf); git__free(stream->hintpath); git__free(stream); } static int blob_writestream_write(git_writestream *_stream, const char *buffer, size_t len) { blob_writestream *stream = (blob_writestream *) _stream; return git_filebuf_write(&stream->fbuf, buffer, len); } int git_blob_create_from_stream(git_writestream **out, git_repository *repo, const char *hintpath) { int error; git_buf path = GIT_BUF_INIT; blob_writestream *stream; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); stream = git__calloc(1, sizeof(blob_writestream)); GIT_ERROR_CHECK_ALLOC(stream); if (hintpath) { stream->hintpath = git__strdup(hintpath); GIT_ERROR_CHECK_ALLOC(stream->hintpath); } stream->repo = repo; stream->parent.write = blob_writestream_write; stream->parent.close = blob_writestream_close; stream->parent.free = blob_writestream_free; if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0 || (error = git_buf_joinpath(&path, path.ptr, "streamed")) < 0) goto cleanup; if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666, 2 * 1024 * 1024)) < 0) goto cleanup; *out = (git_writestream *) stream; cleanup: if (error < 0) blob_writestream_free((git_writestream *) stream); git_buf_dispose(&path); return error; } int git_blob_create_from_stream_commit(git_oid *out, git_writestream *_stream) { int error; blob_writestream *stream = (blob_writestream *) _stream; /* * We can make this more officient by avoiding writing to * disk, but for now let's re-use the helper functions we * have. */ if ((error = git_filebuf_flush(&stream->fbuf)) < 0) goto cleanup; error = git_blob__create_from_paths(out, NULL, stream->repo, stream->fbuf.path_lock, stream->hintpath, 0, !!stream->hintpath); cleanup: blob_writestream_free(_stream); return error; } int git_blob_is_binary(const git_blob *blob) { git_buf content = GIT_BUF_INIT; git_object_size_t size; GIT_ASSERT_ARG(blob); size = git_blob_rawsize(blob); git_buf_attach_notowned(&content, git_blob_rawcontent(blob), (size_t)min(size, GIT_FILTER_BYTES_TO_CHECK_NUL)); return git_buf_is_binary(&content); } int git_blob_filter_options_init( git_blob_filter_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, git_blob_filter_options, GIT_BLOB_FILTER_OPTIONS_INIT); return 0; } int git_blob_filter( git_buf *out, git_blob *blob, const char *path, git_blob_filter_options *given_opts) { int error = 0; git_filter_list *fl = NULL; git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT; git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT; GIT_ASSERT_ARG(blob); GIT_ASSERT_ARG(path); GIT_ASSERT_ARG(out); GIT_ERROR_CHECK_VERSION( given_opts, GIT_BLOB_FILTER_OPTIONS_VERSION, "git_blob_filter_options"); if (git_buf_sanitize(out) < 0) return -1; if (given_opts != NULL) memcpy(&opts, given_opts, sizeof(git_blob_filter_options)); if ((opts.flags & GIT_BLOB_FILTER_CHECK_FOR_BINARY) != 0 && git_blob_is_binary(blob)) return 0; if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) filter_opts.flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES; if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD) != 0) filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD; if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) { filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_COMMIT; #ifndef GIT_DEPRECATE_HARD if (opts.commit_id) git_oid_cpy(&filter_opts.attr_commit_id, opts.commit_id); else #endif git_oid_cpy(&filter_opts.attr_commit_id, &opts.attr_commit_id); } if (!(error = git_filter_list_load_ext( &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE, &filter_opts))) { error = git_filter_list_apply_to_blob(out, fl, blob); git_filter_list_free(fl); } return error; } /* Deprecated functions */ #ifndef GIT_DEPRECATE_HARD int git_blob_create_frombuffer( git_oid *id, git_repository *repo, const void *buffer, size_t len) { return git_blob_create_from_buffer(id, repo, buffer, len); } int git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path) { return git_blob_create_from_workdir(id, repo, relative_path); } int git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path) { return git_blob_create_from_disk(id, repo, path); } int git_blob_create_fromstream( git_writestream **out, git_repository *repo, const char *hintpath) { return git_blob_create_from_stream(out, repo, hintpath); } int git_blob_create_fromstream_commit( git_oid *out, git_writestream *stream) { return git_blob_create_from_stream_commit(out, stream); } int git_blob_filtered_content( git_buf *out, git_blob *blob, const char *path, int check_for_binary_data) { git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT; if (check_for_binary_data) opts.flags |= GIT_BLOB_FILTER_CHECK_FOR_BINARY; else opts.flags &= ~GIT_BLOB_FILTER_CHECK_FOR_BINARY; return git_blob_filter(out, blob, path, &opts); } #endif git2r/src/libgit2/src/transport.c0000644000175000017500000001302214125111754016544 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/types.h" #include "git2/remote.h" #include "git2/net.h" #include "git2/transport.h" #include "git2/sys/transport.h" #include "path.h" typedef struct transport_definition { char *prefix; git_transport_cb fn; void *param; } transport_definition; static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL }; static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL }; #ifdef GIT_SSH static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0, NULL }; #endif static transport_definition local_transport_definition = { "file://", git_transport_local, NULL }; static transport_definition transports[] = { { "git://", git_transport_smart, &git_subtransport_definition }, { "http://", git_transport_smart, &http_subtransport_definition }, { "https://", git_transport_smart, &http_subtransport_definition }, { "file://", git_transport_local, NULL }, #ifdef GIT_SSH { "ssh://", git_transport_smart, &ssh_subtransport_definition }, { "ssh+git://", git_transport_smart, &ssh_subtransport_definition }, { "git+ssh://", git_transport_smart, &ssh_subtransport_definition }, #endif { NULL, 0, 0 } }; static git_vector custom_transports = GIT_VECTOR_INIT; #define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1 static transport_definition * transport_find_by_url(const char *url) { size_t i = 0; transport_definition *d; /* Find a user transport who wants to deal with this URI */ git_vector_foreach(&custom_transports, i, d) { if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) { return d; } } /* Find a system transport for this URI */ for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) { d = &transports[i]; if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) { return d; } } return NULL; } static int transport_find_fn( git_transport_cb *out, const char *url, void **param) { transport_definition *definition = transport_find_by_url(url); #ifdef GIT_WIN32 /* On Windows, it might not be possible to discern between absolute local * and ssh paths - first check if this is a valid local path that points * to a directory and if so assume local path, else assume SSH */ /* Check to see if the path points to a file on the local file system */ if (!definition && git_path_exists(url) && git_path_isdir(url)) definition = &local_transport_definition; #endif /* For other systems, perform the SSH check first, to avoid going to the * filesystem if it is not necessary */ /* It could be a SSH remote path. Check to see if there's a : */ if (!definition && strrchr(url, ':')) { /* re-search transports again with ssh:// as url * so that we can find a third party ssh transport */ definition = transport_find_by_url("ssh://"); } #ifndef GIT_WIN32 /* Check to see if the path points to a file on the local file system */ if (!definition && git_path_exists(url) && git_path_isdir(url)) definition = &local_transport_definition; #endif if (!definition) return GIT_ENOTFOUND; *out = definition->fn; *param = definition->param; return 0; } /************** * Public API * **************/ int git_transport_new(git_transport **out, git_remote *owner, const char *url) { git_transport_cb fn; git_transport *transport; void *param; int error; if ((error = transport_find_fn(&fn, url, ¶m)) == GIT_ENOTFOUND) { git_error_set(GIT_ERROR_NET, "unsupported URL protocol"); return -1; } else if (error < 0) return error; if ((error = fn(&transport, owner, param)) < 0) return error; GIT_ERROR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport"); *out = transport; return 0; } int git_transport_register( const char *scheme, git_transport_cb cb, void *param) { git_buf prefix = GIT_BUF_INIT; transport_definition *d, *definition = NULL; size_t i; int error = 0; GIT_ASSERT_ARG(scheme); GIT_ASSERT_ARG(cb); if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0) goto on_error; git_vector_foreach(&custom_transports, i, d) { if (strcasecmp(d->prefix, prefix.ptr) == 0) { error = GIT_EEXISTS; goto on_error; } } definition = git__calloc(1, sizeof(transport_definition)); GIT_ERROR_CHECK_ALLOC(definition); definition->prefix = git_buf_detach(&prefix); definition->fn = cb; definition->param = param; if (git_vector_insert(&custom_transports, definition) < 0) goto on_error; return 0; on_error: git_buf_dispose(&prefix); git__free(definition); return error; } int git_transport_unregister(const char *scheme) { git_buf prefix = GIT_BUF_INIT; transport_definition *d; size_t i; int error = 0; GIT_ASSERT_ARG(scheme); if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0) goto done; git_vector_foreach(&custom_transports, i, d) { if (strcasecmp(d->prefix, prefix.ptr) == 0) { if ((error = git_vector_remove(&custom_transports, i)) < 0) goto done; git__free(d->prefix); git__free(d); if (!custom_transports.length) git_vector_free(&custom_transports); error = 0; goto done; } } error = GIT_ENOTFOUND; done: git_buf_dispose(&prefix); return error; } int git_transport_init(git_transport *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_transport, GIT_TRANSPORT_INIT); return 0; } git2r/src/libgit2/src/merge_driver.c0000644000175000017500000002413214125111754017166 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "merge_driver.h" #include "vector.h" #include "runtime.h" #include "merge.h" #include "git2/merge.h" #include "git2/sys/merge.h" static const char *merge_driver_name__text = "text"; static const char *merge_driver_name__union = "union"; static const char *merge_driver_name__binary = "binary"; struct merge_driver_registry { git_rwlock lock; git_vector drivers; }; typedef struct { git_merge_driver *driver; int initialized; char name[GIT_FLEX_ARRAY]; } git_merge_driver_entry; static struct merge_driver_registry merge_driver_registry; static void git_merge_driver_global_shutdown(void); git_repository *git_merge_driver_source_repo( const git_merge_driver_source *src) { GIT_ASSERT_ARG_WITH_RETVAL(src, NULL); return src->repo; } const git_index_entry *git_merge_driver_source_ancestor( const git_merge_driver_source *src) { GIT_ASSERT_ARG_WITH_RETVAL(src, NULL); return src->ancestor; } const git_index_entry *git_merge_driver_source_ours( const git_merge_driver_source *src) { GIT_ASSERT_ARG_WITH_RETVAL(src, NULL); return src->ours; } const git_index_entry *git_merge_driver_source_theirs( const git_merge_driver_source *src) { GIT_ASSERT_ARG_WITH_RETVAL(src, NULL); return src->theirs; } const git_merge_file_options *git_merge_driver_source_file_options( const git_merge_driver_source *src) { GIT_ASSERT_ARG_WITH_RETVAL(src, NULL); return src->file_opts; } int git_merge_driver__builtin_apply( git_merge_driver *self, const char **path_out, uint32_t *mode_out, git_buf *merged_out, const char *filter_name, const git_merge_driver_source *src) { git_merge_driver__builtin *driver = (git_merge_driver__builtin *)self; git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_file_result result = {0}; int error; GIT_UNUSED(filter_name); if (src->file_opts) memcpy(&file_opts, src->file_opts, sizeof(git_merge_file_options)); if (driver->favor) file_opts.favor = driver->favor; if ((error = git_merge_file_from_index(&result, src->repo, src->ancestor, src->ours, src->theirs, &file_opts)) < 0) goto done; if (!result.automergeable && !(file_opts.flags & GIT_MERGE_FILE_FAVOR__CONFLICTED)) { error = GIT_EMERGECONFLICT; goto done; } *path_out = git_merge_file__best_path( src->ancestor ? src->ancestor->path : NULL, src->ours ? src->ours->path : NULL, src->theirs ? src->theirs->path : NULL); *mode_out = git_merge_file__best_mode( src->ancestor ? src->ancestor->mode : 0, src->ours ? src->ours->mode : 0, src->theirs ? src->theirs->mode : 0); merged_out->ptr = (char *)result.ptr; merged_out->size = result.len; merged_out->asize = result.len; result.ptr = NULL; done: git_merge_file_result_free(&result); return error; } static int merge_driver_binary_apply( git_merge_driver *self, const char **path_out, uint32_t *mode_out, git_buf *merged_out, const char *filter_name, const git_merge_driver_source *src) { GIT_UNUSED(self); GIT_UNUSED(path_out); GIT_UNUSED(mode_out); GIT_UNUSED(merged_out); GIT_UNUSED(filter_name); GIT_UNUSED(src); return GIT_EMERGECONFLICT; } static int merge_driver_entry_cmp(const void *a, const void *b) { const git_merge_driver_entry *entry_a = a; const git_merge_driver_entry *entry_b = b; return strcmp(entry_a->name, entry_b->name); } static int merge_driver_entry_search(const void *a, const void *b) { const char *name_a = a; const git_merge_driver_entry *entry_b = b; return strcmp(name_a, entry_b->name); } git_merge_driver__builtin git_merge_driver__text = { { GIT_MERGE_DRIVER_VERSION, NULL, NULL, git_merge_driver__builtin_apply, }, GIT_MERGE_FILE_FAVOR_NORMAL }; git_merge_driver__builtin git_merge_driver__union = { { GIT_MERGE_DRIVER_VERSION, NULL, NULL, git_merge_driver__builtin_apply, }, GIT_MERGE_FILE_FAVOR_UNION }; git_merge_driver git_merge_driver__binary = { GIT_MERGE_DRIVER_VERSION, NULL, NULL, merge_driver_binary_apply }; /* Note: callers must lock the registry before calling this function */ static int merge_driver_registry_insert( const char *name, git_merge_driver *driver) { git_merge_driver_entry *entry; entry = git__calloc(1, sizeof(git_merge_driver_entry) + strlen(name) + 1); GIT_ERROR_CHECK_ALLOC(entry); strcpy(entry->name, name); entry->driver = driver; return git_vector_insert_sorted( &merge_driver_registry.drivers, entry, NULL); } int git_merge_driver_global_init(void) { int error; if (git_rwlock_init(&merge_driver_registry.lock) < 0) return -1; if ((error = git_vector_init(&merge_driver_registry.drivers, 3, merge_driver_entry_cmp)) < 0) goto done; if ((error = merge_driver_registry_insert( merge_driver_name__text, &git_merge_driver__text.base)) < 0 || (error = merge_driver_registry_insert( merge_driver_name__union, &git_merge_driver__union.base)) < 0 || (error = merge_driver_registry_insert( merge_driver_name__binary, &git_merge_driver__binary)) < 0) goto done; error = git_runtime_shutdown_register(git_merge_driver_global_shutdown); done: if (error < 0) git_vector_free_deep(&merge_driver_registry.drivers); return error; } static void git_merge_driver_global_shutdown(void) { git_merge_driver_entry *entry; size_t i; if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) return; git_vector_foreach(&merge_driver_registry.drivers, i, entry) { if (entry->driver->shutdown) entry->driver->shutdown(entry->driver); git__free(entry); } git_vector_free(&merge_driver_registry.drivers); git_rwlock_wrunlock(&merge_driver_registry.lock); git_rwlock_free(&merge_driver_registry.lock); } /* Note: callers must lock the registry before calling this function */ static int merge_driver_registry_find(size_t *pos, const char *name) { return git_vector_search2(pos, &merge_driver_registry.drivers, merge_driver_entry_search, name); } /* Note: callers must lock the registry before calling this function */ static git_merge_driver_entry *merge_driver_registry_lookup( size_t *pos, const char *name) { git_merge_driver_entry *entry = NULL; if (!merge_driver_registry_find(pos, name)) entry = git_vector_get(&merge_driver_registry.drivers, *pos); return entry; } int git_merge_driver_register(const char *name, git_merge_driver *driver) { int error; GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(driver); if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry"); return -1; } if (!merge_driver_registry_find(NULL, name)) { git_error_set(GIT_ERROR_MERGE, "attempt to reregister existing driver '%s'", name); error = GIT_EEXISTS; goto done; } error = merge_driver_registry_insert(name, driver); done: git_rwlock_wrunlock(&merge_driver_registry.lock); return error; } int git_merge_driver_unregister(const char *name) { git_merge_driver_entry *entry; size_t pos; int error = 0; if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry"); return -1; } if ((entry = merge_driver_registry_lookup(&pos, name)) == NULL) { git_error_set(GIT_ERROR_MERGE, "cannot find merge driver '%s' to unregister", name); error = GIT_ENOTFOUND; goto done; } git_vector_remove(&merge_driver_registry.drivers, pos); if (entry->initialized && entry->driver->shutdown) { entry->driver->shutdown(entry->driver); entry->initialized = false; } git__free(entry); done: git_rwlock_wrunlock(&merge_driver_registry.lock); return error; } git_merge_driver *git_merge_driver_lookup(const char *name) { git_merge_driver_entry *entry; size_t pos; int error; /* If we've decided the merge driver to use internally - and not * based on user configuration (in merge_driver_name_for_path) * then we can use a hardcoded name to compare instead of bothering * to take a lock and look it up in the vector. */ if (name == merge_driver_name__text) return &git_merge_driver__text.base; else if (name == merge_driver_name__binary) return &git_merge_driver__binary; if (git_rwlock_rdlock(&merge_driver_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry"); return NULL; } entry = merge_driver_registry_lookup(&pos, name); git_rwlock_rdunlock(&merge_driver_registry.lock); if (entry == NULL) { git_error_set(GIT_ERROR_MERGE, "cannot use an unregistered filter"); return NULL; } if (!entry->initialized) { if (entry->driver->initialize && (error = entry->driver->initialize(entry->driver)) < 0) return NULL; entry->initialized = 1; } return entry->driver; } static int merge_driver_name_for_path( const char **out, git_repository *repo, const char *path, const char *default_driver) { const char *value; int error; *out = NULL; if ((error = git_attr_get(&value, repo, 0, path, "merge")) < 0) return error; /* set: use the built-in 3-way merge driver ("text") */ if (GIT_ATTR_IS_TRUE(value)) *out = merge_driver_name__text; /* unset: do not merge ("binary") */ else if (GIT_ATTR_IS_FALSE(value)) *out = merge_driver_name__binary; else if (GIT_ATTR_IS_UNSPECIFIED(value) && default_driver) *out = default_driver; else if (GIT_ATTR_IS_UNSPECIFIED(value)) *out = merge_driver_name__text; else *out = value; return 0; } GIT_INLINE(git_merge_driver *) merge_driver_lookup_with_wildcard( const char *name) { git_merge_driver *driver = git_merge_driver_lookup(name); if (driver == NULL) driver = git_merge_driver_lookup("*"); return driver; } int git_merge_driver_for_source( const char **name_out, git_merge_driver **driver_out, const git_merge_driver_source *src) { const char *path, *driver_name; int error = 0; path = git_merge_file__best_path( src->ancestor ? src->ancestor->path : NULL, src->ours ? src->ours->path : NULL, src->theirs ? src->theirs->path : NULL); if ((error = merge_driver_name_for_path( &driver_name, src->repo, path, src->default_driver)) < 0) return error; *name_out = driver_name; *driver_out = merge_driver_lookup_with_wildcard(driver_name); return error; } git2r/src/libgit2/src/regexp.h0000644000175000017500000000630414125111754016014 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_regexp_h__ #define INCLUDE_regexp_h__ #include "common.h" #if defined(GIT_REGEX_BUILTIN) || defined(GIT_REGEX_PCRE) # include "pcre.h" typedef pcre *git_regexp; # define GIT_REGEX_INIT NULL #elif defined(GIT_REGEX_PCRE2) # define PCRE2_CODE_UNIT_WIDTH 8 # include typedef pcre2_code *git_regexp; # define GIT_REGEX_INIT NULL #elif defined(GIT_REGEX_REGCOMP) || defined(GIT_REGEX_REGCOMP_L) # include typedef regex_t git_regexp; # define GIT_REGEX_INIT { 0 } #else # error "No regex backend" #endif /** Options supported by @git_regexp_compile. */ typedef enum { /** Enable case-insensitive matching */ GIT_REGEXP_ICASE = (1 << 0) } git_regexp_flags_t; /** Structure containing information about regular expression matching groups */ typedef struct { /** Start of the given match. -1 if the group didn't match anything */ ssize_t start; /** End of the given match. -1 if the group didn't match anything */ ssize_t end; } git_regmatch; /** * Compile a regular expression. The compiled expression needs to * be cleaned up afterwards with `git_regexp_dispose`. * * @param r Pointer to the storage where to initialize the regular expression. * @param pattern The pattern that shall be compiled. * @param flags Flags to alter how the pattern shall be handled. * 0 for defaults, otherwise see @git_regexp_flags_t. * @return 0 on success, otherwise a negative return value. */ int git_regexp_compile(git_regexp *r, const char *pattern, int flags); /** * Free memory associated with the regular expression * * @param r The regular expression structure to dispose. */ void git_regexp_dispose(git_regexp *r); /** * Test whether a given string matches a compiled regular * expression. * * @param r Compiled regular expression. * @param string String to match against the regular expression. * @return 0 if the string matches, a negative error code * otherwise. GIT_ENOTFOUND if no match was found, * GIT_EINVALIDSPEC if the regular expression matching * was invalid. */ int git_regexp_match(const git_regexp *r, const char *string); /** * Search for matches inside of a given string. * * Given a regular expression with capturing groups, this * function will populate provided @git_regmatch structures with * offsets for each of the given matches. Non-matching groups * will have start and end values of the respective @git_regmatch * structure set to -1. * * @param r Compiled regular expression. * @param string String to match against the regular expression. * @param nmatches Number of @git_regmatch structures provided by * the user. * @param matches Pointer to an array of @git_regmatch structures. * @return 0 if the string matches, a negative error code * otherwise. GIT_ENOTFOUND if no match was found, * GIT_EINVALIDSPEC if the regular expression matching * was invalid. */ int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches); #endif git2r/src/libgit2/src/varint.c0000644000175000017500000000204214125111754016013 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "varint.h" uintmax_t git_decode_varint(const unsigned char *bufp, size_t *varint_len) { const unsigned char *buf = bufp; unsigned char c = *buf++; uintmax_t val = c & 127; while (c & 128) { val += 1; if (!val || MSB(val, 7)) { /* This is not a valid varint_len, so it signals the error */ *varint_len = 0; return 0; /* overflow */ } c = *buf++; val = (val << 7) + (c & 127); } *varint_len = buf - bufp; return val; } int git_encode_varint(unsigned char *buf, size_t bufsize, uintmax_t value) { unsigned char varint[16]; unsigned pos = sizeof(varint) - 1; varint[pos] = value & 127; while (value >>= 7) varint[--pos] = 128 | (--value & 127); if (buf) { if (bufsize < (sizeof(varint) - pos)) return -1; memcpy(buf, varint + pos, sizeof(varint) - pos); } return sizeof(varint) - pos; } git2r/src/libgit2/src/buffer.h0000644000175000017500000002753414125111754016003 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_buffer_h__ #define INCLUDE_buffer_h__ #include "common.h" #include "git2/strarray.h" #include "git2/buffer.h" /* typedef struct { * char *ptr; * size_t asize, size; * } git_buf; */ typedef enum { GIT_BUF_BOM_NONE = 0, GIT_BUF_BOM_UTF8 = 1, GIT_BUF_BOM_UTF16_LE = 2, GIT_BUF_BOM_UTF16_BE = 3, GIT_BUF_BOM_UTF32_LE = 4, GIT_BUF_BOM_UTF32_BE = 5 } git_buf_bom_t; typedef struct { git_buf_bom_t bom; /* BOM found at head of text */ unsigned int nul, cr, lf, crlf; /* NUL, CR, LF and CRLF counts */ unsigned int printable, nonprintable; /* These are just approximations! */ } git_buf_text_stats; extern char git_buf__initbuf[]; extern char git_buf__oom[]; /* Use to initialize buffer structure when git_buf is on stack */ #define GIT_BUF_INIT { git_buf__initbuf, 0, 0 } /** * Static initializer for git_buf from static buffer */ #ifdef GIT_DEPRECATE_HARD # define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) } #endif GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf) { return (buf->ptr != NULL && buf->asize > 0); } /** * Initialize a git_buf structure. * * For the cases where GIT_BUF_INIT cannot be used to do static * initialization. */ extern int git_buf_init(git_buf *buf, size_t initial_size); #ifdef GIT_DEPRECATE_HARD /** * Resize the buffer allocation to make more space. * * This will attempt to grow the buffer to accommodate the target size. * * If the buffer refers to memory that was not allocated by libgit2 (i.e. * the `asize` field is zero), then `ptr` will be replaced with a newly * allocated block of data. Be careful so that memory allocated by the * caller is not lost. As a special variant, if you pass `target_size` as * 0 and the memory is not allocated by libgit2, this will allocate a new * buffer of size `size` and copy the external data into it. * * Currently, this will never shrink a buffer, only expand it. * * If the allocation fails, this will return an error and the buffer will be * marked as invalid for future operations, invaliding the contents. * * @param buffer The buffer to be resized; may or may not be allocated yet * @param target_size The desired available size * @return 0 on success, -1 on allocation failure */ int git_buf_grow(git_buf *buffer, size_t target_size); #endif /** * Resize the buffer allocation to make more space. * * This will attempt to grow the buffer to accommodate the additional size. * It is similar to `git_buf_grow`, but performs the new size calculation, * checking for overflow. * * Like `git_buf_grow`, if this is a user-supplied buffer, this will allocate * a new buffer. */ extern int git_buf_grow_by(git_buf *buffer, size_t additional_size); /** * Attempt to grow the buffer to hold at least `target_size` bytes. * * If the allocation fails, this will return an error. If `mark_oom` is true, * this will mark the buffer as invalid for future operations; if false, * existing buffer content will be preserved, but calling code must handle * that buffer was not expanded. If `preserve_external` is true, then any * existing data pointed to be `ptr` even if `asize` is zero will be copied * into the newly allocated buffer. */ extern int git_buf_try_grow( git_buf *buf, size_t target_size, bool mark_oom); /** * Sanitizes git_buf structures provided from user input. Users of the * library, when providing git_buf's, may wish to provide a NULL ptr for * ease of handling. The buffer routines, however, expect a non-NULL ptr * always. This helper method simply handles NULL input, converting to a * git_buf__initbuf. If a buffer with a non-NULL ptr is passed in, this method * assures that the buffer is '\0'-terminated. */ extern int git_buf_sanitize(git_buf *buf); extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b); extern char *git_buf_detach(git_buf *buf); extern int git_buf_attach(git_buf *buf, char *ptr, size_t asize); /* Populates a `git_buf` where the contents are not "owned" by the * buffer, and calls to `git_buf_dispose` will not free the given buf. */ extern void git_buf_attach_notowned( git_buf *buf, const char *ptr, size_t size); /** * Test if there have been any reallocation failures with this git_buf. * * Any function that writes to a git_buf can fail due to memory allocation * issues. If one fails, the git_buf will be marked with an OOM error and * further calls to modify the buffer will fail. Check git_buf_oom() at the * end of your sequence and it will be true if you ran out of memory at any * point with that buffer. * * @return false if no error, true if allocation error */ GIT_INLINE(bool) git_buf_oom(const git_buf *buf) { return (buf->ptr == git_buf__oom); } /* * Functions below that return int value error codes will return 0 on * success or -1 on failure (which generally means an allocation failed). * Using a git_buf where the allocation has failed with result in -1 from * all further calls using that buffer. As a result, you can ignore the * return code of these functions and call them in a series then just call * git_buf_oom at the end. */ #ifdef GIT_DEPRECATE_HARD int git_buf_set(git_buf *buffer, const void *data, size_t datalen); #endif int git_buf_sets(git_buf *buf, const char *string); int git_buf_putc(git_buf *buf, char c); int git_buf_putcn(git_buf *buf, char c, size_t len); int git_buf_put(git_buf *buf, const char *data, size_t len); int git_buf_puts(git_buf *buf, const char *string); int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); int git_buf_vprintf(git_buf *buf, const char *format, va_list ap); void git_buf_clear(git_buf *buf); void git_buf_consume_bytes(git_buf *buf, size_t len); void git_buf_consume(git_buf *buf, const char *end); void git_buf_truncate(git_buf *buf, size_t len); void git_buf_shorten(git_buf *buf, size_t amount); void git_buf_truncate_at_char(git_buf *buf, char separator); void git_buf_rtruncate_at_char(git_buf *path, char separator); /** General join with separator */ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...); /** Fast join of two strings - first may legally point into `buf` data */ int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b); /** Fast join of three strings - cannot reference `buf` data */ int git_buf_join3(git_buf *buf, char separator, const char *str_a, const char *str_b, const char *str_c); /** * Join two strings as paths, inserting a slash between as needed. * @return 0 on success, -1 on failure */ GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b) { return git_buf_join(buf, '/', a, b); } GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf) { return buf->ptr; } GIT_INLINE(size_t) git_buf_len(const git_buf *buf) { return buf->size; } int git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf); #define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1) GIT_INLINE(ssize_t) git_buf_rfind_next(const git_buf *buf, char ch) { ssize_t idx = (ssize_t)buf->size - 1; while (idx >= 0 && buf->ptr[idx] == ch) idx--; while (idx >= 0 && buf->ptr[idx] != ch) idx--; return idx; } GIT_INLINE(ssize_t) git_buf_rfind(const git_buf *buf, char ch) { ssize_t idx = (ssize_t)buf->size - 1; while (idx >= 0 && buf->ptr[idx] != ch) idx--; return idx; } GIT_INLINE(ssize_t) git_buf_find(const git_buf *buf, char ch) { void *found = memchr(buf->ptr, ch, buf->size); return found ? (ssize_t)((const char *)found - buf->ptr) : -1; } /* Remove whitespace from the end of the buffer */ void git_buf_rtrim(git_buf *buf); int git_buf_cmp(const git_buf *a, const git_buf *b); /* Quote and unquote a buffer as specified in * http://marc.info/?l=git&m=112927316408690&w=2 */ int git_buf_quote(git_buf *buf); int git_buf_unquote(git_buf *buf); /* Write data as base64 encoded in buffer */ int git_buf_encode_base64(git_buf *buf, const char *data, size_t len); /* Decode the given bas64 and write the result to the buffer */ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len); /* Write data as "base85" encoded in buffer */ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len); /* Decode the given "base85" and write the result to the buffer */ int git_buf_decode_base85(git_buf *buf, const char *base64, size_t len, size_t output_len); /* Decode the given percent-encoded string and write the result to the buffer */ int git_buf_decode_percent(git_buf *buf, const char *str, size_t len); /* * Insert, remove or replace a portion of the buffer. * * @param buf The buffer to work with * * @param where The location in the buffer where the transformation * should be applied. * * @param nb_to_remove The number of chars to be removed. 0 to not * remove any character in the buffer. * * @param data A pointer to the data which should be inserted. * * @param nb_to_insert The number of chars to be inserted. 0 to not * insert any character from the buffer. * * @return 0 or an error code. */ int git_buf_splice( git_buf *buf, size_t where, size_t nb_to_remove, const char *data, size_t nb_to_insert); /** * Append string to buffer, prefixing each character from `esc_chars` with * `esc_with` string. * * @param buf Buffer to append data to * @param string String to escape and append * @param esc_chars Characters to be escaped * @param esc_with String to insert in from of each found character * @return 0 on success, <0 on failure (probably allocation problem) */ extern int git_buf_puts_escaped( git_buf *buf, const char *string, const char *esc_chars, const char *esc_with); /** * Append string escaping characters that are regex special */ GIT_INLINE(int) git_buf_puts_escape_regex(git_buf *buf, const char *string) { return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\"); } /** * Unescape all characters in a buffer in place * * I.e. remove backslashes */ extern void git_buf_unescape(git_buf *buf); /** * Replace all \r\n with \n. * * @return 0 on success, -1 on memory error */ extern int git_buf_crlf_to_lf(git_buf *tgt, const git_buf *src); /** * Replace all \n with \r\n. Does not modify existing \r\n. * * @return 0 on success, -1 on memory error */ extern int git_buf_lf_to_crlf(git_buf *tgt, const git_buf *src); /** * Fill buffer with the common prefix of a array of strings * * Buffer will be set to empty if there is no common prefix */ extern int git_buf_common_prefix(git_buf *buf, char *const *const strings, size_t count); /** * Check if a buffer begins with a UTF BOM * * @param bom Set to the type of BOM detected or GIT_BOM_NONE * @param buf Buffer in which to check the first bytes for a BOM * @return Number of bytes of BOM data (or 0 if no BOM found) */ extern int git_buf_detect_bom(git_buf_bom_t *bom, const git_buf *buf); /** * Gather stats for a piece of text * * Fill the `stats` structure with counts of unreadable characters, carriage * returns, etc, so it can be used in heuristics. This automatically skips * a trailing EOF (\032 character). Also it will look for a BOM at the * start of the text and can be told to skip that as well. * * @param stats Structure to be filled in * @param buf Text to process * @param skip_bom Exclude leading BOM from stats if true * @return Does the buffer heuristically look like binary data */ extern bool git_buf_gather_text_stats( git_buf_text_stats *stats, const git_buf *buf, bool skip_bom); #ifdef GIT_DEPRECATE_HARD /** * Check quickly if buffer looks like it contains binary data * * @param buf Buffer to check * @return 1 if buffer looks like non-text data */ int git_buf_is_binary(const git_buf *buf); /** * Check quickly if buffer contains a NUL byte * * @param buf Buffer to check * @return 1 if buffer contains a NUL byte */ int git_buf_contains_nul(const git_buf *buf); #endif #endif git2r/src/libgit2/src/integer.h0000644000175000017500000001454014125111754016160 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_integer_h__ #define INCLUDE_integer_h__ /** @return true if p fits into the range of a size_t */ GIT_INLINE(int) git__is_sizet(int64_t p) { size_t r = (size_t)p; return p == (int64_t)r; } /** @return true if p fits into the range of an ssize_t */ GIT_INLINE(int) git__is_ssizet(size_t p) { ssize_t r = (ssize_t)p; return p == (size_t)r; } /** @return true if p fits into the range of a uint16_t */ GIT_INLINE(int) git__is_uint16(size_t p) { uint16_t r = (uint16_t)p; return p == (size_t)r; } /** @return true if p fits into the range of a uint32_t */ GIT_INLINE(int) git__is_uint32(size_t p) { uint32_t r = (uint32_t)p; return p == (size_t)r; } /** @return true if p fits into the range of an unsigned long */ GIT_INLINE(int) git__is_ulong(int64_t p) { unsigned long r = (unsigned long)p; return p == (int64_t)r; } /** @return true if p fits into the range of an int */ GIT_INLINE(int) git__is_int(int64_t p) { int r = (int)p; return p == (int64_t)r; } /* Use clang/gcc compiler intrinsics whenever possible */ #if (__has_builtin(__builtin_add_overflow) || \ (defined(__GNUC__) && (__GNUC__ >= 5))) # if (SIZE_MAX == UINT_MAX) # define git__add_sizet_overflow(out, one, two) \ __builtin_uadd_overflow(one, two, out) # define git__multiply_sizet_overflow(out, one, two) \ __builtin_umul_overflow(one, two, out) # elif (SIZE_MAX == ULONG_MAX) # define git__add_sizet_overflow(out, one, two) \ __builtin_uaddl_overflow(one, two, out) # define git__multiply_sizet_overflow(out, one, two) \ __builtin_umull_overflow(one, two, out) # elif (SIZE_MAX == ULLONG_MAX) # define git__add_sizet_overflow(out, one, two) \ __builtin_uaddll_overflow(one, two, out) # define git__multiply_sizet_overflow(out, one, two) \ __builtin_umulll_overflow(one, two, out) # else # error compiler has add with overflow intrinsics but SIZE_MAX is unknown # endif # define git__add_int_overflow(out, one, two) \ __builtin_sadd_overflow(one, two, out) # define git__sub_int_overflow(out, one, two) \ __builtin_ssub_overflow(one, two, out) # define git__add_int64_overflow(out, one, two) \ __builtin_add_overflow(one, two, out) /* clang on 32-bit systems produces an undefined reference to `__mulodi4`. */ # if !defined(__clang__) || !defined(GIT_ARCH_32) # define git__multiply_int64_overflow(out, one, two) \ __builtin_mul_overflow(one, two, out) # endif /* Use Microsoft's safe integer handling functions where available */ #elif defined(_MSC_VER) # define ENABLE_INTSAFE_SIGNED_FUNCTIONS # include # define git__add_sizet_overflow(out, one, two) \ (SizeTAdd(one, two, out) != S_OK) # define git__multiply_sizet_overflow(out, one, two) \ (SizeTMult(one, two, out) != S_OK) #define git__add_int_overflow(out, one, two) \ (IntAdd(one, two, out) != S_OK) #define git__sub_int_overflow(out, one, two) \ (IntSub(one, two, out) != S_OK) #define git__add_int64_overflow(out, one, two) \ (LongLongAdd(one, two, out) != S_OK) #define git__multiply_int64_overflow(out, one, two) \ (LongLongMult(one, two, out) != S_OK) #else /** * Sets `one + two` into `out`, unless the arithmetic would overflow. * @return false if the result fits in a `size_t`, true on overflow. */ GIT_INLINE(bool) git__add_sizet_overflow(size_t *out, size_t one, size_t two) { if (SIZE_MAX - one < two) return true; *out = one + two; return false; } /** * Sets `one * two` into `out`, unless the arithmetic would overflow. * @return false if the result fits in a `size_t`, true on overflow. */ GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t two) { if (one && SIZE_MAX / one < two) return true; *out = one * two; return false; } GIT_INLINE(bool) git__add_int_overflow(int *out, int one, int two) { if ((two > 0 && one > (INT_MAX - two)) || (two < 0 && one < (INT_MIN - two))) return true; *out = one + two; return false; } GIT_INLINE(bool) git__sub_int_overflow(int *out, int one, int two) { if ((two > 0 && one < (INT_MIN + two)) || (two < 0 && one > (INT_MAX + two))) return true; *out = one - two; return false; } GIT_INLINE(bool) git__add_int64_overflow(int64_t *out, int64_t one, int64_t two) { if ((two > 0 && one > (INT64_MAX - two)) || (two < 0 && one < (INT64_MIN - two))) return true; *out = one + two; return false; } #endif /* If we could not provide an intrinsic implementation for this, provide a (slow) fallback. */ #if !defined(git__multiply_int64_overflow) GIT_INLINE(bool) git__multiply_int64_overflow(int64_t *out, int64_t one, int64_t two) { /* * Detects whether `INT64_MAX < (one * two) || INT64_MIN > (one * two)`, * without incurring in undefined behavior. That is done by performing the * comparison with a division instead of a multiplication, which translates * to `INT64_MAX / one < two || INT64_MIN / one > two`. Some caveats: * * - The comparison sign is inverted when both sides of the inequality are * multiplied/divided by a negative number, so if `one < 0` the comparison * needs to be flipped. * - `INT64_MAX / -1` itself overflows (or traps), so that case should be * avoided. * - Since the overflow flag is defined as the discrepance between the result * of performing the multiplication in a signed integer at twice the width * of the operands, and the truncated+sign-extended version of that same * result, there are four cases where the result is the opposite of what * would be expected: * * `INT64_MIN * -1` / `-1 * INT64_MIN` * * `INT64_MIN * 1 / `1 * INT64_MIN` */ if (one && two) { if (one > 0 && two > 0) { if (INT64_MAX / one < two) return true; } else if (one < 0 && two < 0) { if ((one == -1 && two == INT64_MIN) || (two == -1 && one == INT64_MIN)) { *out = INT64_MIN; return false; } if (INT64_MAX / one > two) return true; } else if (one > 0 && two < 0) { if ((one == 1 && two == INT64_MIN) || (INT64_MIN / one > two)) return true; } else if (one == -1) { if (INT64_MIN / two > one) return true; } else { if ((one == INT64_MIN && two == 1) || (INT64_MIN / one < two)) return true; } } *out = one * two; return false; } #endif #endif git2r/src/libgit2/src/wildmatch.h0000644000175000017500000000101214125111754016465 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_wildmatch_h__ #define INCLUDE_wildmatch_h__ #include "common.h" #define WM_CASEFOLD 1 #define WM_PATHNAME 2 #define WM_NOMATCH 1 #define WM_MATCH 0 #define WM_ABORT_ALL -1 #define WM_ABORT_TO_STARSTAR -2 int wildmatch(const char *pattern, const char *text, unsigned int flags); #endif git2r/src/libgit2/src/email.c0000644000175000017500000001603714125111754015610 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "email.h" #include "buffer.h" #include "common.h" #include "diff_generate.h" #include "git2/email.h" #include "git2/patch.h" #include "git2/version.h" /* * Git uses a "magic" timestamp to indicate that an email message * is from `git format-patch` (or our equivalent). */ #define EMAIL_TIMESTAMP "Mon Sep 17 00:00:00 2001" GIT_INLINE(int) include_prefix( size_t patch_count, git_email_create_options *opts) { return ((!opts->subject_prefix || *opts->subject_prefix) || (opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 || opts->reroll_number || (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS))); } static int append_prefix( git_buf *out, size_t patch_idx, size_t patch_count, git_email_create_options *opts) { const char *subject_prefix = opts->subject_prefix ? opts->subject_prefix : "PATCH"; git_buf_putc(out, '['); if (*subject_prefix) git_buf_puts(out, subject_prefix); if (opts->reroll_number) { if (*subject_prefix) git_buf_putc(out, ' '); git_buf_printf(out, "v%" PRIuZ, opts->reroll_number); } if ((opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 || (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS))) { size_t start_number = opts->start_number ? opts->start_number : 1; if (*subject_prefix || opts->reroll_number) git_buf_putc(out, ' '); git_buf_printf(out, "%" PRIuZ "/%" PRIuZ, patch_idx + (start_number - 1), patch_count + (start_number - 1)); } git_buf_puts(out, "]"); return git_buf_oom(out) ? -1 : 0; } static int append_subject( git_buf *out, size_t patch_idx, size_t patch_count, const char *summary, git_email_create_options *opts) { bool prefix = include_prefix(patch_count, opts); size_t summary_len = summary ? strlen(summary) : 0; int error; if (summary_len) { const char *nl = strchr(summary, '\n'); if (nl) summary_len = (nl - summary); } if ((error = git_buf_puts(out, "Subject: ")) < 0) return error; if (prefix && (error = append_prefix(out, patch_idx, patch_count, opts)) < 0) return error; if (prefix && summary_len && (error = git_buf_putc(out, ' ')) < 0) return error; if (summary_len && (error = git_buf_put(out, summary, summary_len)) < 0) return error; return git_buf_putc(out, '\n'); } static int append_header( git_buf *out, size_t patch_idx, size_t patch_count, const git_oid *commit_id, const char *summary, const git_signature *author, git_email_create_options *opts) { char id[GIT_OID_HEXSZ]; char date[GIT_DATE_RFC2822_SZ]; int error; if ((error = git_oid_fmt(id, commit_id)) < 0 || (error = git_buf_printf(out, "From %.*s %s\n", GIT_OID_HEXSZ, id, EMAIL_TIMESTAMP)) < 0 || (error = git_buf_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 || (error = git__date_rfc2822_fmt(date, sizeof(date), &author->when)) < 0 || (error = git_buf_printf(out, "Date: %s\n", date)) < 0 || (error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0) return error; if ((error = git_buf_putc(out, '\n')) < 0) return error; return 0; } static int append_body(git_buf *out, const char *body) { size_t body_len; int error; if (!body) return 0; body_len = strlen(body); if ((error = git_buf_puts(out, body)) < 0) return error; if (body_len && body[body_len - 1] != '\n') error = git_buf_putc(out, '\n'); return error; } static int append_diffstat(git_buf *out, git_diff *diff) { git_diff_stats *stats = NULL; unsigned int format_flags; int error; format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY; if ((error = git_diff_get_stats(&stats, diff)) == 0 && (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) == 0) error = git_buf_putc(out, '\n'); git_diff_stats_free(stats); return error; } static int append_patches(git_buf *out, git_diff *diff) { size_t i, deltas; int error = 0; deltas = git_diff_num_deltas(diff); for (i = 0; i < deltas; ++i) { git_patch *patch = NULL; if ((error = git_patch_from_diff(&patch, diff, i)) >= 0) error = git_patch_to_buf(out, patch); git_patch_free(patch); if (error < 0) break; } return error; } int git_email__append_from_diff( git_buf *out, git_diff *diff, size_t patch_idx, size_t patch_count, const git_oid *commit_id, const char *summary, const char *body, const git_signature *author, const git_email_create_options *given_opts) { git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(diff); GIT_ASSERT_ARG(!patch_idx || patch_idx <= patch_count); GIT_ASSERT_ARG(commit_id); GIT_ASSERT_ARG(author); GIT_ERROR_CHECK_VERSION(given_opts, GIT_EMAIL_CREATE_OPTIONS_VERSION, "git_email_create_options"); if (given_opts) memcpy(&opts, given_opts, sizeof(git_email_create_options)); git_buf_sanitize(out); if ((error = append_header(out, patch_idx, patch_count, commit_id, summary, author, &opts)) == 0 && (error = append_body(out, body)) == 0 && (error = git_buf_puts(out, "---\n")) == 0 && (error = append_diffstat(out, diff)) == 0 && (error = append_patches(out, diff)) == 0) error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n"); return error; } int git_email_create_from_diff( git_buf *out, git_diff *diff, size_t patch_idx, size_t patch_count, const git_oid *commit_id, const char *summary, const char *body, const git_signature *author, const git_email_create_options *given_opts) { int error; git_buf_sanitize(out); git_buf_clear(out); error = git_email__append_from_diff(out, diff, patch_idx, patch_count, commit_id, summary, body, author, given_opts); return error; } int git_email_create_from_commit( git_buf *out, git_commit *commit, const git_email_create_options *given_opts) { git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; git_diff *diff = NULL; git_repository *repo; git_diff_options *diff_opts; git_diff_find_options *find_opts; const git_signature *author; const char *summary, *body; const git_oid *commit_id; int error = -1; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(commit); GIT_ERROR_CHECK_VERSION(given_opts, GIT_EMAIL_CREATE_OPTIONS_VERSION, "git_email_create_options"); if (given_opts) memcpy(&opts, given_opts, sizeof(git_email_create_options)); repo = git_commit_owner(commit); author = git_commit_author(commit); summary = git_commit_summary(commit); body = git_commit_body(commit); commit_id = git_commit_id(commit); diff_opts = &opts.diff_opts; find_opts = &opts.diff_find_opts; if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0) goto done; if ((opts.flags & GIT_EMAIL_CREATE_NO_RENAMES) == 0 && (error = git_diff_find_similar(diff, find_opts)) < 0) goto done; error = git_email_create_from_diff(out, diff, 1, 1, commit_id, summary, body, author, &opts); done: git_diff_free(diff); return error; } git2r/src/libgit2/src/patch_generate.c0000644000175000017500000005633214125111754017474 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "patch_generate.h" #include "git2/blob.h" #include "diff.h" #include "diff_generate.h" #include "diff_file.h" #include "diff_driver.h" #include "diff_xdiff.h" #include "delta.h" #include "zstream.h" #include "futils.h" static void diff_output_init( git_patch_generated_output *, const git_diff_options *, git_diff_file_cb, git_diff_binary_cb, git_diff_hunk_cb, git_diff_line_cb, void*); static void diff_output_to_patch( git_patch_generated_output *, git_patch_generated *); static void patch_generated_free(git_patch *p) { git_patch_generated *patch = (git_patch_generated *)p; git_array_clear(patch->base.lines); git_array_clear(patch->base.hunks); git__free((char *)patch->base.binary.old_file.data); git__free((char *)patch->base.binary.new_file.data); git_diff_file_content__clear(&patch->ofile); git_diff_file_content__clear(&patch->nfile); git_diff_free(patch->diff); /* decrements refcount */ patch->diff = NULL; git_pool_clear(&patch->flattened); git__free((char *)patch->base.diff_opts.old_prefix); git__free((char *)patch->base.diff_opts.new_prefix); if (patch->flags & GIT_PATCH_GENERATED_ALLOCATED) git__free(patch); } static void patch_generated_update_binary(git_patch_generated *patch) { if ((patch->base.delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0) return; if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 || (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY; else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE || patch->nfile.file->size > GIT_XDIFF_MAX_SIZE) patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY; else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 && (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0) patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; } static void patch_generated_init_common(git_patch_generated *patch) { patch->base.free_fn = patch_generated_free; patch_generated_update_binary(patch); patch->flags |= GIT_PATCH_GENERATED_INITIALIZED; if (patch->diff) git_diff_addref(patch->diff); } static int patch_generated_normalize_options( git_diff_options *out, const git_diff_options *opts) { if (opts) { GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); memcpy(out, opts, sizeof(git_diff_options)); } else { git_diff_options default_opts = GIT_DIFF_OPTIONS_INIT; memcpy(out, &default_opts, sizeof(git_diff_options)); } out->old_prefix = opts && opts->old_prefix ? git__strdup(opts->old_prefix) : git__strdup(DIFF_OLD_PREFIX_DEFAULT); out->new_prefix = opts && opts->new_prefix ? git__strdup(opts->new_prefix) : git__strdup(DIFF_NEW_PREFIX_DEFAULT); GIT_ERROR_CHECK_ALLOC(out->old_prefix); GIT_ERROR_CHECK_ALLOC(out->new_prefix); return 0; } static int patch_generated_init( git_patch_generated *patch, git_diff *diff, size_t delta_index) { int error = 0; memset(patch, 0, sizeof(*patch)); patch->diff = diff; patch->base.repo = diff->repo; patch->base.delta = git_vector_get(&diff->deltas, delta_index); patch->delta_index = delta_index; if ((error = patch_generated_normalize_options( &patch->base.diff_opts, &diff->opts)) < 0 || (error = git_diff_file_content__init_from_diff( &patch->ofile, diff, patch->base.delta, true)) < 0 || (error = git_diff_file_content__init_from_diff( &patch->nfile, diff, patch->base.delta, false)) < 0) return error; patch_generated_init_common(patch); return 0; } static int patch_generated_alloc_from_diff( git_patch_generated **out, git_diff *diff, size_t delta_index) { int error; git_patch_generated *patch = git__calloc(1, sizeof(git_patch_generated)); GIT_ERROR_CHECK_ALLOC(patch); if (!(error = patch_generated_init(patch, diff, delta_index))) { patch->flags |= GIT_PATCH_GENERATED_ALLOCATED; GIT_REFCOUNT_INC(&patch->base); } else { git__free(patch); patch = NULL; } *out = patch; return error; } GIT_INLINE(bool) should_skip_binary(git_patch_generated *patch, git_diff_file *file) { if ((patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) != 0) return false; return (file->flags & GIT_DIFF_FLAG_BINARY) != 0; } static bool patch_generated_diffable(git_patch_generated *patch) { size_t olen, nlen; if (patch->base.delta->status == GIT_DELTA_UNMODIFIED) return false; /* if we've determined this to be binary (and we are not showing binary * data) then we have skipped loading the map data. instead, query the * file data itself. */ if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0 && (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) == 0) { olen = (size_t)patch->ofile.file->size; nlen = (size_t)patch->nfile.file->size; } else { olen = patch->ofile.map.len; nlen = patch->nfile.map.len; } /* if both sides are empty, files are identical */ if (!olen && !nlen) return false; /* otherwise, check the file sizes and the oid */ return (olen != nlen || !git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id)); } static int patch_generated_load(git_patch_generated *patch, git_patch_generated_output *output) { int error = 0; bool incomplete_data; if ((patch->flags & GIT_PATCH_GENERATED_LOADED) != 0) return 0; /* if no hunk and data callbacks and user doesn't care if data looks * binary, then there is no need to actually load the data */ if ((patch->ofile.opts_flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 && output && !output->binary_cb && !output->hunk_cb && !output->data_cb) return 0; incomplete_data = (((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0) && ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0)); if ((error = git_diff_file_content__load( &patch->ofile, &patch->base.diff_opts)) < 0 || (error = git_diff_file_content__load( &patch->nfile, &patch->base.diff_opts)) < 0 || should_skip_binary(patch, patch->nfile.file)) goto cleanup; /* if previously missing an oid, and now that we have it the two sides * are the same (and not submodules), update MODIFIED -> UNMODIFIED */ if (incomplete_data && patch->ofile.file->mode == patch->nfile.file->mode && patch->ofile.file->mode != GIT_FILEMODE_COMMIT && git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id) && patch->base.delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */ patch->base.delta->status = GIT_DELTA_UNMODIFIED; cleanup: patch_generated_update_binary(patch); if (!error) { if (patch_generated_diffable(patch)) patch->flags |= GIT_PATCH_GENERATED_DIFFABLE; patch->flags |= GIT_PATCH_GENERATED_LOADED; } return error; } static int patch_generated_invoke_file_callback( git_patch_generated *patch, git_patch_generated_output *output) { float progress = patch->diff ? ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; if (!output->file_cb) return 0; return git_error_set_after_callback_function( output->file_cb(patch->base.delta, progress, output->payload), "git_patch"); } static int create_binary( git_diff_binary_t *out_type, char **out_data, size_t *out_datalen, size_t *out_inflatedlen, const char *a_data, size_t a_datalen, const char *b_data, size_t b_datalen) { git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT; size_t delta_data_len = 0; int error; /* The git_delta function accepts unsigned long only */ if (!git__is_ulong(a_datalen) || !git__is_ulong(b_datalen)) return GIT_EBUFS; if ((error = git_zstream_deflatebuf(&deflate, b_data, b_datalen)) < 0) goto done; /* The git_delta function accepts unsigned long only */ if (!git__is_ulong(deflate.size)) { error = GIT_EBUFS; goto done; } if (a_datalen && b_datalen) { void *delta_data; error = git_delta(&delta_data, &delta_data_len, a_data, a_datalen, b_data, b_datalen, deflate.size); if (error == 0) { error = git_zstream_deflatebuf( &delta, delta_data, delta_data_len); git__free(delta_data); } else if (error == GIT_EBUFS) { error = 0; } if (error < 0) goto done; } if (delta.size && delta.size < deflate.size) { *out_type = GIT_DIFF_BINARY_DELTA; *out_datalen = delta.size; *out_data = git_buf_detach(&delta); *out_inflatedlen = delta_data_len; } else { *out_type = GIT_DIFF_BINARY_LITERAL; *out_datalen = deflate.size; *out_data = git_buf_detach(&deflate); *out_inflatedlen = b_datalen; } done: git_buf_dispose(&deflate); git_buf_dispose(&delta); return error; } static int diff_binary(git_patch_generated_output *output, git_patch_generated *patch) { git_diff_binary binary = {0}; const char *old_data = patch->ofile.map.data; const char *new_data = patch->nfile.map.data; size_t old_len = patch->ofile.map.len, new_len = patch->nfile.map.len; int error; /* Only load contents if the user actually wants to diff * binary files. */ if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) { binary.contains_data = 1; /* Create the old->new delta (as the "new" side of the patch), * and the new->old delta (as the "old" side) */ if ((error = create_binary(&binary.old_file.type, (char **)&binary.old_file.data, &binary.old_file.datalen, &binary.old_file.inflatedlen, new_data, new_len, old_data, old_len)) < 0 || (error = create_binary(&binary.new_file.type, (char **)&binary.new_file.data, &binary.new_file.datalen, &binary.new_file.inflatedlen, old_data, old_len, new_data, new_len)) < 0) return error; } error = git_error_set_after_callback_function( output->binary_cb(patch->base.delta, &binary, output->payload), "git_patch"); git__free((char *) binary.old_file.data); git__free((char *) binary.new_file.data); return error; } static int patch_generated_create( git_patch_generated *patch, git_patch_generated_output *output) { int error = 0; if ((patch->flags & GIT_PATCH_GENERATED_DIFFED) != 0) return 0; /* if we are not looking at the binary or text data, don't do the diff */ if (!output->binary_cb && !output->hunk_cb && !output->data_cb) return 0; if ((patch->flags & GIT_PATCH_GENERATED_LOADED) == 0 && (error = patch_generated_load(patch, output)) < 0) return error; if ((patch->flags & GIT_PATCH_GENERATED_DIFFABLE) == 0) return 0; if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0) { if (output->binary_cb) error = diff_binary(output, patch); } else { if (output->diff_cb) error = output->diff_cb(output, patch); } patch->flags |= GIT_PATCH_GENERATED_DIFFED; return error; } static int diff_required(git_diff *diff, const char *action) { if (diff) return 0; git_error_set(GIT_ERROR_INVALID, "must provide valid diff to %s", action); return -1; } typedef struct { git_patch_generated patch; git_diff_delta delta; char paths[GIT_FLEX_ARRAY]; } patch_generated_with_delta; static int diff_single_generate(patch_generated_with_delta *pd, git_xdiff_output *xo) { int error = 0; git_patch_generated *patch = &pd->patch; bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0); bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0); pd->delta.status = has_new ? (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); if (git_oid_equal(&patch->nfile.file->id, &patch->ofile.file->id)) pd->delta.status = GIT_DELTA_UNMODIFIED; patch->base.delta = &pd->delta; patch_generated_init_common(patch); if (pd->delta.status == GIT_DELTA_UNMODIFIED && !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) { /* Even empty patches are flagged as binary, and even though * there's no difference, we flag this as "containing data" * (the data is known to be empty, as opposed to wholly unknown). */ if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) patch->base.binary.contains_data = 1; return error; } error = patch_generated_invoke_file_callback(patch, (git_patch_generated_output *)xo); if (!error) error = patch_generated_create(patch, (git_patch_generated_output *)xo); return error; } static int patch_generated_from_sources( patch_generated_with_delta *pd, git_xdiff_output *xo, git_diff_file_content_src *oldsrc, git_diff_file_content_src *newsrc, const git_diff_options *opts) { int error = 0; git_repository *repo = oldsrc->blob ? git_blob_owner(oldsrc->blob) : newsrc->blob ? git_blob_owner(newsrc->blob) : NULL; git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file; git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile; if ((error = patch_generated_normalize_options(&pd->patch.base.diff_opts, opts)) < 0) return error; if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { void *tmp = lfile; lfile = rfile; rfile = tmp; tmp = ldata; ldata = rdata; rdata = tmp; } pd->patch.base.delta = &pd->delta; if (!oldsrc->as_path) { if (newsrc->as_path) oldsrc->as_path = newsrc->as_path; else oldsrc->as_path = newsrc->as_path = "file"; } else if (!newsrc->as_path) newsrc->as_path = oldsrc->as_path; lfile->path = oldsrc->as_path; rfile->path = newsrc->as_path; if ((error = git_diff_file_content__init_from_src( ldata, repo, opts, oldsrc, lfile)) < 0 || (error = git_diff_file_content__init_from_src( rdata, repo, opts, newsrc, rfile)) < 0) return error; return diff_single_generate(pd, xo); } static int patch_generated_with_delta_alloc( patch_generated_with_delta **out, const char **old_path, const char **new_path) { patch_generated_with_delta *pd; size_t old_len = *old_path ? strlen(*old_path) : 0; size_t new_len = *new_path ? strlen(*new_path) : 0; size_t alloc_len; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*pd), old_len); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, new_len); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); *out = pd = git__calloc(1, alloc_len); GIT_ERROR_CHECK_ALLOC(pd); pd->patch.flags = GIT_PATCH_GENERATED_ALLOCATED; if (*old_path) { memcpy(&pd->paths[0], *old_path, old_len); *old_path = &pd->paths[0]; } else if (*new_path) *old_path = &pd->paths[old_len + 1]; if (*new_path) { memcpy(&pd->paths[old_len + 1], *new_path, new_len); *new_path = &pd->paths[old_len + 1]; } else if (*old_path) *new_path = &pd->paths[0]; return 0; } static int diff_from_sources( git_diff_file_content_src *oldsrc, git_diff_file_content_src *newsrc, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) { int error = 0; patch_generated_with_delta pd; git_xdiff_output xo; memset(&xo, 0, sizeof(xo)); diff_output_init( &xo.output, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); memset(&pd, 0, sizeof(pd)); error = patch_generated_from_sources(&pd, &xo, oldsrc, newsrc, opts); git_patch_free(&pd.patch.base); return error; } static int patch_from_sources( git_patch **out, git_diff_file_content_src *oldsrc, git_diff_file_content_src *newsrc, const git_diff_options *opts) { int error = 0; patch_generated_with_delta *pd; git_xdiff_output xo; GIT_ASSERT_ARG(out); *out = NULL; if ((error = patch_generated_with_delta_alloc( &pd, &oldsrc->as_path, &newsrc->as_path)) < 0) return error; memset(&xo, 0, sizeof(xo)); diff_output_to_patch(&xo.output, &pd->patch); git_xdiff_init(&xo, opts); if (!(error = patch_generated_from_sources(pd, &xo, oldsrc, newsrc, opts))) *out = (git_patch *)pd; else git_patch_free((git_patch *)pd); return error; } int git_diff_blobs( const git_blob *old_blob, const char *old_path, const git_blob *new_blob, const char *new_path, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) { git_diff_file_content_src osrc = GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path); return diff_from_sources( &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); } int git_patch_from_blobs( git_patch **out, const git_blob *old_blob, const char *old_path, const git_blob *new_blob, const char *new_path, const git_diff_options *opts) { git_diff_file_content_src osrc = GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path); return patch_from_sources(out, &osrc, &nsrc, opts); } int git_diff_blob_to_buffer( const git_blob *old_blob, const char *old_path, const char *buf, size_t buflen, const char *buf_path, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) { git_diff_file_content_src osrc = GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path); return diff_from_sources( &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); } int git_patch_from_blob_and_buffer( git_patch **out, const git_blob *old_blob, const char *old_path, const void *buf, size_t buflen, const char *buf_path, const git_diff_options *opts) { git_diff_file_content_src osrc = GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path); return patch_from_sources(out, &osrc, &nsrc, opts); } int git_diff_buffers( const void *old_buf, size_t old_len, const char *old_path, const void *new_buf, size_t new_len, const char *new_path, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) { git_diff_file_content_src osrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path); git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path); return diff_from_sources( &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); } int git_patch_from_buffers( git_patch **out, const void *old_buf, size_t old_len, const char *old_path, const void *new_buf, size_t new_len, const char *new_path, const git_diff_options *opts) { git_diff_file_content_src osrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path); git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path); return patch_from_sources(out, &osrc, &nsrc, opts); } int git_patch_generated_from_diff( git_patch **patch_ptr, git_diff *diff, size_t idx) { int error = 0; git_xdiff_output xo; git_diff_delta *delta = NULL; git_patch_generated *patch = NULL; if (patch_ptr) *patch_ptr = NULL; if (diff_required(diff, "git_patch_from_diff") < 0) return -1; delta = git_vector_get(&diff->deltas, idx); if (!delta) { git_error_set(GIT_ERROR_INVALID, "index out of range for delta in diff"); return GIT_ENOTFOUND; } if (git_diff_delta__should_skip(&diff->opts, delta)) return 0; /* don't load the patch data unless we need it for binary check */ if (!patch_ptr && ((delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0 || (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0)) return 0; if ((error = patch_generated_alloc_from_diff(&patch, diff, idx)) < 0) return error; memset(&xo, 0, sizeof(xo)); diff_output_to_patch(&xo.output, patch); git_xdiff_init(&xo, &diff->opts); error = patch_generated_invoke_file_callback(patch, &xo.output); if (!error) error = patch_generated_create(patch, &xo.output); if (!error) { /* TODO: if cumulative diff size is < 0.5 total size, flatten patch */ /* TODO: and unload the file content */ } if (error || !patch_ptr) git_patch_free(&patch->base); else *patch_ptr = &patch->base; return error; } git_diff_driver *git_patch_generated_driver(git_patch_generated *patch) { /* ofile driver is representative for whole patch */ return patch->ofile.driver; } void git_patch_generated_old_data( char **ptr, size_t *len, git_patch_generated *patch) { *ptr = patch->ofile.map.data; *len = patch->ofile.map.len; } void git_patch_generated_new_data( char **ptr, size_t *len, git_patch_generated *patch) { *ptr = patch->nfile.map.data; *len = patch->nfile.map.len; } static int patch_generated_file_cb( const git_diff_delta *delta, float progress, void *payload) { GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload); return 0; } static int patch_generated_binary_cb( const git_diff_delta *delta, const git_diff_binary *binary, void *payload) { git_patch *patch = payload; GIT_UNUSED(delta); memcpy(&patch->binary, binary, sizeof(git_diff_binary)); if (binary->old_file.data) { patch->binary.old_file.data = git__malloc(binary->old_file.datalen); GIT_ERROR_CHECK_ALLOC(patch->binary.old_file.data); memcpy((char *)patch->binary.old_file.data, binary->old_file.data, binary->old_file.datalen); } if (binary->new_file.data) { patch->binary.new_file.data = git__malloc(binary->new_file.datalen); GIT_ERROR_CHECK_ALLOC(patch->binary.new_file.data); memcpy((char *)patch->binary.new_file.data, binary->new_file.data, binary->new_file.datalen); } return 0; } static int git_patch_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk_, void *payload) { git_patch_generated *patch = payload; git_patch_hunk *hunk; GIT_UNUSED(delta); hunk = git_array_alloc(patch->base.hunks); GIT_ERROR_CHECK_ALLOC(hunk); memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk)); patch->base.header_size += hunk_->header_len; hunk->line_start = git_array_size(patch->base.lines); hunk->line_count = 0; return 0; } static int patch_generated_line_cb( const git_diff_delta *delta, const git_diff_hunk *hunk_, const git_diff_line *line_, void *payload) { git_patch_generated *patch = payload; git_patch_hunk *hunk; git_diff_line *line; GIT_UNUSED(delta); GIT_UNUSED(hunk_); hunk = git_array_last(patch->base.hunks); GIT_ASSERT(hunk); /* programmer error if no hunk is available */ line = git_array_alloc(patch->base.lines); GIT_ERROR_CHECK_ALLOC(line); memcpy(line, line_, sizeof(*line)); /* do some bookkeeping so we can provide old/new line numbers */ patch->base.content_size += line->content_len; if (line->origin == GIT_DIFF_LINE_ADDITION || line->origin == GIT_DIFF_LINE_DELETION) patch->base.content_size += 1; else if (line->origin == GIT_DIFF_LINE_CONTEXT) { patch->base.content_size += 1; patch->base.context_size += line->content_len + 1; } else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL) patch->base.context_size += line->content_len; hunk->line_count++; return 0; } static void diff_output_init( git_patch_generated_output *out, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) { GIT_UNUSED(opts); memset(out, 0, sizeof(*out)); out->file_cb = file_cb; out->binary_cb = binary_cb; out->hunk_cb = hunk_cb; out->data_cb = data_cb; out->payload = payload; } static void diff_output_to_patch( git_patch_generated_output *out, git_patch_generated *patch) { diff_output_init( out, NULL, patch_generated_file_cb, patch_generated_binary_cb, git_patch_hunk_cb, patch_generated_line_cb, patch); } git2r/src/libgit2/src/refdb.h0000644000175000017500000001136114125111754015603 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_refdb_h__ #define INCLUDE_refdb_h__ #include "common.h" #include "git2/refdb.h" #include "repository.h" struct git_refdb { git_refcount rc; git_repository *repo; git_refdb_backend *backend; }; void git_refdb__free(git_refdb *db); int git_refdb_exists( int *exists, git_refdb *refdb, const char *ref_name); int git_refdb_lookup( git_reference **out, git_refdb *refdb, const char *ref_name); /** * Resolve the reference by following symbolic references. * * Given a reference name, this function will follow any symbolic references up * to `max_nesting` deep and return the resolved direct reference. If any of * the intermediate symbolic references points to a non-existing reference, * then that symbolic reference is returned instead with an error code of `0`. * If the given reference is a direct reference already, it is returned * directly. * * If `max_nesting` is `0`, the reference will not be resolved. If it's * negative, it will be set to the default resolve depth which is `5`. * * @param out Pointer to store the result in. * @param db The refdb to use for resolving the reference. * @param ref_name The reference name to lookup and resolve. * @param max_nesting The maximum nesting depth. * @return `0` on success, a negative error code otherwise. */ int git_refdb_resolve( git_reference **out, git_refdb *db, const char *ref_name, int max_nesting); int git_refdb_rename( git_reference **out, git_refdb *db, const char *old_name, const char *new_name, int force, const git_signature *who, const char *message); int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob); int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter); int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target); int git_refdb_delete(git_refdb *refdb, const char *ref_name, const git_oid *old_id, const char *old_target); int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name); int git_refdb_reflog_write(git_reflog *reflog); /** * Determine whether a reflog entry should be created for the given reference. * * Whether or not writing to a reference should create a reflog entry is * dependent on a number of things. Most importantly, there's the * "core.logAllRefUpdates" setting that controls in which situations a * reference should get a corresponding reflog entry. The following values for * it are understood: * * - "false": Do not log reference updates. * * - "true": Log normal reference updates. This will write entries for * references in "refs/heads", "refs/remotes", "refs/notes" and * "HEAD" or if the reference already has a log entry. * * - "always": Always create a reflog entry. * * If unset, the value will default to "true" for non-bare repositories and * "false" for bare ones. * * @param out pointer to which the result will be written, `1` means a reflog * entry should be written, `0` means none should be written. * @param db The refdb to decide this for. * @param ref The reference one wants to check. * @return `0` on success, a negative error code otherwise. */ int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref); /** * Determine whether a reflog entry should be created for HEAD if creating one * for the given reference * * In case the given reference is being pointed to by HEAD, then creating a * reflog entry for this reference also requires us to create a corresponding * reflog entry for HEAD. This function can be used to determine that scenario. * * @param out pointer to which the result will be written, `1` means a reflog * entry should be written, `0` means none should be written. * @param db The refdb to decide this for. * @param ref The reference one wants to check. * @return `0` on success, a negative error code otherwise. */ int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref); int git_refdb_has_log(git_refdb *db, const char *refname); int git_refdb_ensure_log(git_refdb *refdb, const char *refname); int git_refdb_lock(void **payload, git_refdb *db, const char *refname); int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message); #endif git2r/src/libgit2/src/attrcache.c0000644000175000017500000002613614125111754016460 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "attrcache.h" #include "repository.h" #include "attr_file.h" #include "config.h" #include "sysdir.h" #include "ignore.h" GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache) { GIT_UNUSED(cache); /* avoid warning if threading is off */ if (git_mutex_lock(&cache->lock) < 0) { git_error_set(GIT_ERROR_OS, "unable to get attr cache lock"); return -1; } return 0; } GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache) { GIT_UNUSED(cache); /* avoid warning if threading is off */ git_mutex_unlock(&cache->lock); } GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry( git_attr_cache *cache, const char *path) { return git_strmap_get(cache->files, path); } int git_attr_cache__alloc_file_entry( git_attr_file_entry **out, git_repository *repo, const char *base, const char *path, git_pool *pool) { size_t baselen = 0, pathlen = strlen(path); size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1; git_attr_file_entry *ce; if (base != NULL && git_path_root(path) < 0) { baselen = strlen(base); cachesize += baselen; if (baselen && base[baselen - 1] != '/') cachesize++; } ce = git_pool_mallocz(pool, cachesize); GIT_ERROR_CHECK_ALLOC(ce); if (baselen) { memcpy(ce->fullpath, base, baselen); if (base[baselen - 1] != '/') ce->fullpath[baselen++] = '/'; } memcpy(&ce->fullpath[baselen], path, pathlen); if (git_path_validate_workdir_with_len(repo, ce->fullpath, pathlen + baselen) < 0) return -1; ce->path = &ce->fullpath[baselen]; *out = ce; return 0; } /* call with attrcache locked */ static int attr_cache_make_entry( git_attr_file_entry **out, git_repository *repo, const char *path) { git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file_entry *entry = NULL; int error; if ((error = git_attr_cache__alloc_file_entry(&entry, repo, git_repository_workdir(repo), path, &cache->pool)) < 0) return error; if ((error = git_strmap_set(cache->files, entry->path, entry)) < 0) return error; *out = entry; return error; } /* insert entry or replace existing if we raced with another thread */ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) { git_attr_file_entry *entry; git_attr_file *old; if (attr_cache_lock(cache) < 0) return -1; entry = attr_cache_lookup_entry(cache, file->entry->path); GIT_REFCOUNT_OWN(file, entry); GIT_REFCOUNT_INC(file); /* * Replace the existing value if another thread has * created it in the meantime. */ old = git_atomic_swap(entry->file[file->source.type], file); if (old) { GIT_REFCOUNT_OWN(old, NULL); git_attr_file__free(old); } attr_cache_unlock(cache); return 0; } static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) { int error = 0; git_attr_file_entry *entry; git_attr_file *oldfile = NULL; if (!file) return 0; if ((error = attr_cache_lock(cache)) < 0) return error; if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) oldfile = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL); attr_cache_unlock(cache); if (oldfile == file) { GIT_REFCOUNT_OWN(file, NULL); git_attr_file__free(file); } return error; } /* Look up cache entry and file. * - If entry is not present, create it while the cache is locked. * - If file is present, increment refcount before returning it, so the * cache can be unlocked and it won't go away. */ static int attr_cache_lookup( git_attr_file **out_file, git_attr_file_entry **out_entry, git_repository *repo, git_attr_session *attr_session, git_attr_file_source *source) { int error = 0; git_buf path = GIT_BUF_INIT; const char *wd = git_repository_workdir(repo); const char *filename; git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file_entry *entry = NULL; git_attr_file *file = NULL; /* join base and path as needed */ if (source->base != NULL && git_path_root(source->filename) < 0) { git_buf *p = attr_session ? &attr_session->tmp : &path; if (git_buf_joinpath(p, source->base, source->filename) < 0 || git_path_validate_workdir_buf(repo, p) < 0) return -1; filename = p->ptr; } else { filename = source->filename; } if (wd && !git__prefixcmp(filename, wd)) filename += strlen(wd); /* check cache for existing entry */ if ((error = attr_cache_lock(cache)) < 0) goto cleanup; entry = attr_cache_lookup_entry(cache, filename); if (!entry) { error = attr_cache_make_entry(&entry, repo, filename); } else if (entry->file[source->type] != NULL) { file = entry->file[source->type]; GIT_REFCOUNT_INC(file); } attr_cache_unlock(cache); cleanup: *out_file = file; *out_entry = entry; git_buf_dispose(&path); return error; } int git_attr_cache__get( git_attr_file **out, git_repository *repo, git_attr_session *attr_session, git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros) { int error = 0; git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file_entry *entry = NULL; git_attr_file *file = NULL, *updated = NULL; if ((error = attr_cache_lookup(&file, &entry, repo, attr_session, source)) < 0) return error; /* load file if we don't have one or if existing one is out of date */ if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file, source)) > 0) error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser, allow_macros); /* if we loaded the file, insert into and/or update cache */ if (updated) { if ((error = attr_cache_upsert(cache, updated)) < 0) { git_attr_file__free(updated); } else { git_attr_file__free(file); /* offset incref from lookup */ file = updated; } } /* if file could not be loaded */ if (error < 0) { /* remove existing entry */ if (file) { attr_cache_remove(cache, file); git_attr_file__free(file); /* offset incref from lookup */ file = NULL; } /* no error if file simply doesn't exist */ if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } } *out = file; return error; } bool git_attr_cache__is_cached( git_repository *repo, git_attr_file_source_t source_type, const char *filename) { git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file_entry *entry; git_strmap *files; if (!cache || !(files = cache->files)) return false; if ((entry = git_strmap_get(files, filename)) == NULL) return false; return entry && (entry->file[source_type] != NULL); } static int attr_cache__lookup_path( char **out, git_config *cfg, const char *key, const char *fallback) { git_buf buf = GIT_BUF_INIT; int error; git_config_entry *entry = NULL; *out = NULL; if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0) return error; if (entry) { const char *cfgval = entry->value; /* expand leading ~/ as needed */ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') { if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2]))) *out = git_buf_detach(&buf); } else if (cfgval) { *out = git__strdup(cfgval); } } else if (!git_sysdir_find_xdg_file(&buf, fallback)) { *out = git_buf_detach(&buf); } git_config_entry_free(entry); git_buf_dispose(&buf); return error; } static void attr_cache__free(git_attr_cache *cache) { bool unlock; if (!cache) return; unlock = (attr_cache_lock(cache) == 0); if (cache->files != NULL) { git_attr_file_entry *entry; git_attr_file *file; int i; git_strmap_foreach_value(cache->files, entry, { for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) { if ((file = git_atomic_swap(entry->file[i], NULL)) != NULL) { GIT_REFCOUNT_OWN(file, NULL); git_attr_file__free(file); } } }); git_strmap_free(cache->files); } if (cache->macros != NULL) { git_attr_rule *rule; git_strmap_foreach_value(cache->macros, rule, { git_attr_rule__free(rule); }); git_strmap_free(cache->macros); } git_pool_clear(&cache->pool); git__free(cache->cfg_attr_file); cache->cfg_attr_file = NULL; git__free(cache->cfg_excl_file); cache->cfg_excl_file = NULL; if (unlock) attr_cache_unlock(cache); git_mutex_free(&cache->lock); git__free(cache); } int git_attr_cache__init(git_repository *repo) { int ret = 0; git_attr_cache *cache = git_repository_attr_cache(repo); git_config *cfg = NULL; if (cache) return 0; cache = git__calloc(1, sizeof(git_attr_cache)); GIT_ERROR_CHECK_ALLOC(cache); /* set up lock */ if (git_mutex_init(&cache->lock) < 0) { git_error_set(GIT_ERROR_OS, "unable to initialize lock for attr cache"); git__free(cache); return -1; } if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0) goto cancel; /* cache config settings for attributes and ignores */ ret = attr_cache__lookup_path( &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG); if (ret < 0) goto cancel; ret = attr_cache__lookup_path( &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG); if (ret < 0) goto cancel; /* allocate hashtable for attribute and ignore file contents, * hashtable for attribute macros, and string pool */ if ((ret = git_strmap_new(&cache->files)) < 0 || (ret = git_strmap_new(&cache->macros)) < 0 || (ret = git_pool_init(&cache->pool, 1)) < 0) goto cancel; if (git_atomic_compare_and_swap(&repo->attrcache, NULL, cache) != NULL) goto cancel; /* raced with another thread, free this but no error */ git_config_free(cfg); /* insert default macros */ return git_attr_add_macro(repo, "binary", "-diff -merge -text -crlf"); cancel: attr_cache__free(cache); git_config_free(cfg); return ret; } int git_attr_cache_flush(git_repository *repo) { git_attr_cache *cache; /* this could be done less expensively, but for now, we'll just free * the entire attrcache and let the next use reinitialize it... */ if (repo && (cache = git_atomic_swap(repo->attrcache, NULL)) != NULL) attr_cache__free(cache); return 0; } int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) { git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_rule *preexisting; bool locked = false; int error = 0; /* * Callers assume that if we return success, that the * macro will have been adopted by the attributes cache. * Thus, we have to free the macro here if it's not being * added to the cache. * * TODO: generate warning log if (macro->assigns.length == 0) */ if (macro->assigns.length == 0) { git_attr_rule__free(macro); goto out; } if ((error = attr_cache_lock(cache)) < 0) goto out; locked = true; if ((preexisting = git_strmap_get(cache->macros, macro->match.pattern)) != NULL) git_attr_rule__free(preexisting); if ((error = git_strmap_set(cache->macros, macro->match.pattern, macro)) < 0) goto out; out: if (locked) attr_cache_unlock(cache); return error; } git_attr_rule *git_attr_cache__lookup_macro( git_repository *repo, const char *name) { git_strmap *macros = git_repository_attr_cache(repo)->macros; return git_strmap_get(macros, name); } git2r/src/libgit2/src/signature.c0000644000175000017500000001742314125111754016522 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "signature.h" #include "repository.h" #include "git2/common.h" #include "posix.h" void git_signature_free(git_signature *sig) { if (sig == NULL) return; git__free(sig->name); sig->name = NULL; git__free(sig->email); sig->email = NULL; git__free(sig); } static int signature_error(const char *msg) { git_error_set(GIT_ERROR_INVALID, "failed to parse signature - %s", msg); return -1; } static bool contains_angle_brackets(const char *input) { return strchr(input, '<') != NULL || strchr(input, '>') != NULL; } static bool is_crud(unsigned char c) { return c <= 32 || c == '.' || c == ',' || c == ':' || c == ';' || c == '<' || c == '>' || c == '"' || c == '\\' || c == '\''; } static char *extract_trimmed(const char *ptr, size_t len) { while (len && is_crud((unsigned char)ptr[0])) { ptr++; len--; } while (len && is_crud((unsigned char)ptr[len - 1])) { len--; } return git__substrdup(ptr, len); } int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset) { git_signature *p = NULL; GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(email); *sig_out = NULL; if (contains_angle_brackets(name) || contains_angle_brackets(email)) { return signature_error( "Neither `name` nor `email` should contain angle brackets chars."); } p = git__calloc(1, sizeof(git_signature)); GIT_ERROR_CHECK_ALLOC(p); p->name = extract_trimmed(name, strlen(name)); GIT_ERROR_CHECK_ALLOC(p->name); p->email = extract_trimmed(email, strlen(email)); GIT_ERROR_CHECK_ALLOC(p->email); if (p->name[0] == '\0' || p->email[0] == '\0') { git_signature_free(p); return signature_error("Signature cannot have an empty name or email"); } p->when.time = time; p->when.offset = offset; p->when.sign = (offset < 0) ? '-' : '+'; *sig_out = p; return 0; } int git_signature_dup(git_signature **dest, const git_signature *source) { git_signature *signature; if (source == NULL) return 0; signature = git__calloc(1, sizeof(git_signature)); GIT_ERROR_CHECK_ALLOC(signature); signature->name = git__strdup(source->name); GIT_ERROR_CHECK_ALLOC(signature->name); signature->email = git__strdup(source->email); GIT_ERROR_CHECK_ALLOC(signature->email); signature->when.time = source->when.time; signature->when.offset = source->when.offset; signature->when.sign = source->when.sign; *dest = signature; return 0; } int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool) { git_signature *signature; if (source == NULL) return 0; signature = git_pool_mallocz(pool, sizeof(git_signature)); GIT_ERROR_CHECK_ALLOC(signature); signature->name = git_pool_strdup(pool, source->name); GIT_ERROR_CHECK_ALLOC(signature->name); signature->email = git_pool_strdup(pool, source->email); GIT_ERROR_CHECK_ALLOC(signature->email); signature->when.time = source->when.time; signature->when.offset = source->when.offset; signature->when.sign = source->when.sign; *dest = signature; return 0; } int git_signature_now(git_signature **sig_out, const char *name, const char *email) { time_t now; time_t offset; struct tm *utc_tm; git_signature *sig; struct tm _utc; *sig_out = NULL; /* * Get the current time as seconds since the epoch and * transform that into a tm struct containing the time at * UTC. Give that to mktime which considers it a local time * (tm_isdst = -1 asks it to take DST into account) and gives * us that time as seconds since the epoch. The difference * between its return value and 'now' is our offset to UTC. */ time(&now); utc_tm = p_gmtime_r(&now, &_utc); utc_tm->tm_isdst = -1; offset = (time_t)difftime(now, mktime(utc_tm)); offset /= 60; if (git_signature_new(&sig, name, email, now, (int)offset) < 0) return -1; *sig_out = sig; return 0; } int git_signature_default(git_signature **out, git_repository *repo) { int error; git_config *cfg; const char *user_name, *user_email; if ((error = git_repository_config_snapshot(&cfg, repo)) < 0) return error; if (!(error = git_config_get_string(&user_name, cfg, "user.name")) && !(error = git_config_get_string(&user_email, cfg, "user.email"))) error = git_signature_now(out, user_name, user_email); git_config_free(cfg); return error; } int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender) { const char *buffer = *buffer_out; const char *email_start, *email_end; memset(sig, 0, sizeof(git_signature)); if (ender && (buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL) return signature_error("no newline given"); if (header) { const size_t header_len = strlen(header); if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0) return signature_error("expected prefix doesn't match actual"); buffer += header_len; } email_start = git__memrchr(buffer, '<', buffer_end - buffer); email_end = git__memrchr(buffer, '>', buffer_end - buffer); if (!email_start || !email_end || email_end <= email_start) return signature_error("malformed e-mail"); email_start += 1; sig->name = extract_trimmed(buffer, email_start - buffer - 1); sig->email = extract_trimmed(email_start, email_end - email_start); /* Do we even have a time at the end of the signature? */ if (email_end + 2 < buffer_end) { const char *time_start = email_end + 2; const char *time_end; if (git__strntol64(&sig->when.time, time_start, buffer_end - time_start, &time_end, 10) < 0) { git__free(sig->name); git__free(sig->email); sig->name = sig->email = NULL; return signature_error("invalid Unix timestamp"); } /* do we have a timezone? */ if (time_end + 1 < buffer_end) { int offset, hours, mins; const char *tz_start, *tz_end; tz_start = time_end + 1; if ((tz_start[0] != '-' && tz_start[0] != '+') || git__strntol32(&offset, tz_start + 1, buffer_end - tz_start - 1, &tz_end, 10) < 0) { /* malformed timezone, just assume it's zero */ offset = 0; } hours = offset / 100; mins = offset % 100; /* * only store timezone if it's not overflowing; * see http://www.worldtimezone.com/faq.html */ if (hours <= 14 && mins <= 59) { sig->when.offset = (hours * 60) + mins; sig->when.sign = tz_start[0]; if (tz_start[0] == '-') sig->when.offset = -sig->when.offset; } } } *buffer_out = buffer_end + 1; return 0; } int git_signature_from_buffer(git_signature **out, const char *buf) { git_signature *sig; const char *buf_end; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(buf); *out = NULL; sig = git__calloc(1, sizeof(git_signature)); GIT_ERROR_CHECK_ALLOC(sig); buf_end = buf + strlen(buf); error = git_signature__parse(sig, &buf, buf_end, NULL, '\0'); if (error) git__free(sig); else *out = sig; return error; } void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig) { int offset, hours, mins; char sign; offset = sig->when.offset; sign = (sig->when.offset < 0 || sig->when.sign == '-') ? '-' : '+'; if (offset < 0) offset = -offset; hours = offset / 60; mins = offset % 60; git_buf_printf(buf, "%s%s <%s> %u %c%02d%02d\n", header ? header : "", sig->name, sig->email, (unsigned)sig->when.time, sign, hours, mins); } bool git_signature__equal(const git_signature *one, const git_signature *two) { GIT_ASSERT_ARG(one); GIT_ASSERT_ARG(two); return git__strcmp(one->name, two->name) == 0 && git__strcmp(one->email, two->email) == 0 && one->when.time == two->when.time && one->when.offset == two->when.offset && one->when.sign == two->when.sign; } git2r/src/libgit2/src/cherrypick.c0000644000175000017500000001524414125111754016663 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "repository.h" #include "filebuf.h" #include "merge.h" #include "vector.h" #include "index.h" #include "git2/types.h" #include "git2/merge.h" #include "git2/cherrypick.h" #include "git2/commit.h" #include "git2/sys/commit.h" #define GIT_CHERRYPICK_FILE_MODE 0666 static int write_cherrypick_head( git_repository *repo, const char *commit_oidstr) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; int error = 0; if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_CHERRYPICK_HEAD_FILE)) >= 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_CHERRYPICK_FILE_MODE)) >= 0 && (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) error = git_filebuf_commit(&file); if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } static int write_merge_msg( git_repository *repo, const char *commit_msg) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; int error = 0; if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_CHERRYPICK_FILE_MODE)) < 0 || (error = git_filebuf_printf(&file, "%s", commit_msg)) < 0) goto cleanup; error = git_filebuf_commit(&file); cleanup: if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } static int cherrypick_normalize_opts( git_repository *repo, git_cherrypick_options *opts, const git_cherrypick_options *given, const char *their_label) { int error = 0; unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS; GIT_UNUSED(repo); if (given != NULL) memcpy(opts, given, sizeof(git_cherrypick_options)); else { git_cherrypick_options default_opts = GIT_CHERRYPICK_OPTIONS_INIT; memcpy(opts, &default_opts, sizeof(git_cherrypick_options)); } if (!opts->checkout_opts.checkout_strategy) opts->checkout_opts.checkout_strategy = default_checkout_strategy; if (!opts->checkout_opts.our_label) opts->checkout_opts.our_label = "HEAD"; if (!opts->checkout_opts.their_label) opts->checkout_opts.their_label = their_label; return error; } static int cherrypick_state_cleanup(git_repository *repo) { const char *state_files[] = { GIT_CHERRYPICK_HEAD_FILE, GIT_MERGE_MSG_FILE }; return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); } static int cherrypick_seterr(git_commit *commit, const char *fmt) { char commit_oidstr[GIT_OID_HEXSZ + 1]; git_error_set(GIT_ERROR_CHERRYPICK, fmt, git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); return -1; } int git_cherrypick_commit( git_index **out, git_repository *repo, git_commit *cherrypick_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_opts) { git_commit *parent_commit = NULL; git_tree *parent_tree = NULL, *our_tree = NULL, *cherrypick_tree = NULL; int parent = 0, error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(cherrypick_commit); GIT_ASSERT_ARG(our_commit); if (git_commit_parentcount(cherrypick_commit) > 1) { if (!mainline) return cherrypick_seterr(cherrypick_commit, "mainline branch is not specified but %s is a merge commit"); parent = mainline; } else { if (mainline) return cherrypick_seterr(cherrypick_commit, "mainline branch specified but %s is not a merge commit"); parent = git_commit_parentcount(cherrypick_commit); } if (parent && ((error = git_commit_parent(&parent_commit, cherrypick_commit, (parent - 1))) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0)) goto done; if ((error = git_commit_tree(&cherrypick_tree, cherrypick_commit)) < 0 || (error = git_commit_tree(&our_tree, our_commit)) < 0) goto done; error = git_merge_trees(out, repo, parent_tree, our_tree, cherrypick_tree, merge_opts); done: git_tree_free(parent_tree); git_tree_free(our_tree); git_tree_free(cherrypick_tree); git_commit_free(parent_commit); return error; } int git_cherrypick( git_repository *repo, git_commit *commit, const git_cherrypick_options *given_opts) { git_cherrypick_options opts; git_reference *our_ref = NULL; git_commit *our_commit = NULL; char commit_oidstr[GIT_OID_HEXSZ + 1]; const char *commit_msg, *commit_summary; git_buf their_label = GIT_BUF_INIT; git_index *index = NULL; git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(commit); GIT_ERROR_CHECK_VERSION(given_opts, GIT_CHERRYPICK_OPTIONS_VERSION, "git_cherrypick_options"); if ((error = git_repository__ensure_not_bare(repo, "cherry-pick")) < 0) return error; if ((commit_msg = git_commit_message(commit)) == NULL || (commit_summary = git_commit_summary(commit)) == NULL) { error = -1; goto on_error; } git_oid_nfmt(commit_oidstr, sizeof(commit_oidstr), git_commit_id(commit)); if ((error = write_merge_msg(repo, commit_msg)) < 0 || (error = git_buf_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 || (error = cherrypick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 || (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 || (error = write_cherrypick_head(repo, commit_oidstr)) < 0 || (error = git_repository_head(&our_ref, repo)) < 0 || (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJECT_COMMIT)) < 0 || (error = git_cherrypick_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 || (error = git_merge__check_result(repo, index)) < 0 || (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 || (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 || (error = git_indexwriter_commit(&indexwriter)) < 0) goto on_error; goto done; on_error: cherrypick_state_cleanup(repo); done: git_indexwriter_cleanup(&indexwriter); git_index_free(index); git_commit_free(our_commit); git_reference_free(our_ref); git_buf_dispose(&their_label); return error; } int git_cherrypick_options_init( git_cherrypick_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_cherrypick_options, GIT_CHERRYPICK_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_cherrypick_init_options( git_cherrypick_options *opts, unsigned int version) { return git_cherrypick_options_init(opts, version); } #endif git2r/src/libgit2/src/oidarray.h0000644000175000017500000000102414125111754016326 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_oidarray_h__ #define INCLUDE_oidarray_h__ #include "common.h" #include "git2/oidarray.h" #include "array.h" typedef git_array_t(git_oid) git_array_oid_t; extern void git_oidarray__reverse(git_oidarray *arr); extern void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array); #endif git2r/src/libgit2/src/trailer.c0000644000175000017500000002270514125111754016162 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "array.h" #include "common.h" #include "git2/message.h" #include #include #include #define COMMENT_LINE_CHAR '#' #define TRAILER_SEPARATORS ":" static const char *const git_generated_prefixes[] = { "Signed-off-by: ", "(cherry picked from commit ", NULL }; static int is_blank_line(const char *str) { const char *s = str; while (*s && *s != '\n' && isspace(*s)) s++; return !*s || *s == '\n'; } static const char *next_line(const char *str) { const char *nl = strchr(str, '\n'); if (nl) { return nl + 1; } else { /* return pointer to the NUL terminator: */ return str + strlen(str); } } /* * Return the position of the start of the last line. If len is 0, return 0. */ static bool last_line(size_t *out, const char *buf, size_t len) { size_t i; *out = 0; if (len == 0) return false; if (len == 1) return true; /* * Skip the last character (in addition to the null terminator), * because if the last character is a newline, it is considered as part * of the last line anyway. */ i = len - 2; for (; i > 0; i--) { if (buf[i] == '\n') { *out = i + 1; return true; } } return true; } /* * If the given line is of the form * "..." or "...", sets out * to the location of the separator and returns true. Otherwise, returns * false. The optional whitespace is allowed there primarily to allow things * like "Bug #43" where is "Bug" and is "#". * * The separator-starts-line case (in which this function returns true and * sets out to 0) is distinguished from the non-well-formed-line case (in * which this function returns false) because some callers of this function * need such a distinction. */ static bool find_separator(size_t *out, const char *line, const char *separators) { int whitespace_found = 0; const char *c; for (c = line; *c; c++) { if (strchr(separators, *c)) { *out = c - line; return true; } if (!whitespace_found && (isalnum(*c) || *c == '-')) continue; if (c != line && (*c == ' ' || *c == '\t')) { whitespace_found = 1; continue; } break; } return false; } /* * Inspect the given string and determine the true "end" of the log message, in * order to find where to put a new Signed-off-by: line. Ignored are * trailing comment lines and blank lines. To support "git commit -s * --amend" on an existing commit, we also ignore "Conflicts:". To * support "git commit -v", we truncate at cut lines. * * Returns the number of bytes from the tail to ignore, to be fed as * the second parameter to append_signoff(). */ static size_t ignore_non_trailer(const char *buf, size_t len) { size_t boc = 0, bol = 0; int in_old_conflicts_block = 0; size_t cutoff = len; while (bol < cutoff) { const char *next_line = memchr(buf + bol, '\n', len - bol); if (!next_line) next_line = buf + len; else next_line++; if (buf[bol] == COMMENT_LINE_CHAR || buf[bol] == '\n') { /* is this the first of the run of comments? */ if (!boc) boc = bol; /* otherwise, it is just continuing */ } else if (git__prefixcmp(buf + bol, "Conflicts:\n") == 0) { in_old_conflicts_block = 1; if (!boc) boc = bol; } else if (in_old_conflicts_block && buf[bol] == '\t') { ; /* a pathname in the conflicts block */ } else if (boc) { /* the previous was not trailing comment */ boc = 0; in_old_conflicts_block = 0; } bol = next_line - buf; } return boc ? len - boc : len - cutoff; } /* * Return the position of the start of the patch or the length of str if there * is no patch in the message. */ static size_t find_patch_start(const char *str) { const char *s; for (s = str; *s; s = next_line(s)) { if (git__prefixcmp(s, "---") == 0) return s - str; } return s - str; } /* * Return the position of the first trailer line or len if there are no * trailers. */ static size_t find_trailer_start(const char *buf, size_t len) { const char *s; size_t end_of_title, l; int only_spaces = 1; int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0; /* * Number of possible continuation lines encountered. This will be * reset to 0 if we encounter a trailer (since those lines are to be * considered continuations of that trailer), and added to * non_trailer_lines if we encounter a non-trailer (since those lines * are to be considered non-trailers). */ int possible_continuation_lines = 0; /* The first paragraph is the title and cannot be trailers */ for (s = buf; s < buf + len; s = next_line(s)) { if (s[0] == COMMENT_LINE_CHAR) continue; if (is_blank_line(s)) break; } end_of_title = s - buf; /* * Get the start of the trailers by looking starting from the end for a * blank line before a set of non-blank lines that (i) are all * trailers, or (ii) contains at least one Git-generated trailer and * consists of at least 25% trailers. */ l = len; while (last_line(&l, buf, l) && l >= end_of_title) { const char *bol = buf + l; const char *const *p; size_t separator_pos = 0; if (bol[0] == COMMENT_LINE_CHAR) { non_trailer_lines += possible_continuation_lines; possible_continuation_lines = 0; continue; } if (is_blank_line(bol)) { if (only_spaces) continue; non_trailer_lines += possible_continuation_lines; if (recognized_prefix && trailer_lines * 3 >= non_trailer_lines) return next_line(bol) - buf; else if (trailer_lines && !non_trailer_lines) return next_line(bol) - buf; return len; } only_spaces = 0; for (p = git_generated_prefixes; *p; p++) { if (git__prefixcmp(bol, *p) == 0) { trailer_lines++; possible_continuation_lines = 0; recognized_prefix = 1; goto continue_outer_loop; } } find_separator(&separator_pos, bol, TRAILER_SEPARATORS); if (separator_pos >= 1 && !isspace(bol[0])) { trailer_lines++; possible_continuation_lines = 0; if (recognized_prefix) continue; } else if (isspace(bol[0])) possible_continuation_lines++; else { non_trailer_lines++; non_trailer_lines += possible_continuation_lines; possible_continuation_lines = 0; } continue_outer_loop: ; } return len; } /* Return the position of the end of the trailers. */ static size_t find_trailer_end(const char *buf, size_t len) { return len - ignore_non_trailer(buf, len); } static char *extract_trailer_block(const char *message, size_t *len) { size_t patch_start = find_patch_start(message); size_t trailer_end = find_trailer_end(message, patch_start); size_t trailer_start = find_trailer_start(message, trailer_end); size_t trailer_len = trailer_end - trailer_start; char *buffer = git__malloc(trailer_len + 1); if (buffer == NULL) return NULL; memcpy(buffer, message + trailer_start, trailer_len); buffer[trailer_len] = 0; *len = trailer_len; return buffer; } enum trailer_state { S_START = 0, S_KEY = 1, S_KEY_WS = 2, S_SEP_WS = 3, S_VALUE = 4, S_VALUE_NL = 5, S_VALUE_END = 6, S_IGNORE = 7, }; #define NEXT(st) { state = (st); ptr++; continue; } #define GOTO(st) { state = (st); continue; } typedef git_array_t(git_message_trailer) git_array_trailer_t; int git_message_trailers(git_message_trailer_array *trailer_arr, const char *message) { enum trailer_state state = S_START; int rc = 0; char *ptr; char *key = NULL; char *value = NULL; git_array_trailer_t arr = GIT_ARRAY_INIT; size_t trailer_len; char *trailer = extract_trailer_block(message, &trailer_len); if (trailer == NULL) return -1; for (ptr = trailer;;) { switch (state) { case S_START: { if (*ptr == 0) { goto ret; } key = ptr; GOTO(S_KEY); } case S_KEY: { if (*ptr == 0) { goto ret; } if (isalnum(*ptr) || *ptr == '-') { /* legal key character */ NEXT(S_KEY); } if (*ptr == ' ' || *ptr == '\t') { /* optional whitespace before separator */ *ptr = 0; NEXT(S_KEY_WS); } if (strchr(TRAILER_SEPARATORS, *ptr)) { *ptr = 0; NEXT(S_SEP_WS); } /* illegal character */ GOTO(S_IGNORE); } case S_KEY_WS: { if (*ptr == 0) { goto ret; } if (*ptr == ' ' || *ptr == '\t') { NEXT(S_KEY_WS); } if (strchr(TRAILER_SEPARATORS, *ptr)) { NEXT(S_SEP_WS); } /* illegal character */ GOTO(S_IGNORE); } case S_SEP_WS: { if (*ptr == 0) { goto ret; } if (*ptr == ' ' || *ptr == '\t') { NEXT(S_SEP_WS); } value = ptr; NEXT(S_VALUE); } case S_VALUE: { if (*ptr == 0) { GOTO(S_VALUE_END); } if (*ptr == '\n') { NEXT(S_VALUE_NL); } NEXT(S_VALUE); } case S_VALUE_NL: { if (*ptr == ' ') { /* continuation; */ NEXT(S_VALUE); } ptr[-1] = 0; GOTO(S_VALUE_END); } case S_VALUE_END: { git_message_trailer *t = git_array_alloc(arr); t->key = key; t->value = value; key = NULL; value = NULL; GOTO(S_START); } case S_IGNORE: { if (*ptr == 0) { goto ret; } if (*ptr == '\n') { NEXT(S_START); } NEXT(S_IGNORE); } } } ret: trailer_arr->_trailer_block = trailer; trailer_arr->trailers = arr.ptr; trailer_arr->count = arr.size; return rc; } void git_message_trailer_array_free(git_message_trailer_array *arr) { git__free(arr->_trailer_block); git__free(arr->trailers); } git2r/src/libgit2/src/netops.h0000644000175000017500000000333214125111754016030 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_netops_h__ #define INCLUDE_netops_h__ #include "common.h" #include "posix.h" #include "stream.h" #include "net.h" #ifdef GIT_OPENSSL # include "streams/openssl.h" #endif typedef struct gitno_ssl { #ifdef GIT_OPENSSL SSL *ssl; #else size_t dummy; #endif } gitno_ssl; /* Represents a socket that may or may not be using SSL */ typedef struct gitno_socket { GIT_SOCKET socket; gitno_ssl ssl; } gitno_socket; typedef struct gitno_buffer { char *data; size_t len; size_t offset; int (*recv)(struct gitno_buffer *buffer); void *cb_data; } gitno_buffer; /* Flags to gitno_connect */ enum { /* Attempt to create an SSL connection. */ GITNO_CONNECT_SSL = 1, }; /** * Check if the name in a cert matches the wanted hostname * * Check if a pattern from a certificate matches the hostname we * wanted to connect to according to RFC2818 rules (which specifies * HTTP over TLS). Mainly, an asterisk matches anything, but is * limited to a single url component. * * Note that this does not set an error message. It expects the user * to provide the message for the user. */ int gitno__match_host(const char *pattern, const char *host); void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len); void gitno_buffer_setup_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data); int gitno_recv(gitno_buffer *buf); int gitno_consume(gitno_buffer *buf, const char *ptr); void gitno_consume_n(gitno_buffer *buf, size_t cons); #endif git2r/src/libgit2/src/remote.h0000644000175000017500000000306114125111754016012 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_remote_h__ #define INCLUDE_remote_h__ #include "common.h" #include "git2/remote.h" #include "git2/transport.h" #include "git2/sys/transport.h" #include "refspec.h" #include "vector.h" #include "net.h" #define GIT_REMOTE_ORIGIN "origin" struct git_remote { char *name; char *url; char *pushurl; git_vector refs; git_vector refspecs; git_vector active_refspecs; git_vector passive_refspecs; git_transport *transport; git_repository *repo; git_push *push; git_indexer_progress stats; unsigned int need_pack; git_remote_autotag_option_t download_tags; int prune_refs; int passed_refspecs; }; typedef struct git_remote_connection_opts { const git_strarray *custom_headers; const git_proxy_options *proxy; } git_remote_connection_opts; #define GIT_REMOTE_CONNECTION_OPTIONS_INIT { NULL, NULL } int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn); int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks); int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url); git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname); git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname); #endif git2r/src/libgit2/src/vector.h0000644000175000017500000001000614125111754016016 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_vector_h__ #define INCLUDE_vector_h__ #include "common.h" typedef int (*git_vector_cmp)(const void *, const void *); enum { GIT_VECTOR_SORTED = (1u << 0), GIT_VECTOR_FLAG_MAX = (1u << 1), }; typedef struct git_vector { size_t _alloc_size; git_vector_cmp _cmp; void **contents; size_t length; uint32_t flags; } git_vector; #define GIT_VECTOR_INIT {0} GIT_WARN_UNUSED_RESULT int git_vector_init( git_vector *v, size_t initial_size, git_vector_cmp cmp); void git_vector_free(git_vector *v); void git_vector_free_deep(git_vector *v); /* free each entry and self */ void git_vector_clear(git_vector *v); GIT_WARN_UNUSED_RESULT int git_vector_dup( git_vector *v, const git_vector *src, git_vector_cmp cmp); void git_vector_swap(git_vector *a, git_vector *b); int git_vector_size_hint(git_vector *v, size_t size_hint); void **git_vector_detach(size_t *size, size_t *asize, git_vector *v); void git_vector_sort(git_vector *v); /** Linear search for matching entry using internal comparison function */ int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry); /** Linear search for matching entry using explicit comparison function */ int git_vector_search2(size_t *at_pos, const git_vector *v, git_vector_cmp cmp, const void *key); /** * Binary search for matching entry using explicit comparison function that * returns position where item would go if not found. */ int git_vector_bsearch2( size_t *at_pos, git_vector *v, git_vector_cmp cmp, const void *key); /** Binary search for matching entry using internal comparison function */ GIT_INLINE(int) git_vector_bsearch(size_t *at_pos, git_vector *v, const void *key) { return git_vector_bsearch2(at_pos, v, v->_cmp, key); } GIT_INLINE(void *) git_vector_get(const git_vector *v, size_t position) { return (position < v->length) ? v->contents[position] : NULL; } #define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL) GIT_INLINE(size_t) git_vector_length(const git_vector *v) { return v->length; } GIT_INLINE(void *) git_vector_last(const git_vector *v) { return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL; } #define git_vector_foreach(v, iter, elem) \ for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ ) #define git_vector_rforeach(v, iter, elem) \ for ((iter) = (v)->length - 1; (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- ) int git_vector_insert(git_vector *v, void *element); int git_vector_insert_sorted(git_vector *v, void *element, int (*on_dup)(void **old, void *new)); int git_vector_remove(git_vector *v, size_t idx); void git_vector_pop(git_vector *v); void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *)); void git_vector_remove_matching( git_vector *v, int (*match)(const git_vector *v, size_t idx, void *payload), void *payload); int git_vector_resize_to(git_vector *v, size_t new_length); int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len); int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len); int git_vector_set(void **old, git_vector *v, size_t position, void *value); /** Check if vector is sorted */ #define git_vector_is_sorted(V) (((V)->flags & GIT_VECTOR_SORTED) != 0) /** Directly set sorted state of vector */ #define git_vector_set_sorted(V,S) do { \ (V)->flags = (S) ? ((V)->flags | GIT_VECTOR_SORTED) : \ ((V)->flags & ~GIT_VECTOR_SORTED); } while (0) /** Set the comparison function used for sorting the vector */ GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp) { if (cmp != v->_cmp) { v->_cmp = cmp; git_vector_set_sorted(v, 0); } } /* Just use this in tests, not for realz. returns -1 if not sorted */ int git_vector_verify_sorted(const git_vector *v); /** * Reverse the vector in-place. */ void git_vector_reverse(git_vector *v); #endif git2r/src/libgit2/src/buffer.c0000644000175000017500000007447214125111754016001 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "buffer.h" #include "posix.h" #include "git2/buffer.h" #include /* Used as default value for git_buf->ptr so that people can always * assume ptr is non-NULL and zero terminated even for new git_bufs. */ char git_buf__initbuf[1]; char git_buf__oom[1]; #define ENSURE_SIZE(b, d) \ if ((b)->ptr == git_buf__oom || \ ((d) > (b)->asize && git_buf_grow((b), (d)) < 0))\ return -1; int git_buf_init(git_buf *buf, size_t initial_size) { buf->asize = 0; buf->size = 0; buf->ptr = git_buf__initbuf; ENSURE_SIZE(buf, initial_size); return 0; } int git_buf_try_grow( git_buf *buf, size_t target_size, bool mark_oom) { char *new_ptr; size_t new_size; if (buf->ptr == git_buf__oom) return -1; if (buf->asize == 0 && buf->size != 0) { git_error_set(GIT_ERROR_INVALID, "cannot grow a borrowed buffer"); return GIT_EINVALID; } if (!target_size) target_size = buf->size; if (target_size <= buf->asize) return 0; if (buf->asize == 0) { new_size = target_size; new_ptr = NULL; } else { new_size = buf->asize; /* * Grow the allocated buffer by 1.5 to allow * re-use of memory holes resulting from the * realloc. If this is still too small, then just * use the target size. */ if ((new_size = (new_size << 1) - (new_size >> 1)) < target_size) new_size = target_size; new_ptr = buf->ptr; } /* round allocation up to multiple of 8 */ new_size = (new_size + 7) & ~7; if (new_size < buf->size) { if (mark_oom) { if (buf->ptr && buf->ptr != git_buf__initbuf) git__free(buf->ptr); buf->ptr = git_buf__oom; } git_error_set_oom(); return -1; } new_ptr = git__realloc(new_ptr, new_size); if (!new_ptr) { if (mark_oom) { if (buf->ptr && (buf->ptr != git_buf__initbuf)) git__free(buf->ptr); buf->ptr = git_buf__oom; } return -1; } buf->asize = new_size; buf->ptr = new_ptr; /* truncate the existing buffer size if necessary */ if (buf->size >= buf->asize) buf->size = buf->asize - 1; buf->ptr[buf->size] = '\0'; return 0; } int git_buf_grow(git_buf *buffer, size_t target_size) { return git_buf_try_grow(buffer, target_size, true); } int git_buf_grow_by(git_buf *buffer, size_t additional_size) { size_t newsize; if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) { buffer->ptr = git_buf__oom; return -1; } return git_buf_try_grow(buffer, newsize, true); } void git_buf_dispose(git_buf *buf) { if (!buf) return; if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom) git__free(buf->ptr); git_buf_init(buf, 0); } #ifndef GIT_DEPRECATE_HARD void git_buf_free(git_buf *buf) { git_buf_dispose(buf); } #endif int git_buf_sanitize(git_buf *buf) { if (buf->ptr == NULL) { GIT_ASSERT_ARG(buf->size == 0 && buf->asize == 0); buf->ptr = git_buf__initbuf; } else if (buf->asize > buf->size) { buf->ptr[buf->size] = '\0'; } return 0; } void git_buf_clear(git_buf *buf) { buf->size = 0; if (!buf->ptr) { buf->ptr = git_buf__initbuf; buf->asize = 0; } if (buf->asize > 0) buf->ptr[0] = '\0'; } int git_buf_set(git_buf *buf, const void *data, size_t len) { size_t alloclen; if (len == 0 || data == NULL) { git_buf_clear(buf); } else { if (data != buf->ptr) { GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1); ENSURE_SIZE(buf, alloclen); memmove(buf->ptr, data, len); } buf->size = len; if (buf->asize > buf->size) buf->ptr[buf->size] = '\0'; } return 0; } int git_buf_sets(git_buf *buf, const char *string) { return git_buf_set(buf, string, string ? strlen(string) : 0); } int git_buf_putc(git_buf *buf, char c) { size_t new_size; GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, 2); ENSURE_SIZE(buf, new_size); buf->ptr[buf->size++] = c; buf->ptr[buf->size] = '\0'; return 0; } int git_buf_putcn(git_buf *buf, char c, size_t len) { size_t new_size; GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1); ENSURE_SIZE(buf, new_size); memset(buf->ptr + buf->size, c, len); buf->size += len; buf->ptr[buf->size] = '\0'; return 0; } int git_buf_put(git_buf *buf, const char *data, size_t len) { if (len) { size_t new_size; GIT_ASSERT_ARG(data); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1); ENSURE_SIZE(buf, new_size); memmove(buf->ptr + buf->size, data, len); buf->size += len; buf->ptr[buf->size] = '\0'; } return 0; } int git_buf_puts(git_buf *buf, const char *string) { GIT_ASSERT_ARG(string); return git_buf_put(buf, string, strlen(string)); } static const char base64_encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int git_buf_encode_base64(git_buf *buf, const char *data, size_t len) { size_t extra = len % 3; uint8_t *write, a, b, c; const uint8_t *read = (const uint8_t *)data; size_t blocks = (len / 3) + !!extra, alloclen; GIT_ERROR_CHECK_ALLOC_ADD(&blocks, blocks, 1); GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size); ENSURE_SIZE(buf, alloclen); write = (uint8_t *)&buf->ptr[buf->size]; /* convert each run of 3 bytes into 4 output bytes */ for (len -= extra; len > 0; len -= 3) { a = *read++; b = *read++; c = *read++; *write++ = base64_encode[a >> 2]; *write++ = base64_encode[(a & 0x03) << 4 | b >> 4]; *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6]; *write++ = base64_encode[c & 0x3f]; } if (extra > 0) { a = *read++; b = (extra > 1) ? *read++ : 0; *write++ = base64_encode[a >> 2]; *write++ = base64_encode[(a & 0x03) << 4 | b >> 4]; *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '='; *write++ = '='; } buf->size = ((char *)write) - buf->ptr; buf->ptr[buf->size] = '\0'; return 0; } /* The inverse of base64_encode */ static const int8_t base64_decode[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len) { size_t i; int8_t a, b, c, d; size_t orig_size = buf->size, new_size; if (len % 4) { git_error_set(GIT_ERROR_INVALID, "invalid base64 input"); return -1; } GIT_ASSERT_ARG(len % 4 == 0); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1); ENSURE_SIZE(buf, new_size); for (i = 0; i < len; i += 4) { if ((a = base64_decode[(unsigned char)base64[i]]) < 0 || (b = base64_decode[(unsigned char)base64[i+1]]) < 0 || (c = base64_decode[(unsigned char)base64[i+2]]) < 0 || (d = base64_decode[(unsigned char)base64[i+3]]) < 0) { buf->size = orig_size; buf->ptr[buf->size] = '\0'; git_error_set(GIT_ERROR_INVALID, "invalid base64 input"); return -1; } buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4); buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f); } buf->ptr[buf->size] = '\0'; return 0; } static const char base85_encode[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; int git_buf_encode_base85(git_buf *buf, const char *data, size_t len) { size_t blocks = (len / 4) + !!(len % 4), alloclen; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); ENSURE_SIZE(buf, alloclen); while (len) { uint32_t acc = 0; char b85[5]; int i; for (i = 24; i >= 0; i -= 8) { uint8_t ch = *data++; acc |= (uint32_t)ch << i; if (--len == 0) break; } for (i = 4; i >= 0; i--) { int val = acc % 85; acc /= 85; b85[i] = base85_encode[val]; } for (i = 0; i < 5; i++) buf->ptr[buf->size++] = b85[i]; } buf->ptr[buf->size] = '\0'; return 0; } /* The inverse of base85_encode */ static const int8_t base85_decode[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 73, 74, 75, 76, 77, 78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80, 81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; int git_buf_decode_base85( git_buf *buf, const char *base85, size_t base85_len, size_t output_len) { size_t orig_size = buf->size, new_size; if (base85_len % 5 || output_len > base85_len * 4 / 5) { git_error_set(GIT_ERROR_INVALID, "invalid base85 input"); return -1; } GIT_ERROR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1); ENSURE_SIZE(buf, new_size); while (output_len) { unsigned acc = 0; int de, cnt = 4; unsigned char ch; do { ch = *base85++; de = base85_decode[ch]; if (--de < 0) goto on_error; acc = acc * 85 + de; } while (--cnt); ch = *base85++; de = base85_decode[ch]; if (--de < 0) goto on_error; /* Detect overflow. */ if (0xffffffff / 85 < acc || 0xffffffff - de < (acc *= 85)) goto on_error; acc += de; cnt = (output_len < 4) ? (int)output_len : 4; output_len -= cnt; do { acc = (acc << 8) | (acc >> 24); buf->ptr[buf->size++] = acc; } while (--cnt); } buf->ptr[buf->size] = 0; return 0; on_error: buf->size = orig_size; buf->ptr[buf->size] = '\0'; git_error_set(GIT_ERROR_INVALID, "invalid base85 input"); return -1; } #define HEX_DECODE(c) ((c | 32) % 39 - 9) int git_buf_decode_percent( git_buf *buf, const char *str, size_t str_len) { size_t str_pos, new_size; GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, str_len); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1); ENSURE_SIZE(buf, new_size); for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) { if (str[str_pos] == '%' && str_len > str_pos + 2 && isxdigit(str[str_pos + 1]) && isxdigit(str[str_pos + 2])) { buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) + HEX_DECODE(str[str_pos + 2]); str_pos += 2; } else { buf->ptr[buf->size] = str[str_pos]; } } buf->ptr[buf->size] = '\0'; return 0; } int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) { size_t expected_size, new_size; int len; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2); GIT_ERROR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size); ENSURE_SIZE(buf, expected_size); while (1) { va_list args; va_copy(args, ap); len = p_vsnprintf( buf->ptr + buf->size, buf->asize - buf->size, format, args ); va_end(args); if (len < 0) { git__free(buf->ptr); buf->ptr = git_buf__oom; return -1; } if ((size_t)len + 1 <= buf->asize - buf->size) { buf->size += len; break; } GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len); GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1); ENSURE_SIZE(buf, new_size); } return 0; } int git_buf_printf(git_buf *buf, const char *format, ...) { int r; va_list ap; va_start(ap, format); r = git_buf_vprintf(buf, format, ap); va_end(ap); return r; } int git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf) { size_t copylen; GIT_ASSERT_ARG(data); GIT_ASSERT_ARG(datasize); GIT_ASSERT_ARG(buf); data[0] = '\0'; if (buf->size == 0 || buf->asize <= 0) return 0; copylen = buf->size; if (copylen > datasize - 1) copylen = datasize - 1; memmove(data, buf->ptr, copylen); data[copylen] = '\0'; return 0; } void git_buf_consume_bytes(git_buf *buf, size_t len) { git_buf_consume(buf, buf->ptr + len); } void git_buf_consume(git_buf *buf, const char *end) { if (end > buf->ptr && end <= buf->ptr + buf->size) { size_t consumed = end - buf->ptr; memmove(buf->ptr, end, buf->size - consumed); buf->size -= consumed; buf->ptr[buf->size] = '\0'; } } void git_buf_truncate(git_buf *buf, size_t len) { if (len >= buf->size) return; buf->size = len; if (buf->size < buf->asize) buf->ptr[buf->size] = '\0'; } void git_buf_shorten(git_buf *buf, size_t amount) { if (buf->size > amount) git_buf_truncate(buf, buf->size - amount); else git_buf_clear(buf); } void git_buf_truncate_at_char(git_buf *buf, char separator) { ssize_t idx = git_buf_find(buf, separator); if (idx >= 0) git_buf_truncate(buf, (size_t)idx); } void git_buf_rtruncate_at_char(git_buf *buf, char separator) { ssize_t idx = git_buf_rfind_next(buf, separator); git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx); } void git_buf_swap(git_buf *buf_a, git_buf *buf_b) { git_buf t = *buf_a; *buf_a = *buf_b; *buf_b = t; } char *git_buf_detach(git_buf *buf) { char *data = buf->ptr; if (buf->asize == 0 || buf->ptr == git_buf__oom) return NULL; git_buf_init(buf, 0); return data; } int git_buf_attach(git_buf *buf, char *ptr, size_t asize) { git_buf_dispose(buf); if (ptr) { buf->ptr = ptr; buf->size = strlen(ptr); if (asize) buf->asize = (asize < buf->size) ? buf->size + 1 : asize; else /* pass 0 to fall back on strlen + 1 */ buf->asize = buf->size + 1; } ENSURE_SIZE(buf, asize); return 0; } void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size) { if (git_buf_is_allocated(buf)) git_buf_dispose(buf); if (!size) { git_buf_init(buf, 0); } else { buf->ptr = (char *)ptr; buf->asize = 0; buf->size = size; } } int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) { va_list ap; int i; size_t total_size = 0, original_size = buf->size; char *out, *original = buf->ptr; if (buf->size > 0 && buf->ptr[buf->size - 1] != separator) ++total_size; /* space for initial separator */ /* Make two passes to avoid multiple reallocation */ va_start(ap, nbuf); for (i = 0; i < nbuf; ++i) { const char *segment; size_t segment_len; segment = va_arg(ap, const char *); if (!segment) continue; segment_len = strlen(segment); GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len); if (segment_len == 0 || segment[segment_len - 1] != separator) GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1); } va_end(ap); /* expand buffer if needed */ if (total_size == 0) return 0; GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1); if (git_buf_grow_by(buf, total_size) < 0) return -1; out = buf->ptr + buf->size; /* append separator to existing buf if needed */ if (buf->size > 0 && out[-1] != separator) *out++ = separator; va_start(ap, nbuf); for (i = 0; i < nbuf; ++i) { const char *segment; size_t segment_len; segment = va_arg(ap, const char *); if (!segment) continue; /* deal with join that references buffer's original content */ if (segment >= original && segment < original + original_size) { size_t offset = (segment - original); segment = buf->ptr + offset; segment_len = original_size - offset; } else { segment_len = strlen(segment); } /* skip leading separators */ if (out > buf->ptr && out[-1] == separator) while (segment_len > 0 && *segment == separator) { segment++; segment_len--; } /* copy over next buffer */ if (segment_len > 0) { memmove(out, segment, segment_len); out += segment_len; } /* append trailing separator (except for last item) */ if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator) *out++ = separator; } va_end(ap); /* set size based on num characters actually written */ buf->size = out - buf->ptr; buf->ptr[buf->size] = '\0'; return 0; } int git_buf_join( git_buf *buf, char separator, const char *str_a, const char *str_b) { size_t strlen_a = str_a ? strlen(str_a) : 0; size_t strlen_b = strlen(str_b); size_t alloc_len; int need_sep = 0; ssize_t offset_a = -1; /* not safe to have str_b point internally to the buffer */ if (buf->size) GIT_ASSERT_ARG(str_b < buf->ptr || str_b >= buf->ptr + buf->size); /* figure out if we need to insert a separator */ if (separator && strlen_a) { while (*str_b == separator) { str_b++; strlen_b--; } if (str_a[strlen_a - 1] != separator) need_sep = 1; } /* str_a could be part of the buffer */ if (buf->size && str_a >= buf->ptr && str_a < buf->ptr + buf->size) offset_a = str_a - buf->ptr; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1); ENSURE_SIZE(buf, alloc_len); /* fix up internal pointers */ if (offset_a >= 0) str_a = buf->ptr + offset_a; /* do the actual copying */ if (offset_a != 0 && str_a) memmove(buf->ptr, str_a, strlen_a); if (need_sep) buf->ptr[strlen_a] = separator; memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b); buf->size = strlen_a + strlen_b + need_sep; buf->ptr[buf->size] = '\0'; return 0; } int git_buf_join3( git_buf *buf, char separator, const char *str_a, const char *str_b, const char *str_c) { size_t len_a = strlen(str_a), len_b = strlen(str_b), len_c = strlen(str_c), len_total; int sep_a = 0, sep_b = 0; char *tgt; /* for this function, disallow pointers into the existing buffer */ GIT_ASSERT(str_a < buf->ptr || str_a >= buf->ptr + buf->size); GIT_ASSERT(str_b < buf->ptr || str_b >= buf->ptr + buf->size); GIT_ASSERT(str_c < buf->ptr || str_c >= buf->ptr + buf->size); if (separator) { if (len_a > 0) { while (*str_b == separator) { str_b++; len_b--; } sep_a = (str_a[len_a - 1] != separator); } if (len_a > 0 || len_b > 0) while (*str_c == separator) { str_c++; len_c--; } if (len_b > 0) sep_b = (str_b[len_b - 1] != separator); } GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a); GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_b); GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b); GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_c); GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, 1); ENSURE_SIZE(buf, len_total); tgt = buf->ptr; if (len_a) { memcpy(tgt, str_a, len_a); tgt += len_a; } if (sep_a) *tgt++ = separator; if (len_b) { memcpy(tgt, str_b, len_b); tgt += len_b; } if (sep_b) *tgt++ = separator; if (len_c) memcpy(tgt, str_c, len_c); buf->size = len_a + sep_a + len_b + sep_b + len_c; buf->ptr[buf->size] = '\0'; return 0; } void git_buf_rtrim(git_buf *buf) { while (buf->size > 0) { if (!git__isspace(buf->ptr[buf->size - 1])) break; buf->size--; } if (buf->asize > buf->size) buf->ptr[buf->size] = '\0'; } int git_buf_cmp(const git_buf *a, const git_buf *b) { int result = memcmp(a->ptr, b->ptr, min(a->size, b->size)); return (result != 0) ? result : (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0; } int git_buf_splice( git_buf *buf, size_t where, size_t nb_to_remove, const char *data, size_t nb_to_insert) { char *splice_loc; size_t new_size, alloc_size; GIT_ASSERT(buf); GIT_ASSERT(where <= buf->size); GIT_ASSERT(nb_to_remove <= buf->size - where); splice_loc = buf->ptr + where; /* Ported from git.git * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176 */ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1); ENSURE_SIZE(buf, alloc_size); memmove(splice_loc + nb_to_insert, splice_loc + nb_to_remove, buf->size - where - nb_to_remove); memcpy(splice_loc, data, nb_to_insert); buf->size = new_size; buf->ptr[buf->size] = '\0'; return 0; } /* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */ int git_buf_quote(git_buf *buf) { const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' }; git_buf quoted = GIT_BUF_INIT; size_t i = 0; bool quote = false; int error = 0; /* walk to the first char that needs quoting */ if (buf->size && buf->ptr[0] == '!') quote = true; for (i = 0; !quote && i < buf->size; i++) { if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' || buf->ptr[i] < ' ' || buf->ptr[i] > '~') { quote = true; break; } } if (!quote) goto done; git_buf_putc("ed, '"'); git_buf_put("ed, buf->ptr, i); for (; i < buf->size; i++) { /* whitespace - use the map above, which is ordered by ascii value */ if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') { git_buf_putc("ed, '\\'); git_buf_putc("ed, whitespace[buf->ptr[i] - '\a']); } /* double quote and backslash must be escaped */ else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') { git_buf_putc("ed, '\\'); git_buf_putc("ed, buf->ptr[i]); } /* escape anything unprintable as octal */ else if (buf->ptr[i] != ' ' && (buf->ptr[i] < '!' || buf->ptr[i] > '~')) { git_buf_printf("ed, "\\%03o", (unsigned char)buf->ptr[i]); } /* yay, printable! */ else { git_buf_putc("ed, buf->ptr[i]); } } git_buf_putc("ed, '"'); if (git_buf_oom("ed)) { error = -1; goto done; } git_buf_swap("ed, buf); done: git_buf_dispose("ed); return error; } /* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */ int git_buf_unquote(git_buf *buf) { size_t i, j; char ch; git_buf_rtrim(buf); if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"') goto invalid; for (i = 0, j = 1; j < buf->size-1; i++, j++) { ch = buf->ptr[j]; if (ch == '\\') { if (j == buf->size-2) goto invalid; ch = buf->ptr[++j]; switch (ch) { /* \" or \\ simply copy the char in */ case '"': case '\\': break; /* add the appropriate escaped char */ case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case 'v': ch = '\v'; break; /* \xyz digits convert to the char*/ case '0': case '1': case '2': case '3': if (j == buf->size-3) { git_error_set(GIT_ERROR_INVALID, "truncated quoted character \\%c", ch); return -1; } if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' || buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') { git_error_set(GIT_ERROR_INVALID, "truncated quoted character \\%c%c%c", buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]); return -1; } ch = ((buf->ptr[j] - '0') << 6) | ((buf->ptr[j+1] - '0') << 3) | (buf->ptr[j+2] - '0'); j += 2; break; default: git_error_set(GIT_ERROR_INVALID, "invalid quoted character \\%c", ch); return -1; } } buf->ptr[i] = ch; } buf->ptr[i] = '\0'; buf->size = i; return 0; invalid: git_error_set(GIT_ERROR_INVALID, "invalid quoted line"); return -1; } int git_buf_puts_escaped( git_buf *buf, const char *string, const char *esc_chars, const char *esc_with) { const char *scan; size_t total = 0, esc_len = strlen(esc_with), count, alloclen; if (!string) return 0; for (scan = string; *scan; ) { /* count run of non-escaped characters */ count = strcspn(scan, esc_chars); total += count; scan += count; /* count run of escaped characters */ count = strspn(scan, esc_chars); total += count * (esc_len + 1); scan += count; } GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, total, 1); if (git_buf_grow_by(buf, alloclen) < 0) return -1; for (scan = string; *scan; ) { count = strcspn(scan, esc_chars); memmove(buf->ptr + buf->size, scan, count); scan += count; buf->size += count; for (count = strspn(scan, esc_chars); count > 0; --count) { /* copy escape sequence */ memmove(buf->ptr + buf->size, esc_with, esc_len); buf->size += esc_len; /* copy character to be escaped */ buf->ptr[buf->size] = *scan; buf->size++; scan++; } } buf->ptr[buf->size] = '\0'; return 0; } void git_buf_unescape(git_buf *buf) { buf->size = git__unescape(buf->ptr); } int git_buf_crlf_to_lf(git_buf *tgt, const git_buf *src) { const char *scan = src->ptr; const char *scan_end = src->ptr + src->size; const char *next = memchr(scan, '\r', src->size); size_t new_size; char *out; GIT_ASSERT(tgt != src); if (!next) return git_buf_set(tgt, src->ptr, src->size); /* reduce reallocs while in the loop */ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, src->size, 1); if (git_buf_grow(tgt, new_size) < 0) return -1; out = tgt->ptr; tgt->size = 0; /* Find the next \r and copy whole chunk up to there to tgt */ for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) { if (next > scan) { size_t copylen = (size_t)(next - scan); memcpy(out, scan, copylen); out += copylen; } /* Do not drop \r unless it is followed by \n */ if (next + 1 == scan_end || next[1] != '\n') *out++ = '\r'; } /* Copy remaining input into dest */ if (scan < scan_end) { size_t remaining = (size_t)(scan_end - scan); memcpy(out, scan, remaining); out += remaining; } tgt->size = (size_t)(out - tgt->ptr); tgt->ptr[tgt->size] = '\0'; return 0; } int git_buf_lf_to_crlf(git_buf *tgt, const git_buf *src) { const char *start = src->ptr; const char *end = start + src->size; const char *scan = start; const char *next = memchr(scan, '\n', src->size); size_t alloclen; GIT_ASSERT(tgt != src); if (!next) return git_buf_set(tgt, src->ptr, src->size); /* attempt to reduce reallocs while in the loop */ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); if (git_buf_grow(tgt, alloclen) < 0) return -1; tgt->size = 0; for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) { size_t copylen = next - scan; /* if we find mixed line endings, carry on */ if (copylen && next[-1] == '\r') copylen--; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, copylen, 3); if (git_buf_grow_by(tgt, alloclen) < 0) return -1; if (copylen) { memcpy(tgt->ptr + tgt->size, scan, copylen); tgt->size += copylen; } tgt->ptr[tgt->size++] = '\r'; tgt->ptr[tgt->size++] = '\n'; } tgt->ptr[tgt->size] = '\0'; return git_buf_put(tgt, scan, end - scan); } int git_buf_common_prefix(git_buf *buf, char *const *const strings, size_t count) { size_t i; const char *str, *pfx; git_buf_clear(buf); if (!strings || !count) return 0; /* initialize common prefix to first string */ if (git_buf_sets(buf, strings[0]) < 0) return -1; /* go through the rest of the strings, truncating to shared prefix */ for (i = 1; i < count; ++i) { for (str = strings[i], pfx = buf->ptr; *str && *str == *pfx; str++, pfx++) /* scanning */; git_buf_truncate(buf, pfx - buf->ptr); if (!buf->size) break; } return 0; } int git_buf_is_binary(const git_buf *buf) { const char *scan = buf->ptr, *end = buf->ptr + buf->size; git_buf_bom_t bom; int printable = 0, nonprintable = 0; scan += git_buf_detect_bom(&bom, buf); if (bom > GIT_BUF_BOM_UTF8) return 1; while (scan < end) { unsigned char c = *scan++; /* Printable characters are those above SPACE (0x1F) excluding DEL, * and including BS, ESC and FF. */ if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014') printable++; else if (c == '\0') return true; else if (!git__isspace(c)) nonprintable++; } return ((printable >> 7) < nonprintable); } int git_buf_contains_nul(const git_buf *buf) { return (memchr(buf->ptr, '\0', buf->size) != NULL); } int git_buf_detect_bom(git_buf_bom_t *bom, const git_buf *buf) { const char *ptr; size_t len; *bom = GIT_BUF_BOM_NONE; /* need at least 2 bytes to look for any BOM */ if (buf->size < 2) return 0; ptr = buf->ptr; len = buf->size; switch (*ptr++) { case 0: if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') { *bom = GIT_BUF_BOM_UTF32_BE; return 4; } break; case '\xEF': if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') { *bom = GIT_BUF_BOM_UTF8; return 3; } break; case '\xFE': if (*ptr == '\xFF') { *bom = GIT_BUF_BOM_UTF16_BE; return 2; } break; case '\xFF': if (*ptr != '\xFE') break; if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) { *bom = GIT_BUF_BOM_UTF32_LE; return 4; } else { *bom = GIT_BUF_BOM_UTF16_LE; return 2; } break; default: break; } return 0; } bool git_buf_gather_text_stats( git_buf_text_stats *stats, const git_buf *buf, bool skip_bom) { const char *scan = buf->ptr, *end = buf->ptr + buf->size; int skip; memset(stats, 0, sizeof(*stats)); /* BOM detection */ skip = git_buf_detect_bom(&stats->bom, buf); if (skip_bom) scan += skip; /* Ignore EOF character */ if (buf->size > 0 && end[-1] == '\032') end--; /* Counting loop */ while (scan < end) { unsigned char c = *scan++; if (c > 0x1F && c != 0x7F) stats->printable++; else switch (c) { case '\0': stats->nul++; stats->nonprintable++; break; case '\n': stats->lf++; break; case '\r': stats->cr++; if (scan < end && *scan == '\n') stats->crlf++; break; case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/ stats->printable++; break; default: stats->nonprintable++; break; } } /* Treat files with a bare CR as binary */ return (stats->cr != stats->crlf || stats->nul > 0 || ((stats->printable >> 7) < stats->nonprintable)); } git2r/src/libgit2/src/push.c0000644000175000017500000003107114125111754015473 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "push.h" #include "git2.h" #include "pack.h" #include "pack-objects.h" #include "remote.h" #include "vector.h" #include "tree.h" static int push_spec_rref_cmp(const void *a, const void *b) { const push_spec *push_spec_a = a, *push_spec_b = b; return strcmp(push_spec_a->refspec.dst, push_spec_b->refspec.dst); } static int push_status_ref_cmp(const void *a, const void *b) { const push_status *push_status_a = a, *push_status_b = b; return strcmp(push_status_a->ref, push_status_b->ref); } int git_push_new(git_push **out, git_remote *remote) { git_push *p; *out = NULL; p = git__calloc(1, sizeof(*p)); GIT_ERROR_CHECK_ALLOC(p); p->repo = remote->repo; p->remote = remote; p->report_status = 1; p->pb_parallelism = 1; if (git_vector_init(&p->specs, 0, push_spec_rref_cmp) < 0) { git__free(p); return -1; } if (git_vector_init(&p->status, 0, push_status_ref_cmp) < 0) { git_vector_free(&p->specs); git__free(p); return -1; } if (git_vector_init(&p->updates, 0, NULL) < 0) { git_vector_free(&p->status); git_vector_free(&p->specs); git__free(p); return -1; } *out = p; return 0; } int git_push_set_options(git_push *push, const git_push_options *opts) { if (!push || !opts) return -1; GIT_ERROR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options"); push->pb_parallelism = opts->pb_parallelism; push->connection.custom_headers = &opts->custom_headers; push->connection.proxy = &opts->proxy_opts; return 0; } static void free_refspec(push_spec *spec) { if (spec == NULL) return; git_refspec__dispose(&spec->refspec); git__free(spec); } static int check_rref(char *ref) { if (git__prefixcmp(ref, "refs/")) { git_error_set(GIT_ERROR_INVALID, "not a valid reference '%s'", ref); return -1; } return 0; } static int check_lref(git_push *push, char *ref) { /* lref must be resolvable to an existing object */ git_object *obj; int error = git_revparse_single(&obj, push->repo, ref); git_object_free(obj); if (!error) return 0; if (error == GIT_ENOTFOUND) git_error_set(GIT_ERROR_REFERENCE, "src refspec '%s' does not match any existing object", ref); else git_error_set(GIT_ERROR_INVALID, "not a valid reference '%s'", ref); return -1; } static int parse_refspec(git_push *push, push_spec **spec, const char *str) { push_spec *s; *spec = NULL; s = git__calloc(1, sizeof(*s)); GIT_ERROR_CHECK_ALLOC(s); if (git_refspec__parse(&s->refspec, str, false) < 0) { git_error_set(GIT_ERROR_INVALID, "invalid refspec %s", str); goto on_error; } if (s->refspec.src && s->refspec.src[0] != '\0' && check_lref(push, s->refspec.src) < 0) { goto on_error; } if (check_rref(s->refspec.dst) < 0) goto on_error; *spec = s; return 0; on_error: free_refspec(s); return -1; } int git_push_add_refspec(git_push *push, const char *refspec) { push_spec *spec; if (parse_refspec(push, &spec, refspec) < 0 || git_vector_insert(&push->specs, spec) < 0) return -1; return 0; } int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks) { git_buf remote_ref_name = GIT_BUF_INIT; size_t i, j; git_refspec *fetch_spec; push_spec *push_spec = NULL; git_reference *remote_ref; push_status *status; int error = 0; git_vector_foreach(&push->status, i, status) { int fire_callback = 1; /* Skip unsuccessful updates which have non-empty messages */ if (status->msg) continue; /* Find the corresponding remote ref */ fetch_spec = git_remote__matching_refspec(push->remote, status->ref); if (!fetch_spec) continue; /* Clear the buffer which can be dirty from previous iteration */ git_buf_clear(&remote_ref_name); if ((error = git_refspec_transform(&remote_ref_name, fetch_spec, status->ref)) < 0) goto on_error; /* Find matching push ref spec */ git_vector_foreach(&push->specs, j, push_spec) { if (!strcmp(push_spec->refspec.dst, status->ref)) break; } /* Could not find the corresponding push ref spec for this push update */ if (j == push->specs.length) continue; /* Update the remote ref */ if (git_oid_is_zero(&push_spec->loid)) { error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name)); if (error >= 0) { error = git_reference_delete(remote_ref); git_reference_free(remote_ref); } } else { error = git_reference_create(NULL, push->remote->repo, git_buf_cstr(&remote_ref_name), &push_spec->loid, 1, "update by push"); } if (error < 0) { if (error != GIT_ENOTFOUND) goto on_error; git_error_clear(); fire_callback = 0; } if (fire_callback && callbacks && callbacks->update_tips) { error = callbacks->update_tips(git_buf_cstr(&remote_ref_name), &push_spec->roid, &push_spec->loid, callbacks->payload); if (error < 0) goto on_error; } } error = 0; on_error: git_buf_dispose(&remote_ref_name); return error; } /** * Insert all tags until we find a non-tag object, which is returned * in `out`. */ static int enqueue_tag(git_object **out, git_push *push, git_oid *id) { git_object *obj = NULL, *target = NULL; int error; if ((error = git_object_lookup(&obj, push->repo, id, GIT_OBJECT_TAG)) < 0) return error; while (git_object_type(obj) == GIT_OBJECT_TAG) { if ((error = git_packbuilder_insert(push->pb, git_object_id(obj), NULL)) < 0) break; if ((error = git_tag_target(&target, (git_tag *) obj)) < 0) break; git_object_free(obj); obj = target; } if (error < 0) git_object_free(obj); else *out = obj; return error; } static int queue_objects(git_push *push) { git_remote_head *head; push_spec *spec; git_revwalk *rw; unsigned int i; int error = -1; if (git_revwalk_new(&rw, push->repo) < 0) return -1; git_revwalk_sorting(rw, GIT_SORT_TIME); git_vector_foreach(&push->specs, i, spec) { git_object_t type; size_t size; if (git_oid_is_zero(&spec->loid)) /* * Delete reference on remote side; * nothing to do here. */ continue; if (git_oid_equal(&spec->loid, &spec->roid)) continue; /* up-to-date */ if (git_odb_read_header(&size, &type, push->repo->_odb, &spec->loid) < 0) goto on_error; if (type == GIT_OBJECT_TAG) { git_object *target; if ((error = enqueue_tag(&target, push, &spec->loid)) < 0) goto on_error; if (git_object_type(target) == GIT_OBJECT_COMMIT) { if (git_revwalk_push(rw, git_object_id(target)) < 0) { git_object_free(target); goto on_error; } } else { if (git_packbuilder_insert( push->pb, git_object_id(target), NULL) < 0) { git_object_free(target); goto on_error; } } git_object_free(target); } else if (git_revwalk_push(rw, &spec->loid) < 0) goto on_error; if (!spec->refspec.force) { git_oid base; if (git_oid_is_zero(&spec->roid)) continue; if (!git_odb_exists(push->repo->_odb, &spec->roid)) { git_error_set(GIT_ERROR_REFERENCE, "cannot push because a reference that you are trying to update on the remote contains commits that are not present locally."); error = GIT_ENONFASTFORWARD; goto on_error; } error = git_merge_base(&base, push->repo, &spec->loid, &spec->roid); if (error == GIT_ENOTFOUND || (!error && !git_oid_equal(&base, &spec->roid))) { git_error_set(GIT_ERROR_REFERENCE, "cannot push non-fastforwardable reference"); error = GIT_ENONFASTFORWARD; goto on_error; } if (error < 0) goto on_error; } } git_vector_foreach(&push->remote->refs, i, head) { if (git_oid_is_zero(&head->oid)) continue; if ((error = git_revwalk_hide(rw, &head->oid)) < 0 && error != GIT_ENOTFOUND && error != GIT_EINVALIDSPEC && error != GIT_EPEEL) goto on_error; } error = git_packbuilder_insert_walk(push->pb, rw); on_error: git_revwalk_free(rw); return error; } static int add_update(git_push *push, push_spec *spec) { git_push_update *u = git__calloc(1, sizeof(git_push_update)); GIT_ERROR_CHECK_ALLOC(u); u->src_refname = git__strdup(spec->refspec.src); GIT_ERROR_CHECK_ALLOC(u->src_refname); u->dst_refname = git__strdup(spec->refspec.dst); GIT_ERROR_CHECK_ALLOC(u->dst_refname); git_oid_cpy(&u->src, &spec->roid); git_oid_cpy(&u->dst, &spec->loid); return git_vector_insert(&push->updates, u); } static int calculate_work(git_push *push) { git_remote_head *head; push_spec *spec; unsigned int i, j; /* Update local and remote oids*/ git_vector_foreach(&push->specs, i, spec) { if (spec->refspec.src && spec->refspec.src[0]!= '\0') { /* This is a create or update. Local ref must exist. */ if (git_reference_name_to_id( &spec->loid, push->repo, spec->refspec.src) < 0) { git_error_set(GIT_ERROR_REFERENCE, "no such reference '%s'", spec->refspec.src); return -1; } } /* Remote ref may or may not (e.g. during create) already exist. */ git_vector_foreach(&push->remote->refs, j, head) { if (!strcmp(spec->refspec.dst, head->name)) { git_oid_cpy(&spec->roid, &head->oid); break; } } if (add_update(push, spec) < 0) return -1; } return 0; } static int do_push(git_push *push, const git_remote_callbacks *callbacks) { int error = 0; git_transport *transport = push->remote->transport; if (!transport->push) { git_error_set(GIT_ERROR_NET, "remote transport doesn't support push"); error = -1; goto on_error; } /* * A pack-file MUST be sent if either create or update command * is used, even if the server already has all the necessary * objects. In this case the client MUST send an empty pack-file. */ if ((error = git_packbuilder_new(&push->pb, push->repo)) < 0) goto on_error; git_packbuilder_set_threads(push->pb, push->pb_parallelism); if (callbacks && callbacks->pack_progress) if ((error = git_packbuilder_set_callbacks(push->pb, callbacks->pack_progress, callbacks->payload)) < 0) goto on_error; if ((error = calculate_work(push)) < 0) goto on_error; if (callbacks && callbacks->push_negotiation && (error = callbacks->push_negotiation((const git_push_update **) push->updates.contents, push->updates.length, callbacks->payload)) < 0) goto on_error; if ((error = queue_objects(push)) < 0 || (error = transport->push(transport, push, callbacks)) < 0) goto on_error; on_error: git_packbuilder_free(push->pb); return error; } static int filter_refs(git_remote *remote) { const git_remote_head **heads; size_t heads_len, i; git_vector_clear(&remote->refs); if (git_remote_ls(&heads, &heads_len, remote) < 0) return -1; for (i = 0; i < heads_len; i++) { if (git_vector_insert(&remote->refs, (void *)heads[i]) < 0) return -1; } return 0; } int git_push_finish(git_push *push, const git_remote_callbacks *callbacks) { int error; if (!git_remote_connected(push->remote) && (error = git_remote__connect(push->remote, GIT_DIRECTION_PUSH, callbacks, &push->connection)) < 0) return error; if ((error = filter_refs(push->remote)) < 0 || (error = do_push(push, callbacks)) < 0) return error; if (!push->unpack_ok) { error = -1; git_error_set(GIT_ERROR_NET, "unpacking the sent packfile failed on the remote"); } return error; } int git_push_status_foreach(git_push *push, int (*cb)(const char *ref, const char *msg, void *data), void *data) { push_status *status; unsigned int i; git_vector_foreach(&push->status, i, status) { int error = cb(status->ref, status->msg, data); if (error) return git_error_set_after_callback(error); } return 0; } void git_push_status_free(push_status *status) { if (status == NULL) return; git__free(status->msg); git__free(status->ref); git__free(status); } void git_push_free(git_push *push) { push_spec *spec; push_status *status; git_push_update *update; unsigned int i; if (push == NULL) return; git_vector_foreach(&push->specs, i, spec) { free_refspec(spec); } git_vector_free(&push->specs); git_vector_foreach(&push->status, i, status) { git_push_status_free(status); } git_vector_free(&push->status); git_vector_foreach(&push->updates, i, update) { git__free(update->src_refname); git__free(update->dst_refname); git__free(update); } git_vector_free(&push->updates); git__free(push); } int git_push_options_init(git_push_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_push_options, GIT_PUSH_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_push_init_options(git_push_options *opts, unsigned int version) { return git_push_options_init(opts, version); } #endif git2r/src/libgit2/src/threadstate.c0000644000175000017500000000416414125111754017027 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "threadstate.h" #include "runtime.h" /** * Handle the thread-local state * * `git_threadstate_global_init` will be called as part * of `git_libgit2_init` (which itself must be called * before calling any other function in the library). * * This function allocates a TLS index to store the per- * thread state. * * Any internal method that requires thread-local state * will then call `git_threadstate_get()` which returns a * pointer to the thread-local state structure; this * structure is lazily allocated on each thread. * * This mechanism will register a shutdown handler * (`git_threadstate_global_shutdown`) which will free the * TLS index. This shutdown handler will be called by * `git_libgit2_shutdown`. */ static git_tlsdata_key tls_key; static void threadstate_dispose(git_threadstate *threadstate) { if (!threadstate) return; if (threadstate->error_t.message != git_buf__initbuf) git__free(threadstate->error_t.message); threadstate->error_t.message = NULL; } static void GIT_SYSTEM_CALL threadstate_free(void *threadstate) { threadstate_dispose(threadstate); git__free(threadstate); } static void git_threadstate_global_shutdown(void) { git_threadstate *threadstate; threadstate = git_tlsdata_get(tls_key); git_tlsdata_set(tls_key, NULL); threadstate_dispose(threadstate); git__free(threadstate); git_tlsdata_dispose(tls_key); } int git_threadstate_global_init(void) { if (git_tlsdata_init(&tls_key, &threadstate_free) != 0) return -1; return git_runtime_shutdown_register(git_threadstate_global_shutdown); } git_threadstate *git_threadstate_get(void) { git_threadstate *threadstate; if ((threadstate = git_tlsdata_get(tls_key)) != NULL) return threadstate; if ((threadstate = git__calloc(1, sizeof(git_threadstate))) == NULL || git_buf_init(&threadstate->error_buf, 0) < 0) return NULL; git_tlsdata_set(tls_key, threadstate); return threadstate; } git2r/src/libgit2/src/ident.c0000644000175000017500000000641214125111754015620 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/sys/filter.h" #include "filter.h" #include "buffer.h" static int ident_find_id( const char **id_start, const char **id_end, const char *start, size_t len) { const char *end = start + len, *found = NULL; while (len > 3 && (found = memchr(start, '$', len)) != NULL) { size_t remaining = (size_t)(end - found) - 1; if (remaining < 3) return GIT_ENOTFOUND; start = found + 1; len = remaining; if (start[0] == 'I' && start[1] == 'd') break; } if (len < 3 || !found) return GIT_ENOTFOUND; *id_start = found; if ((found = memchr(start + 2, '$', len - 2)) == NULL) return GIT_ENOTFOUND; *id_end = found + 1; return 0; } static int ident_insert_id( git_buf *to, const git_buf *from, const git_filter_source *src) { char oid[GIT_OID_HEXSZ+1]; const char *id_start, *id_end, *from_end = from->ptr + from->size; size_t need_size; /* replace $Id$ with blob id */ if (!git_filter_source_id(src)) return GIT_PASSTHROUGH; git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src)); if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0) return GIT_PASSTHROUGH; need_size = (size_t)(id_start - from->ptr) + 5 /* "$Id: " */ + GIT_OID_HEXSZ + 2 /* " $" */ + (size_t)(from_end - id_end); if (git_buf_grow(to, need_size) < 0) return -1; git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr)); git_buf_put(to, "$Id: ", 5); git_buf_put(to, oid, GIT_OID_HEXSZ); git_buf_put(to, " $", 2); git_buf_put(to, id_end, (size_t)(from_end - id_end)); return git_buf_oom(to) ? -1 : 0; } static int ident_remove_id( git_buf *to, const git_buf *from) { const char *id_start, *id_end, *from_end = from->ptr + from->size; size_t need_size; if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0) return GIT_PASSTHROUGH; need_size = (size_t)(id_start - from->ptr) + 4 /* "$Id$" */ + (size_t)(from_end - id_end); if (git_buf_grow(to, need_size) < 0) return -1; git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr)); git_buf_put(to, "$Id$", 4); git_buf_put(to, id_end, (size_t)(from_end - id_end)); return git_buf_oom(to) ? -1 : 0; } static int ident_apply( git_filter *self, void **payload, git_buf *to, const git_buf *from, const git_filter_source *src) { GIT_UNUSED(self); GIT_UNUSED(payload); /* Don't filter binary files */ if (git_buf_is_binary(from)) return GIT_PASSTHROUGH; if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE) return ident_insert_id(to, from, src); else return ident_remove_id(to, from); } static int ident_stream( git_writestream **out, git_filter *self, void **payload, const git_filter_source *src, git_writestream *next) { return git_filter_buffered_stream_new(out, self, ident_apply, NULL, payload, src, next); } git_filter *git_ident_filter_new(void) { git_filter *f = git__calloc(1, sizeof(git_filter)); if (f == NULL) return NULL; f->version = GIT_FILTER_VERSION; f->attributes = "+ident"; /* apply to files with ident attribute set */ f->shutdown = git_filter_free; f->stream = ident_stream; return f; } git2r/src/libgit2/src/index.h0000644000175000017500000001267014125111754015634 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_index_h__ #define INCLUDE_index_h__ #include "common.h" #include "futils.h" #include "filebuf.h" #include "vector.h" #include "idxmap.h" #include "tree-cache.h" #include "git2/odb.h" #include "git2/index.h" #define GIT_INDEX_FILE "index" #define GIT_INDEX_FILE_MODE 0666 extern bool git_index__enforce_unsaved_safety; struct git_index { git_refcount rc; char *index_file_path; git_futils_filestamp stamp; git_oid checksum; /* checksum at the end of the file */ git_vector entries; git_idxmap *entries_map; git_vector deleted; /* deleted entries if readers > 0 */ git_atomic32 readers; /* number of active iterators */ unsigned int on_disk:1; unsigned int ignore_case:1; unsigned int distrust_filemode:1; unsigned int no_symlinks:1; unsigned int dirty:1; /* whether we have unsaved changes */ git_tree_cache *tree; git_pool tree_pool; git_vector names; git_vector reuc; git_vector_cmp entries_cmp_path; git_vector_cmp entries_search; git_vector_cmp entries_search_path; git_vector_cmp reuc_search; unsigned int version; }; struct git_index_iterator { git_index *index; git_vector snap; size_t cur; }; struct git_index_conflict_iterator { git_index *index; size_t cur; }; extern void git_index_entry__init_from_stat( git_index_entry *entry, struct stat *st, bool trust_mode); /* Index entry comparison functions for array sorting */ extern int git_index_entry_cmp(const void *a, const void *b); extern int git_index_entry_icmp(const void *a, const void *b); /* Index entry search functions for search using a search spec */ extern int git_index_entry_srch(const void *a, const void *b); extern int git_index_entry_isrch(const void *a, const void *b); /* Index time handling functions */ GIT_INLINE(bool) git_index_time_eq(const git_index_time *one, const git_index_time *two) { if (one->seconds != two->seconds) return false; #ifdef GIT_USE_NSEC if (one->nanoseconds != two->nanoseconds) return false; #endif return true; } /* * Test if the given index time is newer than the given existing index entry. * If the timestamps are exactly equivalent, then the given index time is * considered "racily newer" than the existing index entry. */ GIT_INLINE(bool) git_index_entry_newer_than_index( const git_index_entry *entry, git_index *index) { /* If we never read the index, we can't have this race either */ if (!index || index->stamp.mtime.tv_sec == 0) return false; /* If the timestamp is the same or newer than the index, it's racy */ #if defined(GIT_USE_NSEC) if ((int32_t)index->stamp.mtime.tv_sec < entry->mtime.seconds) return true; else if ((int32_t)index->stamp.mtime.tv_sec > entry->mtime.seconds) return false; else return (uint32_t)index->stamp.mtime.tv_nsec <= entry->mtime.nanoseconds; #else return ((int32_t)index->stamp.mtime.tv_sec) <= entry->mtime.seconds; #endif } /* Search index for `path`, returning GIT_ENOTFOUND if it does not exist * (but not setting an error message). * * `at_pos` is set to the position where it is or would be inserted. * Pass `path_len` as strlen of path or 0 to call strlen internally. */ extern int git_index__find_pos( size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage); extern int git_index__fill(git_index *index, const git_vector *source_entries); extern void git_index__set_ignore_case(git_index *index, bool ignore_case); extern unsigned int git_index__create_mode(unsigned int mode); GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index) { return &index->stamp; } extern int git_index__changed_relative_to(git_index *index, const git_oid *checksum); /* Copy the current entries vector *and* increment the index refcount. * Call `git_index__release_snapshot` when done. */ extern int git_index_snapshot_new(git_vector *snap, git_index *index); extern void git_index_snapshot_release(git_vector *snap, git_index *index); /* Allow searching in a snapshot; entries must already be sorted! */ extern int git_index_snapshot_find( size_t *at_pos, git_vector *snap, git_vector_cmp entry_srch, const char *path, size_t path_len, int stage); /* Replace an index with a new index */ int git_index_read_index(git_index *index, const git_index *new_index); GIT_INLINE(int) git_index_is_dirty(git_index *index) { return index->dirty; } extern int git_index_read_safely(git_index *index); typedef struct { git_index *index; git_filebuf file; unsigned int should_write:1; } git_indexwriter; #define GIT_INDEXWRITER_INIT { NULL, GIT_FILEBUF_INIT } /* Lock the index for eventual writing. */ extern int git_indexwriter_init(git_indexwriter *writer, git_index *index); /* Lock the index for eventual writing by a repository operation: a merge, * revert, cherry-pick or a rebase. Note that the given checkout strategy * will be updated for the operation's use so that checkout will not write * the index. */ extern int git_indexwriter_init_for_operation( git_indexwriter *writer, git_repository *repo, unsigned int *checkout_strategy); /* Write the index and unlock it. */ extern int git_indexwriter_commit(git_indexwriter *writer); /* Cleanup an index writing session, unlocking the file (if it is still * locked and freeing any data structures. */ extern void git_indexwriter_cleanup(git_indexwriter *writer); #endif git2r/src/libgit2/src/diff_stats.c0000644000175000017500000002052214125111754016641 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "vector.h" #include "diff.h" #include "patch_generate.h" #define DIFF_RENAME_FILE_SEPARATOR " => " #define STATS_FULL_MIN_SCALE 7 typedef struct { size_t insertions; size_t deletions; } diff_file_stats; struct git_diff_stats { git_diff *diff; diff_file_stats *filestats; size_t files_changed; size_t insertions; size_t deletions; size_t renames; size_t max_name; size_t max_filestat; int max_digits; }; static int digits_for_value(size_t val) { int count = 1; size_t placevalue = 10; while (val >= placevalue) { ++count; placevalue *= 10; } return count; } static int diff_file_stats_full_to_buf( git_buf *out, const git_diff_delta *delta, const diff_file_stats *filestat, const git_diff_stats *stats, size_t width) { const char *old_path = NULL, *new_path = NULL, *adddel_path = NULL; size_t padding; git_object_size_t old_size, new_size; old_path = delta->old_file.path; new_path = delta->new_file.path; old_size = delta->old_file.size; new_size = delta->new_file.size; if (old_path && new_path && strcmp(old_path, new_path) != 0) { size_t common_dirlen; int error; padding = stats->max_name - strlen(old_path) - strlen(new_path); if ((common_dirlen = git_path_common_dirlen(old_path, new_path)) && common_dirlen <= INT_MAX) { error = git_buf_printf(out, " %.*s{%s"DIFF_RENAME_FILE_SEPARATOR"%s}", (int) common_dirlen, old_path, old_path + common_dirlen, new_path + common_dirlen); } else { error = git_buf_printf(out, " %s" DIFF_RENAME_FILE_SEPARATOR "%s", old_path, new_path); } if (error < 0) goto on_error; } else { adddel_path = new_path ? new_path : old_path; if (git_buf_printf(out, " %s", adddel_path) < 0) goto on_error; padding = stats->max_name - strlen(adddel_path); if (stats->renames > 0) padding += strlen(DIFF_RENAME_FILE_SEPARATOR); } if (git_buf_putcn(out, ' ', padding) < 0 || git_buf_puts(out, " | ") < 0) goto on_error; if (delta->flags & GIT_DIFF_FLAG_BINARY) { if (git_buf_printf(out, "Bin %" PRId64 " -> %" PRId64 " bytes", old_size, new_size) < 0) goto on_error; } else { if (git_buf_printf(out, "%*" PRIuZ, stats->max_digits, filestat->insertions + filestat->deletions) < 0) goto on_error; if (filestat->insertions || filestat->deletions) { if (git_buf_putc(out, ' ') < 0) goto on_error; if (!width) { if (git_buf_putcn(out, '+', filestat->insertions) < 0 || git_buf_putcn(out, '-', filestat->deletions) < 0) goto on_error; } else { size_t total = filestat->insertions + filestat->deletions; size_t full = (total * width + stats->max_filestat / 2) / stats->max_filestat; size_t plus = full * filestat->insertions / total; size_t minus = full - plus; if (git_buf_putcn(out, '+', max(plus, 1)) < 0 || git_buf_putcn(out, '-', max(minus, 1)) < 0) goto on_error; } } } git_buf_putc(out, '\n'); on_error: return (git_buf_oom(out) ? -1 : 0); } static int diff_file_stats_number_to_buf( git_buf *out, const git_diff_delta *delta, const diff_file_stats *filestats) { int error; const char *path = delta->new_file.path; if (delta->flags & GIT_DIFF_FLAG_BINARY) error = git_buf_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path); else error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n", filestats->insertions, filestats->deletions, path); return error; } static int diff_file_stats_summary_to_buf( git_buf *out, const git_diff_delta *delta) { if (delta->old_file.mode != delta->new_file.mode) { if (delta->old_file.mode == 0) { git_buf_printf(out, " create mode %06o %s\n", delta->new_file.mode, delta->new_file.path); } else if (delta->new_file.mode == 0) { git_buf_printf(out, " delete mode %06o %s\n", delta->old_file.mode, delta->old_file.path); } else { git_buf_printf(out, " mode change %06o => %06o %s\n", delta->old_file.mode, delta->new_file.mode, delta->new_file.path); } } return 0; } int git_diff_get_stats( git_diff_stats **out, git_diff *diff) { size_t i, deltas; size_t total_insertions = 0, total_deletions = 0; git_diff_stats *stats = NULL; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(diff); stats = git__calloc(1, sizeof(git_diff_stats)); GIT_ERROR_CHECK_ALLOC(stats); deltas = git_diff_num_deltas(diff); stats->filestats = git__calloc(deltas, sizeof(diff_file_stats)); if (!stats->filestats) { git__free(stats); return -1; } stats->diff = diff; GIT_REFCOUNT_INC(diff); for (i = 0; i < deltas && !error; ++i) { git_patch *patch = NULL; size_t add = 0, remove = 0, namelen; const git_diff_delta *delta; if ((error = git_patch_from_diff(&patch, diff, i)) < 0) break; /* keep a count of renames because it will affect formatting */ delta = patch->delta; /* TODO ugh */ namelen = strlen(delta->new_file.path); if (delta->old_file.path && strcmp(delta->old_file.path, delta->new_file.path) != 0) { namelen += strlen(delta->old_file.path); stats->renames++; } /* and, of course, count the line stats */ error = git_patch_line_stats(NULL, &add, &remove, patch); git_patch_free(patch); stats->filestats[i].insertions = add; stats->filestats[i].deletions = remove; total_insertions += add; total_deletions += remove; if (stats->max_name < namelen) stats->max_name = namelen; if (stats->max_filestat < add + remove) stats->max_filestat = add + remove; } stats->files_changed = deltas; stats->insertions = total_insertions; stats->deletions = total_deletions; stats->max_digits = digits_for_value(stats->max_filestat + 1); if (error < 0) { git_diff_stats_free(stats); stats = NULL; } *out = stats; return error; } size_t git_diff_stats_files_changed( const git_diff_stats *stats) { GIT_ASSERT_ARG(stats); return stats->files_changed; } size_t git_diff_stats_insertions( const git_diff_stats *stats) { GIT_ASSERT_ARG(stats); return stats->insertions; } size_t git_diff_stats_deletions( const git_diff_stats *stats) { GIT_ASSERT_ARG(stats); return stats->deletions; } int git_diff_stats_to_buf( git_buf *out, const git_diff_stats *stats, git_diff_stats_format_t format, size_t width) { int error = 0; size_t i; const git_diff_delta *delta; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(stats); if (format & GIT_DIFF_STATS_NUMBER) { for (i = 0; i < stats->files_changed; ++i) { if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) continue; error = diff_file_stats_number_to_buf( out, delta, &stats->filestats[i]); if (error < 0) return error; } } if (format & GIT_DIFF_STATS_FULL) { if (width > 0) { if (width > stats->max_name + stats->max_digits + 5) width -= (stats->max_name + stats->max_digits + 5); if (width < STATS_FULL_MIN_SCALE) width = STATS_FULL_MIN_SCALE; } if (width > stats->max_filestat) width = 0; for (i = 0; i < stats->files_changed; ++i) { if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) continue; error = diff_file_stats_full_to_buf( out, delta, &stats->filestats[i], stats, width); if (error < 0) return error; } } if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) { git_buf_printf( out, " %" PRIuZ " file%s changed", stats->files_changed, stats->files_changed != 1 ? "s" : ""); if (stats->insertions || stats->deletions == 0) git_buf_printf( out, ", %" PRIuZ " insertion%s(+)", stats->insertions, stats->insertions != 1 ? "s" : ""); if (stats->deletions || stats->insertions == 0) git_buf_printf( out, ", %" PRIuZ " deletion%s(-)", stats->deletions, stats->deletions != 1 ? "s" : ""); git_buf_putc(out, '\n'); if (git_buf_oom(out)) return -1; } if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) { for (i = 0; i < stats->files_changed; ++i) { if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) continue; error = diff_file_stats_summary_to_buf(out, delta); if (error < 0) return error; } } return error; } void git_diff_stats_free(git_diff_stats *stats) { if (stats == NULL) return; git_diff_free(stats->diff); /* bumped refcount in constructor */ git__free(stats->filestats); git__free(stats); } git2r/src/libgit2/src/config_file.c0000644000175000017500000007363714125111754016776 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "config.h" #include "git2/config.h" #include "git2/sys/config.h" #include "array.h" #include "buffer.h" #include "config_backend.h" #include "config_entries.h" #include "config_parse.h" #include "filebuf.h" #include "regexp.h" #include "sysdir.h" #include "wildmatch.h" /* Max depth for [include] directives */ #define MAX_INCLUDE_DEPTH 10 typedef struct config_file { git_futils_filestamp stamp; git_oid checksum; char *path; git_array_t(struct config_file) includes; } config_file; typedef struct { git_config_backend parent; git_mutex values_mutex; git_config_entries *entries; const git_repository *repo; git_config_level_t level; git_array_t(git_config_parser) readers; bool locked; git_filebuf locked_buf; git_buf locked_content; config_file file; } config_file_backend; typedef struct { const git_repository *repo; config_file *file; git_config_entries *entries; git_config_level_t level; unsigned int depth; } config_file_parse_data; static int config_file_read(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth); static int config_file_read_buffer(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen); static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char *value); static char *escape_value(const char *ptr); /** * Take the current values map from the backend and increase its * refcount. This is its own function to make sure we use the mutex to * avoid the map pointer from changing under us. */ static int config_file_entries_take(git_config_entries **out, config_file_backend *b) { int error; if ((error = git_mutex_lock(&b->values_mutex)) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock config backend"); return error; } git_config_entries_incref(b->entries); *out = b->entries; git_mutex_unlock(&b->values_mutex); return 0; } static void config_file_clear(config_file *file) { config_file *include; uint32_t i; if (file == NULL) return; git_array_foreach(file->includes, i, include) { config_file_clear(include); } git_array_clear(file->includes); git__free(file->path); } static int config_file_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); int res; b->level = level; b->repo = repo; if ((res = git_config_entries_new(&b->entries)) < 0) return res; if (!git_path_exists(b->file.path)) return 0; /* * git silently ignores configuration files that are not * readable. We emulate that behavior. This is particularly * important for sandboxed applications on macOS where the * git configuration files may not be readable. */ if (p_access(b->file.path, R_OK) < 0) return GIT_ENOTFOUND; if (res < 0 || (res = config_file_read(b->entries, repo, &b->file, level, 0)) < 0) { git_config_entries_free(b->entries); b->entries = NULL; } return res; } static int config_file_is_modified(int *modified, config_file *file) { config_file *include; git_buf buf = GIT_BUF_INIT; git_oid hash; uint32_t i; int error = 0; *modified = 0; if (!git_futils_filestamp_check(&file->stamp, file->path)) goto check_includes; if ((error = git_futils_readbuffer(&buf, file->path)) < 0) goto out; if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0) goto out; if (!git_oid_equal(&hash, &file->checksum)) { *modified = 1; goto out; } check_includes: git_array_foreach(file->includes, i, include) { if ((error = config_file_is_modified(modified, include)) < 0 || *modified) goto out; } out: git_buf_dispose(&buf); return error; } static void config_file_clear_includes(config_file_backend *cfg) { config_file *include; uint32_t i; git_array_foreach(cfg->file.includes, i, include) config_file_clear(include); git_array_clear(cfg->file.includes); } static int config_file_set_entries(git_config_backend *cfg, git_config_entries *entries) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *old = NULL; int error; if (b->parent.readonly) { git_error_set(GIT_ERROR_CONFIG, "this backend is read-only"); return -1; } if ((error = git_mutex_lock(&b->values_mutex)) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock config backend"); goto out; } old = b->entries; b->entries = entries; git_mutex_unlock(&b->values_mutex); out: git_config_entries_free(old); return error; } static int config_file_refresh_from_buffer(git_config_backend *cfg, const char *buf, size_t buflen) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; int error; config_file_clear_includes(b); if ((error = git_config_entries_new(&entries)) < 0 || (error = config_file_read_buffer(entries, b->repo, &b->file, b->level, 0, buf, buflen)) < 0 || (error = config_file_set_entries(cfg, entries)) < 0) goto out; entries = NULL; out: git_config_entries_free(entries); return error; } static int config_file_refresh(git_config_backend *cfg) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; int error, modified; if (cfg->readonly) return 0; if ((error = config_file_is_modified(&modified, &b->file)) < 0 && error != GIT_ENOTFOUND) goto out; if (!modified) return 0; config_file_clear_includes(b); if ((error = git_config_entries_new(&entries)) < 0 || (error = config_file_read(entries, b->repo, &b->file, b->level, 0)) < 0 || (error = config_file_set_entries(cfg, entries)) < 0) goto out; entries = NULL; out: git_config_entries_free(entries); return (error == GIT_ENOTFOUND) ? 0 : error; } static void config_file_free(git_config_backend *_backend) { config_file_backend *backend = GIT_CONTAINER_OF(_backend, config_file_backend, parent); if (backend == NULL) return; config_file_clear(&backend->file); git_config_entries_free(backend->entries); git_mutex_free(&backend->values_mutex); git__free(backend); } static int config_file_iterator( git_config_iterator **iter, struct git_config_backend *backend) { config_file_backend *b = GIT_CONTAINER_OF(backend, config_file_backend, parent); git_config_entries *dupped = NULL, *entries = NULL; int error; if ((error = config_file_refresh(backend)) < 0 || (error = config_file_entries_take(&entries, b)) < 0 || (error = git_config_entries_dup(&dupped, entries)) < 0 || (error = git_config_entries_iterator_new(iter, dupped)) < 0) goto out; out: /* Let iterator delete duplicated entries when it's done */ git_config_entries_free(entries); git_config_entries_free(dupped); return error; } static int config_file_snapshot(git_config_backend **out, git_config_backend *backend) { return git_config_backend_snapshot(out, backend); } static int config_file_set(git_config_backend *cfg, const char *name, const char *value) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries; git_config_entry *existing; char *key, *esc_value = NULL; int error; if ((error = git_config__normalize_name(name, &key)) < 0) return error; if ((error = config_file_entries_take(&entries, b)) < 0) return error; /* Check whether we'd be modifying an included or multivar key */ if ((error = git_config_entries_get_unique(&existing, entries, key)) < 0) { if (error != GIT_ENOTFOUND) goto out; error = 0; } else if ((!existing->value && !value) || (existing->value && value && !strcmp(existing->value, value))) { /* don't update if old and new values already match */ error = 0; goto out; } /* No early returns due to sanity checks, let's write it out and refresh */ if (value) { esc_value = escape_value(value); GIT_ERROR_CHECK_ALLOC(esc_value); } if ((error = config_file_write(b, name, key, NULL, esc_value)) < 0) goto out; out: git_config_entries_free(entries); git__free(esc_value); git__free(key); return error; } /* release the map containing the entry as an equivalent to freeing it */ static void config_file_entry_free(git_config_entry *entry) { git_config_entries *entries = (git_config_entries *) entry->payload; git_config_entries_free(entries); } /* * Internal function that actually gets the value in string form */ static int config_file_get(git_config_backend *cfg, const char *key, git_config_entry **out) { config_file_backend *h = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; git_config_entry *entry; int error = 0; if (!h->parent.readonly && ((error = config_file_refresh(cfg)) < 0)) return error; if ((error = config_file_entries_take(&entries, h)) < 0) return error; if ((error = (git_config_entries_get(&entry, entries, key))) < 0) { git_config_entries_free(entries); return error; } entry->free = config_file_entry_free; entry->payload = entries; *out = entry; return 0; } static int config_file_set_multivar( git_config_backend *cfg, const char *name, const char *regexp, const char *value) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_regexp preg; int result; char *key; GIT_ASSERT_ARG(regexp); if ((result = git_config__normalize_name(name, &key)) < 0) return result; if ((result = git_regexp_compile(&preg, regexp, 0)) < 0) goto out; /* If we do have it, set call config_file_write() and reload */ if ((result = config_file_write(b, name, key, &preg, value)) < 0) goto out; out: git__free(key); git_regexp_dispose(&preg); return result; } static int config_file_delete(git_config_backend *cfg, const char *name) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; git_config_entry *entry; char *key = NULL; int error; if ((error = git_config__normalize_name(name, &key)) < 0) goto out; if ((error = config_file_entries_take(&entries, b)) < 0) goto out; /* Check whether we'd be modifying an included or multivar key */ if ((error = git_config_entries_get_unique(&entry, entries, key)) < 0) { if (error == GIT_ENOTFOUND) git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name); goto out; } if ((error = config_file_write(b, name, entry->name, NULL, NULL)) < 0) goto out; out: git_config_entries_free(entries); git__free(key); return error; } static int config_file_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp) { config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent); git_config_entries *entries = NULL; git_config_entry *entry = NULL; git_regexp preg = GIT_REGEX_INIT; char *key = NULL; int result; if ((result = git_config__normalize_name(name, &key)) < 0) goto out; if ((result = config_file_entries_take(&entries, b)) < 0) goto out; if ((result = git_config_entries_get(&entry, entries, key)) < 0) { if (result == GIT_ENOTFOUND) git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name); goto out; } if ((result = git_regexp_compile(&preg, regexp, 0)) < 0) goto out; if ((result = config_file_write(b, name, key, &preg, NULL)) < 0) goto out; out: git_config_entries_free(entries); git__free(key); git_regexp_dispose(&preg); return result; } static int config_file_lock(git_config_backend *_cfg) { config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent); int error; if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0) return error; error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path); if (error < 0 && error != GIT_ENOTFOUND) { git_filebuf_cleanup(&cfg->locked_buf); return error; } cfg->locked = true; return 0; } static int config_file_unlock(git_config_backend *_cfg, int success) { config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent); int error = 0; if (success) { git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size); error = git_filebuf_commit(&cfg->locked_buf); } git_filebuf_cleanup(&cfg->locked_buf); git_buf_dispose(&cfg->locked_content); cfg->locked = false; return error; } int git_config_backend_from_file(git_config_backend **out, const char *path) { config_file_backend *backend; backend = git__calloc(1, sizeof(config_file_backend)); GIT_ERROR_CHECK_ALLOC(backend); backend->parent.version = GIT_CONFIG_BACKEND_VERSION; git_mutex_init(&backend->values_mutex); backend->file.path = git__strdup(path); GIT_ERROR_CHECK_ALLOC(backend->file.path); git_array_init(backend->file.includes); backend->parent.open = config_file_open; backend->parent.get = config_file_get; backend->parent.set = config_file_set; backend->parent.set_multivar = config_file_set_multivar; backend->parent.del = config_file_delete; backend->parent.del_multivar = config_file_delete_multivar; backend->parent.iterator = config_file_iterator; backend->parent.snapshot = config_file_snapshot; backend->parent.lock = config_file_lock; backend->parent.unlock = config_file_unlock; backend->parent.free = config_file_free; *out = (git_config_backend *)backend; return 0; } static int included_path(git_buf *out, const char *dir, const char *path) { /* From the user's home */ if (path[0] == '~' && path[1] == '/') return git_sysdir_expand_global_file(out, &path[1]); return git_path_join_unrooted(out, path, dir, NULL); } /* Escape the values to write them to the file */ static char *escape_value(const char *ptr) { git_buf buf; size_t len; const char *esc; GIT_ASSERT_ARG_WITH_RETVAL(ptr, NULL); len = strlen(ptr); if (!len) return git__calloc(1, sizeof(char)); if (git_buf_init(&buf, len) < 0) return NULL; while (*ptr != '\0') { if ((esc = strchr(git_config_escaped, *ptr)) != NULL) { git_buf_putc(&buf, '\\'); git_buf_putc(&buf, git_config_escapes[esc - git_config_escaped]); } else { git_buf_putc(&buf, *ptr); } ptr++; } if (git_buf_oom(&buf)) return NULL; return git_buf_detach(&buf); } static int parse_include(config_file_parse_data *parse_data, const char *file) { config_file *include; git_buf path = GIT_BUF_INIT; char *dir; int result; if (!file) return 0; if ((result = git_path_dirname_r(&path, parse_data->file->path)) < 0) return result; dir = git_buf_detach(&path); result = included_path(&path, dir, file); git__free(dir); if (result < 0) return result; include = git_array_alloc(parse_data->file->includes); GIT_ERROR_CHECK_ALLOC(include); memset(include, 0, sizeof(*include)); git_array_init(include->includes); include->path = git_buf_detach(&path); result = config_file_read(parse_data->entries, parse_data->repo, include, parse_data->level, parse_data->depth+1); if (result == GIT_ENOTFOUND) { git_error_clear(); result = 0; } return result; } static int do_match_gitdir( int *matches, const git_repository *repo, const char *cfg_file, const char *condition, bool case_insensitive) { git_buf pattern = GIT_BUF_INIT, gitdir = GIT_BUF_INIT; int error; if (condition[0] == '.' && git_path_is_dirsep(condition[1])) { git_path_dirname_r(&pattern, cfg_file); git_buf_joinpath(&pattern, pattern.ptr, condition + 2); } else if (condition[0] == '~' && git_path_is_dirsep(condition[1])) git_sysdir_expand_global_file(&pattern, condition + 1); else if (!git_path_is_absolute(condition)) git_buf_joinpath(&pattern, "**", condition); else git_buf_sets(&pattern, condition); if (git_path_is_dirsep(condition[strlen(condition) - 1])) git_buf_puts(&pattern, "**"); if (git_buf_oom(&pattern)) { error = -1; goto out; } if ((error = git_repository_item_path(&gitdir, repo, GIT_REPOSITORY_ITEM_GITDIR)) < 0) goto out; if (git_path_is_dirsep(gitdir.ptr[gitdir.size - 1])) git_buf_truncate(&gitdir, gitdir.size - 1); *matches = wildmatch(pattern.ptr, gitdir.ptr, WM_PATHNAME | (case_insensitive ? WM_CASEFOLD : 0)) == WM_MATCH; out: git_buf_dispose(&pattern); git_buf_dispose(&gitdir); return error; } static int conditional_match_gitdir( int *matches, const git_repository *repo, const char *cfg_file, const char *value) { return do_match_gitdir(matches, repo, cfg_file, value, false); } static int conditional_match_gitdir_i( int *matches, const git_repository *repo, const char *cfg_file, const char *value) { return do_match_gitdir(matches, repo, cfg_file, value, true); } static int conditional_match_onbranch( int *matches, const git_repository *repo, const char *cfg_file, const char *condition) { git_buf reference = GIT_BUF_INIT, buf = GIT_BUF_INIT; int error; GIT_UNUSED(cfg_file); /* * NOTE: you cannot use `git_repository_head` here. Looking up the * HEAD reference will create the ODB, which causes us to read the * repo's config for keys like core.precomposeUnicode. As we're * just parsing the config right now, though, this would result in * an endless recursion. */ if ((error = git_buf_joinpath(&buf, git_repository_path(repo), GIT_HEAD_FILE)) < 0 || (error = git_futils_readbuffer(&reference, buf.ptr)) < 0) goto out; git_buf_rtrim(&reference); if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF))) goto out; git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF)); if (git__strncmp(reference.ptr, GIT_REFS_HEADS_DIR, strlen(GIT_REFS_HEADS_DIR))) goto out; git_buf_consume(&reference, reference.ptr + strlen(GIT_REFS_HEADS_DIR)); /* * If the condition ends with a '/', then we should treat it as if * it had '**' appended. */ if ((error = git_buf_sets(&buf, condition)) < 0) goto out; if (git_path_is_dirsep(condition[strlen(condition) - 1]) && (error = git_buf_puts(&buf, "**")) < 0) goto out; *matches = wildmatch(buf.ptr, reference.ptr, WM_PATHNAME) == WM_MATCH; out: git_buf_dispose(&reference); git_buf_dispose(&buf); return error; } static const struct { const char *prefix; int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value); } conditions[] = { { "gitdir:", conditional_match_gitdir }, { "gitdir/i:", conditional_match_gitdir_i }, { "onbranch:", conditional_match_onbranch } }; static int parse_conditional_include(config_file_parse_data *parse_data, const char *section, const char *file) { char *condition; size_t i; int error = 0, matches; if (!parse_data->repo || !file) return 0; condition = git__substrdup(section + strlen("includeIf."), strlen(section) - strlen("includeIf.") - strlen(".path")); for (i = 0; i < ARRAY_SIZE(conditions); i++) { if (git__prefixcmp(condition, conditions[i].prefix)) continue; if ((error = conditions[i].matches(&matches, parse_data->repo, parse_data->file->path, condition + strlen(conditions[i].prefix))) < 0) break; if (matches) error = parse_include(parse_data, file); break; } git__free(condition); return error; } static int read_on_variable( git_config_parser *reader, const char *current_section, const char *var_name, const char *var_value, const char *line, size_t line_len, void *data) { config_file_parse_data *parse_data = (config_file_parse_data *)data; git_buf buf = GIT_BUF_INIT; git_config_entry *entry; const char *c; int result = 0; GIT_UNUSED(reader); GIT_UNUSED(line); GIT_UNUSED(line_len); if (current_section) { /* TODO: Once warnings lang, we should likely warn * here. Git appears to warn in most cases if it sees * un-namespaced config options. */ git_buf_puts(&buf, current_section); git_buf_putc(&buf, '.'); } for (c = var_name; *c; c++) git_buf_putc(&buf, git__tolower(*c)); if (git_buf_oom(&buf)) return -1; entry = git__calloc(1, sizeof(git_config_entry)); GIT_ERROR_CHECK_ALLOC(entry); entry->name = git_buf_detach(&buf); entry->value = var_value ? git__strdup(var_value) : NULL; entry->level = parse_data->level; entry->include_depth = parse_data->depth; if ((result = git_config_entries_append(parse_data->entries, entry)) < 0) return result; result = 0; /* Add or append the new config option */ if (!git__strcmp(entry->name, "include.path")) result = parse_include(parse_data, entry->value); else if (!git__prefixcmp(entry->name, "includeif.") && !git__suffixcmp(entry->name, ".path")) result = parse_conditional_include(parse_data, entry->name, entry->value); return result; } static int config_file_read_buffer( git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen) { config_file_parse_data parse_data; git_config_parser reader; int error; if (depth >= MAX_INCLUDE_DEPTH) { git_error_set(GIT_ERROR_CONFIG, "maximum config include depth reached"); return -1; } /* Initialize the reading position */ reader.path = file->path; git_parse_ctx_init(&reader.ctx, buf, buflen); /* If the file is empty, there's nothing for us to do */ if (!reader.ctx.content || *reader.ctx.content == '\0') { error = 0; goto out; } parse_data.repo = repo; parse_data.file = file; parse_data.entries = entries; parse_data.level = level; parse_data.depth = depth; error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data); out: return error; } static int config_file_read( git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth) { git_buf contents = GIT_BUF_INIT; struct stat st; int error; if (p_stat(file->path, &st) < 0) { error = git_path_set_error(errno, file->path, "stat"); goto out; } if ((error = git_futils_readbuffer(&contents, file->path)) < 0) goto out; git_futils_filestamp_set_from_stat(&file->stamp, &st); if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0) goto out; if ((error = config_file_read_buffer(entries, repo, file, level, depth, contents.ptr, contents.size)) < 0) goto out; out: git_buf_dispose(&contents); return error; } static int write_section(git_buf *fbuf, const char *key) { int result; const char *dot; git_buf buf = GIT_BUF_INIT; /* All of this just for [section "subsection"] */ dot = strchr(key, '.'); git_buf_putc(&buf, '['); if (dot == NULL) { git_buf_puts(&buf, key); } else { char *escaped; git_buf_put(&buf, key, dot - key); escaped = escape_value(dot + 1); GIT_ERROR_CHECK_ALLOC(escaped); git_buf_printf(&buf, " \"%s\"", escaped); git__free(escaped); } git_buf_puts(&buf, "]\n"); if (git_buf_oom(&buf)) return -1; result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size); git_buf_dispose(&buf); return result; } static const char *quotes_for_value(const char *value) { const char *ptr; if (value[0] == ' ' || value[0] == '\0') return "\""; for (ptr = value; *ptr; ++ptr) { if (*ptr == ';' || *ptr == '#') return "\""; } if (ptr[-1] == ' ') return "\""; return ""; } struct write_data { git_buf *buf; git_buf buffered_comment; unsigned int in_section : 1, preg_replaced : 1; const char *orig_section; const char *section; const char *orig_name; const char *name; const git_regexp *preg; const char *value; }; static int write_line_to(git_buf *buf, const char *line, size_t line_len) { int result = git_buf_put(buf, line, line_len); if (!result && line_len && line[line_len-1] != '\n') result = git_buf_printf(buf, "\n"); return result; } static int write_line(struct write_data *write_data, const char *line, size_t line_len) { return write_line_to(write_data->buf, line, line_len); } static int write_value(struct write_data *write_data) { const char *q; int result; q = quotes_for_value(write_data->value); result = git_buf_printf(write_data->buf, "\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q); /* If we are updating a single name/value, we're done. Setting `value` * to `NULL` will prevent us from trying to write it again later (in * `write_on_section`) if we see the same section repeated. */ if (!write_data->preg) write_data->value = NULL; return result; } static int write_on_section( git_config_parser *reader, const char *current_section, const char *line, size_t line_len, void *data) { struct write_data *write_data = (struct write_data *)data; int result = 0; GIT_UNUSED(reader); /* If we were previously in the correct section (but aren't anymore) * and haven't written our value (for a simple name/value set, not * a multivar), then append it to the end of the section before writing * the new one. */ if (write_data->in_section && !write_data->preg && write_data->value) result = write_value(write_data); write_data->in_section = strcmp(current_section, write_data->section) == 0; /* * If there were comments just before this section, dump them as well. */ if (!result) { result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size); git_buf_clear(&write_data->buffered_comment); } if (!result) result = write_line(write_data, line, line_len); return result; } static int write_on_variable( git_config_parser *reader, const char *current_section, const char *var_name, const char *var_value, const char *line, size_t line_len, void *data) { struct write_data *write_data = (struct write_data *)data; bool has_matched = false; int error; GIT_UNUSED(reader); GIT_UNUSED(current_section); /* * If there were comments just before this variable, let's dump them as well. */ if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0) return error; git_buf_clear(&write_data->buffered_comment); /* See if we are to update this name/value pair; first examine name */ if (write_data->in_section && strcasecmp(write_data->name, var_name) == 0) has_matched = true; /* If we have a regex to match the value, see if it matches */ if (has_matched && write_data->preg != NULL) has_matched = (git_regexp_match(write_data->preg, var_value) == 0); /* If this isn't the name/value we're looking for, simply dump the * existing data back out and continue on. */ if (!has_matched) return write_line(write_data, line, line_len); write_data->preg_replaced = 1; /* If value is NULL, we are deleting this value; write nothing. */ if (!write_data->value) return 0; return write_value(write_data); } static int write_on_comment(git_config_parser *reader, const char *line, size_t line_len, void *data) { struct write_data *write_data; GIT_UNUSED(reader); write_data = (struct write_data *)data; return write_line_to(&write_data->buffered_comment, line, line_len); } static int write_on_eof( git_config_parser *reader, const char *current_section, void *data) { struct write_data *write_data = (struct write_data *)data; int result = 0; GIT_UNUSED(reader); /* * If we've buffered comments when reaching EOF, make sure to dump them. */ if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0) return result; /* If we are at the EOF and have not written our value (again, for a * simple name/value set, not a multivar) then we have never seen the * section in question and should create a new section and write the * value. */ if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) { /* write the section header unless we're already in it */ if (!current_section || strcmp(current_section, write_data->section)) result = write_section(write_data->buf, write_data->orig_section); if (!result) result = write_value(write_data); } return result; } /* * This is pretty much the parsing, except we write out anything we don't have */ static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char *value) { char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot; git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT; git_config_parser parser = GIT_CONFIG_PARSER_INIT; git_filebuf file = GIT_FILEBUF_INIT; struct write_data write_data; int error; memset(&write_data, 0, sizeof(write_data)); if (cfg->locked) { error = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content) == NULL ? "" : git_buf_cstr(&cfg->locked_content)); } else { if ((error = git_filebuf_open(&file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) goto done; /* We need to read in our own config file */ error = git_futils_readbuffer(&contents, cfg->file.path); } if (error < 0 && error != GIT_ENOTFOUND) goto done; if ((git_config_parser_init(&parser, cfg->file.path, contents.ptr, contents.size)) < 0) goto done; ldot = strrchr(key, '.'); name = ldot + 1; section = git__strndup(key, ldot - key); GIT_ERROR_CHECK_ALLOC(section); ldot = strrchr(orig_key, '.'); orig_name = ldot + 1; orig_section = git__strndup(orig_key, ldot - orig_key); GIT_ERROR_CHECK_ALLOC(orig_section); write_data.buf = &buf; write_data.orig_section = orig_section; write_data.section = section; write_data.orig_name = orig_name; write_data.name = name; write_data.preg = preg; write_data.value = value; if ((error = git_config_parse(&parser, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data)) < 0) goto done; if (cfg->locked) { size_t len = buf.asize; /* Update our copy with the modified contents */ git_buf_dispose(&cfg->locked_content); git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len); } else { git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); if ((error = git_filebuf_commit(&file)) < 0) goto done; if ((error = config_file_refresh_from_buffer(&cfg->parent, buf.ptr, buf.size)) < 0) goto done; } done: git__free(section); git__free(orig_section); git_buf_dispose(&write_data.buffered_comment); git_buf_dispose(&buf); git_buf_dispose(&contents); git_filebuf_cleanup(&file); git_config_parser_dispose(&parser); return error; } git2r/src/libgit2/src/reset.c0000644000175000017500000001224414125111754015637 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "commit.h" #include "tag.h" #include "merge.h" #include "diff.h" #include "annotated_commit.h" #include "git2/reset.h" #include "git2/checkout.h" #include "git2/merge.h" #include "git2/refs.h" #define ERROR_MSG "Cannot perform reset" int git_reset_default( git_repository *repo, const git_object *target, const git_strarray *pathspecs) { git_object *commit = NULL; git_tree *tree = NULL; git_diff *diff = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; size_t i, max_i; git_index_entry entry; int error; git_index *index = NULL; GIT_ASSERT_ARG(pathspecs && pathspecs->count > 0); memset(&entry, 0, sizeof(git_index_entry)); if ((error = git_repository_index(&index, repo)) < 0) goto cleanup; if (target) { if (git_object_owner(target) != repo) { git_error_set(GIT_ERROR_OBJECT, "%s_default - The given target does not belong to this repository.", ERROR_MSG); return -1; } if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 || (error = git_commit_tree(&tree, (git_commit *)commit)) < 0) goto cleanup; } opts.pathspec = *pathspecs; opts.flags = GIT_DIFF_REVERSE; if ((error = git_diff_tree_to_index( &diff, repo, tree, index, &opts)) < 0) goto cleanup; for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) { const git_diff_delta *delta = git_diff_get_delta(diff, i); GIT_ASSERT(delta->status == GIT_DELTA_ADDED || delta->status == GIT_DELTA_MODIFIED || delta->status == GIT_DELTA_CONFLICTED || delta->status == GIT_DELTA_DELETED); error = git_index_conflict_remove(index, delta->old_file.path); if (error < 0) { if (delta->status == GIT_DELTA_ADDED && error == GIT_ENOTFOUND) git_error_clear(); else goto cleanup; } if (delta->status == GIT_DELTA_DELETED) { if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0) goto cleanup; } else { entry.mode = delta->new_file.mode; git_oid_cpy(&entry.id, &delta->new_file.id); entry.path = (char *)delta->new_file.path; if ((error = git_index_add(index, &entry)) < 0) goto cleanup; } } error = git_index_write(index); cleanup: git_object_free(commit); git_tree_free(tree); git_index_free(index); git_diff_free(diff); return error; } static int reset( git_repository *repo, const git_object *target, const char *to, git_reset_t reset_type, const git_checkout_options *checkout_opts) { git_object *commit = NULL; git_index *index = NULL; git_tree *tree = NULL; int error = 0; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_buf log_message = GIT_BUF_INIT; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(target); if (checkout_opts) opts = *checkout_opts; if (git_object_owner(target) != repo) { git_error_set(GIT_ERROR_OBJECT, "%s - The given target does not belong to this repository.", ERROR_MSG); return -1; } if (reset_type != GIT_RESET_SOFT && (error = git_repository__ensure_not_bare(repo, reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0) return error; if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 || (error = git_repository_index(&index, repo)) < 0 || (error = git_commit_tree(&tree, (git_commit *)commit)) < 0) goto cleanup; if (reset_type == GIT_RESET_SOFT && (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE || git_index_has_conflicts(index))) { git_error_set(GIT_ERROR_OBJECT, "%s (soft) in the middle of a merge", ERROR_MSG); error = GIT_EUNMERGED; goto cleanup; } if ((error = git_buf_printf(&log_message, "reset: moving to %s", to)) < 0) return error; if (reset_type == GIT_RESET_HARD) { /* overwrite working directory with the new tree */ opts.checkout_strategy = GIT_CHECKOUT_FORCE; if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0) goto cleanup; } /* move HEAD to the new target */ if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE, git_object_id(commit), NULL, git_buf_cstr(&log_message))) < 0) goto cleanup; if (reset_type > GIT_RESET_SOFT) { /* reset index to the target content */ if ((error = git_index_read_tree(index, tree)) < 0 || (error = git_index_write(index)) < 0) goto cleanup; if ((error = git_repository_state_cleanup(repo)) < 0) { git_error_set(GIT_ERROR_INDEX, "%s - failed to clean up merge data", ERROR_MSG); goto cleanup; } } cleanup: git_object_free(commit); git_index_free(index); git_tree_free(tree); git_buf_dispose(&log_message); return error; } int git_reset( git_repository *repo, const git_object *target, git_reset_t reset_type, const git_checkout_options *checkout_opts) { return reset(repo, target, git_oid_tostr_s(git_object_id(target)), reset_type, checkout_opts); } int git_reset_from_annotated( git_repository *repo, const git_annotated_commit *commit, git_reset_t reset_type, const git_checkout_options *checkout_opts) { return reset(repo, (git_object *) commit->commit, commit->description, reset_type, checkout_opts); } git2r/src/libgit2/src/diff.c0000644000175000017500000002042314125111754015423 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "diff.h" #include "common.h" #include "patch.h" #include "email.h" #include "commit.h" #include "index.h" #include "diff_generate.h" #include "git2/version.h" #include "git2/email.h" struct patch_id_args { git_hash_ctx ctx; git_oid result; int first_file; }; GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta) { const char *str = delta->old_file.path; if (!str || delta->status == GIT_DELTA_ADDED || delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED) str = delta->new_file.path; return str; } int git_diff_delta__cmp(const void *a, const void *b) { const git_diff_delta *da = a, *db = b; int val = strcmp(diff_delta__path(da), diff_delta__path(db)); return val ? val : ((int)da->status - (int)db->status); } int git_diff_delta__casecmp(const void *a, const void *b) { const git_diff_delta *da = a, *db = b; int val = strcasecmp(diff_delta__path(da), diff_delta__path(db)); return val ? val : ((int)da->status - (int)db->status); } int git_diff__entry_cmp(const void *a, const void *b) { const git_index_entry *entry_a = a; const git_index_entry *entry_b = b; return strcmp(entry_a->path, entry_b->path); } int git_diff__entry_icmp(const void *a, const void *b) { const git_index_entry *entry_a = a; const git_index_entry *entry_b = b; return strcasecmp(entry_a->path, entry_b->path); } void git_diff_free(git_diff *diff) { if (!diff) return; GIT_REFCOUNT_DEC(diff, diff->free_fn); } void git_diff_addref(git_diff *diff) { GIT_REFCOUNT_INC(diff); } size_t git_diff_num_deltas(const git_diff *diff) { GIT_ASSERT_ARG(diff); return diff->deltas.length; } size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type) { size_t i, count = 0; const git_diff_delta *delta; GIT_ASSERT_ARG(diff); git_vector_foreach(&diff->deltas, i, delta) { count += (delta->status == type); } return count; } const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx) { GIT_ASSERT_ARG_WITH_RETVAL(diff, NULL); return git_vector_get(&diff->deltas, idx); } int git_diff_is_sorted_icase(const git_diff *diff) { return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0; } int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff) { GIT_ASSERT_ARG(out); GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata"); out->stat_calls = diff->perf.stat_calls; out->oid_calculations = diff->perf.oid_calculations; return 0; } int git_diff_foreach( git_diff *diff, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) { int error = 0; git_diff_delta *delta; size_t idx; GIT_ASSERT_ARG(diff); git_vector_foreach(&diff->deltas, idx, delta) { git_patch *patch; /* check flags against patch status */ if (git_diff_delta__should_skip(&diff->opts, delta)) continue; if ((error = git_patch_from_diff(&patch, diff, idx)) != 0) break; error = git_patch__invoke_callbacks(patch, file_cb, binary_cb, hunk_cb, data_cb, payload); git_patch_free(patch); if (error) break; } return error; } #ifndef GIT_DEPRECATE_HARD int git_diff_format_email( git_buf *out, git_diff *diff, const git_diff_format_email_options *opts) { git_email_create_options email_create_opts = GIT_EMAIL_CREATE_OPTIONS_INIT; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(diff); GIT_ASSERT_ARG(opts && opts->summary && opts->id && opts->author); GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, "git_format_email_options"); if ((opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0) email_create_opts.subject_prefix = ""; error = git_email__append_from_diff(out, diff, opts->patch_no, opts->total_patches, opts->id, opts->summary, opts->body, opts->author, &email_create_opts); return error; } int git_diff_commit_as_email( git_buf *out, git_repository *repo, git_commit *commit, size_t patch_no, size_t total_patches, uint32_t flags, const git_diff_options *diff_opts) { git_diff *diff = NULL; git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; const git_oid *commit_id; const char *summary, *body; const git_signature *author; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(commit); commit_id = git_commit_id(commit); summary = git_commit_summary(commit); body = git_commit_body(commit); author = git_commit_author(commit); if ((flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0) opts.subject_prefix = ""; if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0) return error; error = git_email_create_from_diff(out, diff, patch_no, total_patches, commit_id, summary, body, author, &opts); git_diff_free(diff); return error; } int git_diff_init_options(git_diff_options *opts, unsigned int version) { return git_diff_options_init(opts, version); } int git_diff_find_init_options( git_diff_find_options *opts, unsigned int version) { return git_diff_find_options_init(opts, version); } int git_diff_format_email_options_init( git_diff_format_email_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_diff_format_email_options, GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT); return 0; } int git_diff_format_email_init_options( git_diff_format_email_options *opts, unsigned int version) { return git_diff_format_email_options_init(opts, version); } #endif int git_diff_options_init(git_diff_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT); return 0; } int git_diff_find_options_init( git_diff_find_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT); return 0; } static int flush_hunk(git_oid *result, git_hash_ctx *ctx) { git_oid hash; unsigned short carry = 0; int error, i; if ((error = git_hash_final(&hash, ctx)) < 0 || (error = git_hash_init(ctx)) < 0) return error; for (i = 0; i < GIT_OID_RAWSZ; i++) { carry += result->id[i] + hash.id[i]; result->id[i] = (unsigned char)carry; carry >>= 8; } return 0; } static void strip_spaces(git_buf *buf) { char *src = buf->ptr, *dst = buf->ptr; char c; size_t len = 0; while ((c = *src++) != '\0') { if (!git__isspace(c)) { *dst++ = c; len++; } } git_buf_truncate(buf, len); } static int diff_patchid_print_callback_to_buf( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { struct patch_id_args *args = (struct patch_id_args *) payload; git_buf buf = GIT_BUF_INIT; int error = 0; if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL || line->origin == GIT_DIFF_LINE_ADD_EOFNL || line->origin == GIT_DIFF_LINE_DEL_EOFNL) goto out; if ((error = git_diff_print_callback__to_buf(delta, hunk, line, &buf)) < 0) goto out; strip_spaces(&buf); if (line->origin == GIT_DIFF_LINE_FILE_HDR && !args->first_file && (error = flush_hunk(&args->result, &args->ctx) < 0)) goto out; if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0) goto out; if (line->origin == GIT_DIFF_LINE_FILE_HDR && args->first_file) args->first_file = 0; out: git_buf_dispose(&buf); return error; } int git_diff_patchid_options_init(git_diff_patchid_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_INIT); return 0; } int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts) { struct patch_id_args args; int error; GIT_ERROR_CHECK_VERSION( opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options"); memset(&args, 0, sizeof(args)); args.first_file = 1; if ((error = git_hash_ctx_init(&args.ctx)) < 0) goto out; if ((error = git_diff_print(diff, GIT_DIFF_FORMAT_PATCH_ID, diff_patchid_print_callback_to_buf, &args)) < 0) goto out; if ((error = (flush_hunk(&args.result, &args.ctx))) < 0) goto out; git_oid_cpy(out, &args.result); out: git_hash_ctx_cleanup(&args.ctx); return error; } git2r/src/libgit2/src/odb.h0000644000175000017500000000765614125111754015301 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_odb_h__ #define INCLUDE_odb_h__ #include "common.h" #include "git2/odb.h" #include "git2/oid.h" #include "git2/types.h" #include "git2/sys/commit_graph.h" #include "cache.h" #include "commit_graph.h" #include "filter.h" #include "posix.h" #include "vector.h" #define GIT_OBJECTS_DIR "objects/" #define GIT_OBJECT_DIR_MODE 0777 #define GIT_OBJECT_FILE_MODE 0444 #define GIT_ODB_DEFAULT_LOOSE_PRIORITY 1 #define GIT_ODB_DEFAULT_PACKED_PRIORITY 2 extern bool git_odb__strict_hash_verification; /* DO NOT EXPORT */ typedef struct { void *data; /**< Raw, decompressed object data. */ size_t len; /**< Total number of bytes in data. */ git_object_t type; /**< Type of this object. */ } git_rawobj; /* EXPORT */ struct git_odb_object { git_cached_obj cached; void *buffer; }; /* EXPORT */ struct git_odb { git_refcount rc; git_mutex lock; /* protects backends */ git_vector backends; git_cache own_cache; git_commit_graph *cgraph; unsigned int do_fsync :1; }; typedef enum { GIT_ODB_CAP_FROM_OWNER = -1, } git_odb_cap_t; /* * Set the capabilities for the object database. */ int git_odb__set_caps(git_odb *odb, int caps); /* * Add the default loose and packed backends for a database. */ int git_odb__add_default_backends( git_odb *db, const char *objects_dir, bool as_alternates, int alternate_depth); /* * Hash a git_rawobj internally. * The `git_rawobj` is supposed to be previously initialized */ int git_odb__hashobj(git_oid *id, git_rawobj *obj); /* * Format the object header such as it would appear in the on-disk object */ int git_odb__format_object_header(size_t *out_len, char *hdr, size_t hdr_size, git_object_size_t obj_len, git_object_t obj_type); /* * Hash an open file descriptor. * This is a performance call when the contents of a fd need to be hashed, * but the fd is already open and we have the size of the contents. * * Saves us some `stat` calls. * * The fd is never closed, not even on error. It must be opened and closed * by the caller */ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_object_t type); /* * Hash an open file descriptor applying an array of filters * Acts just like git_odb__hashfd with the addition of filters... */ int git_odb__hashfd_filtered( git_oid *out, git_file fd, size_t len, git_object_t type, git_filter_list *fl); /* * Hash a `path`, assuming it could be a POSIX symlink: if the path is a * symlink, then the raw contents of the symlink will be hashed. Otherwise, * this will fallback to `git_odb__hashfd`. * * The hash type for this call is always `GIT_OBJECT_BLOB` because * symlinks may only point to blobs. */ int git_odb__hashlink(git_oid *out, const char *path); /** * Generate a GIT_EMISMATCH error for the ODB. */ int git_odb__error_mismatch( const git_oid *expected, const git_oid *actual); /* * Generate a GIT_ENOTFOUND error for the ODB. */ int git_odb__error_notfound( const char *message, const git_oid *oid, size_t oid_len); /* * Generate a GIT_EAMBIGUOUS error for the ODB. */ int git_odb__error_ambiguous(const char *message); /* * Attempt to read object header or just return whole object if it could * not be read. */ int git_odb__read_header_or_object( git_odb_object **out, size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id); /* * Attempt to get the ODB's commit-graph file. This object is still owned by * the ODB. If the repository does not contain a commit-graph, it will return * GIT_ENOTFOUND. */ int git_odb__get_commit_graph_file(git_commit_graph_file **out, git_odb *odb); /* freshen an entry in the object database */ int git_odb__freshen(git_odb *db, const git_oid *id); /* fully free the object; internal method, DO NOT EXPORT */ void git_odb_object__free(void *object); #endif git2r/src/libgit2/src/stash.c0000644000175000017500000006607214125111754015647 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "repository.h" #include "commit.h" #include "message.h" #include "tree.h" #include "reflog.h" #include "blob.h" #include "git2/diff.h" #include "git2/stash.h" #include "git2/status.h" #include "git2/checkout.h" #include "git2/index.h" #include "git2/transaction.h" #include "git2/merge.h" #include "index.h" #include "signature.h" #include "iterator.h" #include "merge.h" #include "diff.h" #include "diff_generate.h" static int create_error(int error, const char *msg) { git_error_set(GIT_ERROR_STASH, "cannot stash changes - %s", msg); return error; } static int retrieve_head(git_reference **out, git_repository *repo) { int error = git_repository_head(out, repo); if (error == GIT_EUNBORNBRANCH) return create_error(error, "you do not have the initial commit yet."); return error; } static int append_abbreviated_oid(git_buf *out, const git_oid *b_commit) { char *formatted_oid; formatted_oid = git_oid_allocfmt(b_commit); GIT_ERROR_CHECK_ALLOC(formatted_oid); git_buf_put(out, formatted_oid, 7); git__free(formatted_oid); return git_buf_oom(out) ? -1 : 0; } static int append_commit_description(git_buf *out, git_commit *commit) { const char *summary = git_commit_summary(commit); GIT_ERROR_CHECK_ALLOC(summary); if (append_abbreviated_oid(out, git_commit_id(commit)) < 0) return -1; git_buf_putc(out, ' '); git_buf_puts(out, summary); git_buf_putc(out, '\n'); return git_buf_oom(out) ? -1 : 0; } static int retrieve_base_commit_and_message( git_commit **b_commit, git_buf *stash_message, git_repository *repo) { git_reference *head = NULL; int error; if ((error = retrieve_head(&head, repo)) < 0) return error; if (strcmp("HEAD", git_reference_name(head)) == 0) error = git_buf_puts(stash_message, "(no branch): "); else error = git_buf_printf( stash_message, "%s: ", git_reference_name(head) + strlen(GIT_REFS_HEADS_DIR)); if (error < 0) goto cleanup; if ((error = git_commit_lookup( b_commit, repo, git_reference_target(head))) < 0) goto cleanup; if ((error = append_commit_description(stash_message, *b_commit)) < 0) goto cleanup; cleanup: git_reference_free(head); return error; } static int build_tree_from_index( git_tree **out, git_repository *repo, git_index *index) { int error; git_oid i_tree_oid; if ((error = git_index_write_tree_to(&i_tree_oid, index, repo)) < 0) return error; return git_tree_lookup(out, repo, &i_tree_oid); } static int commit_index( git_commit **i_commit, git_repository *repo, git_index *index, const git_signature *stasher, const char *message, const git_commit *parent) { git_tree *i_tree = NULL; git_oid i_commit_oid; git_buf msg = GIT_BUF_INIT; int error; if ((error = build_tree_from_index(&i_tree, repo, index)) < 0) goto cleanup; if ((error = git_buf_printf(&msg, "index on %s\n", message)) < 0) goto cleanup; if ((error = git_commit_create( &i_commit_oid, git_index_owner(index), NULL, stasher, stasher, NULL, git_buf_cstr(&msg), i_tree, 1, &parent)) < 0) goto cleanup; error = git_commit_lookup(i_commit, git_index_owner(index), &i_commit_oid); cleanup: git_tree_free(i_tree); git_buf_dispose(&msg); return error; } struct stash_update_rules { bool include_changed; bool include_untracked; bool include_ignored; }; /* * Similar to git_index_add_bypath but able to operate on any * index without making assumptions about the repository's index */ static int stash_to_index( git_repository *repo, git_index *index, const char *path) { git_index *repo_index = NULL; git_index_entry entry = {{0}}; struct stat st; int error; if (!git_repository_is_bare(repo) && (error = git_repository_index__weakptr(&repo_index, repo)) < 0) return error; if ((error = git_blob__create_from_paths( &entry.id, &st, repo, NULL, path, 0, true)) < 0) return error; git_index_entry__init_from_stat(&entry, &st, (repo_index == NULL || !repo_index->distrust_filemode)); entry.path = path; return git_index_add(index, &entry); } static int stash_update_index_from_diff( git_repository *repo, git_index *index, const git_diff *diff, struct stash_update_rules *data) { int error = 0; size_t d, max_d = git_diff_num_deltas(diff); for (d = 0; !error && d < max_d; ++d) { const char *add_path = NULL; const git_diff_delta *delta = git_diff_get_delta(diff, d); switch (delta->status) { case GIT_DELTA_IGNORED: if (data->include_ignored) add_path = delta->new_file.path; break; case GIT_DELTA_UNTRACKED: if (data->include_untracked && delta->new_file.mode != GIT_FILEMODE_TREE) add_path = delta->new_file.path; break; case GIT_DELTA_ADDED: case GIT_DELTA_MODIFIED: if (data->include_changed) add_path = delta->new_file.path; break; case GIT_DELTA_DELETED: if (data->include_changed && !git_index_find(NULL, index, delta->old_file.path)) error = git_index_remove(index, delta->old_file.path, 0); break; default: /* Unimplemented */ git_error_set( GIT_ERROR_INVALID, "cannot update index. Unimplemented status (%d)", delta->status); return -1; } if (add_path != NULL) error = stash_to_index(repo, index, add_path); } return error; } static int build_untracked_tree( git_tree **tree_out, git_repository *repo, git_commit *i_commit, uint32_t flags) { git_index *i_index = NULL; git_tree *i_tree = NULL; git_diff *diff = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; struct stash_update_rules data = {0}; int error; if ((error = git_index_new(&i_index)) < 0) goto cleanup; if (flags & GIT_STASH_INCLUDE_UNTRACKED) { opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS; data.include_untracked = true; } if (flags & GIT_STASH_INCLUDE_IGNORED) { opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_IGNORED_DIRS; data.include_ignored = true; } if ((error = git_commit_tree(&i_tree, i_commit)) < 0) goto cleanup; if ((error = git_diff_tree_to_workdir(&diff, repo, i_tree, &opts)) < 0) goto cleanup; if ((error = stash_update_index_from_diff(repo, i_index, diff, &data)) < 0) goto cleanup; error = build_tree_from_index(tree_out, repo, i_index); cleanup: git_diff_free(diff); git_tree_free(i_tree); git_index_free(i_index); return error; } static int commit_untracked( git_commit **u_commit, git_repository *repo, const git_signature *stasher, const char *message, git_commit *i_commit, uint32_t flags) { git_tree *u_tree = NULL; git_oid u_commit_oid; git_buf msg = GIT_BUF_INIT; int error; if ((error = build_untracked_tree(&u_tree, repo, i_commit, flags)) < 0) goto cleanup; if ((error = git_buf_printf(&msg, "untracked files on %s\n", message)) < 0) goto cleanup; if ((error = git_commit_create( &u_commit_oid, repo, NULL, stasher, stasher, NULL, git_buf_cstr(&msg), u_tree, 0, NULL)) < 0) goto cleanup; error = git_commit_lookup(u_commit, repo, &u_commit_oid); cleanup: git_tree_free(u_tree); git_buf_dispose(&msg); return error; } static git_diff_delta *stash_delta_merge( const git_diff_delta *a, const git_diff_delta *b, git_pool *pool) { /* Special case for stash: if a file is deleted in the index, but exists * in the working tree, we need to stash the workdir copy for the workdir. */ if (a->status == GIT_DELTA_DELETED && b->status == GIT_DELTA_UNTRACKED) { git_diff_delta *dup = git_diff__delta_dup(b, pool); if (dup) dup->status = GIT_DELTA_MODIFIED; return dup; } return git_diff__merge_like_cgit(a, b, pool); } static int build_workdir_tree( git_tree **tree_out, git_repository *repo, git_index *i_index, git_commit *b_commit) { git_tree *b_tree = NULL; git_diff *diff = NULL, *idx_to_wd = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; struct stash_update_rules data = {0}; int error; opts.flags = GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_UNTRACKED; if ((error = git_commit_tree(&b_tree, b_commit)) < 0) goto cleanup; if ((error = git_diff_tree_to_index(&diff, repo, b_tree, i_index, &opts)) < 0 || (error = git_diff_index_to_workdir(&idx_to_wd, repo, i_index, &opts)) < 0 || (error = git_diff__merge(diff, idx_to_wd, stash_delta_merge)) < 0) goto cleanup; data.include_changed = true; if ((error = stash_update_index_from_diff(repo, i_index, diff, &data)) < 0) goto cleanup; error = build_tree_from_index(tree_out, repo, i_index); cleanup: git_diff_free(idx_to_wd); git_diff_free(diff); git_tree_free(b_tree); return error; } static int commit_worktree( git_oid *w_commit_oid, git_repository *repo, const git_signature *stasher, const char *message, git_commit *i_commit, git_commit *b_commit, git_commit *u_commit) { const git_commit *parents[] = { NULL, NULL, NULL }; git_index *i_index = NULL, *r_index = NULL; git_tree *w_tree = NULL; int error = 0, ignorecase; parents[0] = b_commit; parents[1] = i_commit; parents[2] = u_commit; if ((error = git_repository_index(&r_index, repo) < 0) || (error = git_index_new(&i_index)) < 0 || (error = git_index__fill(i_index, &r_index->entries) < 0) || (error = git_repository__configmap_lookup(&ignorecase, repo, GIT_CONFIGMAP_IGNORECASE)) < 0) goto cleanup; git_index__set_ignore_case(i_index, ignorecase); if ((error = build_workdir_tree(&w_tree, repo, i_index, b_commit)) < 0) goto cleanup; error = git_commit_create( w_commit_oid, repo, NULL, stasher, stasher, NULL, message, w_tree, u_commit ? 3 : 2, parents); cleanup: git_tree_free(w_tree); git_index_free(i_index); git_index_free(r_index); return error; } static int prepare_worktree_commit_message(git_buf *out, const char *user_message) { git_buf buf = GIT_BUF_INIT; int error = 0; if (!user_message) { git_buf_printf(&buf, "WIP on %s", git_buf_cstr(out)); } else { const char *colon; if ((colon = strchr(git_buf_cstr(out), ':')) == NULL) goto cleanup; git_buf_puts(&buf, "On "); git_buf_put(&buf, git_buf_cstr(out), colon - out->ptr); git_buf_printf(&buf, ": %s\n", user_message); } if (git_buf_oom(&buf)) { error = -1; goto cleanup; } git_buf_swap(out, &buf); cleanup: git_buf_dispose(&buf); return error; } static int update_reflog( git_oid *w_commit_oid, git_repository *repo, const char *message) { git_reference *stash; int error; if ((error = git_reference_ensure_log(repo, GIT_REFS_STASH_FILE)) < 0) return error; error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, message); git_reference_free(stash); return error; } static int is_dirty_cb(const char *path, unsigned int status, void *payload) { GIT_UNUSED(path); GIT_UNUSED(status); GIT_UNUSED(payload); return GIT_PASSTHROUGH; } static int ensure_there_are_changes_to_stash(git_repository *repo, uint32_t flags) { int error; git_status_options opts = GIT_STATUS_OPTIONS_INIT; opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES; if (flags & GIT_STASH_INCLUDE_UNTRACKED) opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; if (flags & GIT_STASH_INCLUDE_IGNORED) opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL); if (error == GIT_PASSTHROUGH) return 0; if (!error) return create_error(GIT_ENOTFOUND, "there is nothing to stash."); return error; } static int reset_index_and_workdir(git_repository *repo, git_commit *commit, uint32_t flags) { git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_FORCE; if (flags & GIT_STASH_INCLUDE_UNTRACKED) opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; if (flags & GIT_STASH_INCLUDE_IGNORED) opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_IGNORED; return git_checkout_tree(repo, (git_object *)commit, &opts); } int git_stash_save( git_oid *out, git_repository *repo, const git_signature *stasher, const char *message, uint32_t flags) { git_index *index = NULL; git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL; git_buf msg = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(stasher); if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0) return error; if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0) goto cleanup; if ((error = ensure_there_are_changes_to_stash(repo, flags)) < 0) goto cleanup; if ((error = git_repository_index(&index, repo)) < 0) goto cleanup; if ((error = commit_index(&i_commit, repo, index, stasher, git_buf_cstr(&msg), b_commit)) < 0) goto cleanup; if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) && (error = commit_untracked(&u_commit, repo, stasher, git_buf_cstr(&msg), i_commit, flags)) < 0) goto cleanup; if ((error = prepare_worktree_commit_message(&msg, message)) < 0) goto cleanup; if ((error = commit_worktree(out, repo, stasher, git_buf_cstr(&msg), i_commit, b_commit, u_commit)) < 0) goto cleanup; git_buf_rtrim(&msg); if ((error = update_reflog(out, repo, git_buf_cstr(&msg))) < 0) goto cleanup; if ((error = reset_index_and_workdir(repo, (flags & GIT_STASH_KEEP_INDEX) ? i_commit : b_commit, flags)) < 0) goto cleanup; cleanup: git_buf_dispose(&msg); git_commit_free(i_commit); git_commit_free(b_commit); git_commit_free(u_commit); git_index_free(index); return error; } static int retrieve_stash_commit( git_commit **commit, git_repository *repo, size_t index) { git_reference *stash = NULL; git_reflog *reflog = NULL; int error; size_t max; const git_reflog_entry *entry; if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0) goto cleanup; if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0) goto cleanup; max = git_reflog_entrycount(reflog); if (!max || index > max - 1) { error = GIT_ENOTFOUND; git_error_set(GIT_ERROR_STASH, "no stashed state at position %" PRIuZ, index); goto cleanup; } entry = git_reflog_entry_byindex(reflog, index); if ((error = git_commit_lookup(commit, repo, git_reflog_entry_id_new(entry))) < 0) goto cleanup; cleanup: git_reference_free(stash); git_reflog_free(reflog); return error; } static int retrieve_stash_trees( git_tree **out_stash_tree, git_tree **out_base_tree, git_tree **out_index_tree, git_tree **out_index_parent_tree, git_tree **out_untracked_tree, git_commit *stash_commit) { git_tree *stash_tree = NULL; git_commit *base_commit = NULL; git_tree *base_tree = NULL; git_commit *index_commit = NULL; git_tree *index_tree = NULL; git_commit *index_parent_commit = NULL; git_tree *index_parent_tree = NULL; git_commit *untracked_commit = NULL; git_tree *untracked_tree = NULL; int error; if ((error = git_commit_tree(&stash_tree, stash_commit)) < 0) goto cleanup; if ((error = git_commit_parent(&base_commit, stash_commit, 0)) < 0) goto cleanup; if ((error = git_commit_tree(&base_tree, base_commit)) < 0) goto cleanup; if ((error = git_commit_parent(&index_commit, stash_commit, 1)) < 0) goto cleanup; if ((error = git_commit_tree(&index_tree, index_commit)) < 0) goto cleanup; if ((error = git_commit_parent(&index_parent_commit, index_commit, 0)) < 0) goto cleanup; if ((error = git_commit_tree(&index_parent_tree, index_parent_commit)) < 0) goto cleanup; if (git_commit_parentcount(stash_commit) == 3) { if ((error = git_commit_parent(&untracked_commit, stash_commit, 2)) < 0) goto cleanup; if ((error = git_commit_tree(&untracked_tree, untracked_commit)) < 0) goto cleanup; } *out_stash_tree = stash_tree; *out_base_tree = base_tree; *out_index_tree = index_tree; *out_index_parent_tree = index_parent_tree; *out_untracked_tree = untracked_tree; cleanup: git_commit_free(untracked_commit); git_commit_free(index_parent_commit); git_commit_free(index_commit); git_commit_free(base_commit); if (error < 0) { git_tree_free(stash_tree); git_tree_free(base_tree); git_tree_free(index_tree); git_tree_free(index_parent_tree); git_tree_free(untracked_tree); } return error; } static int merge_indexes( git_index **out, git_repository *repo, git_tree *ancestor_tree, git_index *ours_index, git_index *theirs_index) { git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 || (error = git_iterator_for_index(&theirs, repo, theirs_index, &iter_opts)) < 0) goto done; error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL); done: git_iterator_free(ancestor); git_iterator_free(ours); git_iterator_free(theirs); return error; } static int merge_index_and_tree( git_index **out, git_repository *repo, git_tree *ancestor_tree, git_index *ours_index, git_tree *theirs_tree) { git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 || (error = git_iterator_for_tree(&theirs, theirs_tree, &iter_opts)) < 0) goto done; error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL); done: git_iterator_free(ancestor); git_iterator_free(ours); git_iterator_free(theirs); return error; } static void normalize_apply_options( git_stash_apply_options *opts, const git_stash_apply_options *given_apply_opts) { if (given_apply_opts != NULL) { memcpy(opts, given_apply_opts, sizeof(git_stash_apply_options)); } else { git_stash_apply_options default_apply_opts = GIT_STASH_APPLY_OPTIONS_INIT; memcpy(opts, &default_apply_opts, sizeof(git_stash_apply_options)); } opts->checkout_options.checkout_strategy |= GIT_CHECKOUT_NO_REFRESH; if (!opts->checkout_options.our_label) opts->checkout_options.our_label = "Updated upstream"; if (!opts->checkout_options.their_label) opts->checkout_options.their_label = "Stashed changes"; } int git_stash_apply_options_init(git_stash_apply_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_stash_apply_options, GIT_STASH_APPLY_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version) { return git_stash_apply_options_init(opts, version); } #endif #define NOTIFY_PROGRESS(opts, progress_type) \ do { \ if ((opts).progress_cb && \ (error = (opts).progress_cb((progress_type), (opts).progress_payload))) { \ error = (error < 0) ? error : -1; \ goto cleanup; \ } \ } while(false); static int ensure_clean_index(git_repository *repo, git_index *index) { git_tree *head_tree = NULL; git_diff *index_diff = NULL; int error = 0; if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || (error = git_diff_tree_to_index( &index_diff, repo, head_tree, index, NULL)) < 0) goto done; if (git_diff_num_deltas(index_diff) > 0) { git_error_set(GIT_ERROR_STASH, "%" PRIuZ " uncommitted changes exist in the index", git_diff_num_deltas(index_diff)); error = GIT_EUNCOMMITTED; } done: git_diff_free(index_diff); git_tree_free(head_tree); return error; } static int stage_new_file(const git_index_entry **entries, void *data) { git_index *index = data; if(entries[0] == NULL) return git_index_add(index, entries[1]); else return git_index_add(index, entries[0]); } static int stage_new_files( git_index **out, git_tree *parent_tree, git_tree *tree) { git_iterator *iterators[2] = { NULL, NULL }; git_iterator_options iterator_options = GIT_ITERATOR_OPTIONS_INIT; git_index *index = NULL; int error; if ((error = git_index_new(&index)) < 0 || (error = git_iterator_for_tree( &iterators[0], parent_tree, &iterator_options)) < 0 || (error = git_iterator_for_tree( &iterators[1], tree, &iterator_options)) < 0) goto done; error = git_iterator_walk(iterators, 2, stage_new_file, index); done: if (error < 0) git_index_free(index); else *out = index; git_iterator_free(iterators[0]); git_iterator_free(iterators[1]); return error; } int git_stash_apply( git_repository *repo, size_t index, const git_stash_apply_options *given_opts) { git_stash_apply_options opts; unsigned int checkout_strategy; git_commit *stash_commit = NULL; git_tree *stash_tree = NULL; git_tree *stash_parent_tree = NULL; git_tree *index_tree = NULL; git_tree *index_parent_tree = NULL; git_tree *untracked_tree = NULL; git_index *stash_adds = NULL; git_index *repo_index = NULL; git_index *unstashed_index = NULL; git_index *modified_index = NULL; git_index *untracked_index = NULL; int error; GIT_ERROR_CHECK_VERSION(given_opts, GIT_STASH_APPLY_OPTIONS_VERSION, "git_stash_apply_options"); normalize_apply_options(&opts, given_opts); checkout_strategy = opts.checkout_options.checkout_strategy; NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_LOADING_STASH); /* Retrieve commit corresponding to the given stash */ if ((error = retrieve_stash_commit(&stash_commit, repo, index)) < 0) goto cleanup; /* Retrieve all trees in the stash */ if ((error = retrieve_stash_trees( &stash_tree, &stash_parent_tree, &index_tree, &index_parent_tree, &untracked_tree, stash_commit)) < 0) goto cleanup; /* Load repo index */ if ((error = git_repository_index(&repo_index, repo)) < 0) goto cleanup; NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX); if ((error = ensure_clean_index(repo, repo_index)) < 0) goto cleanup; /* Restore index if required */ if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) && git_oid_cmp(git_tree_id(stash_parent_tree), git_tree_id(index_tree))) { if ((error = merge_index_and_tree( &unstashed_index, repo, index_parent_tree, repo_index, index_tree)) < 0) goto cleanup; if (git_index_has_conflicts(unstashed_index)) { error = GIT_ECONFLICT; goto cleanup; } /* Otherwise, stage any new files in the stash tree. (Note: their * previously unstaged contents are staged, not the previously staged.) */ } else if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) == 0) { if ((error = stage_new_files( &stash_adds, stash_parent_tree, stash_tree)) < 0 || (error = merge_indexes( &unstashed_index, repo, stash_parent_tree, repo_index, stash_adds)) < 0) goto cleanup; } NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED); /* Restore modified files in workdir */ if ((error = merge_index_and_tree( &modified_index, repo, stash_parent_tree, repo_index, stash_tree)) < 0) goto cleanup; /* If applicable, restore untracked / ignored files in workdir */ if (untracked_tree) { NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED); if ((error = merge_index_and_tree(&untracked_index, repo, NULL, repo_index, untracked_tree)) < 0) goto cleanup; } if (untracked_index) { opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX; NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED); if ((error = git_checkout_index(repo, untracked_index, &opts.checkout_options)) < 0) goto cleanup; opts.checkout_options.checkout_strategy = checkout_strategy; } /* If there are conflicts in the modified index, then we need to actually * check that out as the repo's index. Otherwise, we don't update the * index. */ if (!git_index_has_conflicts(modified_index)) opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX; /* Check out the modified index using the existing repo index as baseline, * so that existing modifications in the index can be rewritten even when * checking out safely. */ opts.checkout_options.baseline_index = repo_index; NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED); if ((error = git_checkout_index(repo, modified_index, &opts.checkout_options)) < 0) goto cleanup; if (unstashed_index && !git_index_has_conflicts(modified_index)) { if ((error = git_index_read_index(repo_index, unstashed_index)) < 0) goto cleanup; } NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_DONE); error = git_index_write(repo_index); cleanup: git_index_free(untracked_index); git_index_free(modified_index); git_index_free(unstashed_index); git_index_free(stash_adds); git_index_free(repo_index); git_tree_free(untracked_tree); git_tree_free(index_parent_tree); git_tree_free(index_tree); git_tree_free(stash_parent_tree); git_tree_free(stash_tree); git_commit_free(stash_commit); return error; } int git_stash_foreach( git_repository *repo, git_stash_cb callback, void *payload) { git_reference *stash; git_reflog *reflog = NULL; int error; size_t i, max; const git_reflog_entry *entry; error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE); if (error == GIT_ENOTFOUND) { git_error_clear(); return 0; } if (error < 0) goto cleanup; if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0) goto cleanup; max = git_reflog_entrycount(reflog); for (i = 0; i < max; i++) { entry = git_reflog_entry_byindex(reflog, i); error = callback(i, git_reflog_entry_message(entry), git_reflog_entry_id_new(entry), payload); if (error) { git_error_set_after_callback(error); break; } } cleanup: git_reference_free(stash); git_reflog_free(reflog); return error; } int git_stash_drop( git_repository *repo, size_t index) { git_transaction *tx; git_reference *stash = NULL; git_reflog *reflog = NULL; size_t max; int error; if ((error = git_transaction_new(&tx, repo)) < 0) return error; if ((error = git_transaction_lock_ref(tx, GIT_REFS_STASH_FILE)) < 0) goto cleanup; if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0) goto cleanup; if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0) goto cleanup; max = git_reflog_entrycount(reflog); if (!max || index > max - 1) { error = GIT_ENOTFOUND; git_error_set(GIT_ERROR_STASH, "no stashed state at position %" PRIuZ, index); goto cleanup; } if ((error = git_reflog_drop(reflog, index, true)) < 0) goto cleanup; if ((error = git_transaction_set_reflog(tx, GIT_REFS_STASH_FILE, reflog)) < 0) goto cleanup; if (max == 1) { if ((error = git_transaction_remove(tx, GIT_REFS_STASH_FILE)) < 0) goto cleanup; } else if (index == 0) { const git_reflog_entry *entry; entry = git_reflog_entry_byindex(reflog, 0); if ((error = git_transaction_set_target(tx, GIT_REFS_STASH_FILE, &entry->oid_cur, NULL, NULL)) < 0) goto cleanup; } error = git_transaction_commit(tx); cleanup: git_reference_free(stash); git_transaction_free(tx); git_reflog_free(reflog); return error; } int git_stash_pop( git_repository *repo, size_t index, const git_stash_apply_options *options) { int error; if ((error = git_stash_apply(repo, index, options)) < 0) return error; return git_stash_drop(repo, index); } git2r/src/libgit2/src/alloc.h0000644000175000017500000000316214125111754015613 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_alloc_h__ #define INCLUDE_alloc_h__ #include "git2/sys/alloc.h" extern git_allocator git__allocator; #define git__malloc(len) git__allocator.gmalloc(len, __FILE__, __LINE__) #define git__calloc(nelem, elsize) git__allocator.gcalloc(nelem, elsize, __FILE__, __LINE__) #define git__strdup(str) git__allocator.gstrdup(str, __FILE__, __LINE__) #define git__strndup(str, n) git__allocator.gstrndup(str, n, __FILE__, __LINE__) #define git__substrdup(str, n) git__allocator.gsubstrdup(str, n, __FILE__, __LINE__) #define git__realloc(ptr, size) git__allocator.grealloc(ptr, size, __FILE__, __LINE__) #define git__reallocarray(ptr, nelem, elsize) git__allocator.greallocarray(ptr, nelem, elsize, __FILE__, __LINE__) #define git__mallocarray(nelem, elsize) git__allocator.gmallocarray(nelem, elsize, __FILE__, __LINE__) #define git__free git__allocator.gfree /** * This function is being called by our global setup routines to * initialize the standard allocator. */ int git_allocator_global_init(void); /** * Switch out libgit2's global memory allocator * * @param allocator The new allocator that should be used. All function pointers * of it need to be set correctly. * @return An error code or 0. */ int git_allocator_setup(git_allocator *allocator); #endif git2r/src/libgit2/src/revparse.c0000644000175000017500000005054414125111754016351 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "buffer.h" #include "tree.h" #include "refdb.h" #include "regexp.h" #include "git2.h" static int maybe_sha_or_abbrev(git_object **out, git_repository *repo, const char *spec, size_t speclen) { git_oid oid; if (git_oid_fromstrn(&oid, spec, speclen) < 0) return GIT_ENOTFOUND; return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJECT_ANY); } static int maybe_sha(git_object **out, git_repository *repo, const char *spec) { size_t speclen = strlen(spec); if (speclen != GIT_OID_HEXSZ) return GIT_ENOTFOUND; return maybe_sha_or_abbrev(out, repo, spec, speclen); } static int maybe_abbrev(git_object **out, git_repository *repo, const char *spec) { size_t speclen = strlen(spec); return maybe_sha_or_abbrev(out, repo, spec, speclen); } static int build_regex(git_regexp *regex, const char *pattern) { int error; if (*pattern == '\0') { git_error_set(GIT_ERROR_REGEX, "empty pattern"); return GIT_EINVALIDSPEC; } error = git_regexp_compile(regex, pattern, 0); if (!error) return 0; git_regexp_dispose(regex); return error; } static int maybe_describe(git_object**out, git_repository *repo, const char *spec) { const char *substr; int error; git_regexp regex; substr = strstr(spec, "-g"); if (substr == NULL) return GIT_ENOTFOUND; if (build_regex(®ex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0) return -1; error = git_regexp_match(®ex, spec); git_regexp_dispose(®ex); if (error) return GIT_ENOTFOUND; return maybe_abbrev(out, repo, substr+2); } static int revparse_lookup_object( git_object **object_out, git_reference **reference_out, git_repository *repo, const char *spec) { int error; git_reference *ref; if ((error = maybe_sha(object_out, repo, spec)) != GIT_ENOTFOUND) return error; error = git_reference_dwim(&ref, repo, spec); if (!error) { error = git_object_lookup( object_out, repo, git_reference_target(ref), GIT_OBJECT_ANY); if (!error) *reference_out = ref; return error; } if (error != GIT_ENOTFOUND) return error; if ((strlen(spec) < GIT_OID_HEXSZ) && ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND)) return error; if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND) return error; git_error_set(GIT_ERROR_REFERENCE, "revspec '%s' not found", spec); return GIT_ENOTFOUND; } static int try_parse_numeric(int *n, const char *curly_braces_content) { int32_t content; const char *end_ptr; if (git__strntol32(&content, curly_braces_content, strlen(curly_braces_content), &end_ptr, 10) < 0) return -1; if (*end_ptr != '\0') return -1; *n = (int)content; return 0; } static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position) { git_reference *ref = NULL; git_reflog *reflog = NULL; git_regexp preg; int error = -1; size_t i, numentries, cur; const git_reflog_entry *entry; const char *msg; git_buf buf = GIT_BUF_INIT; cur = position; if (*identifier != '\0' || *base_ref != NULL) return GIT_EINVALIDSPEC; if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0) return -1; if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0) goto cleanup; if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0) goto cleanup; numentries = git_reflog_entrycount(reflog); for (i = 0; i < numentries; i++) { git_regmatch regexmatches[2]; entry = git_reflog_entry_byindex(reflog, i); msg = git_reflog_entry_message(entry); if (!msg) continue; if (git_regexp_search(&preg, msg, 2, regexmatches) < 0) continue; cur--; if (cur > 0) continue; if ((git_buf_put(&buf, msg+regexmatches[1].start, regexmatches[1].end - regexmatches[1].start)) < 0) goto cleanup; if ((error = git_reference_dwim(base_ref, repo, git_buf_cstr(&buf))) == 0) goto cleanup; if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; error = maybe_abbrev(out, repo, git_buf_cstr(&buf)); goto cleanup; } error = GIT_ENOTFOUND; cleanup: git_reference_free(ref); git_buf_dispose(&buf); git_regexp_dispose(&preg); git_reflog_free(reflog); return error; } static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier) { git_reflog *reflog; size_t numentries; const git_reflog_entry *entry; bool search_by_pos = (identifier <= 100000000); if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0) return -1; numentries = git_reflog_entrycount(reflog); if (search_by_pos) { if (numentries < identifier + 1) goto notfound; entry = git_reflog_entry_byindex(reflog, identifier); git_oid_cpy(oid, git_reflog_entry_id_new(entry)); } else { size_t i; git_time commit_time; for (i = 0; i < numentries; i++) { entry = git_reflog_entry_byindex(reflog, i); commit_time = git_reflog_entry_committer(entry)->when; if (commit_time.time > (git_time_t)identifier) continue; git_oid_cpy(oid, git_reflog_entry_id_new(entry)); break; } if (i == numentries) goto notfound; } git_reflog_free(reflog); return 0; notfound: git_error_set( GIT_ERROR_REFERENCE, "reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, git_reference_name(ref), numentries, identifier); git_reflog_free(reflog); return GIT_ENOTFOUND; } static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position) { git_reference *ref; git_oid oid; int error = -1; if (*base_ref == NULL) { if ((error = git_reference_dwim(&ref, repo, identifier)) < 0) return error; } else { ref = *base_ref; *base_ref = NULL; } if (position == 0) { error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJECT_ANY); goto cleanup; } if ((error = retrieve_oid_from_reflog(&oid, ref, position)) < 0) goto cleanup; error = git_object_lookup(out, repo, &oid, GIT_OBJECT_ANY); cleanup: git_reference_free(ref); return error; } static int retrieve_remote_tracking_reference(git_reference **base_ref, const char *identifier, git_repository *repo) { git_reference *tracking, *ref; int error = -1; if (*base_ref == NULL) { if ((error = git_reference_dwim(&ref, repo, identifier)) < 0) return error; } else { ref = *base_ref; *base_ref = NULL; } if (!git_reference_is_branch(ref)) { error = GIT_EINVALIDSPEC; goto cleanup; } if ((error = git_branch_upstream(&tracking, ref)) < 0) goto cleanup; *base_ref = tracking; cleanup: git_reference_free(ref); return error; } static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository *repo, const char *curly_braces_content) { bool is_numeric; int parsed = 0, error = -1; git_buf identifier = GIT_BUF_INIT; git_time_t timestamp; GIT_ASSERT(*out == NULL); if (git_buf_put(&identifier, spec, identifier_len) < 0) return -1; is_numeric = !try_parse_numeric(&parsed, curly_braces_content); if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) { error = GIT_EINVALIDSPEC; goto cleanup; } if (is_numeric) { if (parsed < 0) error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, git_buf_cstr(&identifier), -parsed); else error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), parsed); goto cleanup; } if (!strcmp(curly_braces_content, "u") || !strcmp(curly_braces_content, "upstream")) { error = retrieve_remote_tracking_reference(ref, git_buf_cstr(&identifier), repo); goto cleanup; } if (git__date_parse(×tamp, curly_braces_content) < 0) goto cleanup; error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), (size_t)timestamp); cleanup: git_buf_dispose(&identifier); return error; } static git_object_t parse_obj_type(const char *str) { if (!strcmp(str, "commit")) return GIT_OBJECT_COMMIT; if (!strcmp(str, "tree")) return GIT_OBJECT_TREE; if (!strcmp(str, "blob")) return GIT_OBJECT_BLOB; if (!strcmp(str, "tag")) return GIT_OBJECT_TAG; return GIT_OBJECT_INVALID; } static int dereference_to_non_tag(git_object **out, git_object *obj) { if (git_object_type(obj) == GIT_OBJECT_TAG) return git_tag_peel(out, (git_tag *)obj); return git_object_dup(out, obj); } static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n) { git_object *temp_commit = NULL; int error; if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0) return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ? GIT_EINVALIDSPEC : error; if (n == 0) { *out = temp_commit; return 0; } error = git_commit_parent((git_commit **)out, (git_commit*)temp_commit, n - 1); git_object_free(temp_commit); return error; } static int handle_linear_syntax(git_object **out, git_object *obj, int n) { git_object *temp_commit = NULL; int error; if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0) return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ? GIT_EINVALIDSPEC : error; error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n); git_object_free(temp_commit); return error; } static int handle_colon_syntax( git_object **out, git_object *obj, const char *path) { git_object *tree; int error = -1; git_tree_entry *entry = NULL; if ((error = git_object_peel(&tree, obj, GIT_OBJECT_TREE)) < 0) return error == GIT_ENOTFOUND ? GIT_EINVALIDSPEC : error; if (*path == '\0') { *out = tree; return 0; } /* * TODO: Handle the relative path syntax * (:./relative/path and :../relative/path) */ if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0) goto cleanup; error = git_tree_entry_to_object(out, git_object_owner(tree), entry); cleanup: git_tree_entry_free(entry); git_object_free(tree); return error; } static int walk_and_search(git_object **out, git_revwalk *walk, git_regexp *regex) { int error; git_oid oid; git_object *obj; while (!(error = git_revwalk_next(&oid, walk))) { error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJECT_COMMIT); if ((error < 0) && (error != GIT_ENOTFOUND)) return -1; if (!git_regexp_match(regex, git_commit_message((git_commit*)obj))) { *out = obj; return 0; } git_object_free(obj); } if (error < 0 && error == GIT_ITEROVER) error = GIT_ENOTFOUND; return error; } static int handle_grep_syntax(git_object **out, git_repository *repo, const git_oid *spec_oid, const char *pattern) { git_regexp preg; git_revwalk *walk = NULL; int error; if ((error = build_regex(&preg, pattern)) < 0) return error; if ((error = git_revwalk_new(&walk, repo)) < 0) goto cleanup; git_revwalk_sorting(walk, GIT_SORT_TIME); if (spec_oid == NULL) { if ((error = git_revwalk_push_glob(walk, "refs/*")) < 0) goto cleanup; } else if ((error = git_revwalk_push(walk, spec_oid)) < 0) goto cleanup; error = walk_and_search(out, walk, &preg); cleanup: git_regexp_dispose(&preg); git_revwalk_free(walk); return error; } static int handle_caret_curly_syntax(git_object **out, git_object *obj, const char *curly_braces_content) { git_object_t expected_type; if (*curly_braces_content == '\0') return dereference_to_non_tag(out, obj); if (*curly_braces_content == '/') return handle_grep_syntax(out, git_object_owner(obj), git_object_id(obj), curly_braces_content + 1); expected_type = parse_obj_type(curly_braces_content); if (expected_type == GIT_OBJECT_INVALID) return GIT_EINVALIDSPEC; return git_object_peel(out, obj, expected_type); } static int extract_curly_braces_content(git_buf *buf, const char *spec, size_t *pos) { git_buf_clear(buf); GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '@'); (*pos)++; if (spec[*pos] == '\0' || spec[*pos] != '{') return GIT_EINVALIDSPEC; (*pos)++; while (spec[*pos] != '}') { if (spec[*pos] == '\0') return GIT_EINVALIDSPEC; if (git_buf_putc(buf, spec[(*pos)++]) < 0) return -1; } (*pos)++; return 0; } static int extract_path(git_buf *buf, const char *spec, size_t *pos) { git_buf_clear(buf); GIT_ASSERT_ARG(spec[*pos] == ':'); (*pos)++; if (git_buf_puts(buf, spec + *pos) < 0) return -1; *pos += git_buf_len(buf); return 0; } static int extract_how_many(int *n, const char *spec, size_t *pos) { const char *end_ptr; int parsed, accumulated; char kind = spec[*pos]; GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '~'); accumulated = 0; do { do { (*pos)++; accumulated++; } while (spec[(*pos)] == kind && kind == '~'); if (git__isdigit(spec[*pos])) { if (git__strntol32(&parsed, spec + *pos, strlen(spec + *pos), &end_ptr, 10) < 0) return GIT_EINVALIDSPEC; accumulated += (parsed - 1); *pos = end_ptr - spec; } } while (spec[(*pos)] == kind && kind == '~'); *n = accumulated; return 0; } static int object_from_reference(git_object **object, git_reference *reference) { git_reference *resolved = NULL; int error; if (git_reference_resolve(&resolved, reference) < 0) return -1; error = git_object_lookup(object, reference->db->repo, git_reference_target(resolved), GIT_OBJECT_ANY); git_reference_free(resolved); return error; } static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier) { int error; git_buf identifier = GIT_BUF_INIT; if (*object != NULL) return 0; if (*reference != NULL) return object_from_reference(object, *reference); if (!allow_empty_identifier && identifier_len == 0) return GIT_EINVALIDSPEC; if (git_buf_put(&identifier, spec, identifier_len) < 0) return -1; error = revparse_lookup_object(object, reference, repo, git_buf_cstr(&identifier)); git_buf_dispose(&identifier); return error; } static int ensure_base_rev_is_not_known_yet(git_object *object) { if (object == NULL) return 0; return GIT_EINVALIDSPEC; } static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len) { if (object != NULL) return true; if (reference != NULL) return true; if (identifier_len > 0) return true; return false; } static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference) { if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL) return 0; return GIT_EINVALIDSPEC; } static int revparse( git_object **object_out, git_reference **reference_out, size_t *identifier_len_out, git_repository *repo, const char *spec) { size_t pos = 0, identifier_len = 0; int error = -1, n; git_buf buf = GIT_BUF_INIT; git_reference *reference = NULL; git_object *base_rev = NULL; bool should_return_reference = true; GIT_ASSERT_ARG(object_out); GIT_ASSERT_ARG(reference_out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(spec); *object_out = NULL; *reference_out = NULL; while (spec[pos]) { switch (spec[pos]) { case '^': should_return_reference = false; if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if (spec[pos+1] == '{') { git_object *temp_object = NULL; if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0) goto cleanup; if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0) goto cleanup; git_object_free(base_rev); base_rev = temp_object; } else { git_object *temp_object = NULL; if ((error = extract_how_many(&n, spec, &pos)) < 0) goto cleanup; if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0) goto cleanup; git_object_free(base_rev); base_rev = temp_object; } break; case '~': { git_object *temp_object = NULL; should_return_reference = false; if ((error = extract_how_many(&n, spec, &pos)) < 0) goto cleanup; if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0) goto cleanup; git_object_free(base_rev); base_rev = temp_object; break; } case ':': { git_object *temp_object = NULL; should_return_reference = false; if ((error = extract_path(&buf, spec, &pos)) < 0) goto cleanup; if (any_left_hand_identifier(base_rev, reference, identifier_len)) { if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0) goto cleanup; if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0) goto cleanup; } else { if (*git_buf_cstr(&buf) == '/') { if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_buf_cstr(&buf) + 1)) < 0) goto cleanup; } else { /* * TODO: support merge-stage path lookup (":2:Makefile") * and plain index blob lookup (:i-am/a/blob) */ git_error_set(GIT_ERROR_INVALID, "unimplemented"); error = GIT_ERROR; goto cleanup; } } git_object_free(base_rev); base_rev = temp_object; break; } case '@': if (spec[pos+1] == '{') { git_object *temp_object = NULL; if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0) goto cleanup; if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0) goto cleanup; if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_buf_cstr(&buf))) < 0) goto cleanup; if (temp_object != NULL) base_rev = temp_object; break; } /* fall through */ default: if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0) goto cleanup; pos++; identifier_len++; } } if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if (!should_return_reference) { git_reference_free(reference); reference = NULL; } *object_out = base_rev; *reference_out = reference; *identifier_len_out = identifier_len; error = 0; cleanup: if (error) { if (error == GIT_EINVALIDSPEC) git_error_set(GIT_ERROR_INVALID, "failed to parse revision specifier - Invalid pattern '%s'", spec); git_object_free(base_rev); git_reference_free(reference); } git_buf_dispose(&buf); return error; } int git_revparse_ext( git_object **object_out, git_reference **reference_out, git_repository *repo, const char *spec) { int error; size_t identifier_len; git_object *obj = NULL; git_reference *ref = NULL; if ((error = revparse(&obj, &ref, &identifier_len, repo, spec)) < 0) goto cleanup; *object_out = obj; *reference_out = ref; GIT_UNUSED(identifier_len); return 0; cleanup: git_object_free(obj); git_reference_free(ref); return error; } int git_revparse_single(git_object **out, git_repository *repo, const char *spec) { int error; git_object *obj = NULL; git_reference *ref = NULL; *out = NULL; if ((error = git_revparse_ext(&obj, &ref, repo, spec)) < 0) goto cleanup; git_reference_free(ref); *out = obj; return 0; cleanup: git_object_free(obj); git_reference_free(ref); return error; } int git_revparse( git_revspec *revspec, git_repository *repo, const char *spec) { const char *dotdot; int error = 0; GIT_ASSERT_ARG(revspec); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(spec); memset(revspec, 0x0, sizeof(*revspec)); if ((dotdot = strstr(spec, "..")) != NULL) { char *lstr; const char *rstr; revspec->flags = GIT_REVSPEC_RANGE; /* * Following git.git, don't allow '..' because it makes command line * arguments which can be either paths or revisions ambiguous when the * path is almost certainly intended. The empty range '...' is still * allowed. */ if (!git__strcmp(spec, "..")) { git_error_set(GIT_ERROR_INVALID, "Invalid pattern '..'"); return GIT_EINVALIDSPEC; } lstr = git__substrdup(spec, dotdot - spec); rstr = dotdot + 2; if (dotdot[2] == '.') { revspec->flags |= GIT_REVSPEC_MERGE_BASE; rstr++; } error = git_revparse_single( &revspec->from, repo, *lstr == '\0' ? "HEAD" : lstr); if (!error) { error = git_revparse_single( &revspec->to, repo, *rstr == '\0' ? "HEAD" : rstr); } git__free((void*)lstr); } else { revspec->flags = GIT_REVSPEC_SINGLE; error = git_revparse_single(&revspec->from, repo, spec); } return error; } git2r/src/libgit2/src/proxy.c0000644000175000017500000000201514125111754015671 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "proxy.h" #include "git2/proxy.h" int git_proxy_options_init(git_proxy_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_proxy_options, GIT_PROXY_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_proxy_init_options(git_proxy_options *opts, unsigned int version) { return git_proxy_options_init(opts, version); } #endif int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src) { if (!src) { git_proxy_options_init(tgt, GIT_PROXY_OPTIONS_VERSION); return 0; } memcpy(tgt, src, sizeof(git_proxy_options)); if (src->url) { tgt->url = git__strdup(src->url); GIT_ERROR_CHECK_ALLOC(tgt->url); } return 0; } void git_proxy_options_clear(git_proxy_options *opts) { git__free((char *) opts->url); opts->url = NULL; } git2r/src/libgit2/src/branch.c0000644000175000017500000004563514125111754015764 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "branch.h" #include "commit.h" #include "tag.h" #include "config.h" #include "refspec.h" #include "refs.h" #include "remote.h" #include "annotated_commit.h" #include "worktree.h" #include "git2/branch.h" static int retrieve_branch_reference( git_reference **branch_reference_out, git_repository *repo, const char *branch_name, bool is_remote) { git_reference *branch = NULL; int error = 0; char *prefix; git_buf ref_name = GIT_BUF_INIT; prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR; if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0) /* OOM */; else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) git_error_set( GIT_ERROR_REFERENCE, "cannot locate %s branch '%s'", is_remote ? "remote-tracking" : "local", branch_name); *branch_reference_out = branch; /* will be NULL on error */ git_buf_dispose(&ref_name); return error; } static int not_a_local_branch(const char *reference_name) { git_error_set( GIT_ERROR_INVALID, "reference '%s' is not a local branch.", reference_name); return -1; } static int create_branch( git_reference **ref_out, git_repository *repository, const char *branch_name, const git_commit *commit, const char *from, int force) { int is_unmovable_head = 0; git_reference *branch = NULL; git_buf canonical_branch_name = GIT_BUF_INIT, log_message = GIT_BUF_INIT; int error = -1; int bare = git_repository_is_bare(repository); GIT_ASSERT_ARG(branch_name); GIT_ASSERT_ARG(commit); GIT_ASSERT_ARG(ref_out); GIT_ASSERT_ARG(git_commit_owner(commit) == repository); if (!git__strcmp(branch_name, "HEAD")) { git_error_set(GIT_ERROR_REFERENCE, "'HEAD' is not a valid branch name"); error = -1; goto cleanup; } if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) { error = git_branch_is_head(branch); git_reference_free(branch); branch = NULL; if (error < 0) goto cleanup; is_unmovable_head = error; } if (is_unmovable_head && force) { git_error_set(GIT_ERROR_REFERENCE, "cannot force update branch '%s' as it is " "the current HEAD of the repository.", branch_name); error = -1; goto cleanup; } if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0) goto cleanup; if (git_buf_printf(&log_message, "branch: Created from %s", from) < 0) goto cleanup; error = git_reference_create(&branch, repository, git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force, git_buf_cstr(&log_message)); if (!error) *ref_out = branch; cleanup: git_buf_dispose(&canonical_branch_name); git_buf_dispose(&log_message); return error; } int git_branch_create( git_reference **ref_out, git_repository *repository, const char *branch_name, const git_commit *commit, int force) { return create_branch(ref_out, repository, branch_name, commit, git_oid_tostr_s(git_commit_id(commit)), force); } int git_branch_create_from_annotated( git_reference **ref_out, git_repository *repository, const char *branch_name, const git_annotated_commit *commit, int force) { return create_branch(ref_out, repository, branch_name, commit->commit, commit->description, force); } static int branch_is_checked_out(git_repository *worktree, void *payload) { git_reference *branch = (git_reference *) payload; git_reference *head = NULL; int error; if (git_repository_is_bare(worktree)) return 0; if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0) { if (error == GIT_ENOTFOUND) error = 0; goto out; } if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC) goto out; error = !git__strcmp(head->target.symbolic, branch->name); out: git_reference_free(head); return error; } int git_branch_is_checked_out(const git_reference *branch) { GIT_ASSERT_ARG(branch); if (!git_reference_is_branch(branch)) return 0; return git_repository_foreach_worktree(git_reference_owner(branch), branch_is_checked_out, (void *)branch) == 1; } int git_branch_delete(git_reference *branch) { int is_head; git_buf config_section = GIT_BUF_INIT; int error = -1; GIT_ASSERT_ARG(branch); if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) { git_error_set(GIT_ERROR_INVALID, "reference '%s' is not a valid branch.", git_reference_name(branch)); return GIT_ENOTFOUND; } if ((is_head = git_branch_is_head(branch)) < 0) return is_head; if (is_head) { git_error_set(GIT_ERROR_REFERENCE, "cannot delete branch '%s' as it is " "the current HEAD of the repository.", git_reference_name(branch)); return -1; } if (git_reference_is_branch(branch) && git_branch_is_checked_out(branch)) { git_error_set(GIT_ERROR_REFERENCE, "Cannot delete branch '%s' as it is " "the current HEAD of a linked repository.", git_reference_name(branch)); return -1; } if (git_buf_join(&config_section, '.', "branch", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) goto on_error; if (git_config_rename_section( git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0) goto on_error; error = git_reference_delete(branch); on_error: git_buf_dispose(&config_section); return error; } typedef struct { git_reference_iterator *iter; unsigned int flags; } branch_iter; int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter) { branch_iter *iter = (branch_iter *) _iter; git_reference *ref; int error; while ((error = git_reference_next(&ref, iter->iter)) == 0) { if ((iter->flags & GIT_BRANCH_LOCAL) && !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) { *out = ref; *out_type = GIT_BRANCH_LOCAL; return 0; } else if ((iter->flags & GIT_BRANCH_REMOTE) && !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) { *out = ref; *out_type = GIT_BRANCH_REMOTE; return 0; } else { git_reference_free(ref); } } return error; } int git_branch_iterator_new( git_branch_iterator **out, git_repository *repo, git_branch_t list_flags) { branch_iter *iter; iter = git__calloc(1, sizeof(branch_iter)); GIT_ERROR_CHECK_ALLOC(iter); iter->flags = list_flags; if (git_reference_iterator_new(&iter->iter, repo) < 0) { git__free(iter); return -1; } *out = (git_branch_iterator *) iter; return 0; } void git_branch_iterator_free(git_branch_iterator *_iter) { branch_iter *iter = (branch_iter *) _iter; if (iter == NULL) return; git_reference_iterator_free(iter->iter); git__free(iter); } int git_branch_move( git_reference **out, git_reference *branch, const char *new_branch_name, int force) { git_buf new_reference_name = GIT_BUF_INIT, old_config_section = GIT_BUF_INIT, new_config_section = GIT_BUF_INIT, log_message = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(branch); GIT_ASSERT_ARG(new_branch_name); if (!git_reference_is_branch(branch)) return not_a_local_branch(git_reference_name(branch)); if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0) goto done; if ((error = git_buf_printf(&log_message, "branch: renamed %s to %s", git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0) goto done; /* first update ref then config so failure won't trash config */ error = git_reference_rename( out, branch, git_buf_cstr(&new_reference_name), force, git_buf_cstr(&log_message)); if (error < 0) goto done; git_buf_join(&old_config_section, '.', "branch", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); git_buf_join(&new_config_section, '.', "branch", new_branch_name); error = git_config_rename_section( git_reference_owner(branch), git_buf_cstr(&old_config_section), git_buf_cstr(&new_config_section)); done: git_buf_dispose(&new_reference_name); git_buf_dispose(&old_config_section); git_buf_dispose(&new_config_section); git_buf_dispose(&log_message); return error; } int git_branch_lookup( git_reference **ref_out, git_repository *repo, const char *branch_name, git_branch_t branch_type) { int error = -1; GIT_ASSERT_ARG(ref_out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(branch_name); switch (branch_type) { case GIT_BRANCH_LOCAL: case GIT_BRANCH_REMOTE: error = retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE); break; case GIT_BRANCH_ALL: error = retrieve_branch_reference(ref_out, repo, branch_name, false); if (error == GIT_ENOTFOUND) error = retrieve_branch_reference(ref_out, repo, branch_name, true); break; default: GIT_ASSERT(false); } return error; } int git_branch_name( const char **out, const git_reference *ref) { const char *branch_name; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(ref); branch_name = ref->name; if (git_reference_is_branch(ref)) { branch_name += strlen(GIT_REFS_HEADS_DIR); } else if (git_reference_is_remote(ref)) { branch_name += strlen(GIT_REFS_REMOTES_DIR); } else { git_error_set(GIT_ERROR_INVALID, "reference '%s' is neither a local nor a remote branch.", ref->name); return -1; } *out = branch_name; return 0; } static int retrieve_upstream_configuration( git_buf *out, const git_config *config, const char *canonical_branch_name, const char *format) { git_buf buf = GIT_BUF_INIT; int error; if (git_buf_printf(&buf, format, canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0) return -1; error = git_config_get_string_buf(out, config, git_buf_cstr(&buf)); git_buf_dispose(&buf); return error; } int git_branch_upstream_name( git_buf *out, git_repository *repo, const char *refname) { git_buf remote_name = GIT_BUF_INIT; git_buf merge_name = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT; int error = -1; git_remote *remote = NULL; const git_refspec *refspec; git_config *config; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(refname); if ((error = git_buf_sanitize(out)) < 0) return error; if (!git_reference__is_branch(refname)) return not_a_local_branch(refname); if ((error = git_repository_config_snapshot(&config, repo)) < 0) return error; if ((error = retrieve_upstream_configuration( &remote_name, config, refname, "branch.%s.remote")) < 0) goto cleanup; if ((error = retrieve_upstream_configuration( &merge_name, config, refname, "branch.%s.merge")) < 0) goto cleanup; if (git_buf_len(&remote_name) == 0 || git_buf_len(&merge_name) == 0) { git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream", refname); error = GIT_ENOTFOUND; goto cleanup; } if (strcmp(".", git_buf_cstr(&remote_name)) != 0) { if ((error = git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name))) < 0) goto cleanup; refspec = git_remote__matching_refspec(remote, git_buf_cstr(&merge_name)); if (!refspec) { error = GIT_ENOTFOUND; goto cleanup; } if (git_refspec_transform(&buf, refspec, git_buf_cstr(&merge_name)) < 0) goto cleanup; } else if (git_buf_set(&buf, git_buf_cstr(&merge_name), git_buf_len(&merge_name)) < 0) goto cleanup; error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf)); cleanup: git_config_free(config); git_remote_free(remote); git_buf_dispose(&remote_name); git_buf_dispose(&merge_name); git_buf_dispose(&buf); return error; } static int git_branch_upstream_with_format(git_buf *buf, git_repository *repo, const char *refname, const char *format, const char *format_name) { int error; git_config *cfg; if (!git_reference__is_branch(refname)) return not_a_local_branch(refname); if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; if ((error = git_buf_sanitize(buf)) < 0 || (error = retrieve_upstream_configuration(buf, cfg, refname, format)) < 0) return error; if (git_buf_len(buf) == 0) { git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream %s", refname, format_name); error = GIT_ENOTFOUND; git_buf_clear(buf); } return error; } int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname) { return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.remote", "remote"); } int git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname) { return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.merge", "merge"); } int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname) { git_strarray remote_list = {0}; size_t i; git_remote *remote; const git_refspec *fetchspec; int error = 0; char *remote_name = NULL; GIT_ASSERT_ARG(buf); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(refname); if ((error = git_buf_sanitize(buf)) < 0) return error; /* Verify that this is a remote branch */ if (!git_reference__is_remote(refname)) { git_error_set(GIT_ERROR_INVALID, "reference '%s' is not a remote branch.", refname); error = GIT_ERROR; goto cleanup; } /* Get the remotes */ if ((error = git_remote_list(&remote_list, repo)) < 0) goto cleanup; /* Find matching remotes */ for (i = 0; i < remote_list.count; i++) { if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0) continue; fetchspec = git_remote__matching_dst_refspec(remote, refname); if (fetchspec) { /* If we have not already set out yet, then set * it to the matching remote name. Otherwise * multiple remotes match this reference, and it * is ambiguous. */ if (!remote_name) { remote_name = remote_list.strings[i]; } else { git_remote_free(remote); git_error_set(GIT_ERROR_REFERENCE, "reference '%s' is ambiguous", refname); error = GIT_EAMBIGUOUS; goto cleanup; } } git_remote_free(remote); } if (remote_name) { git_buf_clear(buf); error = git_buf_puts(buf, remote_name); } else { git_error_set(GIT_ERROR_REFERENCE, "could not determine remote for '%s'", refname); error = GIT_ENOTFOUND; } cleanup: if (error < 0) git_buf_dispose(buf); git_strarray_dispose(&remote_list); return error; } int git_branch_upstream( git_reference **tracking_out, const git_reference *branch) { int error; git_buf tracking_name = GIT_BUF_INIT; if ((error = git_branch_upstream_name(&tracking_name, git_reference_owner(branch), git_reference_name(branch))) < 0) return error; error = git_reference_lookup( tracking_out, git_reference_owner(branch), git_buf_cstr(&tracking_name)); git_buf_dispose(&tracking_name); return error; } static int unset_upstream(git_config *config, const char *shortname) { git_buf buf = GIT_BUF_INIT; if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0) return -1; if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0) goto on_error; git_buf_clear(&buf); if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0) goto on_error; if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0) goto on_error; git_buf_dispose(&buf); return 0; on_error: git_buf_dispose(&buf); return -1; } int git_branch_set_upstream(git_reference *branch, const char *branch_name) { git_buf key = GIT_BUF_INIT, remote_name = GIT_BUF_INIT, merge_refspec = GIT_BUF_INIT; git_reference *upstream; git_repository *repo; git_remote *remote = NULL; git_config *config; const char *refname, *shortname; int local, error; const git_refspec *fetchspec; refname = git_reference_name(branch); if (!git_reference__is_branch(refname)) return not_a_local_branch(refname); if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0) return -1; shortname = refname + strlen(GIT_REFS_HEADS_DIR); /* We're unsetting, delegate and bail-out */ if (branch_name == NULL) return unset_upstream(config, shortname); repo = git_reference_owner(branch); /* First we need to resolve name to a branch */ if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_LOCAL) == 0) local = 1; else if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_REMOTE) == 0) local = 0; else { git_error_set(GIT_ERROR_REFERENCE, "cannot set upstream for branch '%s'", shortname); return GIT_ENOTFOUND; } /* * If it's a local-tracking branch, its remote is "." (as "the local * repository"), and the branch name is simply the refname. * Otherwise we need to figure out what the remote-tracking branch's * name on the remote is and use that. */ if (local) error = git_buf_puts(&remote_name, "."); else error = git_branch_remote_name(&remote_name, repo, git_reference_name(upstream)); if (error < 0) goto on_error; /* Update the upsteam branch config with the new name */ if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0) goto on_error; if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&remote_name)) < 0) goto on_error; if (local) { /* A local branch uses the upstream refname directly */ if (git_buf_puts(&merge_refspec, git_reference_name(upstream)) < 0) goto on_error; } else { /* We transform the upstream branch name according to the remote's refspecs */ if (git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name)) < 0) goto on_error; fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream)); if (!fetchspec || git_refspec_rtransform(&merge_refspec, fetchspec, git_reference_name(upstream)) < 0) goto on_error; git_remote_free(remote); remote = NULL; } /* Update the merge branch config with the refspec */ git_buf_clear(&key); if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0) goto on_error; if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&merge_refspec)) < 0) goto on_error; git_reference_free(upstream); git_buf_dispose(&key); git_buf_dispose(&remote_name); git_buf_dispose(&merge_refspec); return 0; on_error: git_reference_free(upstream); git_buf_dispose(&key); git_buf_dispose(&remote_name); git_buf_dispose(&merge_refspec); git_remote_free(remote); return -1; } int git_branch_is_head( const git_reference *branch) { git_reference *head; bool is_same = false; int error; GIT_ASSERT_ARG(branch); if (!git_reference_is_branch(branch)) return false; error = git_repository_head(&head, git_reference_owner(branch)); if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND) return false; if (error < 0) return -1; is_same = strcmp( git_reference_name(branch), git_reference_name(head)) == 0; git_reference_free(head); return is_same; } int git_branch_name_is_valid(int *valid, const char *name) { git_buf ref_name = GIT_BUF_INIT; int error = 0; GIT_ASSERT(valid); *valid = 0; /* * Discourage branch name starting with dash, * https://github.com/git/git/commit/6348624010888b * and discourage HEAD as branch name, * https://github.com/git/git/commit/a625b092cc5994 */ if (!name || name[0] == '-' || !git__strcmp(name, "HEAD")) goto done; if ((error = git_buf_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 || (error = git_buf_puts(&ref_name, name)) < 0) goto done; error = git_reference_name_is_valid(valid, ref_name.ptr); done: git_buf_dispose(&ref_name); return error; } git2r/src/libgit2/src/submodule.h0000644000175000017500000001342714125111754016525 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_submodule_h__ #define INCLUDE_submodule_h__ #include "common.h" #include "git2/submodule.h" #include "git2/repository.h" #include "futils.h" /* Notes: * * Submodule information can be in four places: the index, the config files * (both .git/config and .gitmodules), the HEAD tree, and the working * directory. * * In the index: * - submodule is found by path * - may be missing, present, or of the wrong type * - will have an oid if present * * In the HEAD tree: * - submodule is found by path * - may be missing, present, or of the wrong type * - will have an oid if present * * In the config files: * - submodule is found by submodule "name" which is usually the path * - may be missing or present * - will have a name, path, url, and other properties * * In the working directory: * - submodule is found by path * - may be missing, an empty directory, a checked out directory, * or of the wrong type * - if checked out, will have a HEAD oid * - if checked out, will have git history that can be used to compare oids * - if checked out, may have modified files and/or untracked files */ /** * Description of submodule * * This record describes a submodule found in a repository. There should be * an entry for every submodule found in the HEAD and index, and for every * submodule described in .gitmodules. The fields are as follows: * * - `rc` tracks the refcount of how many hash table entries in the * git_submodule_cache there are for this submodule. It only comes into * play if the name and path of the submodule differ. * * - `name` is the name of the submodule from .gitmodules. * - `path` is the path to the submodule from the repo root. It is almost * always the same as `name`. * - `url` is the url for the submodule. * - `update` is a git_submodule_update_t value - see gitmodules(5) update. * - `update_default` is the update value from the config * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore. * - `ignore_default` is the ignore value from the config * - `fetch_recurse` is a git_submodule_recurse_t value - see gitmodules(5) * fetchRecurseSubmodules. * - `fetch_recurse_default` is the recurse value from the config * * - `repo` is the parent repository that contains this submodule. * - `flags` after for internal use, tracking where this submodule has been * found (head, index, config, workdir) and known status info, etc. * - `head_oid` is the SHA1 for the submodule path in the repo HEAD. * - `index_oid` is the SHA1 for the submodule recorded in the index. * - `wd_oid` is the SHA1 for the HEAD of the checked out submodule. * * If the submodule has been added to .gitmodules but not yet git added, * then the `index_oid` will be zero but still marked valid. If the * submodule has been deleted, but the delete has not been committed yet, * then the `index_oid` will be set, but the `url` will be NULL. */ struct git_submodule { git_refcount rc; /* information from config */ char *name; char *path; /* important: may just point to "name" string */ char *url; char *branch; git_submodule_update_t update; git_submodule_update_t update_default; git_submodule_ignore_t ignore; git_submodule_ignore_t ignore_default; git_submodule_recurse_t fetch_recurse; git_submodule_recurse_t fetch_recurse_default; /* internal information */ git_repository *repo; uint32_t flags; git_oid head_oid; git_oid index_oid; git_oid wd_oid; }; /* Additional flags on top of public GIT_SUBMODULE_STATUS values */ enum { GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20), GIT_SUBMODULE_STATUS__HEAD_OID_VALID = (1u << 21), GIT_SUBMODULE_STATUS__INDEX_OID_VALID = (1u << 22), GIT_SUBMODULE_STATUS__WD_OID_VALID = (1u << 23), GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE = (1u << 24), GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE = (1u << 25), GIT_SUBMODULE_STATUS__WD_NOT_SUBMODULE = (1u << 26), GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27), }; #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ ((S) & ~(0xFFFFFFFFu << 20)) /* Initialize an external submodule cache for the provided repo. */ extern int git_submodule_cache_init(git_strmap **out, git_repository *repo); /* Release the resources of the submodule cache. */ extern int git_submodule_cache_free(git_strmap *cache); /* Submodule lookup with an explicit cache */ extern int git_submodule__lookup_with_cache( git_submodule **out, git_repository *repo, const char *path, git_strmap *cache); /* Internal status fn returns status and optionally the various OIDs */ extern int git_submodule__status( unsigned int *out_status, git_oid *out_head_id, git_oid *out_index_id, git_oid *out_wd_id, git_submodule *sm, git_submodule_ignore_t ign); /* Open submodule repository as bare repo for quick HEAD check, etc. */ extern int git_submodule_open_bare( git_repository **repo, git_submodule *submodule); extern int git_submodule_parse_ignore( git_submodule_ignore_t *out, const char *value); extern int git_submodule_parse_update( git_submodule_update_t *out, const char *value); extern int git_submodule__map( git_repository *repo, git_strmap *map); /** * Check whether a submodule's name is valid. * * Check the path against the path validity rules, either the filesystem * defaults (like checkout does) or whichever you want to compare against. * * @param repo the repository which contains the submodule * @param name the name to check * @param flags the `GIT_PATH` flags to use for the check (0 to use filesystem defaults) */ extern int git_submodule_name_is_valid(git_repository *repo, const char *name, int flags); #endif git2r/src/libgit2/src/pqueue.c0000644000175000017500000000513014125111754016015 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "pqueue.h" #include "util.h" #define PQUEUE_LCHILD_OF(I) (((I)<<1)+1) #define PQUEUE_RCHILD_OF(I) (((I)<<1)+2) #define PQUEUE_PARENT_OF(I) (((I)-1)>>1) int git_pqueue_init( git_pqueue *pq, uint32_t flags, size_t init_size, git_vector_cmp cmp) { int error = git_vector_init(pq, init_size, cmp); if (!error) { /* mix in our flags */ pq->flags |= flags; /* if fixed size heap, pretend vector is exactly init_size elements */ if ((flags & GIT_PQUEUE_FIXED_SIZE) && init_size > 0) pq->_alloc_size = init_size; } return error; } static void pqueue_up(git_pqueue *pq, size_t el) { size_t parent_el = PQUEUE_PARENT_OF(el); void *kid = git_vector_get(pq, el); while (el > 0) { void *parent = pq->contents[parent_el]; if (pq->_cmp(parent, kid) <= 0) break; pq->contents[el] = parent; el = parent_el; parent_el = PQUEUE_PARENT_OF(el); } pq->contents[el] = kid; } static void pqueue_down(git_pqueue *pq, size_t el) { void *parent = git_vector_get(pq, el), *kid, *rkid; while (1) { size_t kid_el = PQUEUE_LCHILD_OF(el); if ((kid = git_vector_get(pq, kid_el)) == NULL) break; if ((rkid = git_vector_get(pq, kid_el + 1)) != NULL && pq->_cmp(kid, rkid) > 0) { kid = rkid; kid_el += 1; } if (pq->_cmp(parent, kid) <= 0) break; pq->contents[el] = kid; el = kid_el; } pq->contents[el] = parent; } int git_pqueue_insert(git_pqueue *pq, void *item) { int error = 0; /* if heap is full, pop the top element if new one should replace it */ if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 && pq->length >= pq->_alloc_size) { /* skip this item if below min item in heap or if * we do not have a comparison function */ if (!pq->_cmp || pq->_cmp(item, git_vector_get(pq, 0)) <= 0) return 0; /* otherwise remove the min item before inserting new */ (void)git_pqueue_pop(pq); } if (!(error = git_vector_insert(pq, item)) && pq->_cmp) pqueue_up(pq, pq->length - 1); return error; } void *git_pqueue_pop(git_pqueue *pq) { void *rval; if (!pq->_cmp) { rval = git_vector_last(pq); } else { rval = git_pqueue_get(pq, 0); } if (git_pqueue_size(pq) > 1 && pq->_cmp) { /* move last item to top of heap, shrink, and push item down */ pq->contents[0] = git_vector_last(pq); git_vector_pop(pq); pqueue_down(pq, 0); } else { /* all we need to do is shrink the heap in this case */ git_vector_pop(pq); } return rval; } git2r/src/libgit2/src/transports/0000755000175000017500000000000014145550337016573 5ustar nileshnileshgit2r/src/libgit2/src/transports/winhttp.c0000644000175000017500000012651214125111754020435 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #ifdef GIT_WINHTTP #include "git2.h" #include "git2/transport.h" #include "buffer.h" #include "posix.h" #include "netops.h" #include "smart.h" #include "remote.h" #include "repository.h" #include "http.h" #include "git2/sys/credential.h" #include #include /* For IInternetSecurityManager zone check */ #include #include #define WIDEN2(s) L ## s #define WIDEN(s) WIDEN2(s) #define MAX_CONTENT_TYPE_LEN 100 #define WINHTTP_OPTION_PEERDIST_EXTENSION_STATE 109 #define CACHED_POST_BODY_BUF_SIZE 4096 #define UUID_LENGTH_CCH 32 #define TIMEOUT_INFINITE -1 #define DEFAULT_CONNECT_TIMEOUT 60000 #ifndef WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH #define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0 #endif #ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 # define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 0x00000200 #endif #ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 # define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 0x00000800 #endif #ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 # define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 0x00002000 #endif #ifndef WINHTTP_NO_CLIENT_CERT_CONTEXT # define WINHTTP_NO_CLIENT_CERT_CONTEXT NULL #endif #ifndef HTTP_STATUS_PERMANENT_REDIRECT # define HTTP_STATUS_PERMANENT_REDIRECT 308 #endif #ifndef DWORD_MAX # define DWORD_MAX 0xffffffff #endif bool git_http__expect_continue = false; static const char *prefix_https = "https://"; static const char *upload_pack_service = "upload-pack"; static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack"; static const char *upload_pack_service_url = "/git-upload-pack"; static const char *receive_pack_service = "receive-pack"; static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack"; static const char *receive_pack_service_url = "/git-receive-pack"; static const wchar_t *get_verb = L"GET"; static const wchar_t *post_verb = L"POST"; static const wchar_t *pragma_nocache = L"Pragma: no-cache"; static const wchar_t *transfer_encoding = L"Transfer-Encoding: chunked"; static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA; #if defined(__MINGW32__) static const CLSID CLSID_InternetSecurityManager_mingw = { 0x7B8A2D94, 0x0AC9, 0x11D1, { 0x89, 0x6C, 0x00, 0xC0, 0x4F, 0xB6, 0xBF, 0xC4 } }; static const IID IID_IInternetSecurityManager_mingw = { 0x79EAC9EE, 0xBAF9, 0x11CE, { 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B } }; # define CLSID_InternetSecurityManager CLSID_InternetSecurityManager_mingw # define IID_IInternetSecurityManager IID_IInternetSecurityManager_mingw #endif #define OWNING_SUBTRANSPORT(s) ((winhttp_subtransport *)(s)->parent.subtransport) typedef enum { GIT_WINHTTP_AUTH_BASIC = 1, GIT_WINHTTP_AUTH_NTLM = 2, GIT_WINHTTP_AUTH_NEGOTIATE = 4, GIT_WINHTTP_AUTH_DIGEST = 8, } winhttp_authmechanism_t; typedef struct { git_smart_subtransport_stream parent; const char *service; const char *service_url; const wchar_t *verb; HINTERNET request; wchar_t *request_uri; char *chunk_buffer; unsigned chunk_buffer_len; HANDLE post_body; DWORD post_body_len; unsigned sent_request : 1, received_response : 1, chunked : 1, status_sending_request_reached: 1; } winhttp_stream; typedef struct { git_net_url url; git_credential *cred; int auth_mechanisms; bool url_cred_presented; } winhttp_server; typedef struct { git_smart_subtransport parent; transport_smart *owner; winhttp_server server; winhttp_server proxy; HINTERNET session; HINTERNET connection; } winhttp_subtransport; static int apply_userpass_credentials(HINTERNET request, DWORD target, int mechanisms, git_credential *cred) { git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred; wchar_t *user = NULL, *pass = NULL; int user_len = 0, pass_len = 0, error = 0; DWORD native_scheme; if (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) { native_scheme = WINHTTP_AUTH_SCHEME_NEGOTIATE; } else if (mechanisms & GIT_WINHTTP_AUTH_NTLM) { native_scheme = WINHTTP_AUTH_SCHEME_NTLM; } else if (mechanisms & GIT_WINHTTP_AUTH_DIGEST) { native_scheme = WINHTTP_AUTH_SCHEME_DIGEST; } else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) { native_scheme = WINHTTP_AUTH_SCHEME_BASIC; } else { git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme"); error = GIT_EAUTH; goto done; } if ((error = user_len = git__utf8_to_16_alloc(&user, c->username)) < 0) goto done; if ((error = pass_len = git__utf8_to_16_alloc(&pass, c->password)) < 0) goto done; if (!WinHttpSetCredentials(request, target, native_scheme, user, pass, NULL)) { git_error_set(GIT_ERROR_OS, "failed to set credentials"); error = -1; } done: if (user_len > 0) git__memzero(user, user_len * sizeof(wchar_t)); if (pass_len > 0) git__memzero(pass, pass_len * sizeof(wchar_t)); git__free(user); git__free(pass); return error; } static int apply_default_credentials(HINTERNET request, DWORD target, int mechanisms) { DWORD autologon_level = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW; DWORD native_scheme = 0; if ((mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) != 0) { native_scheme = WINHTTP_AUTH_SCHEME_NEGOTIATE; } else if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0) { native_scheme = WINHTTP_AUTH_SCHEME_NTLM; } else { git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme"); return GIT_EAUTH; } /* * Autologon policy must be "low" to use default creds. * This is safe as the user has explicitly requested it. */ if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &autologon_level, sizeof(DWORD))) { git_error_set(GIT_ERROR_OS, "could not configure logon policy"); return -1; } if (!WinHttpSetCredentials(request, target, native_scheme, NULL, NULL, NULL)) { git_error_set(GIT_ERROR_OS, "could not configure credentials"); return -1; } return 0; } static int acquire_url_cred( git_credential **cred, unsigned int allowed_types, const char *username, const char *password) { if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT) return git_credential_userpass_plaintext_new(cred, username, password); if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0') return git_credential_default_new(cred); return 1; } static int acquire_fallback_cred( git_credential **cred, const char *url, unsigned int allowed_types) { int error = 1; /* If the target URI supports integrated Windows authentication * as an authentication mechanism */ if (GIT_CREDENTIAL_DEFAULT & allowed_types) { wchar_t *wide_url; HRESULT hCoInitResult; /* Convert URL to wide characters */ if (git__utf8_to_16_alloc(&wide_url, url) < 0) { git_error_set(GIT_ERROR_OS, "failed to convert string to wide form"); return -1; } hCoInitResult = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (SUCCEEDED(hCoInitResult) || hCoInitResult == RPC_E_CHANGED_MODE) { IInternetSecurityManager *pISM; /* And if the target URI is in the My Computer, Intranet, or Trusted zones */ if (SUCCEEDED(CoCreateInstance(&CLSID_InternetSecurityManager, NULL, CLSCTX_ALL, &IID_IInternetSecurityManager, (void **)&pISM))) { DWORD dwZone; if (SUCCEEDED(pISM->lpVtbl->MapUrlToZone(pISM, wide_url, &dwZone, 0)) && (URLZONE_LOCAL_MACHINE == dwZone || URLZONE_INTRANET == dwZone || URLZONE_TRUSTED == dwZone)) { git_credential *existing = *cred; if (existing) existing->free(existing); /* Then use default Windows credentials to authenticate this request */ error = git_credential_default_new(cred); } pISM->lpVtbl->Release(pISM); } /* Only uninitialize if the call to CoInitializeEx was successful. */ if (SUCCEEDED(hCoInitResult)) CoUninitialize(); } git__free(wide_url); } return error; } static int certificate_check(winhttp_stream *s, int valid) { int error; winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); PCERT_CONTEXT cert_ctx; DWORD cert_ctx_size = sizeof(cert_ctx); git_cert_x509 cert; /* If there is no override, we should fail if WinHTTP doesn't think it's fine */ if (t->owner->certificate_check_cb == NULL && !valid) { if (!git_error_last()) git_error_set(GIT_ERROR_HTTP, "unknown certificate check failure"); return GIT_ECERTIFICATE; } if (t->owner->certificate_check_cb == NULL || git__strcmp(t->server.url.scheme, "https") != 0) return 0; if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) { git_error_set(GIT_ERROR_OS, "failed to get server certificate"); return -1; } git_error_clear(); cert.parent.cert_type = GIT_CERT_X509; cert.data = cert_ctx->pbCertEncoded; cert.len = cert_ctx->cbCertEncoded; error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->server.url.host, t->owner->message_cb_payload); CertFreeCertificateContext(cert_ctx); if (error == GIT_PASSTHROUGH) error = valid ? 0 : GIT_ECERTIFICATE; if (error < 0 && !git_error_last()) git_error_set(GIT_ERROR_HTTP, "user cancelled certificate check"); return error; } static void winhttp_stream_close(winhttp_stream *s) { if (s->chunk_buffer) { git__free(s->chunk_buffer); s->chunk_buffer = NULL; } if (s->post_body) { CloseHandle(s->post_body); s->post_body = NULL; } if (s->request_uri) { git__free(s->request_uri); s->request_uri = NULL; } if (s->request) { WinHttpCloseHandle(s->request); s->request = NULL; } s->sent_request = 0; } static int apply_credentials( HINTERNET request, git_net_url *url, int target, git_credential *creds, int mechanisms) { int error = 0; GIT_UNUSED(url); /* If we have creds, just apply them */ if (creds && creds->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT) error = apply_userpass_credentials(request, target, mechanisms, creds); else if (creds && creds->credtype == GIT_CREDENTIAL_DEFAULT) error = apply_default_credentials(request, target, mechanisms); return error; } static int winhttp_stream_connect(winhttp_stream *s) { winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); git_buf buf = GIT_BUF_INIT; char *proxy_url = NULL; wchar_t ct[MAX_CONTENT_TYPE_LEN]; LPCWSTR types[] = { L"*/*", NULL }; BOOL peerdist = FALSE; int error = -1; unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS; int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; DWORD autologon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; const char *service_url = s->service_url; size_t i; const git_proxy_options *proxy_opts; /* If path already ends in /, remove the leading slash from service_url */ if ((git__suffixcmp(t->server.url.path, "/") == 0) && (git__prefixcmp(service_url, "/") == 0)) service_url++; /* Prepare URL */ git_buf_printf(&buf, "%s%s", t->server.url.path, service_url); if (git_buf_oom(&buf)) return -1; /* Convert URL to wide characters */ if (git__utf8_to_16_alloc(&s->request_uri, git_buf_cstr(&buf)) < 0) { git_error_set(GIT_ERROR_OS, "failed to convert string to wide form"); goto on_error; } /* Establish request */ s->request = WinHttpOpenRequest( t->connection, s->verb, s->request_uri, NULL, WINHTTP_NO_REFERER, types, git__strcmp(t->server.url.scheme, "https") == 0 ? WINHTTP_FLAG_SECURE : 0); if (!s->request) { git_error_set(GIT_ERROR_OS, "failed to open request"); goto on_error; } /* Never attempt default credentials; we'll provide them explicitly. */ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_AUTOLOGON_POLICY, &autologon_policy, sizeof(DWORD))) return -1; if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) { git_error_set(GIT_ERROR_OS, "failed to set timeouts for WinHTTP"); goto on_error; } proxy_opts = &t->owner->proxy; if (proxy_opts->type == GIT_PROXY_AUTO) { /* Set proxy if necessary */ if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0) goto on_error; } else if (proxy_opts->type == GIT_PROXY_SPECIFIED) { proxy_url = git__strdup(proxy_opts->url); GIT_ERROR_CHECK_ALLOC(proxy_url); } if (proxy_url) { git_buf processed_url = GIT_BUF_INIT; WINHTTP_PROXY_INFO proxy_info; wchar_t *proxy_wide; git_net_url_dispose(&t->proxy.url); if ((error = git_net_url_parse(&t->proxy.url, proxy_url)) < 0) goto on_error; if (strcmp(t->proxy.url.scheme, "http") != 0 && strcmp(t->proxy.url.scheme, "https") != 0) { git_error_set(GIT_ERROR_HTTP, "invalid URL: '%s'", proxy_url); error = -1; goto on_error; } git_buf_puts(&processed_url, t->proxy.url.scheme); git_buf_PUTS(&processed_url, "://"); if (git_net_url_is_ipv6(&t->proxy.url)) git_buf_putc(&processed_url, '['); git_buf_puts(&processed_url, t->proxy.url.host); if (git_net_url_is_ipv6(&t->proxy.url)) git_buf_putc(&processed_url, ']'); if (!git_net_url_is_default_port(&t->proxy.url)) git_buf_printf(&processed_url, ":%s", t->proxy.url.port); if (git_buf_oom(&processed_url)) { error = -1; goto on_error; } /* Convert URL to wide characters */ error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr); git_buf_dispose(&processed_url); if (error < 0) goto on_error; proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; proxy_info.lpszProxy = proxy_wide; proxy_info.lpszProxyBypass = NULL; if (!WinHttpSetOption(s->request, WINHTTP_OPTION_PROXY, &proxy_info, sizeof(WINHTTP_PROXY_INFO))) { git_error_set(GIT_ERROR_OS, "failed to set proxy"); git__free(proxy_wide); goto on_error; } git__free(proxy_wide); if ((error = apply_credentials(s->request, &t->proxy.url, WINHTTP_AUTH_TARGET_PROXY, t->proxy.cred, t->proxy.auth_mechanisms)) < 0) goto on_error; } /* Disable WinHTTP redirects so we can handle them manually. Why, you ask? * http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/b2ff8879-ab9f-4218-8f09-16d25dff87ae */ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_DISABLE_FEATURE, &disable_redirects, sizeof(disable_redirects))) { git_error_set(GIT_ERROR_OS, "failed to disable redirects"); error = -1; goto on_error; } /* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP * adds itself. This option may not be supported by the underlying * platform, so we do not error-check it */ WinHttpSetOption(s->request, WINHTTP_OPTION_PEERDIST_EXTENSION_STATE, &peerdist, sizeof(peerdist)); /* Send Pragma: no-cache header */ if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { git_error_set(GIT_ERROR_OS, "failed to add a header to the request"); goto on_error; } if (post_verb == s->verb) { /* Send Content-Type and Accept headers -- only necessary on a POST */ git_buf_clear(&buf); if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", s->service) < 0) goto on_error; if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { git_error_set(GIT_ERROR_OS, "failed to convert content-type to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { git_error_set(GIT_ERROR_OS, "failed to add a header to the request"); goto on_error; } git_buf_clear(&buf); if (git_buf_printf(&buf, "Accept: application/x-git-%s-result", s->service) < 0) goto on_error; if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { git_error_set(GIT_ERROR_OS, "failed to convert accept header to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { git_error_set(GIT_ERROR_OS, "failed to add a header to the request"); goto on_error; } } for (i = 0; i < t->owner->custom_headers.count; i++) { if (t->owner->custom_headers.strings[i]) { git_buf_clear(&buf); git_buf_puts(&buf, t->owner->custom_headers.strings[i]); if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { git_error_set(GIT_ERROR_OS, "failed to convert custom header to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { git_error_set(GIT_ERROR_OS, "failed to add a header to the request"); goto on_error; } } } /* If requested, disable certificate validation */ if (strcmp(t->server.url.scheme, "https") == 0) { int flags; if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0) goto on_error; } if ((error = apply_credentials(s->request, &t->server.url, WINHTTP_AUTH_TARGET_SERVER, t->server.cred, t->server.auth_mechanisms)) < 0) goto on_error; /* We've done everything up to calling WinHttpSendRequest. */ error = 0; on_error: if (error < 0) winhttp_stream_close(s); git__free(proxy_url); git_buf_dispose(&buf); return error; } static int parse_unauthorized_response( int *allowed_types, int *allowed_mechanisms, HINTERNET request) { DWORD supported, first, target; *allowed_types = 0; *allowed_mechanisms = 0; /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes(). * We can assume this was already done, since we know we are unauthorized. */ if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) { git_error_set(GIT_ERROR_OS, "failed to parse supported auth schemes"); return GIT_EAUTH; } if (WINHTTP_AUTH_SCHEME_NTLM & supported) { *allowed_types |= GIT_CREDENTIAL_USERPASS_PLAINTEXT; *allowed_types |= GIT_CREDENTIAL_DEFAULT; *allowed_mechanisms |= GIT_WINHTTP_AUTH_NTLM; } if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) { *allowed_types |= GIT_CREDENTIAL_DEFAULT; *allowed_mechanisms |= GIT_WINHTTP_AUTH_NEGOTIATE; } if (WINHTTP_AUTH_SCHEME_BASIC & supported) { *allowed_types |= GIT_CREDENTIAL_USERPASS_PLAINTEXT; *allowed_mechanisms |= GIT_WINHTTP_AUTH_BASIC; } if (WINHTTP_AUTH_SCHEME_DIGEST & supported) { *allowed_types |= GIT_CREDENTIAL_USERPASS_PLAINTEXT; *allowed_mechanisms |= GIT_WINHTTP_AUTH_DIGEST; } return 0; } static int write_chunk(HINTERNET request, const char *buffer, size_t len) { DWORD bytes_written; git_buf buf = GIT_BUF_INIT; /* Chunk header */ git_buf_printf(&buf, "%"PRIXZ"\r\n", len); if (git_buf_oom(&buf)) return -1; if (!WinHttpWriteData(request, git_buf_cstr(&buf), (DWORD)git_buf_len(&buf), &bytes_written)) { git_buf_dispose(&buf); git_error_set(GIT_ERROR_OS, "failed to write chunk header"); return -1; } git_buf_dispose(&buf); /* Chunk body */ if (!WinHttpWriteData(request, buffer, (DWORD)len, &bytes_written)) { git_error_set(GIT_ERROR_OS, "failed to write chunk"); return -1; } /* Chunk footer */ if (!WinHttpWriteData(request, "\r\n", 2, &bytes_written)) { git_error_set(GIT_ERROR_OS, "failed to write chunk footer"); return -1; } return 0; } static int winhttp_close_connection(winhttp_subtransport *t) { int ret = 0; if (t->connection) { if (!WinHttpCloseHandle(t->connection)) { git_error_set(GIT_ERROR_OS, "unable to close connection"); ret = -1; } t->connection = NULL; } if (t->session) { if (!WinHttpCloseHandle(t->session)) { git_error_set(GIT_ERROR_OS, "unable to close session"); ret = -1; } t->session = NULL; } return ret; } static void CALLBACK winhttp_status( HINTERNET connection, DWORD_PTR ctx, DWORD code, LPVOID info, DWORD info_len) { DWORD status; GIT_UNUSED(connection); GIT_UNUSED(info_len); switch (code) { case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: status = *((DWORD *)info); if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)) git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name"); else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)) git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired"); else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)) git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA"); else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)) git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid"); else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)) git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed"); else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)) git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked"); else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)) git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded"); else git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status); break; case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: ((winhttp_stream *) ctx)->status_sending_request_reached = 1; break; } } static int winhttp_connect( winhttp_subtransport *t) { wchar_t *wide_host = NULL; int32_t port; wchar_t *wide_ua = NULL; git_buf ipv6 = GIT_BUF_INIT, ua = GIT_BUF_INIT; const char *host; int error = -1; int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3; t->session = NULL; t->connection = NULL; /* Prepare port */ if (git__strntol32(&port, t->server.url.port, strlen(t->server.url.port), NULL, 10) < 0) goto on_error; /* IPv6? Add braces around the host. */ if (git_net_url_is_ipv6(&t->server.url)) { if (git_buf_printf(&ipv6, "[%s]", t->server.url.host) < 0) goto on_error; host = ipv6.ptr; } else { host = t->server.url.host; } /* Prepare host */ if (git__utf8_to_16_alloc(&wide_host, host) < 0) { git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters"); goto on_error; } if (git_http__user_agent(&ua) < 0) goto on_error; if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) { git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters"); goto on_error; } /* Establish session */ t->session = WinHttpOpen( wide_ua, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!t->session) { git_error_set(GIT_ERROR_OS, "failed to init WinHTTP"); goto on_error; } /* * Do a best-effort attempt to enable TLS 1.3 and 1.2 but allow this to * fail; if TLS 1.2 or 1.3 support is not available for some reason, * ignore the failure (it will keep the default protocols). */ if (WinHttpSetOption(t->session, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols)) == FALSE) { protocols &= ~WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3; WinHttpSetOption(t->session, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols)); } if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) { git_error_set(GIT_ERROR_OS, "failed to set timeouts for WinHTTP"); goto on_error; } /* Establish connection */ t->connection = WinHttpConnect( t->session, wide_host, (INTERNET_PORT) port, 0); if (!t->connection) { git_error_set(GIT_ERROR_OS, "failed to connect to host"); goto on_error; } if (WinHttpSetStatusCallback( t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, 0 ) == WINHTTP_INVALID_STATUS_CALLBACK) { git_error_set(GIT_ERROR_OS, "failed to set status callback"); goto on_error; } error = 0; on_error: if (error < 0) winhttp_close_connection(t); git_buf_dispose(&ua); git_buf_dispose(&ipv6); git__free(wide_host); git__free(wide_ua); return error; } static int do_send_request(winhttp_stream *s, size_t len, bool chunked) { int attempts; bool success; if (len > DWORD_MAX) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return -1; } for (attempts = 0; attempts < 5; attempts++) { if (chunked) { success = WinHttpSendRequest(s->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, (DWORD_PTR)s); } else { success = WinHttpSendRequest(s->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, (DWORD)len, (DWORD_PTR)s); } if (success || GetLastError() != (DWORD)SEC_E_BUFFER_TOO_SMALL) break; } return success ? 0 : -1; } static int send_request(winhttp_stream *s, size_t len, bool chunked) { int request_failed = 1, error, attempts = 0; DWORD ignore_flags, send_request_error; git_error_clear(); while (request_failed && attempts++ < 3) { int cert_valid = 1; int client_cert_requested = 0; request_failed = 0; if ((error = do_send_request(s, len, chunked)) < 0) { send_request_error = GetLastError(); request_failed = 1; switch (send_request_error) { case ERROR_WINHTTP_SECURE_FAILURE: cert_valid = 0; break; case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED: client_cert_requested = 1; break; default: git_error_set(GIT_ERROR_OS, "failed to send request"); return -1; } } /* * Only check the certificate if we were able to reach the sending request phase, or * received a secure failure error. Otherwise, the server certificate won't be available * since the request wasn't able to complete (e.g. proxy auth required) */ if (!cert_valid || (!request_failed && s->status_sending_request_reached)) { git_error_clear(); if ((error = certificate_check(s, cert_valid)) < 0) { if (!git_error_last()) git_error_set(GIT_ERROR_OS, "user cancelled certificate check"); return error; } } /* if neither the request nor the certificate check returned errors, we're done */ if (!request_failed) return 0; if (!cert_valid) { ignore_flags = no_check_cert_flags; if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) { git_error_set(GIT_ERROR_OS, "failed to set security options"); return -1; } } if (client_cert_requested) { /* * Client certificates are not supported, explicitly tell the server that * (it's possible a client certificate was requested but is not required) */ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, WINHTTP_NO_CLIENT_CERT_CONTEXT, 0)) { git_error_set(GIT_ERROR_OS, "failed to set client cert context"); return -1; } } } return error; } static int acquire_credentials( HINTERNET request, winhttp_server *server, const char *url_str, git_credential_acquire_cb cred_cb, void *cred_cb_payload) { int allowed_types; int error = 1; if (parse_unauthorized_response(&allowed_types, &server->auth_mechanisms, request) < 0) return -1; if (allowed_types) { git_credential_free(server->cred); server->cred = NULL; /* Start with URL-specified credentials, if there were any. */ if (!server->url_cred_presented && server->url.username && server->url.password) { error = acquire_url_cred(&server->cred, allowed_types, server->url.username, server->url.password); server->url_cred_presented = 1; if (error < 0) return error; } /* Next use the user-defined callback, if there is one. */ if (error > 0 && cred_cb) { error = cred_cb(&server->cred, url_str, server->url.username, allowed_types, cred_cb_payload); /* Treat GIT_PASSTHROUGH as though git_credential_acquire_cb isn't set */ if (error == GIT_PASSTHROUGH) error = 1; else if (error < 0) return error; } /* Finally, invoke the fallback default credential lookup. */ if (error > 0) { error = acquire_fallback_cred(&server->cred, url_str, allowed_types); if (error < 0) return error; } } /* * No error occurred but we could not find appropriate credentials. * This behaves like a pass-through. */ return error; } static int winhttp_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { winhttp_stream *s = (winhttp_stream *)stream; winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); DWORD dw_bytes_read; char replay_count = 0; int error; replay: /* Enforce a reasonable cap on the number of replays */ if (replay_count++ >= GIT_HTTP_REPLAY_MAX) { git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays"); return GIT_ERROR; /* not GIT_EAUTH because the exact cause is not clear */ } /* Connect if necessary */ if (!s->request && winhttp_stream_connect(s) < 0) return -1; if (!s->received_response) { DWORD status_code, status_code_length, content_type_length, bytes_written; char expected_content_type_8[MAX_CONTENT_TYPE_LEN]; wchar_t expected_content_type[MAX_CONTENT_TYPE_LEN], content_type[MAX_CONTENT_TYPE_LEN]; if (!s->sent_request) { if ((error = send_request(s, s->post_body_len, false)) < 0) return error; s->sent_request = 1; } if (s->chunked) { GIT_ASSERT(s->verb == post_verb); /* Flush, if necessary */ if (s->chunk_buffer_len > 0 && write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; /* Write the final chunk. */ if (!WinHttpWriteData(s->request, "0\r\n\r\n", 5, &bytes_written)) { git_error_set(GIT_ERROR_OS, "failed to write final chunk"); return -1; } } else if (s->post_body) { char *buffer; DWORD len = s->post_body_len, bytes_read; if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body, 0, 0, FILE_BEGIN) && NO_ERROR != GetLastError()) { git_error_set(GIT_ERROR_OS, "failed to reset file pointer"); return -1; } buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE); GIT_ERROR_CHECK_ALLOC(buffer); while (len > 0) { DWORD bytes_written; if (!ReadFile(s->post_body, buffer, min(CACHED_POST_BODY_BUF_SIZE, len), &bytes_read, NULL) || !bytes_read) { git__free(buffer); git_error_set(GIT_ERROR_OS, "failed to read from temp file"); return -1; } if (!WinHttpWriteData(s->request, buffer, bytes_read, &bytes_written)) { git__free(buffer); git_error_set(GIT_ERROR_OS, "failed to write data"); return -1; } len -= bytes_read; GIT_ASSERT(bytes_read == bytes_written); } git__free(buffer); /* Eagerly close the temp file */ CloseHandle(s->post_body); s->post_body = NULL; } if (!WinHttpReceiveResponse(s->request, 0)) { git_error_set(GIT_ERROR_OS, "failed to receive response"); return -1; } /* Verify that we got a 200 back */ status_code_length = sizeof(status_code); if (!WinHttpQueryHeaders(s->request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_length, WINHTTP_NO_HEADER_INDEX)) { git_error_set(GIT_ERROR_OS, "failed to retrieve status code"); return -1; } /* The implementation of WinHTTP prior to Windows 7 will not * redirect to an identical URI. Some Git hosters use self-redirects * as part of their DoS mitigation strategy. Check first to see if we * have a redirect status code, and that we haven't already streamed * a post body. (We can't replay a streamed POST.) */ if (!s->chunked && (HTTP_STATUS_MOVED == status_code || HTTP_STATUS_REDIRECT == status_code || (HTTP_STATUS_REDIRECT_METHOD == status_code && get_verb == s->verb) || HTTP_STATUS_REDIRECT_KEEP_VERB == status_code || HTTP_STATUS_PERMANENT_REDIRECT == status_code)) { /* Check for Windows 7. This workaround is only necessary on * Windows Vista and earlier. Windows 7 is version 6.1. */ wchar_t *location; DWORD location_length; char *location8; /* OK, fetch the Location header from the redirect. */ if (WinHttpQueryHeaders(s->request, WINHTTP_QUERY_LOCATION, WINHTTP_HEADER_NAME_BY_INDEX, WINHTTP_NO_OUTPUT_BUFFER, &location_length, WINHTTP_NO_HEADER_INDEX) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { git_error_set(GIT_ERROR_OS, "failed to read Location header"); return -1; } location = git__malloc(location_length); GIT_ERROR_CHECK_ALLOC(location); if (!WinHttpQueryHeaders(s->request, WINHTTP_QUERY_LOCATION, WINHTTP_HEADER_NAME_BY_INDEX, location, &location_length, WINHTTP_NO_HEADER_INDEX)) { git_error_set(GIT_ERROR_OS, "failed to read Location header"); git__free(location); return -1; } /* Convert the Location header to UTF-8 */ if (git__utf16_to_8_alloc(&location8, location) < 0) { git_error_set(GIT_ERROR_OS, "failed to convert Location header to UTF-8"); git__free(location); return -1; } git__free(location); /* Replay the request */ winhttp_stream_close(s); if (!git__prefixcmp_icase(location8, prefix_https)) { /* Upgrade to secure connection; disconnect and start over */ if (git_net_url_apply_redirect(&t->server.url, location8, s->service_url) < 0) { git__free(location8); return -1; } winhttp_close_connection(t); if (winhttp_connect(t) < 0) return -1; } git__free(location8); goto replay; } /* Handle authentication failures */ if (status_code == HTTP_STATUS_DENIED) { int error = acquire_credentials(s->request, &t->server, t->owner->url, t->owner->cred_acquire_cb, t->owner->cred_acquire_payload); if (error < 0) { return error; } else if (!error) { GIT_ASSERT(t->server.cred); winhttp_stream_close(s); goto replay; } } else if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) { int error = acquire_credentials(s->request, &t->proxy, t->owner->proxy.url, t->owner->proxy.credentials, t->owner->proxy.payload); if (error < 0) { return error; } else if (!error) { GIT_ASSERT(t->proxy.cred); winhttp_stream_close(s); goto replay; } } if (HTTP_STATUS_OK != status_code) { git_error_set(GIT_ERROR_HTTP, "request failed with status code: %lu", status_code); return -1; } /* Verify that we got the correct content-type back */ if (post_verb == s->verb) p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service); else p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service); if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) { git_error_set(GIT_ERROR_OS, "failed to convert expected content-type to wide characters"); return -1; } content_type_length = sizeof(content_type); if (!WinHttpQueryHeaders(s->request, WINHTTP_QUERY_CONTENT_TYPE, WINHTTP_HEADER_NAME_BY_INDEX, &content_type, &content_type_length, WINHTTP_NO_HEADER_INDEX)) { git_error_set(GIT_ERROR_OS, "failed to retrieve response content-type"); return -1; } if (wcscmp(expected_content_type, content_type)) { git_error_set(GIT_ERROR_HTTP, "received unexpected content-type"); return -1; } s->received_response = 1; } if (!WinHttpReadData(s->request, (LPVOID)buffer, (DWORD)buf_size, &dw_bytes_read)) { git_error_set(GIT_ERROR_OS, "failed to read data"); return -1; } *bytes_read = dw_bytes_read; return 0; } static int winhttp_stream_write_single( git_smart_subtransport_stream *stream, const char *buffer, size_t len) { winhttp_stream *s = (winhttp_stream *)stream; DWORD bytes_written; int error; if (!s->request && winhttp_stream_connect(s) < 0) return -1; /* This implementation of write permits only a single call. */ if (s->sent_request) { git_error_set(GIT_ERROR_HTTP, "subtransport configured for only one write"); return -1; } if ((error = send_request(s, len, false)) < 0) return error; s->sent_request = 1; if (!WinHttpWriteData(s->request, (LPCVOID)buffer, (DWORD)len, &bytes_written)) { git_error_set(GIT_ERROR_OS, "failed to write data"); return -1; } GIT_ASSERT((DWORD)len == bytes_written); return 0; } static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch) { UUID uuid; RPC_STATUS status = UuidCreate(&uuid); int result; if (RPC_S_OK != status && RPC_S_UUID_LOCAL_ONLY != status && RPC_S_UUID_NO_ADDRESS != status) { git_error_set(GIT_ERROR_HTTP, "unable to generate name for temp file"); return -1; } if (buffer_len_cch < UUID_LENGTH_CCH + 1) { git_error_set(GIT_ERROR_HTTP, "buffer too small for name of temp file"); return -1; } #if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API) result = swprintf_s(buffer, buffer_len_cch, #else result = wsprintfW(buffer, #endif L"%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x", uuid.Data1, uuid.Data2, uuid.Data3, uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3], uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]); if (result < UUID_LENGTH_CCH) { git_error_set(GIT_ERROR_OS, "unable to generate name for temp file"); return -1; } return 0; } static int get_temp_file(LPWSTR buffer, DWORD buffer_len_cch) { size_t len; if (!GetTempPathW(buffer_len_cch, buffer)) { git_error_set(GIT_ERROR_OS, "failed to get temp path"); return -1; } len = wcslen(buffer); if (buffer[len - 1] != '\\' && len < buffer_len_cch) buffer[len++] = '\\'; if (put_uuid_string(&buffer[len], (size_t)buffer_len_cch - len) < 0) return -1; return 0; } static int winhttp_stream_write_buffered( git_smart_subtransport_stream *stream, const char *buffer, size_t len) { winhttp_stream *s = (winhttp_stream *)stream; DWORD bytes_written; if (!s->request && winhttp_stream_connect(s) < 0) return -1; /* Buffer the payload, using a temporary file so we delegate * memory management of the data to the operating system. */ if (!s->post_body) { wchar_t temp_path[MAX_PATH + 1]; if (get_temp_file(temp_path, MAX_PATH + 1) < 0) return -1; s->post_body = CreateFileW(temp_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == s->post_body) { s->post_body = NULL; git_error_set(GIT_ERROR_OS, "failed to create temporary file"); return -1; } } if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) { git_error_set(GIT_ERROR_OS, "failed to write to temporary file"); return -1; } GIT_ASSERT((DWORD)len == bytes_written); s->post_body_len += bytes_written; return 0; } static int winhttp_stream_write_chunked( git_smart_subtransport_stream *stream, const char *buffer, size_t len) { winhttp_stream *s = (winhttp_stream *)stream; int error; if (!s->request && winhttp_stream_connect(s) < 0) return -1; if (!s->sent_request) { /* Send Transfer-Encoding: chunked header */ if (!WinHttpAddRequestHeaders(s->request, transfer_encoding, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { git_error_set(GIT_ERROR_OS, "failed to add a header to the request"); return -1; } if ((error = send_request(s, 0, true)) < 0) return error; s->sent_request = 1; } if (len > CACHED_POST_BODY_BUF_SIZE) { /* Flush, if necessary */ if (s->chunk_buffer_len > 0) { if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; } /* Write chunk directly */ if (write_chunk(s->request, buffer, len) < 0) return -1; } else { /* Append as much to the buffer as we can */ int count = (int)min(CACHED_POST_BODY_BUF_SIZE - s->chunk_buffer_len, len); if (!s->chunk_buffer) { s->chunk_buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE); GIT_ERROR_CHECK_ALLOC(s->chunk_buffer); } memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count); s->chunk_buffer_len += count; buffer += count; len -= count; /* Is the buffer full? If so, then flush */ if (CACHED_POST_BODY_BUF_SIZE == s->chunk_buffer_len) { if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; /* Is there any remaining data from the source? */ if (len > 0) { memcpy(s->chunk_buffer, buffer, len); s->chunk_buffer_len = (unsigned int)len; } } } return 0; } static void winhttp_stream_free(git_smart_subtransport_stream *stream) { winhttp_stream *s = (winhttp_stream *)stream; winhttp_stream_close(s); git__free(s); } static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream) { winhttp_stream *s; if (!stream) return -1; s = git__calloc(1, sizeof(winhttp_stream)); GIT_ERROR_CHECK_ALLOC(s); s->parent.subtransport = &t->parent; s->parent.read = winhttp_stream_read; s->parent.write = winhttp_stream_write_single; s->parent.free = winhttp_stream_free; *stream = s; return 0; } static int winhttp_uploadpack_ls( winhttp_subtransport *t, winhttp_stream *s) { GIT_UNUSED(t); s->service = upload_pack_service; s->service_url = upload_pack_ls_service_url; s->verb = get_verb; return 0; } static int winhttp_uploadpack( winhttp_subtransport *t, winhttp_stream *s) { GIT_UNUSED(t); s->service = upload_pack_service; s->service_url = upload_pack_service_url; s->verb = post_verb; return 0; } static int winhttp_receivepack_ls( winhttp_subtransport *t, winhttp_stream *s) { GIT_UNUSED(t); s->service = receive_pack_service; s->service_url = receive_pack_ls_service_url; s->verb = get_verb; return 0; } static int winhttp_receivepack( winhttp_subtransport *t, winhttp_stream *s) { GIT_UNUSED(t); /* WinHTTP only supports Transfer-Encoding: chunked * on Windows Vista (NT 6.0) and higher. */ s->chunked = git_has_win32_version(6, 0, 0); if (s->chunked) s->parent.write = winhttp_stream_write_chunked; else s->parent.write = winhttp_stream_write_buffered; s->service = receive_pack_service; s->service_url = receive_pack_service_url; s->verb = post_verb; return 0; } static int winhttp_action( git_smart_subtransport_stream **stream, git_smart_subtransport *subtransport, const char *url, git_smart_service_t action) { winhttp_subtransport *t = (winhttp_subtransport *)subtransport; winhttp_stream *s; int ret = -1; if (!t->connection) if ((ret = git_net_url_parse(&t->server.url, url)) < 0 || (ret = winhttp_connect(t)) < 0) return ret; if (winhttp_stream_alloc(t, &s) < 0) return -1; if (!stream) return -1; switch (action) { case GIT_SERVICE_UPLOADPACK_LS: ret = winhttp_uploadpack_ls(t, s); break; case GIT_SERVICE_UPLOADPACK: ret = winhttp_uploadpack(t, s); break; case GIT_SERVICE_RECEIVEPACK_LS: ret = winhttp_receivepack_ls(t, s); break; case GIT_SERVICE_RECEIVEPACK: ret = winhttp_receivepack(t, s); break; default: GIT_ASSERT(0); } if (!ret) *stream = &s->parent; return ret; } static int winhttp_close(git_smart_subtransport *subtransport) { winhttp_subtransport *t = (winhttp_subtransport *)subtransport; git_net_url_dispose(&t->server.url); git_net_url_dispose(&t->proxy.url); if (t->server.cred) { t->server.cred->free(t->server.cred); t->server.cred = NULL; } if (t->proxy.cred) { t->proxy.cred->free(t->proxy.cred); t->proxy.cred = NULL; } return winhttp_close_connection(t); } static void winhttp_free(git_smart_subtransport *subtransport) { winhttp_subtransport *t = (winhttp_subtransport *)subtransport; winhttp_close(subtransport); git__free(t); } int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param) { winhttp_subtransport *t; GIT_UNUSED(param); if (!out) return -1; t = git__calloc(1, sizeof(winhttp_subtransport)); GIT_ERROR_CHECK_ALLOC(t); t->owner = (transport_smart *)owner; t->parent.action = winhttp_action; t->parent.close = winhttp_close; t->parent.free = winhttp_free; *out = (git_smart_subtransport *) t; return 0; } #endif /* GIT_WINHTTP */ git2r/src/libgit2/src/transports/http.c0000644000175000017500000004720014125111754017713 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #ifndef GIT_WINHTTP #include "git2.h" #include "http_parser.h" #include "buffer.h" #include "net.h" #include "netops.h" #include "remote.h" #include "git2/sys/credential.h" #include "smart.h" #include "auth.h" #include "http.h" #include "auth_negotiate.h" #include "auth_ntlm.h" #include "trace.h" #include "streams/tls.h" #include "streams/socket.h" #include "httpclient.h" bool git_http__expect_continue = false; typedef enum { HTTP_STATE_NONE = 0, HTTP_STATE_SENDING_REQUEST, HTTP_STATE_RECEIVING_RESPONSE, HTTP_STATE_DONE } http_state; typedef struct { git_http_method method; const char *url; const char *request_type; const char *response_type; unsigned chunked : 1; } http_service; typedef struct { git_smart_subtransport_stream parent; const http_service *service; http_state state; unsigned replay_count; } http_stream; typedef struct { git_net_url url; git_credential *cred; unsigned auth_schemetypes; unsigned url_cred_presented : 1; } http_server; typedef struct { git_smart_subtransport parent; transport_smart *owner; http_server server; http_server proxy; git_http_client *http_client; } http_subtransport; static const http_service upload_pack_ls_service = { GIT_HTTP_METHOD_GET, "/info/refs?service=git-upload-pack", NULL, "application/x-git-upload-pack-advertisement", 0 }; static const http_service upload_pack_service = { GIT_HTTP_METHOD_POST, "/git-upload-pack", "application/x-git-upload-pack-request", "application/x-git-upload-pack-result", 0 }; static const http_service receive_pack_ls_service = { GIT_HTTP_METHOD_GET, "/info/refs?service=git-receive-pack", NULL, "application/x-git-receive-pack-advertisement", 0 }; static const http_service receive_pack_service = { GIT_HTTP_METHOD_POST, "/git-receive-pack", "application/x-git-receive-pack-request", "application/x-git-receive-pack-result", 1 }; #define SERVER_TYPE_REMOTE "remote" #define SERVER_TYPE_PROXY "proxy" #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport) static int apply_url_credentials( git_credential **cred, unsigned int allowed_types, const char *username, const char *password) { GIT_ASSERT_ARG(username); if (!password) password = ""; if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT) return git_credential_userpass_plaintext_new(cred, username, password); if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0') return git_credential_default_new(cred); return GIT_PASSTHROUGH; } GIT_INLINE(void) free_cred(git_credential **cred) { if (*cred) { git_credential_free(*cred); (*cred) = NULL; } } static int handle_auth( http_server *server, const char *server_type, const char *url, unsigned int allowed_schemetypes, unsigned int allowed_credtypes, git_credential_acquire_cb callback, void *callback_payload) { int error = 1; if (server->cred) free_cred(&server->cred); /* Start with URL-specified credentials, if there were any. */ if ((allowed_credtypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT) && !server->url_cred_presented && server->url.username) { error = apply_url_credentials(&server->cred, allowed_credtypes, server->url.username, server->url.password); server->url_cred_presented = 1; /* treat GIT_PASSTHROUGH as if callback isn't set */ if (error == GIT_PASSTHROUGH) error = 1; } if (error > 0 && callback) { error = callback(&server->cred, url, server->url.username, allowed_credtypes, callback_payload); /* treat GIT_PASSTHROUGH as if callback isn't set */ if (error == GIT_PASSTHROUGH) error = 1; } if (error > 0) { git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type); error = GIT_EAUTH; } if (!error) server->auth_schemetypes = allowed_schemetypes; return error; } GIT_INLINE(int) handle_remote_auth( http_stream *stream, git_http_response *response) { http_subtransport *transport = OWNING_SUBTRANSPORT(stream); if (response->server_auth_credtypes == 0) { git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support"); return GIT_EAUTH; } /* Otherwise, prompt for credentials. */ return handle_auth( &transport->server, SERVER_TYPE_REMOTE, transport->owner->url, response->server_auth_schemetypes, response->server_auth_credtypes, transport->owner->cred_acquire_cb, transport->owner->cred_acquire_payload); } GIT_INLINE(int) handle_proxy_auth( http_stream *stream, git_http_response *response) { http_subtransport *transport = OWNING_SUBTRANSPORT(stream); if (response->proxy_auth_credtypes == 0) { git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support"); return GIT_EAUTH; } /* Otherwise, prompt for credentials. */ return handle_auth( &transport->proxy, SERVER_TYPE_PROXY, transport->owner->proxy.url, response->server_auth_schemetypes, response->proxy_auth_credtypes, transport->owner->proxy.credentials, transport->owner->proxy.payload); } static int handle_response( bool *complete, http_stream *stream, git_http_response *response, bool allow_replay) { http_subtransport *transport = OWNING_SUBTRANSPORT(stream); int error; *complete = false; if (allow_replay && git_http_response_is_redirect(response)) { if (!response->location) { git_error_set(GIT_ERROR_HTTP, "redirect without location"); return -1; } if (git_net_url_apply_redirect(&transport->server.url, response->location, stream->service->url) < 0) { return -1; } return 0; } else if (git_http_response_is_redirect(response)) { git_error_set(GIT_ERROR_HTTP, "unexpected redirect"); return -1; } /* If we're in the middle of challenge/response auth, continue. */ if (allow_replay && response->resend_credentials) { return 0; } else if (allow_replay && response->status == GIT_HTTP_STATUS_UNAUTHORIZED) { if ((error = handle_remote_auth(stream, response)) < 0) return error; return git_http_client_skip_body(transport->http_client); } else if (allow_replay && response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { if ((error = handle_proxy_auth(stream, response)) < 0) return error; return git_http_client_skip_body(transport->http_client); } else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED || response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure"); return GIT_EAUTH; } if (response->status != GIT_HTTP_STATUS_OK) { git_error_set(GIT_ERROR_HTTP, "unexpected http status code: %d", response->status); return -1; } /* The response must contain a Content-Type header. */ if (!response->content_type) { git_error_set(GIT_ERROR_HTTP, "no content-type header in response"); return -1; } /* The Content-Type header must match our expectation. */ if (strcmp(response->content_type, stream->service->response_type) != 0) { git_error_set(GIT_ERROR_HTTP, "invalid content-type: '%s'", response->content_type); return -1; } *complete = true; stream->state = HTTP_STATE_RECEIVING_RESPONSE; return 0; } static int lookup_proxy( bool *out_use, http_subtransport *transport) { const char *proxy; git_remote *remote; char *config = NULL; int error = 0; *out_use = false; git_net_url_dispose(&transport->proxy.url); switch (transport->owner->proxy.type) { case GIT_PROXY_SPECIFIED: proxy = transport->owner->proxy.url; break; case GIT_PROXY_AUTO: remote = transport->owner->owner; error = git_remote__http_proxy(&config, remote, &transport->server.url); if (error || !config) goto done; proxy = config; break; default: return 0; } if (!proxy || (error = git_net_url_parse(&transport->proxy.url, proxy)) < 0) goto done; *out_use = true; done: git__free(config); return error; } static int generate_request( git_net_url *url, git_http_request *request, http_stream *stream, size_t len) { http_subtransport *transport = OWNING_SUBTRANSPORT(stream); bool use_proxy = false; int error; if ((error = git_net_url_joinpath(url, &transport->server.url, stream->service->url)) < 0 || (error = lookup_proxy(&use_proxy, transport)) < 0) return error; request->method = stream->service->method; request->url = url; request->credentials = transport->server.cred; request->proxy = use_proxy ? &transport->proxy.url : NULL; request->proxy_credentials = transport->proxy.cred; request->custom_headers = &transport->owner->custom_headers; if (stream->service->method == GIT_HTTP_METHOD_POST) { request->chunked = stream->service->chunked; request->content_length = stream->service->chunked ? 0 : len; request->content_type = stream->service->request_type; request->accept = stream->service->response_type; request->expect_continue = git_http__expect_continue; } return 0; } /* * Read from an HTTP transport - for the first invocation of this function * (ie, when stream->state == HTTP_STATE_NONE), we'll send a GET request * to the remote host. We will stream that data back on all subsequent * calls. */ static int http_stream_read( git_smart_subtransport_stream *s, char *buffer, size_t buffer_size, size_t *out_len) { http_stream *stream = (http_stream *)s; http_subtransport *transport = OWNING_SUBTRANSPORT(stream); git_net_url url = GIT_NET_URL_INIT; git_net_url proxy_url = GIT_NET_URL_INIT; git_http_request request = {0}; git_http_response response = {0}; bool complete; int error; *out_len = 0; if (stream->state == HTTP_STATE_NONE) { stream->state = HTTP_STATE_SENDING_REQUEST; stream->replay_count = 0; } /* * Formulate the URL, send the request and read the response * headers. Some of the request body may also be read. */ while (stream->state == HTTP_STATE_SENDING_REQUEST && stream->replay_count < GIT_HTTP_REPLAY_MAX) { git_net_url_dispose(&url); git_net_url_dispose(&proxy_url); git_http_response_dispose(&response); if ((error = generate_request(&url, &request, stream, 0)) < 0 || (error = git_http_client_send_request( transport->http_client, &request)) < 0 || (error = git_http_client_read_response( &response, transport->http_client)) < 0 || (error = handle_response(&complete, stream, &response, true)) < 0) goto done; if (complete) break; stream->replay_count++; } if (stream->state == HTTP_STATE_SENDING_REQUEST) { git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays"); error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */ goto done; } GIT_ASSERT(stream->state == HTTP_STATE_RECEIVING_RESPONSE); error = git_http_client_read_body(transport->http_client, buffer, buffer_size); if (error > 0) { *out_len = error; error = 0; } done: git_net_url_dispose(&url); git_net_url_dispose(&proxy_url); git_http_response_dispose(&response); return error; } static bool needs_probe(http_stream *stream) { http_subtransport *transport = OWNING_SUBTRANSPORT(stream); return (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM || transport->server.auth_schemetypes == GIT_HTTP_AUTH_NEGOTIATE); } static int send_probe(http_stream *stream) { http_subtransport *transport = OWNING_SUBTRANSPORT(stream); git_http_client *client = transport->http_client; const char *probe = "0000"; size_t len = 4; git_net_url url = GIT_NET_URL_INIT; git_http_request request = {0}; git_http_response response = {0}; bool complete = false; size_t step, steps = 1; int error; /* NTLM requires a full challenge/response */ if (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM) steps = GIT_AUTH_STEPS_NTLM; /* * Send at most two requests: one without any authentication to see * if we get prompted to authenticate. If we do, send a second one * with the first authentication message. The final authentication * message with the response will occur with the *actual* POST data. */ for (step = 0; step < steps && !complete; step++) { git_net_url_dispose(&url); git_http_response_dispose(&response); if ((error = generate_request(&url, &request, stream, len)) < 0 || (error = git_http_client_send_request(client, &request)) < 0 || (error = git_http_client_send_body(client, probe, len)) < 0 || (error = git_http_client_read_response(&response, client)) < 0 || (error = git_http_client_skip_body(client)) < 0 || (error = handle_response(&complete, stream, &response, true)) < 0) goto done; } done: git_http_response_dispose(&response); git_net_url_dispose(&url); return error; } /* * Write to an HTTP transport - for the first invocation of this function * (ie, when stream->state == HTTP_STATE_NONE), we'll send a POST request * to the remote host. If we're sending chunked data, then subsequent calls * will write the additional data given in the buffer. If we're not chunking, * then the caller should have given us all the data in the original call. * The caller should call http_stream_read_response to get the result. */ static int http_stream_write( git_smart_subtransport_stream *s, const char *buffer, size_t len) { http_stream *stream = GIT_CONTAINER_OF(s, http_stream, parent); http_subtransport *transport = OWNING_SUBTRANSPORT(stream); git_net_url url = GIT_NET_URL_INIT; git_http_request request = {0}; git_http_response response = {0}; int error; while (stream->state == HTTP_STATE_NONE && stream->replay_count < GIT_HTTP_REPLAY_MAX) { git_net_url_dispose(&url); git_http_response_dispose(&response); /* * If we're authenticating with a connection-based mechanism * (NTLM, Kerberos), send a "probe" packet. Servers SHOULD * authenticate an entire keep-alive connection, so ideally * we should not need to authenticate but some servers do * not support this. By sending a probe packet, we'll be * able to follow up with a second POST using the actual * data (and, in the degenerate case, the authentication * header as well). */ if (needs_probe(stream) && (error = send_probe(stream)) < 0) goto done; /* Send the regular POST request. */ if ((error = generate_request(&url, &request, stream, len)) < 0 || (error = git_http_client_send_request( transport->http_client, &request)) < 0) goto done; if (request.expect_continue && git_http_client_has_response(transport->http_client)) { bool complete; /* * If we got a response to an expect/continue, then * it's something other than a 100 and we should * deal with the response somehow. */ if ((error = git_http_client_read_response(&response, transport->http_client)) < 0 || (error = handle_response(&complete, stream, &response, true)) < 0) goto done; } else { stream->state = HTTP_STATE_SENDING_REQUEST; } stream->replay_count++; } if (stream->state == HTTP_STATE_NONE) { git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays"); error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */ goto done; } GIT_ASSERT(stream->state == HTTP_STATE_SENDING_REQUEST); error = git_http_client_send_body(transport->http_client, buffer, len); done: git_http_response_dispose(&response); git_net_url_dispose(&url); return error; } /* * Read from an HTTP transport after it has been written to. This is the * response from a POST request made by http_stream_write. */ static int http_stream_read_response( git_smart_subtransport_stream *s, char *buffer, size_t buffer_size, size_t *out_len) { http_stream *stream = (http_stream *)s; http_subtransport *transport = OWNING_SUBTRANSPORT(stream); git_http_client *client = transport->http_client; git_http_response response = {0}; bool complete; int error; *out_len = 0; if (stream->state == HTTP_STATE_SENDING_REQUEST) { if ((error = git_http_client_read_response(&response, client)) < 0 || (error = handle_response(&complete, stream, &response, false)) < 0) goto done; GIT_ASSERT(complete); stream->state = HTTP_STATE_RECEIVING_RESPONSE; } error = git_http_client_read_body(client, buffer, buffer_size); if (error > 0) { *out_len = error; error = 0; } done: git_http_response_dispose(&response); return error; } static void http_stream_free(git_smart_subtransport_stream *stream) { http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent); git__free(s); } static const http_service *select_service(git_smart_service_t action) { switch (action) { case GIT_SERVICE_UPLOADPACK_LS: return &upload_pack_ls_service; case GIT_SERVICE_UPLOADPACK: return &upload_pack_service; case GIT_SERVICE_RECEIVEPACK_LS: return &receive_pack_ls_service; case GIT_SERVICE_RECEIVEPACK: return &receive_pack_service; } return NULL; } static int http_action( git_smart_subtransport_stream **out, git_smart_subtransport *t, const char *url, git_smart_service_t action) { http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent); http_stream *stream; const http_service *service; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(t); *out = NULL; /* * If we've seen a redirect then preserve the location that we've * been given. This is important to continue authorization against * the redirect target, not the user-given source; the endpoint may * have redirected us from HTTP->HTTPS and is using an auth mechanism * that would be insecure in plaintext (eg, HTTP Basic). */ if (!git_net_url_valid(&transport->server.url) && (error = git_net_url_parse(&transport->server.url, url)) < 0) return error; if ((service = select_service(action)) == NULL) { git_error_set(GIT_ERROR_HTTP, "invalid action"); return -1; } stream = git__calloc(sizeof(http_stream), 1); GIT_ERROR_CHECK_ALLOC(stream); if (!transport->http_client) { git_http_client_options opts = {0}; opts.server_certificate_check_cb = transport->owner->certificate_check_cb; opts.server_certificate_check_payload = transport->owner->message_cb_payload; opts.proxy_certificate_check_cb = transport->owner->proxy.certificate_check; opts.proxy_certificate_check_payload = transport->owner->proxy.payload; if (git_http_client_new(&transport->http_client, &opts) < 0) return -1; } stream->service = service; stream->parent.subtransport = &transport->parent; if (service->method == GIT_HTTP_METHOD_GET) { stream->parent.read = http_stream_read; } else { stream->parent.write = http_stream_write; stream->parent.read = http_stream_read_response; } stream->parent.free = http_stream_free; *out = (git_smart_subtransport_stream *)stream; return 0; } static int http_close(git_smart_subtransport *t) { http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent); free_cred(&transport->server.cred); free_cred(&transport->proxy.cred); transport->server.url_cred_presented = false; transport->proxy.url_cred_presented = false; git_net_url_dispose(&transport->server.url); git_net_url_dispose(&transport->proxy.url); return 0; } static void http_free(git_smart_subtransport *t) { http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent); git_http_client_free(transport->http_client); http_close(t); git__free(transport); } int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param) { http_subtransport *transport; GIT_UNUSED(param); GIT_ASSERT_ARG(out); transport = git__calloc(sizeof(http_subtransport), 1); GIT_ERROR_CHECK_ALLOC(transport); transport->owner = (transport_smart *)owner; transport->parent.action = http_action; transport->parent.close = http_close; transport->parent.free = http_free; *out = (git_smart_subtransport *) transport; return 0; } #endif /* !GIT_WINHTTP */ git2r/src/libgit2/src/transports/smart_pkt.c0000644000175000017500000003236014125111754020741 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/types.h" #include "git2/errors.h" #include "git2/refs.h" #include "git2/revwalk.h" #include "smart.h" #include "util.h" #include "netops.h" #include "posix.h" #include "buffer.h" #include #define PKT_LEN_SIZE 4 static const char pkt_done_str[] = "0009done\n"; static const char pkt_flush_str[] = "0000"; static const char pkt_have_prefix[] = "0032have "; static const char pkt_want_prefix[] = "0032want "; static int flush_pkt(git_pkt **out) { git_pkt *pkt; pkt = git__malloc(sizeof(git_pkt)); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_FLUSH; *out = pkt; return 0; } /* the rest of the line will be useful for multi_ack and multi_ack_detailed */ static int ack_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ack *pkt; pkt = git__calloc(1, sizeof(git_pkt_ack)); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_ACK; if (git__prefixncmp(line, len, "ACK ")) goto out_err; line += 4; len -= 4; if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->oid, line) < 0) goto out_err; line += GIT_OID_HEXSZ; len -= GIT_OID_HEXSZ; if (len && line[0] == ' ') { line++; len--; if (!git__prefixncmp(line, len, "continue")) pkt->status = GIT_ACK_CONTINUE; else if (!git__prefixncmp(line, len, "common")) pkt->status = GIT_ACK_COMMON; else if (!git__prefixncmp(line, len, "ready")) pkt->status = GIT_ACK_READY; else goto out_err; } *out = (git_pkt *) pkt; return 0; out_err: git_error_set(GIT_ERROR_NET, "error parsing ACK pkt-line"); git__free(pkt); return -1; } static int nak_pkt(git_pkt **out) { git_pkt *pkt; pkt = git__malloc(sizeof(git_pkt)); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_NAK; *out = pkt; return 0; } static int comment_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_comment *pkt; size_t alloclen; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_comment), len); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); pkt = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_COMMENT; memcpy(pkt->comment, line, len); pkt->comment[len] = '\0'; *out = (git_pkt *) pkt; return 0; } static int err_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_err *pkt = NULL; size_t alloclen; /* Remove "ERR " from the line */ if (git__prefixncmp(line, len, "ERR ")) goto out_err; line += 4; len -= 4; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); pkt = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_ERR; pkt->len = len; memcpy(pkt->error, line, len); pkt->error[len] = '\0'; *out = (git_pkt *) pkt; return 0; out_err: git_error_set(GIT_ERROR_NET, "error parsing ERR pkt-line"); git__free(pkt); return -1; } static int data_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_data *pkt; size_t alloclen; line++; len--; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len); pkt = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_DATA; pkt->len = len; memcpy(pkt->data, line, len); *out = (git_pkt *) pkt; return 0; } static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_progress *pkt; size_t alloclen; line++; len--; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len); pkt = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_PROGRESS; pkt->len = len; memcpy(pkt->data, line, len); *out = (git_pkt *) pkt; return 0; } static int sideband_error_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_err *pkt; size_t alloc_len; line++; len--; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(git_pkt_err), len); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1); pkt = git__malloc(alloc_len); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_ERR; pkt->len = (int)len; memcpy(pkt->error, line, len); pkt->error[len] = '\0'; *out = (git_pkt *)pkt; return 0; } /* * Parse an other-ref line. */ static int ref_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ref *pkt; size_t alloclen; pkt = git__calloc(1, sizeof(git_pkt_ref)); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_REF; if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->head.oid, line) < 0) goto out_err; line += GIT_OID_HEXSZ; len -= GIT_OID_HEXSZ; if (git__prefixncmp(line, len, " ")) goto out_err; line++; len--; if (!len) goto out_err; if (line[len - 1] == '\n') --len; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1); pkt->head.name = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(pkt->head.name); memcpy(pkt->head.name, line, len); pkt->head.name[len] = '\0'; if (strlen(pkt->head.name) < len) pkt->capabilities = strchr(pkt->head.name, '\0') + 1; *out = (git_pkt *)pkt; return 0; out_err: git_error_set(GIT_ERROR_NET, "error parsing REF pkt-line"); if (pkt) git__free(pkt->head.name); git__free(pkt); return -1; } static int ok_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ok *pkt; size_t alloc_len; pkt = git__malloc(sizeof(*pkt)); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_OK; if (git__prefixncmp(line, len, "ok ")) goto out_err; line += 3; len -= 3; if (len && line[len - 1] == '\n') --len; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, len, 1); pkt->ref = git__malloc(alloc_len); GIT_ERROR_CHECK_ALLOC(pkt->ref); memcpy(pkt->ref, line, len); pkt->ref[len] = '\0'; *out = (git_pkt *)pkt; return 0; out_err: git_error_set(GIT_ERROR_NET, "error parsing OK pkt-line"); git__free(pkt); return -1; } static int ng_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ng *pkt; const char *ptr, *eol; size_t alloclen; pkt = git__malloc(sizeof(*pkt)); GIT_ERROR_CHECK_ALLOC(pkt); pkt->ref = NULL; pkt->type = GIT_PKT_NG; eol = line + len; if (git__prefixncmp(line, len, "ng ")) goto out_err; line += 3; if (!(ptr = memchr(line, ' ', eol - line))) goto out_err; len = ptr - line; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1); pkt->ref = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(pkt->ref); memcpy(pkt->ref, line, len); pkt->ref[len] = '\0'; line = ptr + 1; if (line >= eol) goto out_err; if (!(ptr = memchr(line, '\n', eol - line))) goto out_err; len = ptr - line; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1); pkt->msg = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(pkt->msg); memcpy(pkt->msg, line, len); pkt->msg[len] = '\0'; *out = (git_pkt *)pkt; return 0; out_err: git_error_set(GIT_ERROR_NET, "invalid packet line"); git__free(pkt->ref); git__free(pkt); return -1; } static int unpack_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_unpack *pkt; pkt = git__malloc(sizeof(*pkt)); GIT_ERROR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_UNPACK; if (!git__prefixncmp(line, len, "unpack ok")) pkt->unpack_ok = 1; else pkt->unpack_ok = 0; *out = (git_pkt *)pkt; return 0; } static int parse_len(size_t *out, const char *line, size_t linelen) { char num[PKT_LEN_SIZE + 1]; int i, k, error; int32_t len; const char *num_end; /* Not even enough for the length */ if (linelen < PKT_LEN_SIZE) return GIT_EBUFS; memcpy(num, line, PKT_LEN_SIZE); num[PKT_LEN_SIZE] = '\0'; for (i = 0; i < PKT_LEN_SIZE; ++i) { if (!isxdigit(num[i])) { /* Make sure there are no special characters before passing to error message */ for (k = 0; k < PKT_LEN_SIZE; ++k) { if(!isprint(num[k])) { num[k] = '.'; } } git_error_set(GIT_ERROR_NET, "invalid hex digit in length: '%s'", num); return -1; } } if ((error = git__strntol32(&len, num, PKT_LEN_SIZE, &num_end, 16)) < 0) return error; if (len < 0) return -1; *out = (size_t) len; return 0; } /* * As per the documentation, the syntax is: * * pkt-line = data-pkt / flush-pkt * data-pkt = pkt-len pkt-payload * pkt-len = 4*(HEXDIG) * pkt-payload = (pkt-len -4)*(OCTET) * flush-pkt = "0000" * * Which means that the first four bytes are the length of the line, * in ASCII hexadecimal (including itself) */ int git_pkt_parse_line( git_pkt **pkt, const char **endptr, const char *line, size_t linelen) { int error; size_t len; if ((error = parse_len(&len, line, linelen)) < 0) { /* * If we fail to parse the length, it might be * because the server is trying to send us the * packfile already or because we do not yet have * enough data. */ if (error == GIT_EBUFS) ; else if (!git__prefixncmp(line, linelen, "PACK")) git_error_set(GIT_ERROR_NET, "unexpected pack file"); else git_error_set(GIT_ERROR_NET, "bad packet length"); return error; } /* * Make sure there is enough in the buffer to satisfy * this line. */ if (linelen < len) return GIT_EBUFS; /* * The length has to be exactly 0 in case of a flush * packet or greater than PKT_LEN_SIZE, as the decoded * length includes its own encoded length of four bytes. */ if (len != 0 && len < PKT_LEN_SIZE) return GIT_ERROR; line += PKT_LEN_SIZE; /* * The Git protocol does not specify empty lines as part * of the protocol. Not knowing what to do with an empty * line, we should return an error upon hitting one. */ if (len == PKT_LEN_SIZE) { git_error_set_str(GIT_ERROR_NET, "Invalid empty packet"); return GIT_ERROR; } if (len == 0) { /* Flush pkt */ *endptr = line; return flush_pkt(pkt); } len -= PKT_LEN_SIZE; /* the encoded length includes its own size */ if (*line == GIT_SIDE_BAND_DATA) error = data_pkt(pkt, line, len); else if (*line == GIT_SIDE_BAND_PROGRESS) error = sideband_progress_pkt(pkt, line, len); else if (*line == GIT_SIDE_BAND_ERROR) error = sideband_error_pkt(pkt, line, len); else if (!git__prefixncmp(line, len, "ACK")) error = ack_pkt(pkt, line, len); else if (!git__prefixncmp(line, len, "NAK")) error = nak_pkt(pkt); else if (!git__prefixncmp(line, len, "ERR")) error = err_pkt(pkt, line, len); else if (*line == '#') error = comment_pkt(pkt, line, len); else if (!git__prefixncmp(line, len, "ok")) error = ok_pkt(pkt, line, len); else if (!git__prefixncmp(line, len, "ng")) error = ng_pkt(pkt, line, len); else if (!git__prefixncmp(line, len, "unpack")) error = unpack_pkt(pkt, line, len); else error = ref_pkt(pkt, line, len); *endptr = line + len; return error; } void git_pkt_free(git_pkt *pkt) { if (pkt == NULL) { return; } if (pkt->type == GIT_PKT_REF) { git_pkt_ref *p = (git_pkt_ref *) pkt; git__free(p->head.name); git__free(p->head.symref_target); } if (pkt->type == GIT_PKT_OK) { git_pkt_ok *p = (git_pkt_ok *) pkt; git__free(p->ref); } if (pkt->type == GIT_PKT_NG) { git_pkt_ng *p = (git_pkt_ng *) pkt; git__free(p->ref); git__free(p->msg); } git__free(pkt); } int git_pkt_buffer_flush(git_buf *buf) { return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str)); } static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_buf *buf) { git_buf str = GIT_BUF_INIT; char oid[GIT_OID_HEXSZ +1] = {0}; size_t len; /* Prefer multi_ack_detailed */ if (caps->multi_ack_detailed) git_buf_puts(&str, GIT_CAP_MULTI_ACK_DETAILED " "); else if (caps->multi_ack) git_buf_puts(&str, GIT_CAP_MULTI_ACK " "); /* Prefer side-band-64k if the server supports both */ if (caps->side_band_64k) git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K); else if (caps->side_band) git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND); if (caps->include_tag) git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " "); if (caps->thin_pack) git_buf_puts(&str, GIT_CAP_THIN_PACK " "); if (caps->ofs_delta) git_buf_puts(&str, GIT_CAP_OFS_DELTA " "); if (git_buf_oom(&str)) return -1; len = strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + git_buf_len(&str) + 1 /* LF */; if (len > 0xffff) { git_error_set(GIT_ERROR_NET, "tried to produce packet with invalid length %" PRIuZ, len); return -1; } git_buf_grow_by(buf, len); git_oid_fmt(oid, &head->oid); git_buf_printf(buf, "%04xwant %s %s\n", (unsigned int)len, oid, git_buf_cstr(&str)); git_buf_dispose(&str); GIT_ERROR_CHECK_ALLOC_BUF(buf); return 0; } /* * All "want" packets have the same length and format, so what we do * is overwrite the OID each time. */ int git_pkt_buffer_wants( const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_buf *buf) { size_t i = 0; const git_remote_head *head; if (caps->common) { for (; i < count; ++i) { head = refs[i]; if (!head->local) break; } if (buffer_want_with_caps(refs[i], caps, buf) < 0) return -1; i++; } for (; i < count; ++i) { char oid[GIT_OID_HEXSZ]; head = refs[i]; if (head->local) continue; git_oid_fmt(oid, &head->oid); git_buf_put(buf, pkt_want_prefix, strlen(pkt_want_prefix)); git_buf_put(buf, oid, GIT_OID_HEXSZ); git_buf_putc(buf, '\n'); if (git_buf_oom(buf)) return -1; } return git_pkt_buffer_flush(buf); } int git_pkt_buffer_have(git_oid *oid, git_buf *buf) { char oidhex[GIT_OID_HEXSZ + 1]; memset(oidhex, 0x0, sizeof(oidhex)); git_oid_fmt(oidhex, oid); return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex); } int git_pkt_buffer_done(git_buf *buf) { return git_buf_puts(buf, pkt_done_str); } git2r/src/libgit2/src/transports/local.c0000644000175000017500000004261714125111754020035 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/types.h" #include "git2/net.h" #include "git2/repository.h" #include "git2/object.h" #include "git2/tag.h" #include "git2/transport.h" #include "git2/revwalk.h" #include "git2/odb_backend.h" #include "git2/pack.h" #include "git2/commit.h" #include "git2/revparse.h" #include "pack-objects.h" #include "refs.h" #include "posix.h" #include "path.h" #include "buffer.h" #include "repository.h" #include "odb.h" #include "push.h" #include "remote.h" #include "proxy.h" typedef struct { git_transport parent; git_remote *owner; char *url; int direction; int flags; git_atomic32 cancelled; git_repository *repo; git_transport_message_cb progress_cb; git_transport_message_cb error_cb; void *message_cb_payload; git_vector refs; unsigned connected : 1, have_refs : 1; } transport_local; static void free_head(git_remote_head *head) { git__free(head->name); git__free(head->symref_target); git__free(head); } static void free_heads(git_vector *heads) { git_remote_head *head; size_t i; git_vector_foreach(heads, i, head) free_head(head); git_vector_free(heads); } static int add_ref(transport_local *t, const char *name) { const char peeled[] = "^{}"; git_reference *ref, *resolved; git_remote_head *head; git_oid obj_id; git_object *obj = NULL, *target = NULL; git_buf buf = GIT_BUF_INIT; int error; if ((error = git_reference_lookup(&ref, t->repo, name)) < 0) return error; error = git_reference_resolve(&resolved, ref); if (error < 0) { git_reference_free(ref); if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) { /* This is actually okay. Empty repos often have a HEAD that * points to a nonexistent "refs/heads/master". */ git_error_clear(); return 0; } return error; } git_oid_cpy(&obj_id, git_reference_target(resolved)); git_reference_free(resolved); head = git__calloc(1, sizeof(git_remote_head)); GIT_ERROR_CHECK_ALLOC(head); head->name = git__strdup(name); GIT_ERROR_CHECK_ALLOC(head->name); git_oid_cpy(&head->oid, &obj_id); if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { head->symref_target = git__strdup(git_reference_symbolic_target(ref)); GIT_ERROR_CHECK_ALLOC(head->symref_target); } git_reference_free(ref); if ((error = git_vector_insert(&t->refs, head)) < 0) { free_head(head); return error; } /* If it's not a tag, we don't need to try to peel it */ if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) return 0; if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJECT_ANY)) < 0) return error; head = NULL; /* If it's not an annotated tag, or if we're mocking * git-receive-pack, just get out */ if (git_object_type(obj) != GIT_OBJECT_TAG || t->direction != GIT_DIRECTION_FETCH) { git_object_free(obj); return 0; } /* And if it's a tag, peel it, and add it to the list */ head = git__calloc(1, sizeof(git_remote_head)); GIT_ERROR_CHECK_ALLOC(head); if (git_buf_join(&buf, 0, name, peeled) < 0) { free_head(head); return -1; } head->name = git_buf_detach(&buf); if (!(error = git_tag_peel(&target, (git_tag *)obj))) { git_oid_cpy(&head->oid, git_object_id(target)); if ((error = git_vector_insert(&t->refs, head)) < 0) { free_head(head); } } git_object_free(obj); git_object_free(target); return error; } static int store_refs(transport_local *t) { size_t i; git_remote_head *head; git_strarray ref_names = {0}; GIT_ASSERT_ARG(t); if (git_reference_list(&ref_names, t->repo) < 0) goto on_error; /* Clear all heads we might have fetched in a previous connect */ git_vector_foreach(&t->refs, i, head) { git__free(head->name); git__free(head); } /* Clear the vector so we can reuse it */ git_vector_clear(&t->refs); /* Sort the references first */ git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb); /* Add HEAD iff direction is fetch */ if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0) goto on_error; for (i = 0; i < ref_names.count; ++i) { if (add_ref(t, ref_names.strings[i]) < 0) goto on_error; } t->have_refs = 1; git_strarray_dispose(&ref_names); return 0; on_error: git_vector_free(&t->refs); git_strarray_dispose(&ref_names); return -1; } /* * Try to open the url as a git directory. The direction doesn't * matter in this case because we're calculating the heads ourselves. */ static int local_connect( git_transport *transport, const char *url, git_credential_acquire_cb cred_acquire_cb, void *cred_acquire_payload, const git_proxy_options *proxy, int direction, int flags) { git_repository *repo; int error; transport_local *t = (transport_local *) transport; const char *path; git_buf buf = GIT_BUF_INIT; GIT_UNUSED(cred_acquire_cb); GIT_UNUSED(cred_acquire_payload); GIT_UNUSED(proxy); if (t->connected) return 0; free_heads(&t->refs); t->url = git__strdup(url); GIT_ERROR_CHECK_ALLOC(t->url); t->direction = direction; t->flags = flags; /* 'url' may be a url or path; convert to a path */ if ((error = git_path_from_url_or_path(&buf, url)) < 0) { git_buf_dispose(&buf); return error; } path = git_buf_cstr(&buf); error = git_repository_open(&repo, path); git_buf_dispose(&buf); if (error < 0) return -1; t->repo = repo; if (store_refs(t) < 0) return -1; t->connected = 1; return 0; } static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport) { transport_local *t = (transport_local *)transport; if (!t->have_refs) { git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs"); return -1; } *out = (const git_remote_head **)t->refs.contents; *size = t->refs.length; return 0; } static int local_negotiate_fetch( git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count) { transport_local *t = (transport_local*)transport; git_remote_head *rhead; unsigned int i; GIT_UNUSED(refs); GIT_UNUSED(count); /* Fill in the loids */ git_vector_foreach(&t->refs, i, rhead) { git_object *obj; int error = git_revparse_single(&obj, repo, rhead->name); if (!error) git_oid_cpy(&rhead->loid, git_object_id(obj)); else if (error != GIT_ENOTFOUND) return error; else git_error_clear(); git_object_free(obj); } return 0; } static int local_push_update_remote_ref( git_repository *remote_repo, const char *lref, const char *rref, git_oid *loid, git_oid *roid) { int error; git_reference *remote_ref = NULL; /* check for lhs, if it's empty it means to delete */ if (lref[0] != '\0') { /* Create or update a ref */ error = git_reference_create(NULL, remote_repo, rref, loid, !git_oid_is_zero(roid), NULL); } else { /* Delete a ref */ if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) { if (error == GIT_ENOTFOUND) error = 0; return error; } error = git_reference_delete(remote_ref); git_reference_free(remote_ref); } return error; } static int transfer_to_push_transfer(const git_indexer_progress *stats, void *payload) { const git_remote_callbacks *cbs = payload; if (!cbs || !cbs->push_transfer_progress) return 0; return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes, cbs->payload); } static int local_push( git_transport *transport, git_push *push, const git_remote_callbacks *cbs) { transport_local *t = (transport_local *)transport; git_repository *remote_repo = NULL; push_spec *spec; char *url = NULL; const char *path; git_buf buf = GIT_BUF_INIT, odb_path = GIT_BUF_INIT; int error; size_t j; GIT_UNUSED(cbs); /* 'push->remote->url' may be a url or path; convert to a path */ if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) { git_buf_dispose(&buf); return error; } path = git_buf_cstr(&buf); error = git_repository_open(&remote_repo, path); git_buf_dispose(&buf); if (error < 0) return error; /* We don't currently support pushing locally to non-bare repos. Proper non-bare repo push support would require checking configs to see if we should override the default 'don't let this happen' behavior. Note that this is only an issue when pushing to the current branch, but we forbid all pushes just in case */ if (!remote_repo->is_bare) { error = GIT_EBAREREPO; git_error_set(GIT_ERROR_INVALID, "local push doesn't (yet) support pushing to non-bare repos."); goto on_error; } if ((error = git_repository_item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0 || (error = git_buf_joinpath(&odb_path, odb_path.ptr, "pack")) < 0) goto on_error; error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs); git_buf_dispose(&odb_path); if (error < 0) goto on_error; push->unpack_ok = 1; git_vector_foreach(&push->specs, j, spec) { push_status *status; const git_error *last; char *ref = spec->refspec.dst; status = git__calloc(1, sizeof(push_status)); if (!status) goto on_error; status->ref = git__strdup(ref); if (!status->ref) { git_push_status_free(status); goto on_error; } error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst, &spec->loid, &spec->roid); switch (error) { case GIT_OK: break; case GIT_EINVALIDSPEC: status->msg = git__strdup("funny refname"); break; case GIT_ENOTFOUND: status->msg = git__strdup("Remote branch not found to delete"); break; default: last = git_error_last(); if (last && last->message) status->msg = git__strdup(last->message); else status->msg = git__strdup("Unspecified error encountered"); break; } /* failed to allocate memory for a status message */ if (error < 0 && !status->msg) { git_push_status_free(status); goto on_error; } /* failed to insert the ref update status */ if ((error = git_vector_insert(&push->status, status)) < 0) { git_push_status_free(status); goto on_error; } } if (push->specs.length) { int flags = t->flags; url = git__strdup(t->url); if (!url || t->parent.close(&t->parent) < 0 || t->parent.connect(&t->parent, url, NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags)) goto on_error; } error = 0; on_error: git_repository_free(remote_repo); git__free(url); return error; } typedef struct foreach_data { git_indexer_progress *stats; git_indexer_progress_cb progress_cb; void *progress_payload; git_odb_writepack *writepack; } foreach_data; static int foreach_cb(void *buf, size_t len, void *payload) { foreach_data *data = (foreach_data*)payload; data->stats->received_bytes += len; return data->writepack->append(data->writepack, buf, len, data->stats); } static const char *counting_objects_fmt = "Counting objects %d\r"; static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)"; static int local_counting(int stage, unsigned int current, unsigned int total, void *payload) { git_buf progress_info = GIT_BUF_INIT; transport_local *t = payload; int error; if (!t->progress_cb) return 0; if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) { git_buf_printf(&progress_info, counting_objects_fmt, current); } else if (stage == GIT_PACKBUILDER_DELTAFICATION) { float perc = (((float) current) / total) * 100; git_buf_printf(&progress_info, compressing_objects_fmt, perc, current, total); if (current == total) git_buf_printf(&progress_info, ", done\n"); else git_buf_putc(&progress_info, '\r'); } if (git_buf_oom(&progress_info)) return -1; error = t->progress_cb(git_buf_cstr(&progress_info), (int)git_buf_len(&progress_info), t->message_cb_payload); git_buf_dispose(&progress_info); return error; } static int foreach_reference_cb(git_reference *reference, void *payload) { git_revwalk *walk = (git_revwalk *)payload; int error; if (git_reference_type(reference) != GIT_REFERENCE_DIRECT) { git_reference_free(reference); return 0; } error = git_revwalk_hide(walk, git_reference_target(reference)); /* The reference is in the local repository, so the target may not * exist on the remote. It also may not be a commit. */ if (error == GIT_ENOTFOUND || error == GIT_ERROR_INVALID) { git_error_clear(); error = 0; } git_reference_free(reference); return error; } static int local_download_pack( git_transport *transport, git_repository *repo, git_indexer_progress *stats, git_indexer_progress_cb progress_cb, void *progress_payload) { transport_local *t = (transport_local*)transport; git_revwalk *walk = NULL; git_remote_head *rhead; unsigned int i; int error = -1; git_packbuilder *pack = NULL; git_odb_writepack *writepack = NULL; git_odb *odb = NULL; git_buf progress_info = GIT_BUF_INIT; if ((error = git_revwalk_new(&walk, t->repo)) < 0) goto cleanup; git_revwalk_sorting(walk, GIT_SORT_TIME); if ((error = git_packbuilder_new(&pack, t->repo)) < 0) goto cleanup; git_packbuilder_set_callbacks(pack, local_counting, t); stats->total_objects = 0; stats->indexed_objects = 0; stats->received_objects = 0; stats->received_bytes = 0; git_vector_foreach(&t->refs, i, rhead) { git_object *obj; if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJECT_ANY)) < 0) goto cleanup; if (git_object_type(obj) == GIT_OBJECT_COMMIT) { /* Revwalker includes only wanted commits */ error = git_revwalk_push(walk, &rhead->oid); } else { /* Tag or some other wanted object. Add it on its own */ error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name); } git_object_free(obj); if (error < 0) goto cleanup; } if ((error = git_reference_foreach(repo, foreach_reference_cb, walk))) goto cleanup; if ((error = git_packbuilder_insert_walk(pack, walk))) goto cleanup; if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0) goto cleanup; if (t->progress_cb && (error = t->progress_cb(git_buf_cstr(&progress_info), (int)git_buf_len(&progress_info), t->message_cb_payload)) < 0) goto cleanup; /* Walk the objects, building a packfile */ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) goto cleanup; /* One last one with the newline */ git_buf_clear(&progress_info); git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack)); if ((error = git_buf_putc(&progress_info, '\n')) < 0) goto cleanup; if (t->progress_cb && (error = t->progress_cb(git_buf_cstr(&progress_info), (int)git_buf_len(&progress_info), t->message_cb_payload)) < 0) goto cleanup; if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0) goto cleanup; /* Write the data to the ODB */ { foreach_data data = {0}; data.stats = stats; data.progress_cb = progress_cb; data.progress_payload = progress_payload; data.writepack = writepack; /* autodetect */ git_packbuilder_set_threads(pack, 0); if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0) goto cleanup; } error = writepack->commit(writepack, stats); cleanup: if (writepack) writepack->free(writepack); git_buf_dispose(&progress_info); git_packbuilder_free(pack); git_revwalk_free(walk); return error; } static int local_set_callbacks( git_transport *transport, git_transport_message_cb progress_cb, git_transport_message_cb error_cb, git_transport_certificate_check_cb certificate_check_cb, void *message_cb_payload) { transport_local *t = (transport_local *)transport; GIT_UNUSED(certificate_check_cb); t->progress_cb = progress_cb; t->error_cb = error_cb; t->message_cb_payload = message_cb_payload; return 0; } static int local_is_connected(git_transport *transport) { transport_local *t = (transport_local *)transport; return t->connected; } static int local_read_flags(git_transport *transport, int *flags) { transport_local *t = (transport_local *)transport; *flags = t->flags; return 0; } static void local_cancel(git_transport *transport) { transport_local *t = (transport_local *)transport; git_atomic32_set(&t->cancelled, 1); } static int local_close(git_transport *transport) { transport_local *t = (transport_local *)transport; t->connected = 0; if (t->repo) { git_repository_free(t->repo); t->repo = NULL; } if (t->url) { git__free(t->url); t->url = NULL; } return 0; } static void local_free(git_transport *transport) { transport_local *t = (transport_local *)transport; free_heads(&t->refs); /* Close the transport, if it's still open. */ local_close(transport); /* Free the transport */ git__free(t); } /************** * Public API * **************/ int git_transport_local(git_transport **out, git_remote *owner, void *param) { int error; transport_local *t; GIT_UNUSED(param); t = git__calloc(1, sizeof(transport_local)); GIT_ERROR_CHECK_ALLOC(t); t->parent.version = GIT_TRANSPORT_VERSION; t->parent.set_callbacks = local_set_callbacks; t->parent.connect = local_connect; t->parent.negotiate_fetch = local_negotiate_fetch; t->parent.download_pack = local_download_pack; t->parent.push = local_push; t->parent.close = local_close; t->parent.free = local_free; t->parent.ls = local_ls; t->parent.is_connected = local_is_connected; t->parent.read_flags = local_read_flags; t->parent.cancel = local_cancel; if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) { git__free(t); return error; } t->owner = owner; *out = (git_transport *) t; return 0; } git2r/src/libgit2/src/transports/smart_protocol.c0000644000175000017500000006205514125111754022010 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2.h" #include "git2/odb_backend.h" #include "smart.h" #include "refs.h" #include "repository.h" #include "push.h" #include "pack-objects.h" #include "remote.h" #include "util.h" #include "revwalk.h" #define NETWORK_XFER_THRESHOLD (100*1024) /* The minimal interval between progress updates (in seconds). */ #define MIN_PROGRESS_UPDATE_INTERVAL 0.5 bool git_smart__ofs_delta_enabled = true; int git_smart__store_refs(transport_smart *t, int flushes) { gitno_buffer *buf = &t->buffer; git_vector *refs = &t->refs; int error, flush = 0, recvd; const char *line_end = NULL; git_pkt *pkt = NULL; size_t i; /* Clear existing refs in case git_remote_connect() is called again * after git_remote_disconnect(). */ git_vector_foreach(refs, i, pkt) { git_pkt_free(pkt); } git_vector_clear(refs); pkt = NULL; do { if (buf->offset > 0) error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset); else error = GIT_EBUFS; if (error < 0 && error != GIT_EBUFS) return error; if (error == GIT_EBUFS) { if ((recvd = gitno_recv(buf)) < 0) return recvd; if (recvd == 0) { git_error_set(GIT_ERROR_NET, "early EOF"); return GIT_EEOF; } continue; } if (gitno_consume(buf, line_end) < 0) return -1; if (pkt->type == GIT_PKT_ERR) { git_error_set(GIT_ERROR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error); git__free(pkt); return -1; } if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0) return -1; if (pkt->type == GIT_PKT_FLUSH) { flush++; git_pkt_free(pkt); } } while (flush < flushes); return flush; } static int append_symref(const char **out, git_vector *symrefs, const char *ptr) { int error; const char *end; git_buf buf = GIT_BUF_INIT; git_refspec *mapping = NULL; ptr += strlen(GIT_CAP_SYMREF); if (*ptr != '=') goto on_invalid; ptr++; if (!(end = strchr(ptr, ' ')) && !(end = strchr(ptr, '\0'))) goto on_invalid; if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0) return error; /* symref mapping has refspec format */ mapping = git__calloc(1, sizeof(git_refspec)); GIT_ERROR_CHECK_ALLOC(mapping); error = git_refspec__parse(mapping, git_buf_cstr(&buf), true); git_buf_dispose(&buf); /* if the error isn't OOM, then it's a parse error; let's use a nicer message */ if (error < 0) { if (git_error_last()->klass != GIT_ERROR_NOMEMORY) goto on_invalid; git__free(mapping); return error; } if ((error = git_vector_insert(symrefs, mapping)) < 0) return error; *out = end; return 0; on_invalid: git_error_set(GIT_ERROR_NET, "remote sent invalid symref"); git_refspec__dispose(mapping); git__free(mapping); return -1; } int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs) { const char *ptr; /* No refs or capabilites, odd but not a problem */ if (pkt == NULL || pkt->capabilities == NULL) return GIT_ENOTFOUND; ptr = pkt->capabilities; while (ptr != NULL && *ptr != '\0') { if (*ptr == ' ') ptr++; if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { caps->common = caps->ofs_delta = 1; ptr += strlen(GIT_CAP_OFS_DELTA); continue; } /* Keep multi_ack_detailed before multi_ack */ if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) { caps->common = caps->multi_ack_detailed = 1; ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED); continue; } if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) { caps->common = caps->multi_ack = 1; ptr += strlen(GIT_CAP_MULTI_ACK); continue; } if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) { caps->common = caps->include_tag = 1; ptr += strlen(GIT_CAP_INCLUDE_TAG); continue; } /* Keep side-band check after side-band-64k */ if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) { caps->common = caps->side_band_64k = 1; ptr += strlen(GIT_CAP_SIDE_BAND_64K); continue; } if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) { caps->common = caps->side_band = 1; ptr += strlen(GIT_CAP_SIDE_BAND); continue; } if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) { caps->common = caps->delete_refs = 1; ptr += strlen(GIT_CAP_DELETE_REFS); continue; } if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) { caps->common = caps->thin_pack = 1; ptr += strlen(GIT_CAP_THIN_PACK); continue; } if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) { int error; if ((error = append_symref(&ptr, symrefs, ptr)) < 0) return error; continue; } /* We don't know this capability, so skip it */ ptr = strchr(ptr, ' '); } return 0; } static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf) { const char *ptr = buf->data, *line_end = ptr; git_pkt *pkt = NULL; int error = 0, ret; do { if (buf->offset > 0) error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset); else error = GIT_EBUFS; if (error == 0) break; /* return the pkt */ if (error < 0 && error != GIT_EBUFS) return error; if ((ret = gitno_recv(buf)) < 0) { return ret; } else if (ret == 0) { git_error_set(GIT_ERROR_NET, "early EOF"); return GIT_EEOF; } } while (error); if (gitno_consume(buf, line_end) < 0) return -1; if (out_type != NULL) *out_type = pkt->type; if (out_pkt != NULL) *out_pkt = pkt; else git__free(pkt); return error; } static int store_common(transport_smart *t) { git_pkt *pkt = NULL; gitno_buffer *buf = &t->buffer; int error; do { if ((error = recv_pkt(&pkt, NULL, buf)) < 0) return error; if (pkt->type != GIT_PKT_ACK) { git__free(pkt); return 0; } if (git_vector_insert(&t->common, pkt) < 0) { git__free(pkt); return -1; } } while (1); return 0; } static int wait_while_ack(gitno_buffer *buf) { int error; git_pkt *pkt = NULL; git_pkt_ack *ack = NULL; while (1) { git_pkt_free(pkt); if ((error = recv_pkt(&pkt, NULL, buf)) < 0) return error; if (pkt->type == GIT_PKT_NAK) break; if (pkt->type != GIT_PKT_ACK) continue; ack = (git_pkt_ack*)pkt; if (ack->status != GIT_ACK_CONTINUE && ack->status != GIT_ACK_COMMON && ack->status != GIT_ACK_READY) { break; } } git_pkt_free(pkt); return 0; } int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count) { transport_smart *t = (transport_smart *)transport; git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; gitno_buffer *buf = &t->buffer; git_buf data = GIT_BUF_INIT; git_revwalk *walk = NULL; int error = -1; git_pkt_type pkt_type; unsigned int i; git_oid oid; if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) return error; if ((error = git_revwalk_new(&walk, repo)) < 0) goto on_error; opts.insert_by_date = 1; if ((error = git_revwalk__push_glob(walk, "refs/*", &opts)) < 0) goto on_error; /* * Our support for ACK extensions is simply to parse them. On * the first ACK we will accept that as enough common * objects. We give up if we haven't found an answer in the * first 256 we send. */ i = 0; while (i < 256) { error = git_revwalk_next(&oid, walk); if (error < 0) { if (GIT_ITEROVER == error) break; goto on_error; } git_pkt_buffer_have(&oid, &data); i++; if (i % 20 == 0) { if (t->cancelled.val) { git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user"); error = GIT_EUSER; goto on_error; } git_pkt_buffer_flush(&data); if (git_buf_oom(&data)) { error = -1; goto on_error; } if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0) goto on_error; git_buf_clear(&data); if (t->caps.multi_ack || t->caps.multi_ack_detailed) { if ((error = store_common(t)) < 0) goto on_error; } else { if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0) goto on_error; if (pkt_type == GIT_PKT_ACK) { break; } else if (pkt_type == GIT_PKT_NAK) { continue; } else { git_error_set(GIT_ERROR_NET, "unexpected pkt type"); error = -1; goto on_error; } } } if (t->common.length > 0) break; if (i % 20 == 0 && t->rpc) { git_pkt_ack *pkt; unsigned int j; if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) goto on_error; git_vector_foreach(&t->common, j, pkt) { if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0) goto on_error; } if (git_buf_oom(&data)) { error = -1; goto on_error; } } } /* Tell the other end that we're done negotiating */ if (t->rpc && t->common.length > 0) { git_pkt_ack *pkt; unsigned int j; if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) goto on_error; git_vector_foreach(&t->common, j, pkt) { if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0) goto on_error; } if (git_buf_oom(&data)) { error = -1; goto on_error; } } if ((error = git_pkt_buffer_done(&data)) < 0) goto on_error; if (t->cancelled.val) { git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user"); error = GIT_EUSER; goto on_error; } if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0) goto on_error; git_buf_dispose(&data); git_revwalk_free(walk); /* Now let's eat up whatever the server gives us */ if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) { if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0) return error; if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { git_error_set(GIT_ERROR_NET, "unexpected pkt type"); return -1; } } else { error = wait_while_ack(buf); } return error; on_error: git_revwalk_free(walk); git_buf_dispose(&data); return error; } static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_indexer_progress *stats) { int recvd; do { if (t->cancelled.val) { git_error_set(GIT_ERROR_NET, "the fetch was cancelled by the user"); return GIT_EUSER; } if (writepack->append(writepack, buf->data, buf->offset, stats) < 0) return -1; gitno_consume_n(buf, buf->offset); if ((recvd = gitno_recv(buf)) < 0) return recvd; } while(recvd > 0); if (writepack->commit(writepack, stats) < 0) return -1; return 0; } struct network_packetsize_payload { git_indexer_progress_cb callback; void *payload; git_indexer_progress *stats; size_t last_fired_bytes; }; static int network_packetsize(size_t received, void *payload) { struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload; /* Accumulate bytes */ npp->stats->received_bytes += received; /* Fire notification if the threshold is reached */ if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) { npp->last_fired_bytes = npp->stats->received_bytes; if (npp->callback(npp->stats, npp->payload)) return GIT_EUSER; } return 0; } int git_smart__download_pack( git_transport *transport, git_repository *repo, git_indexer_progress *stats, git_indexer_progress_cb progress_cb, void *progress_payload) { transport_smart *t = (transport_smart *)transport; gitno_buffer *buf = &t->buffer; git_odb *odb; struct git_odb_writepack *writepack = NULL; int error = 0; struct network_packetsize_payload npp = {0}; memset(stats, 0, sizeof(git_indexer_progress)); if (progress_cb) { npp.callback = progress_cb; npp.payload = progress_payload; npp.stats = stats; t->packetsize_cb = &network_packetsize; t->packetsize_payload = &npp; /* We might have something in the buffer already from negotiate_fetch */ if (t->buffer.offset > 0 && !t->cancelled.val) if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload)) git_atomic32_set(&t->cancelled, 1); } if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)) goto done; /* * If the remote doesn't support the side-band, we can feed * the data directly to the pack writer. Otherwise, we need to * check which one belongs there. */ if (!t->caps.side_band && !t->caps.side_band_64k) { error = no_sideband(t, writepack, buf, stats); goto done; } do { git_pkt *pkt = NULL; /* Check cancellation before network call */ if (t->cancelled.val) { git_error_clear(); error = GIT_EUSER; goto done; } if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) { /* Check cancellation after network call */ if (t->cancelled.val) { git_error_clear(); error = GIT_EUSER; } else if (pkt->type == GIT_PKT_PROGRESS) { if (t->progress_cb) { git_pkt_progress *p = (git_pkt_progress *) pkt; if (p->len > INT_MAX) { git_error_set(GIT_ERROR_NET, "oversized progress message"); error = GIT_ERROR; goto done; } error = t->progress_cb(p->data, (int)p->len, t->message_cb_payload); } } else if (pkt->type == GIT_PKT_DATA) { git_pkt_data *p = (git_pkt_data *) pkt; if (p->len) error = writepack->append(writepack, p->data, p->len, stats); } else if (pkt->type == GIT_PKT_FLUSH) { /* A flush indicates the end of the packfile */ git__free(pkt); break; } } git_pkt_free(pkt); if (error < 0) goto done; } while (1); /* * Trailing execution of progress_cb, if necessary... * Only the callback through the npp datastructure currently * updates the last_fired_bytes value. It is possible that * progress has already been reported with the correct * "received_bytes" value, but until (if?) this is unified * then we will report progress again to be sure that the * correct last received_bytes value is reported. */ if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) { error = npp.callback(npp.stats, npp.payload); if (error != 0) goto done; } error = writepack->commit(writepack, stats); done: if (writepack) writepack->free(writepack); if (progress_cb) { t->packetsize_cb = NULL; t->packetsize_payload = NULL; } return error; } static int gen_pktline(git_buf *buf, git_push *push) { push_spec *spec; size_t i, len; char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1]; old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0'; git_vector_foreach(&push->specs, i, spec) { len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst); if (i == 0) { ++len; /* '\0' */ if (push->report_status) len += strlen(GIT_CAP_REPORT_STATUS) + 1; len += strlen(GIT_CAP_SIDE_BAND_64K) + 1; } git_oid_fmt(old_id, &spec->roid); git_oid_fmt(new_id, &spec->loid); git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst); if (i == 0) { git_buf_putc(buf, '\0'); /* Core git always starts their capabilities string with a space */ if (push->report_status) { git_buf_putc(buf, ' '); git_buf_printf(buf, GIT_CAP_REPORT_STATUS); } git_buf_putc(buf, ' '); git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K); } git_buf_putc(buf, '\n'); } git_buf_puts(buf, "0000"); return git_buf_oom(buf) ? -1 : 0; } static int add_push_report_pkt(git_push *push, git_pkt *pkt) { push_status *status; switch (pkt->type) { case GIT_PKT_OK: status = git__calloc(1, sizeof(push_status)); GIT_ERROR_CHECK_ALLOC(status); status->msg = NULL; status->ref = git__strdup(((git_pkt_ok *)pkt)->ref); if (!status->ref || git_vector_insert(&push->status, status) < 0) { git_push_status_free(status); return -1; } break; case GIT_PKT_NG: status = git__calloc(1, sizeof(push_status)); GIT_ERROR_CHECK_ALLOC(status); status->ref = git__strdup(((git_pkt_ng *)pkt)->ref); status->msg = git__strdup(((git_pkt_ng *)pkt)->msg); if (!status->ref || !status->msg || git_vector_insert(&push->status, status) < 0) { git_push_status_free(status); return -1; } break; case GIT_PKT_UNPACK: push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok; break; case GIT_PKT_FLUSH: return GIT_ITEROVER; default: git_error_set(GIT_ERROR_NET, "report-status: protocol error"); return -1; } return 0; } static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf) { git_pkt *pkt; const char *line, *line_end = NULL; size_t line_len; int error; int reading_from_buf = data_pkt_buf->size > 0; if (reading_from_buf) { /* We had an existing partial packet, so add the new * packet to the buffer and parse the whole thing */ git_buf_put(data_pkt_buf, data_pkt->data, data_pkt->len); line = data_pkt_buf->ptr; line_len = data_pkt_buf->size; } else { line = data_pkt->data; line_len = data_pkt->len; } while (line_len > 0) { error = git_pkt_parse_line(&pkt, &line_end, line, line_len); if (error == GIT_EBUFS) { /* Buffer the data when the inner packet is split * across multiple sideband packets */ if (!reading_from_buf) git_buf_put(data_pkt_buf, line, line_len); error = 0; goto done; } else if (error < 0) goto done; /* Advance in the buffer */ line_len -= (line_end - line); line = line_end; error = add_push_report_pkt(push, pkt); git_pkt_free(pkt); if (error < 0 && error != GIT_ITEROVER) goto done; } error = 0; done: if (reading_from_buf) git_buf_consume(data_pkt_buf, line_end); return error; } static int parse_report(transport_smart *transport, git_push *push) { git_pkt *pkt = NULL; const char *line_end = NULL; gitno_buffer *buf = &transport->buffer; int error, recvd; git_buf data_pkt_buf = GIT_BUF_INIT; for (;;) { if (buf->offset > 0) error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset); else error = GIT_EBUFS; if (error < 0 && error != GIT_EBUFS) { error = -1; goto done; } if (error == GIT_EBUFS) { if ((recvd = gitno_recv(buf)) < 0) { error = recvd; goto done; } if (recvd == 0) { git_error_set(GIT_ERROR_NET, "early EOF"); error = GIT_EEOF; goto done; } continue; } if (gitno_consume(buf, line_end) < 0) return -1; error = 0; switch (pkt->type) { case GIT_PKT_DATA: /* This is a sideband packet which contains other packets */ error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf); break; case GIT_PKT_ERR: git_error_set(GIT_ERROR_NET, "report-status: Error reported: %s", ((git_pkt_err *)pkt)->error); error = -1; break; case GIT_PKT_PROGRESS: if (transport->progress_cb) { git_pkt_progress *p = (git_pkt_progress *) pkt; if (p->len > INT_MAX) { git_error_set(GIT_ERROR_NET, "oversized progress message"); error = GIT_ERROR; goto done; } error = transport->progress_cb(p->data, (int)p->len, transport->message_cb_payload); } break; default: error = add_push_report_pkt(push, pkt); break; } git_pkt_free(pkt); /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */ if (error == GIT_ITEROVER) { error = 0; if (data_pkt_buf.size > 0) { /* If there was data remaining in the pack data buffer, * then the server sent a partial pkt-line */ git_error_set(GIT_ERROR_NET, "incomplete pack data pkt-line"); error = GIT_ERROR; } goto done; } if (error < 0) { goto done; } } done: git_buf_dispose(&data_pkt_buf); return error; } static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec) { git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref)); GIT_ERROR_CHECK_ALLOC(added); added->type = GIT_PKT_REF; git_oid_cpy(&added->head.oid, &push_spec->loid); added->head.name = git__strdup(push_spec->refspec.dst); if (!added->head.name || git_vector_insert(refs, added) < 0) { git_pkt_free((git_pkt *)added); return -1; } return 0; } static int update_refs_from_report( git_vector *refs, git_vector *push_specs, git_vector *push_report) { git_pkt_ref *ref; push_spec *push_spec; push_status *push_status; size_t i, j, refs_len; int cmp; /* For each push spec we sent to the server, we should have * gotten back a status packet in the push report */ if (push_specs->length != push_report->length) { git_error_set(GIT_ERROR_NET, "report-status: protocol error"); return -1; } /* We require that push_specs be sorted with push_spec_rref_cmp, * and that push_report be sorted with push_status_ref_cmp */ git_vector_sort(push_specs); git_vector_sort(push_report); git_vector_foreach(push_specs, i, push_spec) { push_status = git_vector_get(push_report, i); /* For each push spec we sent to the server, we should have * gotten back a status packet in the push report which matches */ if (strcmp(push_spec->refspec.dst, push_status->ref)) { git_error_set(GIT_ERROR_NET, "report-status: protocol error"); return -1; } } /* We require that refs be sorted with ref_name_cmp */ git_vector_sort(refs); i = j = 0; refs_len = refs->length; /* Merge join push_specs with refs */ while (i < push_specs->length && j < refs_len) { push_spec = git_vector_get(push_specs, i); push_status = git_vector_get(push_report, i); ref = git_vector_get(refs, j); cmp = strcmp(push_spec->refspec.dst, ref->head.name); /* Iterate appropriately */ if (cmp <= 0) i++; if (cmp >= 0) j++; /* Add case */ if (cmp < 0 && !push_status->msg && add_ref_from_push_spec(refs, push_spec) < 0) return -1; /* Update case, delete case */ if (cmp == 0 && !push_status->msg) git_oid_cpy(&ref->head.oid, &push_spec->loid); } for (; i < push_specs->length; i++) { push_spec = git_vector_get(push_specs, i); push_status = git_vector_get(push_report, i); /* Add case */ if (!push_status->msg && add_ref_from_push_spec(refs, push_spec) < 0) return -1; } /* Remove any refs which we updated to have a zero OID. */ git_vector_rforeach(refs, i, ref) { if (git_oid_is_zero(&ref->head.oid)) { git_vector_remove(refs, i); git_pkt_free((git_pkt *)ref); } } git_vector_sort(refs); return 0; } struct push_packbuilder_payload { git_smart_subtransport_stream *stream; git_packbuilder *pb; git_push_transfer_progress_cb cb; void *cb_payload; size_t last_bytes; double last_progress_report_time; }; static int stream_thunk(void *buf, size_t size, void *data) { int error = 0; struct push_packbuilder_payload *payload = data; if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0) return error; if (payload->cb) { double current_time = git__timer(); double elapsed = current_time - payload->last_progress_report_time; payload->last_bytes += size; if (elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { payload->last_progress_report_time = current_time; error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload); } } return error; } int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs) { transport_smart *t = (transport_smart *)transport; struct push_packbuilder_payload packbuilder_payload = {0}; git_buf pktline = GIT_BUF_INIT; int error = 0, need_pack = 0; push_spec *spec; unsigned int i; packbuilder_payload.pb = push->pb; if (cbs && cbs->push_transfer_progress) { packbuilder_payload.cb = cbs->push_transfer_progress; packbuilder_payload.cb_payload = cbs->payload; } #ifdef PUSH_DEBUG { git_remote_head *head; char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0'; git_vector_foreach(&push->remote->refs, i, head) { git_oid_fmt(hex, &head->oid); fprintf(stderr, "%s (%s)\n", hex, head->name); } git_vector_foreach(&push->specs, i, spec) { git_oid_fmt(hex, &spec->roid); fprintf(stderr, "%s (%s) -> ", hex, spec->lref); git_oid_fmt(hex, &spec->loid); fprintf(stderr, "%s (%s)\n", hex, spec->rref ? spec->rref : spec->lref); } } #endif /* * Figure out if we need to send a packfile; which is in all * cases except when we only send delete commands */ git_vector_foreach(&push->specs, i, spec) { if (spec->refspec.src && spec->refspec.src[0] != '\0') { need_pack = 1; break; } } if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 || (error = gen_pktline(&pktline, push)) < 0 || (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0) goto done; if (need_pack && (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0) goto done; /* If we sent nothing or the server doesn't support report-status, then * we consider the pack to have been unpacked successfully */ if (!push->specs.length || !push->report_status) push->unpack_ok = 1; else if ((error = parse_report(t, push)) < 0) goto done; /* If progress is being reported write the final report */ if (cbs && cbs->push_transfer_progress) { error = cbs->push_transfer_progress( push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, cbs->payload); if (error < 0) goto done; } if (push->status.length) { error = update_refs_from_report(&t->refs, &push->specs, &push->status); if (error < 0) goto done; error = git_smart__update_heads(t, NULL); } done: git_buf_dispose(&pktline); return error; } git2r/src/libgit2/src/transports/auth_ntlm.c0000644000175000017500000001213614125111754020727 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "git2.h" #include "common.h" #include "buffer.h" #include "auth.h" #include "auth_ntlm.h" #include "git2/sys/credential.h" #ifdef GIT_NTLM #include "ntlmclient.h" typedef struct { git_http_auth_context parent; ntlm_client *ntlm; char *challenge; bool complete; } http_auth_ntlm_context; static int ntlm_set_challenge( git_http_auth_context *c, const char *challenge) { http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; GIT_ASSERT_ARG(ctx); GIT_ASSERT_ARG(challenge); git__free(ctx->challenge); ctx->challenge = git__strdup(challenge); GIT_ERROR_CHECK_ALLOC(ctx->challenge); return 0; } static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred) { git_credential_userpass_plaintext *cred; const char *sep, *username; char *domain = NULL, *domainuser = NULL; int error = 0; GIT_ASSERT(_cred->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT); cred = (git_credential_userpass_plaintext *)_cred; if ((sep = strchr(cred->username, '\\')) != NULL) { domain = git__strndup(cred->username, (sep - cred->username)); GIT_ERROR_CHECK_ALLOC(domain); domainuser = git__strdup(sep + 1); GIT_ERROR_CHECK_ALLOC(domainuser); username = domainuser; } else { username = cred->username; } if (ntlm_client_set_credentials(ctx->ntlm, username, domain, cred->password) < 0) { git_error_set(GIT_ERROR_NET, "could not set credentials: %s", ntlm_client_errmsg(ctx->ntlm)); error = -1; goto done; } done: git__free(domain); git__free(domainuser); return error; } static int ntlm_next_token( git_buf *buf, git_http_auth_context *c, git_credential *cred) { http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; git_buf input_buf = GIT_BUF_INIT; const unsigned char *msg; size_t challenge_len, msg_len; int error = GIT_EAUTH; GIT_ASSERT_ARG(buf); GIT_ASSERT_ARG(ctx); GIT_ASSERT(ctx->ntlm); challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0; if (ctx->complete) ntlm_client_reset(ctx->ntlm); /* * Set us complete now since it's the default case; the one * incomplete case (successfully created a client request) * will explicitly set that it requires a second step. */ ctx->complete = true; if (cred && ntlm_set_credentials(ctx, cred) != 0) goto done; if (challenge_len < 4) { git_error_set(GIT_ERROR_NET, "no ntlm challenge sent from server"); goto done; } else if (challenge_len == 4) { if (memcmp(ctx->challenge, "NTLM", 4) != 0) { git_error_set(GIT_ERROR_NET, "server did not request NTLM"); goto done; } if (ntlm_client_negotiate(&msg, &msg_len, ctx->ntlm) != 0) { git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s", ntlm_client_errmsg(ctx->ntlm)); goto done; } ctx->complete = false; } else { if (memcmp(ctx->challenge, "NTLM ", 5) != 0) { git_error_set(GIT_ERROR_NET, "challenge from server was not NTLM"); goto done; } if (git_buf_decode_base64(&input_buf, ctx->challenge + 5, challenge_len - 5) < 0) { git_error_set(GIT_ERROR_NET, "invalid NTLM challenge from server"); goto done; } if (ntlm_client_set_challenge(ctx->ntlm, (const unsigned char *)input_buf.ptr, input_buf.size) != 0) { git_error_set(GIT_ERROR_NET, "ntlm challenge failed: %s", ntlm_client_errmsg(ctx->ntlm)); goto done; } if (ntlm_client_response(&msg, &msg_len, ctx->ntlm) != 0) { git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s", ntlm_client_errmsg(ctx->ntlm)); goto done; } } git_buf_puts(buf, "NTLM "); git_buf_encode_base64(buf, (const char *)msg, msg_len); if (git_buf_oom(buf)) goto done; error = 0; done: git_buf_dispose(&input_buf); return error; } static int ntlm_is_complete(git_http_auth_context *c) { http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; GIT_ASSERT_ARG(ctx); return (ctx->complete == true); } static void ntlm_context_free(git_http_auth_context *c) { http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; ntlm_client_free(ctx->ntlm); git__free(ctx->challenge); git__free(ctx); } static int ntlm_init_context( http_auth_ntlm_context *ctx, const git_net_url *url) { GIT_UNUSED(url); if ((ctx->ntlm = ntlm_client_init(NTLM_CLIENT_DEFAULTS)) == NULL) { git_error_set_oom(); return -1; } return 0; } int git_http_auth_ntlm( git_http_auth_context **out, const git_net_url *url) { http_auth_ntlm_context *ctx; GIT_UNUSED(url); *out = NULL; ctx = git__calloc(1, sizeof(http_auth_ntlm_context)); GIT_ERROR_CHECK_ALLOC(ctx); if (ntlm_init_context(ctx, url) < 0) { git__free(ctx); return -1; } ctx->parent.type = GIT_HTTP_AUTH_NTLM; ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT; ctx->parent.connection_affinity = 1; ctx->parent.set_challenge = ntlm_set_challenge; ctx->parent.next_token = ntlm_next_token; ctx->parent.is_complete = ntlm_is_complete; ctx->parent.free = ntlm_context_free; *out = (git_http_auth_context *)ctx; return 0; } #endif /* GIT_NTLM */ git2r/src/libgit2/src/transports/httpclient.h0000644000175000017500000001463114125111754021121 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transports_httpclient_h__ #define INCLUDE_transports_httpclient_h__ #include "common.h" #include "net.h" #define GIT_HTTP_STATUS_CONTINUE 100 #define GIT_HTTP_STATUS_OK 200 #define GIT_HTTP_MOVED_PERMANENTLY 301 #define GIT_HTTP_FOUND 302 #define GIT_HTTP_SEE_OTHER 303 #define GIT_HTTP_TEMPORARY_REDIRECT 307 #define GIT_HTTP_PERMANENT_REDIRECT 308 #define GIT_HTTP_STATUS_UNAUTHORIZED 401 #define GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED 407 typedef struct git_http_client git_http_client; /** Method for the HTTP request */ typedef enum { GIT_HTTP_METHOD_GET, GIT_HTTP_METHOD_POST, GIT_HTTP_METHOD_CONNECT } git_http_method; /** An HTTP request */ typedef struct { git_http_method method; /**< Method for the request */ git_net_url *url; /**< Full request URL */ git_net_url *proxy; /**< Proxy to use */ /* Headers */ const char *accept; /**< Contents of the Accept header */ const char *content_type; /**< Content-Type header (for POST) */ git_credential *credentials; /**< Credentials to authenticate with */ git_credential *proxy_credentials; /**< Credentials for proxy */ git_strarray *custom_headers; /**< Additional headers to deliver */ /* To POST a payload, either set content_length OR set chunked. */ size_t content_length; /**< Length of the POST body */ unsigned chunked : 1, /**< Post with chunking */ expect_continue : 1; /**< Use expect/continue negotiation */ } git_http_request; typedef struct { int status; /* Headers */ char *content_type; size_t content_length; char *location; /* Authentication headers */ unsigned server_auth_schemetypes; /**< Schemes requested by remote */ unsigned server_auth_credtypes; /**< Supported cred types for remote */ unsigned proxy_auth_schemetypes; /**< Schemes requested by proxy */ unsigned proxy_auth_credtypes; /**< Supported cred types for proxy */ unsigned chunked : 1, /**< Response body is chunked */ resend_credentials : 1; /**< Resend with authentication */ } git_http_response; typedef struct { /** Certificate check callback for the remote */ git_transport_certificate_check_cb server_certificate_check_cb; void *server_certificate_check_payload; /** Certificate check callback for the proxy */ git_transport_certificate_check_cb proxy_certificate_check_cb; void *proxy_certificate_check_payload; } git_http_client_options; /** * Create a new httpclient instance with the given options. * * @param out pointer to receive the new instance * @param opts options to create the client with or NULL for defaults */ extern int git_http_client_new( git_http_client **out, git_http_client_options *opts); /* * Sends a request to the host specified by the request URL. If the * method is POST, either the content_length or the chunked flag must * be specified. The body should be provided in subsequent calls to * git_http_client_send_body. * * @param client the client to write the request to * @param request the request to send */ extern int git_http_client_send_request( git_http_client *client, git_http_request *request); /* * After sending a request, there may already be a response to read -- * either because there was a non-continue response to an expect: continue * request, or because the server pipelined a response to us before we even * sent the request. Examine the state. * * @param client the client to examine * @return true if there's already a response to read, false otherwise */ extern bool git_http_client_has_response(git_http_client *client); /** * Sends the given buffer to the remote as part of the request body. The * request must have specified either a content_length or the chunked flag. * * @param client the client to write the request body to * @param buffer the request body * @param buffer_len number of bytes of the buffer to send */ extern int git_http_client_send_body( git_http_client *client, const char *buffer, size_t buffer_len); /** * Reads the headers of a response to a request. This will consume the * entirety of the headers of a response from the server. The body (if any) * can be read by calling git_http_client_read_body. Callers must free * the response with git_http_response_dispose. * * @param response pointer to the response object to fill * @param client the client to read the response from */ extern int git_http_client_read_response( git_http_response *response, git_http_client *client); /** * Reads some or all of the body of a response. At most buffer_size (or * INT_MAX) bytes will be read and placed into the buffer provided. The * number of bytes read will be returned, or 0 to indicate that the end of * the body has been read. * * @param client the client to read the response from * @param buffer pointer to the buffer to fill * @param buffer_size the maximum number of bytes to read * @return the number of bytes read, 0 on end of body, or error code */ extern int git_http_client_read_body( git_http_client *client, char *buffer, size_t buffer_size); /** * Reads all of the (remainder of the) body of the response and ignores it. * None of the data from the body will be returned to the caller. * * @param client the client to read the response from * @return 0 or an error code */ extern int git_http_client_skip_body(git_http_client *client); /** * Examines the status code of the response to determine if it is a * redirect of any type (eg, 301, 302, etc). * * @param response the response to inspect * @return true if the response is a redirect, false otherwise */ extern bool git_http_response_is_redirect(git_http_response *response); /** * Frees any memory associated with the response. * * @param response the response to free */ extern void git_http_response_dispose(git_http_response *response); /** * Frees any memory associated with the client. If any sockets are open, * they will be closed. * * @param client the client to free */ extern void git_http_client_free(git_http_client *client); #endif git2r/src/libgit2/src/transports/auth.h0000644000175000017500000000335514125111754017705 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transports_auth_h__ #define INCLUDE_transports_auth_h__ #include "common.h" #include "git2.h" #include "netops.h" typedef enum { GIT_HTTP_AUTH_BASIC = 1, GIT_HTTP_AUTH_NEGOTIATE = 2, GIT_HTTP_AUTH_NTLM = 4, } git_http_auth_t; typedef struct git_http_auth_context git_http_auth_context; struct git_http_auth_context { /** Type of scheme */ git_http_auth_t type; /** Supported credentials */ git_credential_t credtypes; /** Connection affinity or request affinity */ unsigned connection_affinity : 1; /** Sets the challenge on the authentication context */ int (*set_challenge)(git_http_auth_context *ctx, const char *challenge); /** Gets the next authentication token from the context */ int (*next_token)(git_buf *out, git_http_auth_context *ctx, git_credential *cred); /** Examines if all tokens have been presented. */ int (*is_complete)(git_http_auth_context *ctx); /** Frees the authentication context */ void (*free)(git_http_auth_context *ctx); }; typedef struct { /** Type of scheme */ git_http_auth_t type; /** Name of the scheme (as used in the Authorization header) */ const char *name; /** Credential types this scheme supports */ git_credential_t credtypes; /** Function to initialize an authentication context */ int (*init_context)( git_http_auth_context **out, const git_net_url *url); } git_http_auth_scheme; int git_http_auth_dummy( git_http_auth_context **out, const git_net_url *url); int git_http_auth_basic( git_http_auth_context **out, const git_net_url *url); #endif git2r/src/libgit2/src/transports/smart.c0000644000175000017500000003331114125111754020060 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "smart.h" #include "git2.h" #include "refs.h" #include "refspec.h" #include "proxy.h" static int git_smart__recv_cb(gitno_buffer *buf) { transport_smart *t = (transport_smart *) buf->cb_data; size_t old_len, bytes_read; int error; GIT_ASSERT(t->current_stream); old_len = buf->offset; if ((error = t->current_stream->read(t->current_stream, buf->data + buf->offset, buf->len - buf->offset, &bytes_read)) < 0) return error; buf->offset += bytes_read; if (t->packetsize_cb && !t->cancelled.val) { error = t->packetsize_cb(bytes_read, t->packetsize_payload); if (error) { git_atomic32_set(&t->cancelled, 1); return GIT_EUSER; } } return (int)(buf->offset - old_len); } GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport) { if (t->current_stream) { t->current_stream->free(t->current_stream); t->current_stream = NULL; } if (close_subtransport) { git__free(t->url); t->url = NULL; if (t->wrapped->close(t->wrapped) < 0) return -1; } return 0; } static int git_smart__set_callbacks( git_transport *transport, git_transport_message_cb progress_cb, git_transport_message_cb error_cb, git_transport_certificate_check_cb certificate_check_cb, void *message_cb_payload) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); t->progress_cb = progress_cb; t->error_cb = error_cb; t->certificate_check_cb = certificate_check_cb; t->message_cb_payload = message_cb_payload; return 0; } static size_t http_header_name_length(const char *http_header) { const char *colon = strchr(http_header, ':'); if (!colon) return 0; return colon - http_header; } static bool is_malformed_http_header(const char *http_header) { const char *c; size_t name_len; /* Disallow \r and \n */ c = strchr(http_header, '\r'); if (c) return true; c = strchr(http_header, '\n'); if (c) return true; /* Require a header name followed by : */ name_len = http_header_name_length(http_header); if (name_len < 1) return true; return false; } static char *forbidden_custom_headers[] = { "User-Agent", "Host", "Accept", "Content-Type", "Transfer-Encoding", "Content-Length", }; static bool is_forbidden_custom_header(const char *custom_header) { unsigned long i; size_t name_len = http_header_name_length(custom_header); /* Disallow headers that we set */ for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++) if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0) return true; return false; } static int git_smart__set_custom_headers( git_transport *transport, const git_strarray *custom_headers) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); size_t i; if (t->custom_headers.count) git_strarray_dispose(&t->custom_headers); if (!custom_headers) return 0; for (i = 0; i < custom_headers->count; i++) { if (is_malformed_http_header(custom_headers->strings[i])) { git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]); return -1; } if (is_forbidden_custom_header(custom_headers->strings[i])) { git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]); return -1; } } return git_strarray_copy(&t->custom_headers, custom_headers); } int git_smart__update_heads(transport_smart *t, git_vector *symrefs) { size_t i; git_pkt *pkt; git_vector_clear(&t->heads); git_vector_foreach(&t->refs, i, pkt) { git_pkt_ref *ref = (git_pkt_ref *) pkt; if (pkt->type != GIT_PKT_REF) continue; if (symrefs) { git_refspec *spec; git_buf buf = GIT_BUF_INIT; size_t j; int error = 0; git_vector_foreach(symrefs, j, spec) { git_buf_clear(&buf); if (git_refspec_src_matches(spec, ref->head.name) && !(error = git_refspec_transform(&buf, spec, ref->head.name))) { git__free(ref->head.symref_target); ref->head.symref_target = git_buf_detach(&buf); } } git_buf_dispose(&buf); if (error < 0) return error; } if (git_vector_insert(&t->heads, &ref->head) < 0) return -1; } return 0; } static void free_symrefs(git_vector *symrefs) { git_refspec *spec; size_t i; git_vector_foreach(symrefs, i, spec) { git_refspec__dispose(spec); git__free(spec); } git_vector_free(symrefs); } static int git_smart__connect( git_transport *transport, const char *url, git_credential_acquire_cb cred_acquire_cb, void *cred_acquire_payload, const git_proxy_options *proxy, int direction, int flags) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); git_smart_subtransport_stream *stream; int error; git_pkt *pkt; git_pkt_ref *first; git_vector symrefs; git_smart_service_t service; if (git_smart__reset_stream(t, true) < 0) return -1; t->url = git__strdup(url); GIT_ERROR_CHECK_ALLOC(t->url); git_proxy_options_clear(&t->proxy); if (git_proxy_options_dup(&t->proxy, proxy) < 0) return -1; t->direction = direction; t->flags = flags; t->cred_acquire_cb = cred_acquire_cb; t->cred_acquire_payload = cred_acquire_payload; if (GIT_DIRECTION_FETCH == t->direction) service = GIT_SERVICE_UPLOADPACK_LS; else if (GIT_DIRECTION_PUSH == t->direction) service = GIT_SERVICE_RECEIVEPACK_LS; else { git_error_set(GIT_ERROR_NET, "invalid direction"); return -1; } if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0) return error; /* Save off the current stream (i.e. socket) that we are working with */ t->current_stream = stream; gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); /* 2 flushes for RPC; 1 for stateful */ if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0) return error; /* Strip the comment packet for RPC */ if (t->rpc) { pkt = (git_pkt *)git_vector_get(&t->refs, 0); if (!pkt || GIT_PKT_COMMENT != pkt->type) { git_error_set(GIT_ERROR_NET, "invalid response"); return -1; } else { /* Remove the comment pkt from the list */ git_vector_remove(&t->refs, 0); git__free(pkt); } } /* We now have loaded the refs. */ t->have_refs = 1; pkt = (git_pkt *)git_vector_get(&t->refs, 0); if (pkt && GIT_PKT_REF != pkt->type) { git_error_set(GIT_ERROR_NET, "invalid response"); return -1; } first = (git_pkt_ref *)pkt; if ((error = git_vector_init(&symrefs, 1, NULL)) < 0) return error; /* Detect capabilities */ if ((error = git_smart__detect_caps(first, &t->caps, &symrefs)) == 0) { /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */ if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") && git_oid_is_zero(&first->head.oid)) { git_vector_clear(&t->refs); git_pkt_free((git_pkt *)first); } /* Keep a list of heads for _ls */ git_smart__update_heads(t, &symrefs); } else if (error == GIT_ENOTFOUND) { /* There was no ref packet received, or the cap list was empty */ error = 0; } else { git_error_set(GIT_ERROR_NET, "invalid response"); goto cleanup; } if (t->rpc && (error = git_smart__reset_stream(t, false)) < 0) goto cleanup; /* We're now logically connected. */ t->connected = 1; cleanup: free_symrefs(&symrefs); return error; } static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); if (!t->have_refs) { git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs"); return -1; } *out = (const git_remote_head **) t->heads.contents; *size = t->heads.length; return 0; } int git_smart__negotiation_step(git_transport *transport, void *data, size_t len) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); git_smart_subtransport_stream *stream; int error; if (t->rpc && git_smart__reset_stream(t, false) < 0) return -1; if (GIT_DIRECTION_FETCH != t->direction) { git_error_set(GIT_ERROR_NET, "this operation is only valid for fetch"); return -1; } if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0) return error; /* If this is a stateful implementation, the stream we get back should be the same */ GIT_ASSERT(t->rpc || t->current_stream == stream); /* Save off the current stream (i.e. socket) that we are working with */ t->current_stream = stream; if ((error = stream->write(stream, (const char *)data, len)) < 0) return error; gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); return 0; } int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **stream) { int error; if (t->rpc && git_smart__reset_stream(t, false) < 0) return -1; if (GIT_DIRECTION_PUSH != t->direction) { git_error_set(GIT_ERROR_NET, "this operation is only valid for push"); return -1; } if ((error = t->wrapped->action(stream, t->wrapped, t->url, GIT_SERVICE_RECEIVEPACK)) < 0) return error; /* If this is a stateful implementation, the stream we get back should be the same */ GIT_ASSERT(t->rpc || t->current_stream == *stream); /* Save off the current stream (i.e. socket) that we are working with */ t->current_stream = *stream; gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); return 0; } static void git_smart__cancel(git_transport *transport) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); git_atomic32_set(&t->cancelled, 1); } static int git_smart__is_connected(git_transport *transport) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); return t->connected; } static int git_smart__read_flags(git_transport *transport, int *flags) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); *flags = t->flags; return 0; } static int git_smart__close(git_transport *transport) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); git_vector *common = &t->common; unsigned int i; git_pkt *p; int ret; git_smart_subtransport_stream *stream; const char flush[] = "0000"; /* * If we're still connected at this point and not using RPC, * we should say goodbye by sending a flush, or git-daemon * will complain that we disconnected unexpectedly. */ if (t->connected && !t->rpc && !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) { t->current_stream->write(t->current_stream, flush, 4); } ret = git_smart__reset_stream(t, true); git_vector_foreach(common, i, p) git_pkt_free(p); git_vector_free(common); if (t->url) { git__free(t->url); t->url = NULL; } t->connected = 0; return ret; } static void git_smart__free(git_transport *transport) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); git_vector *refs = &t->refs; unsigned int i; git_pkt *p; /* Make sure that the current stream is closed, if we have one. */ git_smart__close(transport); /* Free the subtransport */ t->wrapped->free(t->wrapped); git_vector_free(&t->heads); git_vector_foreach(refs, i, p) git_pkt_free(p); git_vector_free(refs); git__free((char *)t->proxy.url); git_strarray_dispose(&t->custom_headers); git__free(t); } static int ref_name_cmp(const void *a, const void *b) { const git_pkt_ref *ref_a = a, *ref_b = b; return strcmp(ref_a->head.name, ref_b->head.name); } int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); GIT_ASSERT_ARG(transport); GIT_ASSERT_ARG(cert); GIT_ASSERT_ARG(hostname); if (!t->certificate_check_cb) return GIT_PASSTHROUGH; return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload); } int git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(transport); if (!t->cred_acquire_cb) return GIT_PASSTHROUGH; return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload); } int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport) { transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); return git_proxy_options_dup(out, &t->proxy); } int git_transport_smart(git_transport **out, git_remote *owner, void *param) { transport_smart *t; git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param; if (!param) return -1; t = git__calloc(1, sizeof(transport_smart)); GIT_ERROR_CHECK_ALLOC(t); t->parent.version = GIT_TRANSPORT_VERSION; t->parent.set_callbacks = git_smart__set_callbacks; t->parent.set_custom_headers = git_smart__set_custom_headers; t->parent.connect = git_smart__connect; t->parent.close = git_smart__close; t->parent.free = git_smart__free; t->parent.negotiate_fetch = git_smart__negotiate_fetch; t->parent.download_pack = git_smart__download_pack; t->parent.push = git_smart__push; t->parent.ls = git_smart__ls; t->parent.is_connected = git_smart__is_connected; t->parent.read_flags = git_smart__read_flags; t->parent.cancel = git_smart__cancel; t->owner = owner; t->rpc = definition->rpc; if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0) { git__free(t); return -1; } if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) { git__free(t); return -1; } if (definition->callback(&t->wrapped, &t->parent, definition->param) < 0) { git__free(t); return -1; } *out = (git_transport *) t; return 0; } git2r/src/libgit2/src/transports/git.c0000644000175000017500000001633714125111754017526 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2.h" #include "buffer.h" #include "netops.h" #include "git2/sys/transport.h" #include "stream.h" #include "streams/socket.h" #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport) static const char prefix_git[] = "git://"; static const char cmd_uploadpack[] = "git-upload-pack"; static const char cmd_receivepack[] = "git-receive-pack"; typedef struct { git_smart_subtransport_stream parent; git_stream *io; const char *cmd; char *url; unsigned sent_command : 1; } git_proto_stream; typedef struct { git_smart_subtransport parent; git_transport *owner; git_proto_stream *current_stream; } git_subtransport; /* * Create a git protocol request. * * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0 */ static int gen_proto(git_buf *request, const char *cmd, const char *url) { char *delim, *repo; char host[] = "host="; size_t len; delim = strchr(url, '/'); if (delim == NULL) { git_error_set(GIT_ERROR_NET, "malformed URL"); return -1; } repo = delim; if (repo[1] == '~') ++repo; delim = strchr(url, ':'); if (delim == NULL) delim = strchr(url, '/'); len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1; git_buf_grow(request, len); git_buf_printf(request, "%04x%s %s%c%s", (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host); git_buf_put(request, url, delim - url); git_buf_putc(request, '\0'); if (git_buf_oom(request)) return -1; return 0; } static int send_command(git_proto_stream *s) { git_buf request = GIT_BUF_INIT; int error; if ((error = gen_proto(&request, s->cmd, s->url)) < 0) goto cleanup; if ((error = git_stream__write_full(s->io, request.ptr, request.size, 0)) < 0) goto cleanup; s->sent_command = 1; cleanup: git_buf_dispose(&request); return error; } static int git_proto_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { int error; git_proto_stream *s = (git_proto_stream *)stream; gitno_buffer buf; *bytes_read = 0; if (!s->sent_command && (error = send_command(s)) < 0) return error; gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size); if ((error = gitno_recv(&buf)) < 0) return error; *bytes_read = buf.offset; return 0; } static int git_proto_stream_write( git_smart_subtransport_stream *stream, const char *buffer, size_t len) { git_proto_stream *s = (git_proto_stream *)stream; int error; if (!s->sent_command && (error = send_command(s)) < 0) return error; return git_stream__write_full(s->io, buffer, len, 0); } static void git_proto_stream_free(git_smart_subtransport_stream *stream) { git_proto_stream *s; git_subtransport *t; if (!stream) return; s = (git_proto_stream *)stream; t = OWNING_SUBTRANSPORT(s); t->current_stream = NULL; git_stream_close(s->io); git_stream_free(s->io); git__free(s->url); git__free(s); } static int git_proto_stream_alloc( git_subtransport *t, const char *url, const char *cmd, const char *host, const char *port, git_smart_subtransport_stream **stream) { git_proto_stream *s; if (!stream) return -1; s = git__calloc(1, sizeof(git_proto_stream)); GIT_ERROR_CHECK_ALLOC(s); s->parent.subtransport = &t->parent; s->parent.read = git_proto_stream_read; s->parent.write = git_proto_stream_write; s->parent.free = git_proto_stream_free; s->cmd = cmd; s->url = git__strdup(url); if (!s->url) { git__free(s); return -1; } if ((git_socket_stream_new(&s->io, host, port)) < 0) return -1; GIT_ERROR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream"); *stream = &s->parent; return 0; } static int _git_uploadpack_ls( git_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { git_net_url urldata = GIT_NET_URL_INIT; const char *stream_url = url; const char *host, *port; git_proto_stream *s; int error; *stream = NULL; if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); if ((error = git_net_url_parse(&urldata, url)) < 0) return error; host = urldata.host; port = urldata.port ? urldata.port : GIT_DEFAULT_PORT; error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream); git_net_url_dispose(&urldata); if (error < 0) { git_proto_stream_free(*stream); return error; } s = (git_proto_stream *) *stream; if ((error = git_stream_connect(s->io)) < 0) { git_proto_stream_free(*stream); return error; } t->current_stream = s; return 0; } static int _git_uploadpack( git_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { GIT_UNUSED(url); if (t->current_stream) { *stream = &t->current_stream->parent; return 0; } git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK"); return -1; } static int _git_receivepack_ls( git_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { git_net_url urldata = GIT_NET_URL_INIT; const char *stream_url = url; git_proto_stream *s; int error; *stream = NULL; if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); if ((error = git_net_url_parse(&urldata, url)) < 0) return error; error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, urldata.host, urldata.port, stream); git_net_url_dispose(&urldata); if (error < 0) { git_proto_stream_free(*stream); return error; } s = (git_proto_stream *) *stream; if ((error = git_stream_connect(s->io)) < 0) return error; t->current_stream = s; return 0; } static int _git_receivepack( git_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { GIT_UNUSED(url); if (t->current_stream) { *stream = &t->current_stream->parent; return 0; } git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK"); return -1; } static int _git_action( git_smart_subtransport_stream **stream, git_smart_subtransport *subtransport, const char *url, git_smart_service_t action) { git_subtransport *t = (git_subtransport *) subtransport; switch (action) { case GIT_SERVICE_UPLOADPACK_LS: return _git_uploadpack_ls(t, url, stream); case GIT_SERVICE_UPLOADPACK: return _git_uploadpack(t, url, stream); case GIT_SERVICE_RECEIVEPACK_LS: return _git_receivepack_ls(t, url, stream); case GIT_SERVICE_RECEIVEPACK: return _git_receivepack(t, url, stream); } *stream = NULL; return -1; } static int _git_close(git_smart_subtransport *subtransport) { git_subtransport *t = (git_subtransport *) subtransport; GIT_ASSERT(!t->current_stream); GIT_UNUSED(t); return 0; } static void _git_free(git_smart_subtransport *subtransport) { git_subtransport *t = (git_subtransport *) subtransport; git__free(t); } int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner, void *param) { git_subtransport *t; GIT_UNUSED(param); if (!out) return -1; t = git__calloc(1, sizeof(git_subtransport)); GIT_ERROR_CHECK_ALLOC(t); t->owner = owner; t->parent.action = _git_action; t->parent.close = _git_close; t->parent.free = _git_free; *out = (git_smart_subtransport *) t; return 0; } git2r/src/libgit2/src/transports/credential_helpers.c0000644000175000017500000000354614125111754022575 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/credential_helpers.h" int git_credential_userpass( git_credential **cred, const char *url, const char *user_from_url, unsigned int allowed_types, void *payload) { git_credential_userpass_payload *userpass = (git_credential_userpass_payload*)payload; const char *effective_username = NULL; GIT_UNUSED(url); if (!userpass || !userpass->password) return -1; /* Username resolution: a username can be passed with the URL, the * credentials payload, or both. Here's what we do. Note that if we get * this far, we know that any password the url may contain has already * failed at least once, so we ignore it. * * | Payload | URL | Used | * +-------------+----------+-----------+ * | yes | no | payload | * | yes | yes | payload | * | no | yes | url | * | no | no | FAIL | */ if (userpass->username) effective_username = userpass->username; else if (user_from_url) effective_username = user_from_url; else return -1; if (GIT_CREDENTIAL_USERNAME & allowed_types) return git_credential_username_new(cred, effective_username); if ((GIT_CREDENTIAL_USERPASS_PLAINTEXT & allowed_types) == 0 || git_credential_userpass_plaintext_new(cred, effective_username, userpass->password) < 0) return -1; return 0; } /* Deprecated credential functions */ #ifndef GIT_DEPRECATE_HARD int git_cred_userpass( git_credential **out, const char *url, const char *user_from_url, unsigned int allowed_types, void *payload) { return git_credential_userpass(out, url, user_from_url, allowed_types, payload); } #endif git2r/src/libgit2/src/transports/http.h0000644000175000017500000000117014125111754017714 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transports_http_h__ #define INCLUDE_transports_http_h__ #include "buffer.h" #include "settings.h" #include "httpclient.h" #define GIT_HTTP_REPLAY_MAX 15 extern bool git_http__expect_continue; GIT_INLINE(int) git_http__user_agent(git_buf *buf) { const char *ua = git_libgit2__user_agent(); if (!ua) ua = "libgit2 " LIBGIT2_VERSION; return git_buf_printf(buf, "git/2.0 (%s)", ua); } #endif git2r/src/libgit2/src/transports/auth.c0000644000175000017500000000271014125111754017672 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "auth.h" #include "git2.h" #include "buffer.h" #include "git2/sys/credential.h" static int basic_next_token( git_buf *out, git_http_auth_context *ctx, git_credential *c) { git_credential_userpass_plaintext *cred; git_buf raw = GIT_BUF_INIT; int error = GIT_EAUTH; GIT_UNUSED(ctx); if (c->credtype != GIT_CREDENTIAL_USERPASS_PLAINTEXT) { git_error_set(GIT_ERROR_INVALID, "invalid credential type for basic auth"); goto on_error; } cred = (git_credential_userpass_plaintext *)c; git_buf_printf(&raw, "%s:%s", cred->username, cred->password); if (git_buf_oom(&raw) || git_buf_puts(out, "Basic ") < 0 || git_buf_encode_base64(out, git_buf_cstr(&raw), raw.size) < 0) goto on_error; error = 0; on_error: if (raw.size) git__memzero(raw.ptr, raw.size); git_buf_dispose(&raw); return error; } static git_http_auth_context basic_context = { GIT_HTTP_AUTH_BASIC, GIT_CREDENTIAL_USERPASS_PLAINTEXT, 0, NULL, basic_next_token, NULL, NULL }; int git_http_auth_basic( git_http_auth_context **out, const git_net_url *url) { GIT_UNUSED(url); *out = &basic_context; return 0; } int git_http_auth_dummy( git_http_auth_context **out, const git_net_url *url) { GIT_UNUSED(url); *out = NULL; return GIT_PASSTHROUGH; } git2r/src/libgit2/src/transports/auth_negotiate.h0000644000175000017500000000113514125111754021736 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transports_auth_negotiate_h__ #define INCLUDE_transports_auth_negotiate_h__ #include "common.h" #include "git2.h" #include "auth.h" #if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK) extern int git_http_auth_negotiate( git_http_auth_context **out, const git_net_url *url); #else #define git_http_auth_negotiate git_http_auth_dummy #endif /* GIT_GSSAPI */ #endif git2r/src/libgit2/src/transports/credential.c0000644000175000017500000002436714125111754021057 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/credential.h" #include "git2/sys/credential.h" #include "git2/credential_helpers.h" static int git_credential_ssh_key_type_new( git_credential **cred, const char *username, const char *publickey, const char *privatekey, const char *passphrase, git_credential_t credtype); int git_credential_has_username(git_credential *cred) { if (cred->credtype == GIT_CREDENTIAL_DEFAULT) return 0; return 1; } const char *git_credential_get_username(git_credential *cred) { switch (cred->credtype) { case GIT_CREDENTIAL_USERNAME: { git_credential_username *c = (git_credential_username *) cred; return c->username; } case GIT_CREDENTIAL_USERPASS_PLAINTEXT: { git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *) cred; return c->username; } case GIT_CREDENTIAL_SSH_KEY: case GIT_CREDENTIAL_SSH_MEMORY: { git_credential_ssh_key *c = (git_credential_ssh_key *) cred; return c->username; } case GIT_CREDENTIAL_SSH_CUSTOM: { git_credential_ssh_custom *c = (git_credential_ssh_custom *) cred; return c->username; } case GIT_CREDENTIAL_SSH_INTERACTIVE: { git_credential_ssh_interactive *c = (git_credential_ssh_interactive *) cred; return c->username; } default: return NULL; } } static void plaintext_free(struct git_credential *cred) { git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred; git__free(c->username); /* Zero the memory which previously held the password */ if (c->password) { size_t pass_len = strlen(c->password); git__memzero(c->password, pass_len); git__free(c->password); } git__free(c); } int git_credential_userpass_plaintext_new( git_credential **cred, const char *username, const char *password) { git_credential_userpass_plaintext *c; GIT_ASSERT_ARG(cred); GIT_ASSERT_ARG(username); GIT_ASSERT_ARG(password); c = git__malloc(sizeof(git_credential_userpass_plaintext)); GIT_ERROR_CHECK_ALLOC(c); c->parent.credtype = GIT_CREDENTIAL_USERPASS_PLAINTEXT; c->parent.free = plaintext_free; c->username = git__strdup(username); if (!c->username) { git__free(c); return -1; } c->password = git__strdup(password); if (!c->password) { git__free(c->username); git__free(c); return -1; } *cred = &c->parent; return 0; } static void ssh_key_free(struct git_credential *cred) { git_credential_ssh_key *c = (git_credential_ssh_key *)cred; git__free(c->username); if (c->privatekey) { /* Zero the memory which previously held the private key */ size_t key_len = strlen(c->privatekey); git__memzero(c->privatekey, key_len); git__free(c->privatekey); } if (c->passphrase) { /* Zero the memory which previously held the passphrase */ size_t pass_len = strlen(c->passphrase); git__memzero(c->passphrase, pass_len); git__free(c->passphrase); } if (c->publickey) { /* Zero the memory which previously held the public key */ size_t key_len = strlen(c->publickey); git__memzero(c->publickey, key_len); git__free(c->publickey); } git__free(c); } static void ssh_interactive_free(struct git_credential *cred) { git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred; git__free(c->username); git__free(c); } static void ssh_custom_free(struct git_credential *cred) { git_credential_ssh_custom *c = (git_credential_ssh_custom *)cred; git__free(c->username); if (c->publickey) { /* Zero the memory which previously held the publickey */ size_t key_len = strlen(c->publickey); git__memzero(c->publickey, key_len); git__free(c->publickey); } git__free(c); } static void default_free(struct git_credential *cred) { git_credential_default *c = (git_credential_default *)cred; git__free(c); } static void username_free(struct git_credential *cred) { git__free(cred); } int git_credential_ssh_key_new( git_credential **cred, const char *username, const char *publickey, const char *privatekey, const char *passphrase) { return git_credential_ssh_key_type_new( cred, username, publickey, privatekey, passphrase, GIT_CREDENTIAL_SSH_KEY); } int git_credential_ssh_key_memory_new( git_credential **cred, const char *username, const char *publickey, const char *privatekey, const char *passphrase) { #ifdef GIT_SSH_MEMORY_CREDENTIALS return git_credential_ssh_key_type_new( cred, username, publickey, privatekey, passphrase, GIT_CREDENTIAL_SSH_MEMORY); #else GIT_UNUSED(cred); GIT_UNUSED(username); GIT_UNUSED(publickey); GIT_UNUSED(privatekey); GIT_UNUSED(passphrase); git_error_set(GIT_ERROR_INVALID, "this version of libgit2 was not built with ssh memory credentials."); return -1; #endif } static int git_credential_ssh_key_type_new( git_credential **cred, const char *username, const char *publickey, const char *privatekey, const char *passphrase, git_credential_t credtype) { git_credential_ssh_key *c; GIT_ASSERT_ARG(username); GIT_ASSERT_ARG(cred); GIT_ASSERT_ARG(privatekey); c = git__calloc(1, sizeof(git_credential_ssh_key)); GIT_ERROR_CHECK_ALLOC(c); c->parent.credtype = credtype; c->parent.free = ssh_key_free; c->username = git__strdup(username); GIT_ERROR_CHECK_ALLOC(c->username); c->privatekey = git__strdup(privatekey); GIT_ERROR_CHECK_ALLOC(c->privatekey); if (publickey) { c->publickey = git__strdup(publickey); GIT_ERROR_CHECK_ALLOC(c->publickey); } if (passphrase) { c->passphrase = git__strdup(passphrase); GIT_ERROR_CHECK_ALLOC(c->passphrase); } *cred = &c->parent; return 0; } int git_credential_ssh_interactive_new( git_credential **out, const char *username, git_credential_ssh_interactive_cb prompt_callback, void *payload) { git_credential_ssh_interactive *c; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(username); GIT_ASSERT_ARG(prompt_callback); c = git__calloc(1, sizeof(git_credential_ssh_interactive)); GIT_ERROR_CHECK_ALLOC(c); c->parent.credtype = GIT_CREDENTIAL_SSH_INTERACTIVE; c->parent.free = ssh_interactive_free; c->username = git__strdup(username); GIT_ERROR_CHECK_ALLOC(c->username); c->prompt_callback = prompt_callback; c->payload = payload; *out = &c->parent; return 0; } int git_credential_ssh_key_from_agent(git_credential **cred, const char *username) { git_credential_ssh_key *c; GIT_ASSERT_ARG(username); GIT_ASSERT_ARG(cred); c = git__calloc(1, sizeof(git_credential_ssh_key)); GIT_ERROR_CHECK_ALLOC(c); c->parent.credtype = GIT_CREDENTIAL_SSH_KEY; c->parent.free = ssh_key_free; c->username = git__strdup(username); GIT_ERROR_CHECK_ALLOC(c->username); c->privatekey = NULL; *cred = &c->parent; return 0; } int git_credential_ssh_custom_new( git_credential **cred, const char *username, const char *publickey, size_t publickey_len, git_credential_sign_cb sign_callback, void *payload) { git_credential_ssh_custom *c; GIT_ASSERT_ARG(username); GIT_ASSERT_ARG(cred); c = git__calloc(1, sizeof(git_credential_ssh_custom)); GIT_ERROR_CHECK_ALLOC(c); c->parent.credtype = GIT_CREDENTIAL_SSH_CUSTOM; c->parent.free = ssh_custom_free; c->username = git__strdup(username); GIT_ERROR_CHECK_ALLOC(c->username); if (publickey_len > 0) { c->publickey = git__malloc(publickey_len); GIT_ERROR_CHECK_ALLOC(c->publickey); memcpy(c->publickey, publickey, publickey_len); } c->publickey_len = publickey_len; c->sign_callback = sign_callback; c->payload = payload; *cred = &c->parent; return 0; } int git_credential_default_new(git_credential **cred) { git_credential_default *c; GIT_ASSERT_ARG(cred); c = git__calloc(1, sizeof(git_credential_default)); GIT_ERROR_CHECK_ALLOC(c); c->credtype = GIT_CREDENTIAL_DEFAULT; c->free = default_free; *cred = c; return 0; } int git_credential_username_new(git_credential **cred, const char *username) { git_credential_username *c; size_t len, allocsize; GIT_ASSERT_ARG(cred); len = strlen(username); GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, sizeof(git_credential_username), len); GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, allocsize, 1); c = git__malloc(allocsize); GIT_ERROR_CHECK_ALLOC(c); c->parent.credtype = GIT_CREDENTIAL_USERNAME; c->parent.free = username_free; memcpy(c->username, username, len + 1); *cred = (git_credential *) c; return 0; } void git_credential_free(git_credential *cred) { if (!cred) return; cred->free(cred); } /* Deprecated credential functions */ #ifndef GIT_DEPRECATE_HARD int git_cred_has_username(git_credential *cred) { return git_credential_has_username(cred); } const char *git_cred_get_username(git_credential *cred) { return git_credential_get_username(cred); } int git_cred_userpass_plaintext_new( git_credential **out, const char *username, const char *password) { return git_credential_userpass_plaintext_new(out,username, password); } int git_cred_default_new(git_credential **out) { return git_credential_default_new(out); } int git_cred_username_new(git_credential **out, const char *username) { return git_credential_username_new(out, username); } int git_cred_ssh_key_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase) { return git_credential_ssh_key_new(out, username, publickey, privatekey, passphrase); } int git_cred_ssh_key_memory_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase) { return git_credential_ssh_key_memory_new(out, username, publickey, privatekey, passphrase); } int git_cred_ssh_interactive_new( git_credential **out, const char *username, git_credential_ssh_interactive_cb prompt_callback, void *payload) { return git_credential_ssh_interactive_new(out, username, prompt_callback, payload); } int git_cred_ssh_key_from_agent( git_credential **out, const char *username) { return git_credential_ssh_key_from_agent(out, username); } int git_cred_ssh_custom_new( git_credential **out, const char *username, const char *publickey, size_t publickey_len, git_credential_sign_cb sign_callback, void *payload) { return git_credential_ssh_custom_new(out, username, publickey, publickey_len, sign_callback, payload); } void git_cred_free(git_credential *cred) { git_credential_free(cred); } #endif git2r/src/libgit2/src/transports/ssh.h0000644000175000017500000000054414125111754017536 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transports_ssh_h__ #define INCLUDE_transports_ssh_h__ #include "common.h" int git_transport_ssh_global_init(void); #endif git2r/src/libgit2/src/transports/ssh.c0000644000175000017500000005304614125111754017536 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "ssh.h" #ifdef GIT_SSH #include #endif #include "runtime.h" #include "git2.h" #include "buffer.h" #include "net.h" #include "netops.h" #include "smart.h" #include "streams/socket.h" #include "git2/credential.h" #include "git2/sys/credential.h" #ifdef GIT_SSH #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport) static const char *ssh_prefixes[] = { "ssh://", "ssh+git://", "git+ssh://" }; static const char cmd_uploadpack[] = "git-upload-pack"; static const char cmd_receivepack[] = "git-receive-pack"; typedef struct { git_smart_subtransport_stream parent; git_stream *io; LIBSSH2_SESSION *session; LIBSSH2_CHANNEL *channel; const char *cmd; char *url; unsigned sent_command : 1; } ssh_stream; typedef struct { git_smart_subtransport parent; transport_smart *owner; ssh_stream *current_stream; git_credential *cred; char *cmd_uploadpack; char *cmd_receivepack; } ssh_subtransport; static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username); static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg) { char *ssherr; libssh2_session_last_error(session, &ssherr, NULL, 0); git_error_set(GIT_ERROR_SSH, "%s: %s", errmsg, ssherr); } /* * Create a git protocol request. * * For example: git-upload-pack '/libgit2/libgit2' */ static int gen_proto(git_buf *request, const char *cmd, const char *url) { const char *repo; int len; size_t i; for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) { const char *p = ssh_prefixes[i]; if (!git__prefixcmp(url, p)) { url = url + strlen(p); repo = strchr(url, '/'); if (repo && repo[1] == '~') ++repo; goto done; } } repo = strchr(url, ':'); if (repo) repo++; done: if (!repo) { git_error_set(GIT_ERROR_NET, "malformed git protocol URL"); return -1; } len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1; git_buf_grow(request, len); git_buf_puts(request, cmd); git_buf_puts(request, " '"); git_buf_decode_percent(request, repo, strlen(repo)); git_buf_puts(request, "'"); if (git_buf_oom(request)) return -1; return 0; } static int send_command(ssh_stream *s) { int error; git_buf request = GIT_BUF_INIT; error = gen_proto(&request, s->cmd, s->url); if (error < 0) goto cleanup; error = libssh2_channel_exec(s->channel, request.ptr); if (error < LIBSSH2_ERROR_NONE) { ssh_error(s->session, "SSH could not execute request"); goto cleanup; } s->sent_command = 1; cleanup: git_buf_dispose(&request); return error; } static int ssh_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { int rc; ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent); *bytes_read = 0; if (!s->sent_command && send_command(s) < 0) return -1; if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) { ssh_error(s->session, "SSH could not read data"); return -1; } /* * If we can't get anything out of stdout, it's typically a * not-found error, so read from stderr and signal EOF on * stderr. */ if (rc == 0) { if ((rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) { git_error_set(GIT_ERROR_SSH, "%*s", rc, buffer); return GIT_EEOF; } else if (rc < LIBSSH2_ERROR_NONE) { ssh_error(s->session, "SSH could not read stderr"); return -1; } } *bytes_read = rc; return 0; } static int ssh_stream_write( git_smart_subtransport_stream *stream, const char *buffer, size_t len) { ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent); size_t off = 0; ssize_t ret = 0; if (!s->sent_command && send_command(s) < 0) return -1; do { ret = libssh2_channel_write(s->channel, buffer + off, len - off); if (ret < 0) break; off += ret; } while (off < len); if (ret < 0) { ssh_error(s->session, "SSH could not write data"); return -1; } return 0; } static void ssh_stream_free(git_smart_subtransport_stream *stream) { ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent); ssh_subtransport *t; if (!stream) return; t = OWNING_SUBTRANSPORT(s); t->current_stream = NULL; if (s->channel) { libssh2_channel_close(s->channel); libssh2_channel_free(s->channel); s->channel = NULL; } if (s->session) { libssh2_session_disconnect(s->session, "closing transport"); libssh2_session_free(s->session); s->session = NULL; } if (s->io) { git_stream_close(s->io); git_stream_free(s->io); s->io = NULL; } git__free(s->url); git__free(s); } static int ssh_stream_alloc( ssh_subtransport *t, const char *url, const char *cmd, git_smart_subtransport_stream **stream) { ssh_stream *s; GIT_ASSERT_ARG(stream); s = git__calloc(sizeof(ssh_stream), 1); GIT_ERROR_CHECK_ALLOC(s); s->parent.subtransport = &t->parent; s->parent.read = ssh_stream_read; s->parent.write = ssh_stream_write; s->parent.free = ssh_stream_free; s->cmd = cmd; s->url = git__strdup(url); if (!s->url) { git__free(s); return -1; } *stream = &s->parent; return 0; } static int git_ssh_extract_url_parts( git_net_url *urldata, const char *url) { char *colon, *at; const char *start; colon = strchr(url, ':'); at = strchr(url, '@'); if (at) { start = at + 1; urldata->username = git__substrdup(url, at - url); GIT_ERROR_CHECK_ALLOC(urldata->username); } else { start = url; urldata->username = NULL; } if (colon == NULL || (colon < start)) { git_error_set(GIT_ERROR_NET, "malformed URL"); return -1; } urldata->host = git__substrdup(start, colon - start); GIT_ERROR_CHECK_ALLOC(urldata->host); return 0; } static int ssh_agent_auth(LIBSSH2_SESSION *session, git_credential_ssh_key *c) { int rc = LIBSSH2_ERROR_NONE; struct libssh2_agent_publickey *curr, *prev = NULL; LIBSSH2_AGENT *agent = libssh2_agent_init(session); if (agent == NULL) return -1; rc = libssh2_agent_connect(agent); if (rc != LIBSSH2_ERROR_NONE) goto shutdown; rc = libssh2_agent_list_identities(agent); if (rc != LIBSSH2_ERROR_NONE) goto shutdown; while (1) { rc = libssh2_agent_get_identity(agent, &curr, prev); if (rc < 0) goto shutdown; /* rc is set to 1 whenever the ssh agent ran out of keys to check. * Set the error code to authentication failure rather than erroring * out with an untranslatable error code. */ if (rc == 1) { rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; goto shutdown; } rc = libssh2_agent_userauth(agent, c->username, curr); if (rc == 0) break; prev = curr; } shutdown: if (rc != LIBSSH2_ERROR_NONE) ssh_error(session, "error authenticating"); libssh2_agent_disconnect(agent); libssh2_agent_free(agent); return rc; } static int _git_ssh_authenticate_session( LIBSSH2_SESSION *session, git_credential *cred) { int rc; do { git_error_clear(); switch (cred->credtype) { case GIT_CREDENTIAL_USERPASS_PLAINTEXT: { git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred; rc = libssh2_userauth_password(session, c->username, c->password); break; } case GIT_CREDENTIAL_SSH_KEY: { git_credential_ssh_key *c = (git_credential_ssh_key *)cred; if (c->privatekey) rc = libssh2_userauth_publickey_fromfile( session, c->username, c->publickey, c->privatekey, c->passphrase); else rc = ssh_agent_auth(session, c); break; } case GIT_CREDENTIAL_SSH_CUSTOM: { git_credential_ssh_custom *c = (git_credential_ssh_custom *)cred; rc = libssh2_userauth_publickey( session, c->username, (const unsigned char *)c->publickey, c->publickey_len, c->sign_callback, &c->payload); break; } case GIT_CREDENTIAL_SSH_INTERACTIVE: { void **abstract = libssh2_session_abstract(session); git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred; /* ideally, we should be able to set this by calling * libssh2_session_init_ex() instead of libssh2_session_init(). * libssh2's API is inconsistent here i.e. libssh2_userauth_publickey() * allows you to pass the `abstract` as part of the call, whereas * libssh2_userauth_keyboard_interactive() does not! * * The only way to set the `abstract` pointer is by calling * libssh2_session_abstract(), which will replace the existing * pointer as is done below. This is safe for now (at time of writing), * but may not be valid in future. */ *abstract = c->payload; rc = libssh2_userauth_keyboard_interactive( session, c->username, c->prompt_callback); break; } #ifdef GIT_SSH_MEMORY_CREDENTIALS case GIT_CREDENTIAL_SSH_MEMORY: { git_credential_ssh_key *c = (git_credential_ssh_key *)cred; GIT_ASSERT(c->username); GIT_ASSERT(c->privatekey); rc = libssh2_userauth_publickey_frommemory( session, c->username, strlen(c->username), c->publickey, c->publickey ? strlen(c->publickey) : 0, c->privatekey, strlen(c->privatekey), c->passphrase); break; } #endif default: rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; } } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED || rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED) return GIT_EAUTH; if (rc != LIBSSH2_ERROR_NONE) { if (!git_error_last()) ssh_error(session, "Failed to authenticate SSH session"); return -1; } return 0; } static int request_creds(git_credential **out, ssh_subtransport *t, const char *user, int auth_methods) { int error, no_callback = 0; git_credential *cred = NULL; if (!t->owner->cred_acquire_cb) { no_callback = 1; } else { error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods, t->owner->cred_acquire_payload); if (error == GIT_PASSTHROUGH) { no_callback = 1; } else if (error < 0) { return error; } else if (!cred) { git_error_set(GIT_ERROR_SSH, "callback failed to initialize SSH credentials"); return -1; } } if (no_callback) { git_error_set(GIT_ERROR_SSH, "authentication required but no callback set"); return GIT_EAUTH; } if (!(cred->credtype & auth_methods)) { cred->free(cred); git_error_set(GIT_ERROR_SSH, "authentication callback returned unsupported credentials type"); return GIT_EAUTH; } *out = cred; return 0; } static int _git_ssh_session_create( LIBSSH2_SESSION **session, git_stream *io) { int rc = 0; LIBSSH2_SESSION *s; git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent); GIT_ASSERT_ARG(session); s = libssh2_session_init(); if (!s) { git_error_set(GIT_ERROR_NET, "failed to initialize SSH session"); return -1; } do { rc = libssh2_session_handshake(s, socket->s); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc != LIBSSH2_ERROR_NONE) { ssh_error(s, "failed to start SSH session"); libssh2_session_free(s); return -1; } libssh2_session_set_blocking(s, 1); *session = s; return 0; } #define SSH_DEFAULT_PORT "22" static int _git_ssh_setup_conn( ssh_subtransport *t, const char *url, const char *cmd, git_smart_subtransport_stream **stream) { git_net_url urldata = GIT_NET_URL_INIT; int auth_methods, error = 0; size_t i; ssh_stream *s; git_credential *cred = NULL; LIBSSH2_SESSION *session=NULL; LIBSSH2_CHANNEL *channel=NULL; t->current_stream = NULL; *stream = NULL; if (ssh_stream_alloc(t, url, cmd, stream) < 0) return -1; s = (ssh_stream *)*stream; s->session = NULL; s->channel = NULL; for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) { const char *p = ssh_prefixes[i]; if (!git__prefixcmp(url, p)) { if ((error = git_net_url_parse(&urldata, url)) < 0) goto done; goto post_extract; } } if ((error = git_ssh_extract_url_parts(&urldata, url)) < 0) goto done; if (urldata.port == NULL) urldata.port = git__strdup(SSH_DEFAULT_PORT); GIT_ERROR_CHECK_ALLOC(urldata.port); post_extract: if ((error = git_socket_stream_new(&s->io, urldata.host, urldata.port)) < 0 || (error = git_stream_connect(s->io)) < 0) goto done; if ((error = _git_ssh_session_create(&session, s->io)) < 0) goto done; if (t->owner->certificate_check_cb != NULL) { git_cert_hostkey cert = {{ 0 }}, *cert_ptr; const char *key; size_t cert_len; int cert_type; cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2; key = libssh2_session_hostkey(session, &cert_len, &cert_type); if (key != NULL) { cert.type |= GIT_CERT_SSH_RAW; cert.hostkey = key; cert.hostkey_len = cert_len; switch (cert_type) { case LIBSSH2_HOSTKEY_TYPE_RSA: cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA; break; case LIBSSH2_HOSTKEY_TYPE_DSS: cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS; break; #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256; break; case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384; break; case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521; break; #endif #ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 case LIBSSH2_HOSTKEY_TYPE_ED25519: cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519; break; #endif default: cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN; } } #ifdef LIBSSH2_HOSTKEY_HASH_SHA256 key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256); if (key != NULL) { cert.type |= GIT_CERT_SSH_SHA256; memcpy(&cert.hash_sha256, key, 32); } #endif key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); if (key != NULL) { cert.type |= GIT_CERT_SSH_SHA1; memcpy(&cert.hash_sha1, key, 20); } key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5); if (key != NULL) { cert.type |= GIT_CERT_SSH_MD5; memcpy(&cert.hash_md5, key, 16); } if (cert.type == 0) { git_error_set(GIT_ERROR_SSH, "unable to get the host key"); error = -1; goto done; } /* We don't currently trust any hostkeys */ git_error_clear(); cert_ptr = &cert; error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, urldata.host, t->owner->message_cb_payload); if (error < 0 && error != GIT_PASSTHROUGH) { if (!git_error_last()) git_error_set(GIT_ERROR_NET, "user cancelled hostkey check"); goto done; } } /* we need the username to ask for auth methods */ if (!urldata.username) { if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0) goto done; urldata.username = git__strdup(((git_credential_username *) cred)->username); cred->free(cred); cred = NULL; if (!urldata.username) goto done; } else if (urldata.username && urldata.password) { if ((error = git_credential_userpass_plaintext_new(&cred, urldata.username, urldata.password)) < 0) goto done; } if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0) goto done; error = GIT_EAUTH; /* if we already have something to try */ if (cred && auth_methods & cred->credtype) error = _git_ssh_authenticate_session(session, cred); while (error == GIT_EAUTH) { if (cred) { cred->free(cred); cred = NULL; } if ((error = request_creds(&cred, t, urldata.username, auth_methods)) < 0) goto done; if (strcmp(urldata.username, git_credential_get_username(cred))) { git_error_set(GIT_ERROR_SSH, "username does not match previous request"); error = -1; goto done; } error = _git_ssh_authenticate_session(session, cred); if (error == GIT_EAUTH) { /* refresh auth methods */ if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0) goto done; else error = GIT_EAUTH; } } if (error < 0) goto done; channel = libssh2_channel_open_session(session); if (!channel) { error = -1; ssh_error(session, "Failed to open SSH channel"); goto done; } libssh2_channel_set_blocking(channel, 1); s->session = session; s->channel = channel; t->current_stream = s; done: if (error < 0) { ssh_stream_free(*stream); if (session) libssh2_session_free(session); } if (cred) cred->free(cred); git_net_url_dispose(&urldata); return error; } static int ssh_uploadpack_ls( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack; return _git_ssh_setup_conn(t, url, cmd, stream); } static int ssh_uploadpack( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { GIT_UNUSED(url); if (t->current_stream) { *stream = &t->current_stream->parent; return 0; } git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK"); return -1; } static int ssh_receivepack_ls( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack; return _git_ssh_setup_conn(t, url, cmd, stream); } static int ssh_receivepack( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) { GIT_UNUSED(url); if (t->current_stream) { *stream = &t->current_stream->parent; return 0; } git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK"); return -1; } static int _ssh_action( git_smart_subtransport_stream **stream, git_smart_subtransport *subtransport, const char *url, git_smart_service_t action) { ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent); switch (action) { case GIT_SERVICE_UPLOADPACK_LS: return ssh_uploadpack_ls(t, url, stream); case GIT_SERVICE_UPLOADPACK: return ssh_uploadpack(t, url, stream); case GIT_SERVICE_RECEIVEPACK_LS: return ssh_receivepack_ls(t, url, stream); case GIT_SERVICE_RECEIVEPACK: return ssh_receivepack(t, url, stream); } *stream = NULL; return -1; } static int _ssh_close(git_smart_subtransport *subtransport) { ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent); GIT_ASSERT(!t->current_stream); GIT_UNUSED(t); return 0; } static void _ssh_free(git_smart_subtransport *subtransport) { ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent); git__free(t->cmd_uploadpack); git__free(t->cmd_receivepack); git__free(t); } #define SSH_AUTH_PUBLICKEY "publickey" #define SSH_AUTH_PASSWORD "password" #define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive" static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username) { const char *list, *ptr; *out = 0; list = libssh2_userauth_list(session, username, strlen(username)); /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */ if (list == NULL && !libssh2_userauth_authenticated(session)) { ssh_error(session, "Failed to retrieve list of SSH authentication methods"); return GIT_EAUTH; } ptr = list; while (ptr) { if (*ptr == ',') ptr++; if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) { *out |= GIT_CREDENTIAL_SSH_KEY; *out |= GIT_CREDENTIAL_SSH_CUSTOM; #ifdef GIT_SSH_MEMORY_CREDENTIALS *out |= GIT_CREDENTIAL_SSH_MEMORY; #endif ptr += strlen(SSH_AUTH_PUBLICKEY); continue; } if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) { *out |= GIT_CREDENTIAL_USERPASS_PLAINTEXT; ptr += strlen(SSH_AUTH_PASSWORD); continue; } if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) { *out |= GIT_CREDENTIAL_SSH_INTERACTIVE; ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE); continue; } /* Skipt it if we don't know it */ ptr = strchr(ptr, ','); } return 0; } #endif int git_smart_subtransport_ssh( git_smart_subtransport **out, git_transport *owner, void *param) { #ifdef GIT_SSH ssh_subtransport *t; GIT_ASSERT_ARG(out); GIT_UNUSED(param); t = git__calloc(sizeof(ssh_subtransport), 1); GIT_ERROR_CHECK_ALLOC(t); t->owner = (transport_smart *)owner; t->parent.action = _ssh_action; t->parent.close = _ssh_close; t->parent.free = _ssh_free; *out = (git_smart_subtransport *) t; return 0; #else GIT_UNUSED(owner); GIT_UNUSED(param); GIT_ASSERT_ARG(out); *out = NULL; git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support"); return -1; #endif } int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload) { #ifdef GIT_SSH git_strarray *paths = (git_strarray *) payload; git_transport *transport; transport_smart *smart; ssh_subtransport *t; int error; git_smart_subtransport_definition ssh_definition = { git_smart_subtransport_ssh, 0, /* no RPC */ NULL, }; if (paths->count != 2) { git_error_set(GIT_ERROR_SSH, "invalid ssh paths, must be two strings"); return GIT_EINVALIDSPEC; } if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0) return error; smart = (transport_smart *) transport; t = (ssh_subtransport *) smart->wrapped; t->cmd_uploadpack = git__strdup(paths->strings[0]); GIT_ERROR_CHECK_ALLOC(t->cmd_uploadpack); t->cmd_receivepack = git__strdup(paths->strings[1]); GIT_ERROR_CHECK_ALLOC(t->cmd_receivepack); *out = transport; return 0; #else GIT_UNUSED(owner); GIT_UNUSED(payload); GIT_ASSERT_ARG(out); *out = NULL; git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support"); return -1; #endif } #ifdef GIT_SSH static void shutdown_ssh(void) { libssh2_exit(); } #endif int git_transport_ssh_global_init(void) { #ifdef GIT_SSH if (libssh2_init(0) < 0) { git_error_set(GIT_ERROR_SSH, "unable to initialize libssh2"); return -1; } return git_runtime_shutdown_register(shutdown_ssh); #else /* Nothing to initialize */ return 0; #endif } git2r/src/libgit2/src/transports/httpclient.c0000644000175000017500000011547414125111754021123 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2.h" #include "http_parser.h" #include "vector.h" #include "trace.h" #include "httpclient.h" #include "http.h" #include "auth.h" #include "auth_negotiate.h" #include "auth_ntlm.h" #include "git2/sys/credential.h" #include "net.h" #include "stream.h" #include "streams/socket.h" #include "streams/tls.h" #include "auth.h" static git_http_auth_scheme auth_schemes[] = { { GIT_HTTP_AUTH_NEGOTIATE, "Negotiate", GIT_CREDENTIAL_DEFAULT, git_http_auth_negotiate }, { GIT_HTTP_AUTH_NTLM, "NTLM", GIT_CREDENTIAL_USERPASS_PLAINTEXT, git_http_auth_ntlm }, { GIT_HTTP_AUTH_BASIC, "Basic", GIT_CREDENTIAL_USERPASS_PLAINTEXT, git_http_auth_basic }, }; /* * Use a 16kb read buffer to match the maximum size of a TLS packet. This * is critical for compatibility with SecureTransport, which will always do * a network read on every call, even if it has data buffered to return to * you. That buffered data may be the _end_ of a keep-alive response, so * if SecureTransport performs another network read, it will wait until the * server ultimately times out before it returns that buffered data to you. * Since SecureTransport only reads a single TLS packet at a time, by * calling it with a read buffer that is the maximum size of a TLS packet, * we ensure that it will never buffer. */ #define GIT_READ_BUFFER_SIZE (16 * 1024) typedef struct { git_net_url url; git_stream *stream; git_vector auth_challenges; git_http_auth_context *auth_context; } git_http_server; typedef enum { PROXY = 1, SERVER } git_http_server_t; typedef enum { NONE = 0, SENDING_REQUEST, SENDING_BODY, SENT_REQUEST, HAS_EARLY_RESPONSE, READING_RESPONSE, READING_BODY, DONE } http_client_state; /* Parser state */ typedef enum { PARSE_HEADER_NONE = 0, PARSE_HEADER_NAME, PARSE_HEADER_VALUE, PARSE_HEADER_COMPLETE } parse_header_state; typedef enum { PARSE_STATUS_OK, PARSE_STATUS_NO_OUTPUT, PARSE_STATUS_ERROR } parse_status; typedef struct { git_http_client *client; git_http_response *response; /* Temporary buffers to avoid extra mallocs */ git_buf parse_header_name; git_buf parse_header_value; /* Parser state */ int error; parse_status parse_status; /* Headers parsing */ parse_header_state parse_header_state; /* Body parsing */ char *output_buf; /* Caller's output buffer */ size_t output_size; /* Size of caller's output buffer */ size_t output_written; /* Bytes we've written to output buffer */ } http_parser_context; /* HTTP client connection */ struct git_http_client { git_http_client_options opts; /* Are we writing to the proxy or server, and state of the client. */ git_http_server_t current_server; http_client_state state; http_parser parser; git_http_server server; git_http_server proxy; unsigned request_count; unsigned connected : 1, proxy_connected : 1, keepalive : 1, request_chunked : 1; /* Temporary buffers to avoid extra mallocs */ git_buf request_msg; git_buf read_buf; /* A subset of information from the request */ size_t request_body_len, request_body_remain; /* * When state == HAS_EARLY_RESPONSE, the response of our proxy * that we have buffered and will deliver during read_response. */ git_http_response early_response; }; bool git_http_response_is_redirect(git_http_response *response) { return (response->status == GIT_HTTP_MOVED_PERMANENTLY || response->status == GIT_HTTP_FOUND || response->status == GIT_HTTP_SEE_OTHER || response->status == GIT_HTTP_TEMPORARY_REDIRECT || response->status == GIT_HTTP_PERMANENT_REDIRECT); } void git_http_response_dispose(git_http_response *response) { if (!response) return; git__free(response->content_type); git__free(response->location); memset(response, 0, sizeof(git_http_response)); } static int on_header_complete(http_parser *parser) { http_parser_context *ctx = (http_parser_context *) parser->data; git_http_client *client = ctx->client; git_http_response *response = ctx->response; git_buf *name = &ctx->parse_header_name; git_buf *value = &ctx->parse_header_value; if (!strcasecmp("Content-Type", name->ptr)) { if (response->content_type) { git_error_set(GIT_ERROR_HTTP, "multiple content-type headers"); return -1; } response->content_type = git__strndup(value->ptr, value->size); GIT_ERROR_CHECK_ALLOC(ctx->response->content_type); } else if (!strcasecmp("Content-Length", name->ptr)) { int64_t len; if (response->content_length) { git_error_set(GIT_ERROR_HTTP, "multiple content-length headers"); return -1; } if (git__strntol64(&len, value->ptr, value->size, NULL, 10) < 0 || len < 0) { git_error_set(GIT_ERROR_HTTP, "invalid content-length"); return -1; } response->content_length = (size_t)len; } else if (!strcasecmp("Transfer-Encoding", name->ptr) && !strcasecmp("chunked", value->ptr)) { ctx->response->chunked = 1; } else if (!strcasecmp("Proxy-Authenticate", git_buf_cstr(name))) { char *dup = git__strndup(value->ptr, value->size); GIT_ERROR_CHECK_ALLOC(dup); if (git_vector_insert(&client->proxy.auth_challenges, dup) < 0) return -1; } else if (!strcasecmp("WWW-Authenticate", name->ptr)) { char *dup = git__strndup(value->ptr, value->size); GIT_ERROR_CHECK_ALLOC(dup); if (git_vector_insert(&client->server.auth_challenges, dup) < 0) return -1; } else if (!strcasecmp("Location", name->ptr)) { if (response->location) { git_error_set(GIT_ERROR_HTTP, "multiple location headers"); return -1; } response->location = git__strndup(value->ptr, value->size); GIT_ERROR_CHECK_ALLOC(response->location); } return 0; } static int on_header_field(http_parser *parser, const char *str, size_t len) { http_parser_context *ctx = (http_parser_context *) parser->data; switch (ctx->parse_header_state) { /* * We last saw a header value, process the name/value pair and * get ready to handle this new name. */ case PARSE_HEADER_VALUE: if (on_header_complete(parser) < 0) return ctx->parse_status = PARSE_STATUS_ERROR; git_buf_clear(&ctx->parse_header_name); git_buf_clear(&ctx->parse_header_value); /* Fall through */ case PARSE_HEADER_NONE: case PARSE_HEADER_NAME: ctx->parse_header_state = PARSE_HEADER_NAME; if (git_buf_put(&ctx->parse_header_name, str, len) < 0) return ctx->parse_status = PARSE_STATUS_ERROR; break; default: git_error_set(GIT_ERROR_HTTP, "header name seen at unexpected time"); return ctx->parse_status = PARSE_STATUS_ERROR; } return 0; } static int on_header_value(http_parser *parser, const char *str, size_t len) { http_parser_context *ctx = (http_parser_context *) parser->data; switch (ctx->parse_header_state) { case PARSE_HEADER_NAME: case PARSE_HEADER_VALUE: ctx->parse_header_state = PARSE_HEADER_VALUE; if (git_buf_put(&ctx->parse_header_value, str, len) < 0) return ctx->parse_status = PARSE_STATUS_ERROR; break; default: git_error_set(GIT_ERROR_HTTP, "header value seen at unexpected time"); return ctx->parse_status = PARSE_STATUS_ERROR; } return 0; } GIT_INLINE(bool) challenge_matches_scheme( const char *challenge, git_http_auth_scheme *scheme) { const char *scheme_name = scheme->name; size_t scheme_len = strlen(scheme_name); if (!strncasecmp(challenge, scheme_name, scheme_len) && (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' ')) return true; return false; } static git_http_auth_scheme *scheme_for_challenge(const char *challenge) { size_t i; for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) { if (challenge_matches_scheme(challenge, &auth_schemes[i])) return &auth_schemes[i]; } return NULL; } GIT_INLINE(void) collect_authinfo( unsigned int *schemetypes, unsigned int *credtypes, git_vector *challenges) { git_http_auth_scheme *scheme; const char *challenge; size_t i; *schemetypes = 0; *credtypes = 0; git_vector_foreach(challenges, i, challenge) { if ((scheme = scheme_for_challenge(challenge)) != NULL) { *schemetypes |= scheme->type; *credtypes |= scheme->credtypes; } } } static int resend_needed(git_http_client *client, git_http_response *response) { git_http_auth_context *auth_context; if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED && (auth_context = client->server.auth_context) && auth_context->is_complete && !auth_context->is_complete(auth_context)) return 1; if (response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED && (auth_context = client->proxy.auth_context) && auth_context->is_complete && !auth_context->is_complete(auth_context)) return 1; return 0; } static int on_headers_complete(http_parser *parser) { http_parser_context *ctx = (http_parser_context *) parser->data; /* Finalize the last seen header */ switch (ctx->parse_header_state) { case PARSE_HEADER_VALUE: if (on_header_complete(parser) < 0) return ctx->parse_status = PARSE_STATUS_ERROR; /* Fall through */ case PARSE_HEADER_NONE: ctx->parse_header_state = PARSE_HEADER_COMPLETE; break; default: git_error_set(GIT_ERROR_HTTP, "header completion at unexpected time"); return ctx->parse_status = PARSE_STATUS_ERROR; } ctx->response->status = parser->status_code; ctx->client->keepalive = http_should_keep_alive(parser); /* Prepare for authentication */ collect_authinfo(&ctx->response->server_auth_schemetypes, &ctx->response->server_auth_credtypes, &ctx->client->server.auth_challenges); collect_authinfo(&ctx->response->proxy_auth_schemetypes, &ctx->response->proxy_auth_credtypes, &ctx->client->proxy.auth_challenges); ctx->response->resend_credentials = resend_needed(ctx->client, ctx->response); /* Stop parsing. */ http_parser_pause(parser, 1); if (ctx->response->content_type || ctx->response->chunked) ctx->client->state = READING_BODY; else ctx->client->state = DONE; return 0; } static int on_body(http_parser *parser, const char *buf, size_t len) { http_parser_context *ctx = (http_parser_context *) parser->data; size_t max_len; /* Saw data when we expected not to (eg, in consume_response_body) */ if (ctx->output_buf == NULL && ctx->output_size == 0) { ctx->parse_status = PARSE_STATUS_NO_OUTPUT; return 0; } GIT_ASSERT(ctx->output_size >= ctx->output_written); max_len = min(ctx->output_size - ctx->output_written, len); max_len = min(max_len, INT_MAX); memcpy(ctx->output_buf + ctx->output_written, buf, max_len); ctx->output_written += max_len; return 0; } static int on_message_complete(http_parser *parser) { http_parser_context *ctx = (http_parser_context *) parser->data; ctx->client->state = DONE; return 0; } GIT_INLINE(int) stream_write( git_http_server *server, const char *data, size_t len) { git_trace(GIT_TRACE_TRACE, "Sending request:\n%.*s", (int)len, data); return git_stream__write_full(server->stream, data, len, 0); } GIT_INLINE(int) client_write_request(git_http_client *client) { git_stream *stream = client->current_server == PROXY ? client->proxy.stream : client->server.stream; git_trace(GIT_TRACE_TRACE, "Sending request:\n%.*s", (int)client->request_msg.size, client->request_msg.ptr); return git_stream__write_full(stream, client->request_msg.ptr, client->request_msg.size, 0); } static const char *name_for_method(git_http_method method) { switch (method) { case GIT_HTTP_METHOD_GET: return "GET"; case GIT_HTTP_METHOD_POST: return "POST"; case GIT_HTTP_METHOD_CONNECT: return "CONNECT"; } return NULL; } /* * Find the scheme that is suitable for the given credentials, based on the * server's auth challenges. */ static bool best_scheme_and_challenge( git_http_auth_scheme **scheme_out, const char **challenge_out, git_vector *challenges, git_credential *credentials) { const char *challenge; size_t i, j; for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) { git_vector_foreach(challenges, j, challenge) { git_http_auth_scheme *scheme = &auth_schemes[i]; if (challenge_matches_scheme(challenge, scheme) && (scheme->credtypes & credentials->credtype)) { *scheme_out = scheme; *challenge_out = challenge; return true; } } } return false; } /* * Find the challenge from the server for our current auth context. */ static const char *challenge_for_context( git_vector *challenges, git_http_auth_context *auth_ctx) { const char *challenge; size_t i, j; for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) { if (auth_schemes[i].type == auth_ctx->type) { git_http_auth_scheme *scheme = &auth_schemes[i]; git_vector_foreach(challenges, j, challenge) { if (challenge_matches_scheme(challenge, scheme)) return challenge; } } } return NULL; } static const char *init_auth_context( git_http_server *server, git_vector *challenges, git_credential *credentials) { git_http_auth_scheme *scheme; const char *challenge; int error; if (!best_scheme_and_challenge(&scheme, &challenge, challenges, credentials)) { git_error_set(GIT_ERROR_HTTP, "could not find appropriate mechanism for credentials"); return NULL; } error = scheme->init_context(&server->auth_context, &server->url); if (error == GIT_PASSTHROUGH) { git_error_set(GIT_ERROR_HTTP, "'%s' authentication is not supported", scheme->name); return NULL; } return challenge; } static void free_auth_context(git_http_server *server) { if (!server->auth_context) return; if (server->auth_context->free) server->auth_context->free(server->auth_context); server->auth_context = NULL; } static int apply_credentials( git_buf *buf, git_http_server *server, const char *header_name, git_credential *credentials) { git_http_auth_context *auth = server->auth_context; git_vector *challenges = &server->auth_challenges; const char *challenge; git_buf token = GIT_BUF_INIT; int error = 0; /* We've started a new request without creds; free the context. */ if (auth && !credentials) { free_auth_context(server); return 0; } /* We haven't authenticated, nor were we asked to. Nothing to do. */ if (!auth && !git_vector_length(challenges)) return 0; if (!auth) { challenge = init_auth_context(server, challenges, credentials); auth = server->auth_context; if (!challenge || !auth) { error = -1; goto done; } } else if (auth->set_challenge) { challenge = challenge_for_context(challenges, auth); } if (auth->set_challenge && challenge && (error = auth->set_challenge(auth, challenge)) < 0) goto done; if ((error = auth->next_token(&token, auth, credentials)) < 0) goto done; if (auth->is_complete && auth->is_complete(auth)) { /* * If we're done with an auth mechanism with connection affinity, * we don't need to send any more headers and can dispose the context. */ if (auth->connection_affinity) free_auth_context(server); } else if (!token.size) { git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge"); error = GIT_EAUTH; goto done; } if (token.size > 0) error = git_buf_printf(buf, "%s: %s\r\n", header_name, token.ptr); done: git_buf_dispose(&token); return error; } GIT_INLINE(int) apply_server_credentials( git_buf *buf, git_http_client *client, git_http_request *request) { return apply_credentials(buf, &client->server, "Authorization", request->credentials); } GIT_INLINE(int) apply_proxy_credentials( git_buf *buf, git_http_client *client, git_http_request *request) { return apply_credentials(buf, &client->proxy, "Proxy-Authorization", request->proxy_credentials); } static int puts_host_and_port(git_buf *buf, git_net_url *url, bool force_port) { bool ipv6 = git_net_url_is_ipv6(url); if (ipv6) git_buf_putc(buf, '['); git_buf_puts(buf, url->host); if (ipv6) git_buf_putc(buf, ']'); if (force_port || !git_net_url_is_default_port(url)) { git_buf_putc(buf, ':'); git_buf_puts(buf, url->port); } return git_buf_oom(buf) ? -1 : 0; } static int generate_connect_request( git_http_client *client, git_http_request *request) { git_buf *buf; int error; git_buf_clear(&client->request_msg); buf = &client->request_msg; git_buf_puts(buf, "CONNECT "); puts_host_and_port(buf, &client->server.url, true); git_buf_puts(buf, " HTTP/1.1\r\n"); git_buf_puts(buf, "User-Agent: "); git_http__user_agent(buf); git_buf_puts(buf, "\r\n"); git_buf_puts(buf, "Host: "); puts_host_and_port(buf, &client->server.url, true); git_buf_puts(buf, "\r\n"); if ((error = apply_proxy_credentials(buf, client, request) < 0)) return -1; git_buf_puts(buf, "\r\n"); return git_buf_oom(buf) ? -1 : 0; } static bool use_connect_proxy(git_http_client *client) { return client->proxy.url.host && !strcmp(client->server.url.scheme, "https"); } static int generate_request( git_http_client *client, git_http_request *request) { git_buf *buf; size_t i; int error; GIT_ASSERT_ARG(client); GIT_ASSERT_ARG(request); git_buf_clear(&client->request_msg); buf = &client->request_msg; /* GET|POST path HTTP/1.1 */ git_buf_puts(buf, name_for_method(request->method)); git_buf_putc(buf, ' '); if (request->proxy && strcmp(request->url->scheme, "https")) git_net_url_fmt(buf, request->url); else git_net_url_fmt_path(buf, request->url); git_buf_puts(buf, " HTTP/1.1\r\n"); git_buf_puts(buf, "User-Agent: "); git_http__user_agent(buf); git_buf_puts(buf, "\r\n"); git_buf_puts(buf, "Host: "); puts_host_and_port(buf, request->url, false); git_buf_puts(buf, "\r\n"); if (request->accept) git_buf_printf(buf, "Accept: %s\r\n", request->accept); else git_buf_puts(buf, "Accept: */*\r\n"); if (request->content_type) git_buf_printf(buf, "Content-Type: %s\r\n", request->content_type); if (request->chunked) git_buf_puts(buf, "Transfer-Encoding: chunked\r\n"); if (request->content_length > 0) git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", request->content_length); if (request->expect_continue) git_buf_printf(buf, "Expect: 100-continue\r\n"); if ((error = apply_server_credentials(buf, client, request)) < 0 || (!use_connect_proxy(client) && (error = apply_proxy_credentials(buf, client, request)) < 0)) return error; if (request->custom_headers) { for (i = 0; i < request->custom_headers->count; i++) { const char *hdr = request->custom_headers->strings[i]; if (hdr) git_buf_printf(buf, "%s\r\n", hdr); } } git_buf_puts(buf, "\r\n"); if (git_buf_oom(buf)) return -1; return 0; } static int check_certificate( git_stream *stream, git_net_url *url, int is_valid, git_transport_certificate_check_cb cert_cb, void *cert_cb_payload) { git_cert *cert; git_error_state last_error = {0}; int error; if ((error = git_stream_certificate(&cert, stream)) < 0) return error; git_error_state_capture(&last_error, GIT_ECERTIFICATE); error = cert_cb(cert, is_valid, url->host, cert_cb_payload); if (error == GIT_PASSTHROUGH && !is_valid) return git_error_state_restore(&last_error); else if (error == GIT_PASSTHROUGH) error = 0; else if (error && !git_error_last()) git_error_set(GIT_ERROR_HTTP, "user rejected certificate for %s", url->host); git_error_state_free(&last_error); return error; } static int server_connect_stream( git_http_server *server, git_transport_certificate_check_cb cert_cb, void *cb_payload) { int error; GIT_ERROR_CHECK_VERSION(server->stream, GIT_STREAM_VERSION, "git_stream"); error = git_stream_connect(server->stream); if (error && error != GIT_ECERTIFICATE) return error; if (git_stream_is_encrypted(server->stream) && cert_cb != NULL) error = check_certificate(server->stream, &server->url, !error, cert_cb, cb_payload); return error; } static void reset_auth_connection(git_http_server *server) { /* * If we've authenticated and we're doing "normal" * authentication with a request affinity (Basic, Digest) * then we want to _keep_ our context, since authentication * survives even through non-keep-alive connections. If * we've authenticated and we're doing connection-based * authentication (NTLM, Negotiate) - indicated by the presence * of an `is_complete` callback - then we need to restart * authentication on a new connection. */ if (server->auth_context && server->auth_context->connection_affinity) free_auth_context(server); } /* * Updates the server data structure with the new URL; returns 1 if the server * has changed and we need to reconnect, returns 0 otherwise. */ GIT_INLINE(int) server_setup_from_url( git_http_server *server, git_net_url *url) { if (!server->url.scheme || strcmp(server->url.scheme, url->scheme) || !server->url.host || strcmp(server->url.host, url->host) || !server->url.port || strcmp(server->url.port, url->port)) { git__free(server->url.scheme); git__free(server->url.host); git__free(server->url.port); server->url.scheme = git__strdup(url->scheme); GIT_ERROR_CHECK_ALLOC(server->url.scheme); server->url.host = git__strdup(url->host); GIT_ERROR_CHECK_ALLOC(server->url.host); server->url.port = git__strdup(url->port); GIT_ERROR_CHECK_ALLOC(server->url.port); return 1; } return 0; } static void reset_parser(git_http_client *client) { http_parser_init(&client->parser, HTTP_RESPONSE); } static int setup_hosts( git_http_client *client, git_http_request *request) { int ret, diff = 0; GIT_ASSERT_ARG(client); GIT_ASSERT_ARG(request); GIT_ASSERT(request->url); if ((ret = server_setup_from_url(&client->server, request->url)) < 0) return ret; diff |= ret; if (request->proxy && (ret = server_setup_from_url(&client->proxy, request->proxy)) < 0) return ret; diff |= ret; if (diff) { free_auth_context(&client->server); free_auth_context(&client->proxy); client->connected = 0; } return 0; } GIT_INLINE(int) server_create_stream(git_http_server *server) { git_net_url *url = &server->url; if (strcasecmp(url->scheme, "https") == 0) return git_tls_stream_new(&server->stream, url->host, url->port); else if (strcasecmp(url->scheme, "http") == 0) return git_socket_stream_new(&server->stream, url->host, url->port); git_error_set(GIT_ERROR_HTTP, "unknown http scheme '%s'", url->scheme); return -1; } GIT_INLINE(void) save_early_response( git_http_client *client, git_http_response *response) { /* Buffer the response so we can return it in read_response */ client->state = HAS_EARLY_RESPONSE; memcpy(&client->early_response, response, sizeof(git_http_response)); memset(response, 0, sizeof(git_http_response)); } static int proxy_connect( git_http_client *client, git_http_request *request) { git_http_response response = {0}; int error; if (!client->proxy_connected || !client->keepalive) { git_trace(GIT_TRACE_DEBUG, "Connecting to proxy %s port %s", client->proxy.url.host, client->proxy.url.port); if ((error = server_create_stream(&client->proxy)) < 0 || (error = server_connect_stream(&client->proxy, client->opts.proxy_certificate_check_cb, client->opts.proxy_certificate_check_payload)) < 0) goto done; client->proxy_connected = 1; } client->current_server = PROXY; client->state = SENDING_REQUEST; if ((error = generate_connect_request(client, request)) < 0 || (error = client_write_request(client)) < 0) goto done; client->state = SENT_REQUEST; if ((error = git_http_client_read_response(&response, client)) < 0 || (error = git_http_client_skip_body(client)) < 0) goto done; GIT_ASSERT(client->state == DONE); if (response.status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { save_early_response(client, &response); error = GIT_RETRY; goto done; } else if (response.status != GIT_HTTP_STATUS_OK) { git_error_set(GIT_ERROR_HTTP, "proxy returned unexpected status: %d", response.status); error = -1; goto done; } reset_parser(client); client->state = NONE; done: git_http_response_dispose(&response); return error; } static int server_connect(git_http_client *client) { git_net_url *url = &client->server.url; git_transport_certificate_check_cb cert_cb; void *cert_payload; int error; client->current_server = SERVER; if (client->proxy.stream) error = git_tls_stream_wrap(&client->server.stream, client->proxy.stream, url->host); else error = server_create_stream(&client->server); if (error < 0) goto done; cert_cb = client->opts.server_certificate_check_cb; cert_payload = client->opts.server_certificate_check_payload; error = server_connect_stream(&client->server, cert_cb, cert_payload); done: return error; } GIT_INLINE(void) close_stream(git_http_server *server) { if (server->stream) { git_stream_close(server->stream); git_stream_free(server->stream); server->stream = NULL; } } static int http_client_connect( git_http_client *client, git_http_request *request) { bool use_proxy = false; int error; if ((error = setup_hosts(client, request)) < 0) goto on_error; /* We're connected to our destination server; no need to reconnect */ if (client->connected && client->keepalive && (client->state == NONE || client->state == DONE)) return 0; client->connected = 0; client->request_count = 0; close_stream(&client->server); reset_auth_connection(&client->server); reset_parser(client); /* Reconnect to the proxy if necessary. */ use_proxy = use_connect_proxy(client); if (use_proxy) { if (!client->proxy_connected || !client->keepalive || (client->state != NONE && client->state != DONE)) { close_stream(&client->proxy); reset_auth_connection(&client->proxy); client->proxy_connected = 0; } if ((error = proxy_connect(client, request)) < 0) goto on_error; } git_trace(GIT_TRACE_DEBUG, "Connecting to remote %s port %s", client->server.url.host, client->server.url.port); if ((error = server_connect(client)) < 0) goto on_error; client->connected = 1; return error; on_error: if (error != GIT_RETRY) close_stream(&client->proxy); close_stream(&client->server); return error; } GIT_INLINE(int) client_read(git_http_client *client) { http_parser_context *parser_context = client->parser.data; git_stream *stream; char *buf = client->read_buf.ptr + client->read_buf.size; size_t max_len; ssize_t read_len; stream = client->current_server == PROXY ? client->proxy.stream : client->server.stream; /* * We use a git_buf for convenience, but statically allocate it and * don't resize. Limit our consumption to INT_MAX since calling * functions use an int return type to return number of bytes read. */ max_len = client->read_buf.asize - client->read_buf.size; max_len = min(max_len, INT_MAX); if (parser_context->output_size) max_len = min(max_len, parser_context->output_size); if (max_len == 0) { git_error_set(GIT_ERROR_HTTP, "no room in output buffer"); return -1; } read_len = git_stream_read(stream, buf, max_len); if (read_len >= 0) { client->read_buf.size += read_len; git_trace(GIT_TRACE_TRACE, "Received:\n%.*s", (int)read_len, buf); } return (int)read_len; } static bool parser_settings_initialized; static http_parser_settings parser_settings; GIT_INLINE(http_parser_settings *) http_client_parser_settings(void) { if (!parser_settings_initialized) { parser_settings.on_header_field = on_header_field; parser_settings.on_header_value = on_header_value; parser_settings.on_headers_complete = on_headers_complete; parser_settings.on_body = on_body; parser_settings.on_message_complete = on_message_complete; parser_settings_initialized = true; } return &parser_settings; } GIT_INLINE(int) client_read_and_parse(git_http_client *client) { http_parser *parser = &client->parser; http_parser_context *ctx = (http_parser_context *) parser->data; unsigned char http_errno; int read_len; size_t parsed_len; /* * If we have data in our read buffer, that means we stopped early * when parsing headers. Use the data in the read buffer instead of * reading more from the socket. */ if (!client->read_buf.size && (read_len = client_read(client)) < 0) return read_len; parsed_len = http_parser_execute(parser, http_client_parser_settings(), client->read_buf.ptr, client->read_buf.size); http_errno = client->parser.http_errno; if (parsed_len > INT_MAX) { git_error_set(GIT_ERROR_HTTP, "unexpectedly large parse"); return -1; } if (ctx->parse_status == PARSE_STATUS_ERROR) { client->connected = 0; return ctx->error ? ctx->error : -1; } /* * If we finished reading the headers or body, we paused parsing. * Otherwise the parser will start filling the body, or even parse * a new response if the server pipelined us multiple responses. * (This can happen in response to an expect/continue request, * where the server gives you a 100 and 200 simultaneously.) */ if (http_errno == HPE_PAUSED) { /* * http-parser has a "feature" where it will not deliver the * final byte when paused in a callback. Consume that byte. * https://github.com/nodejs/http-parser/issues/97 */ GIT_ASSERT(client->read_buf.size > parsed_len); http_parser_pause(parser, 0); parsed_len += http_parser_execute(parser, http_client_parser_settings(), client->read_buf.ptr + parsed_len, 1); } /* Most failures will be reported in http_errno */ else if (parser->http_errno != HPE_OK) { git_error_set(GIT_ERROR_HTTP, "http parser error: %s", http_errno_description(http_errno)); return -1; } /* Otherwise we should have consumed the entire buffer. */ else if (parsed_len != client->read_buf.size) { git_error_set(GIT_ERROR_HTTP, "http parser did not consume entire buffer: %s", http_errno_description(http_errno)); return -1; } /* recv returned 0, the server hung up on us */ else if (!parsed_len) { git_error_set(GIT_ERROR_HTTP, "unexpected EOF"); return -1; } git_buf_consume_bytes(&client->read_buf, parsed_len); return (int)parsed_len; } /* * See if we've consumed the entire response body. If the client was * reading the body but did not consume it entirely, it's possible that * they knew that the stream had finished (in a git response, seeing a * final flush) and stopped reading. But if the response was chunked, * we may have not consumed the final chunk marker. Consume it to * ensure that we don't have it waiting in our socket. If there's * more than just a chunk marker, close the connection. */ static void complete_response_body(git_http_client *client) { http_parser_context parser_context = {0}; /* If we're not keeping alive, don't bother. */ if (!client->keepalive) { client->connected = 0; goto done; } parser_context.client = client; client->parser.data = &parser_context; /* If there was an error, just close the connection. */ if (client_read_and_parse(client) < 0 || parser_context.error != HPE_OK || (parser_context.parse_status != PARSE_STATUS_OK && parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) { git_error_clear(); client->connected = 0; } done: git_buf_clear(&client->read_buf); } int git_http_client_send_request( git_http_client *client, git_http_request *request) { git_http_response response = {0}; int error = -1; GIT_ASSERT_ARG(client); GIT_ASSERT_ARG(request); /* If the client did not finish reading, clean up the stream. */ if (client->state == READING_BODY) complete_response_body(client); /* If we're waiting for proxy auth, don't sending more requests. */ if (client->state == HAS_EARLY_RESPONSE) return 0; if (git_trace_level() >= GIT_TRACE_DEBUG) { git_buf url = GIT_BUF_INIT; git_net_url_fmt(&url, request->url); git_trace(GIT_TRACE_DEBUG, "Sending %s request to %s", name_for_method(request->method), url.ptr ? url.ptr : ""); git_buf_dispose(&url); } if ((error = http_client_connect(client, request)) < 0 || (error = generate_request(client, request)) < 0 || (error = client_write_request(client)) < 0) goto done; client->state = SENT_REQUEST; if (request->expect_continue) { if ((error = git_http_client_read_response(&response, client)) < 0 || (error = git_http_client_skip_body(client)) < 0) goto done; error = 0; if (response.status != GIT_HTTP_STATUS_CONTINUE) { save_early_response(client, &response); goto done; } } if (request->content_length || request->chunked) { client->state = SENDING_BODY; client->request_body_len = request->content_length; client->request_body_remain = request->content_length; client->request_chunked = request->chunked; } reset_parser(client); done: if (error == GIT_RETRY) error = 0; git_http_response_dispose(&response); return error; } bool git_http_client_has_response(git_http_client *client) { return (client->state == HAS_EARLY_RESPONSE || client->state > SENT_REQUEST); } int git_http_client_send_body( git_http_client *client, const char *buffer, size_t buffer_len) { git_http_server *server; git_buf hdr = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(client); /* If we're waiting for proxy auth, don't sending more requests. */ if (client->state == HAS_EARLY_RESPONSE) return 0; if (client->state != SENDING_BODY) { git_error_set(GIT_ERROR_HTTP, "client is in invalid state"); return -1; } if (!buffer_len) return 0; server = &client->server; if (client->request_body_len) { GIT_ASSERT(buffer_len <= client->request_body_remain); if ((error = stream_write(server, buffer, buffer_len)) < 0) goto done; client->request_body_remain -= buffer_len; } else { if ((error = git_buf_printf(&hdr, "%" PRIxZ "\r\n", buffer_len)) < 0 || (error = stream_write(server, hdr.ptr, hdr.size)) < 0 || (error = stream_write(server, buffer, buffer_len)) < 0 || (error = stream_write(server, "\r\n", 2)) < 0) goto done; } done: git_buf_dispose(&hdr); return error; } static int complete_request(git_http_client *client) { int error = 0; GIT_ASSERT_ARG(client); GIT_ASSERT(client->state == SENDING_BODY); if (client->request_body_len && client->request_body_remain) { git_error_set(GIT_ERROR_HTTP, "truncated write"); error = -1; } else if (client->request_chunked) { error = stream_write(&client->server, "0\r\n\r\n", 5); } client->state = SENT_REQUEST; return error; } int git_http_client_read_response( git_http_response *response, git_http_client *client) { http_parser_context parser_context = {0}; int error; GIT_ASSERT_ARG(response); GIT_ASSERT_ARG(client); if (client->state == SENDING_BODY) { if ((error = complete_request(client)) < 0) goto done; } if (client->state == HAS_EARLY_RESPONSE) { memcpy(response, &client->early_response, sizeof(git_http_response)); memset(&client->early_response, 0, sizeof(git_http_response)); client->state = DONE; return 0; } if (client->state != SENT_REQUEST) { git_error_set(GIT_ERROR_HTTP, "client is in invalid state"); error = -1; goto done; } git_http_response_dispose(response); if (client->current_server == PROXY) { git_vector_free_deep(&client->proxy.auth_challenges); } else if(client->current_server == SERVER) { git_vector_free_deep(&client->server.auth_challenges); } client->state = READING_RESPONSE; client->keepalive = 0; client->parser.data = &parser_context; parser_context.client = client; parser_context.response = response; while (client->state == READING_RESPONSE) { if ((error = client_read_and_parse(client)) < 0) goto done; } GIT_ASSERT(client->state == READING_BODY || client->state == DONE); done: git_buf_dispose(&parser_context.parse_header_name); git_buf_dispose(&parser_context.parse_header_value); return error; } int git_http_client_read_body( git_http_client *client, char *buffer, size_t buffer_size) { http_parser_context parser_context = {0}; int error = 0; if (client->state == DONE) return 0; if (client->state != READING_BODY) { git_error_set(GIT_ERROR_HTTP, "client is in invalid state"); return -1; } /* * Now we'll read from the socket and http_parser will pipeline the * data directly to the client. */ parser_context.client = client; parser_context.output_buf = buffer; parser_context.output_size = buffer_size; client->parser.data = &parser_context; /* * Clients expect to get a non-zero amount of data from us, * so we either block until we have data to return, until we * hit EOF or there's an error. Do this in a loop, since we * may end up reading only some stream metadata (like chunk * information). */ while (!parser_context.output_written) { error = client_read_and_parse(client); if (error <= 0) goto done; if (client->state == DONE) break; } GIT_ASSERT(parser_context.output_written <= INT_MAX); error = (int)parser_context.output_written; done: if (error < 0) client->connected = 0; return error; } int git_http_client_skip_body(git_http_client *client) { http_parser_context parser_context = {0}; int error; if (client->state == DONE) return 0; if (client->state != READING_BODY) { git_error_set(GIT_ERROR_HTTP, "client is in invalid state"); return -1; } parser_context.client = client; client->parser.data = &parser_context; do { error = client_read_and_parse(client); if (parser_context.error != HPE_OK || (parser_context.parse_status != PARSE_STATUS_OK && parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) { git_error_set(GIT_ERROR_HTTP, "unexpected data handled in callback"); error = -1; } } while (error >= 0 && client->state != DONE); if (error < 0) client->connected = 0; return error; } /* * Create an http_client capable of communicating with the given remote * host. */ int git_http_client_new( git_http_client **out, git_http_client_options *opts) { git_http_client *client; GIT_ASSERT_ARG(out); client = git__calloc(1, sizeof(git_http_client)); GIT_ERROR_CHECK_ALLOC(client); git_buf_init(&client->read_buf, GIT_READ_BUFFER_SIZE); GIT_ERROR_CHECK_ALLOC(client->read_buf.ptr); if (opts) memcpy(&client->opts, opts, sizeof(git_http_client_options)); *out = client; return 0; } GIT_INLINE(void) http_server_close(git_http_server *server) { if (server->stream) { git_stream_close(server->stream); git_stream_free(server->stream); server->stream = NULL; } git_net_url_dispose(&server->url); git_vector_free_deep(&server->auth_challenges); free_auth_context(server); } static void http_client_close(git_http_client *client) { http_server_close(&client->server); http_server_close(&client->proxy); git_buf_dispose(&client->request_msg); client->state = 0; client->request_count = 0; client->connected = 0; client->keepalive = 0; } void git_http_client_free(git_http_client *client) { if (!client) return; http_client_close(client); git_buf_dispose(&client->read_buf); git__free(client); } git2r/src/libgit2/src/transports/auth_ntlm.h0000644000175000017500000000141614125111754020733 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transports_auth_ntlm_h__ #define INCLUDE_transports_auth_ntlm_h__ #include "git2.h" #include "auth.h" /* NTLM requires a full request/challenge/response */ #define GIT_AUTH_STEPS_NTLM 2 #ifdef GIT_NTLM #if defined(GIT_OPENSSL) # define CRYPT_OPENSSL #elif defined(GIT_MBEDTLS) # define CRYPT_MBEDTLS #elif defined(GIT_SECURE_TRANSPORT) # define CRYPT_COMMONCRYPTO #endif extern int git_http_auth_ntlm( git_http_auth_context **out, const git_net_url *url); #else #define git_http_auth_ntlm git_http_auth_dummy #endif /* GIT_NTLM */ #endif git2r/src/libgit2/src/transports/smart.h0000644000175000017500000001107214125111754020065 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transports_smart_h__ #define INCLUDE_transports_smart_h__ #include "common.h" #include "git2.h" #include "vector.h" #include "netops.h" #include "buffer.h" #include "push.h" #include "git2/sys/transport.h" #define GIT_SIDE_BAND_DATA 1 #define GIT_SIDE_BAND_PROGRESS 2 #define GIT_SIDE_BAND_ERROR 3 #define GIT_CAP_OFS_DELTA "ofs-delta" #define GIT_CAP_MULTI_ACK "multi_ack" #define GIT_CAP_MULTI_ACK_DETAILED "multi_ack_detailed" #define GIT_CAP_SIDE_BAND "side-band" #define GIT_CAP_SIDE_BAND_64K "side-band-64k" #define GIT_CAP_INCLUDE_TAG "include-tag" #define GIT_CAP_DELETE_REFS "delete-refs" #define GIT_CAP_REPORT_STATUS "report-status" #define GIT_CAP_THIN_PACK "thin-pack" #define GIT_CAP_SYMREF "symref" extern bool git_smart__ofs_delta_enabled; typedef enum { GIT_PKT_CMD, GIT_PKT_FLUSH, GIT_PKT_REF, GIT_PKT_HAVE, GIT_PKT_ACK, GIT_PKT_NAK, GIT_PKT_COMMENT, GIT_PKT_ERR, GIT_PKT_DATA, GIT_PKT_PROGRESS, GIT_PKT_OK, GIT_PKT_NG, GIT_PKT_UNPACK, } git_pkt_type; /* Used for multi_ack and multi_ack_detailed */ enum git_ack_status { GIT_ACK_NONE, GIT_ACK_CONTINUE, GIT_ACK_COMMON, GIT_ACK_READY }; /* This would be a flush pkt */ typedef struct { git_pkt_type type; } git_pkt; struct git_pkt_cmd { git_pkt_type type; char *cmd; char *path; char *host; }; /* This is a pkt-line with some info in it */ typedef struct { git_pkt_type type; git_remote_head head; char *capabilities; } git_pkt_ref; /* Useful later */ typedef struct { git_pkt_type type; git_oid oid; enum git_ack_status status; } git_pkt_ack; typedef struct { git_pkt_type type; char comment[GIT_FLEX_ARRAY]; } git_pkt_comment; typedef struct { git_pkt_type type; size_t len; char data[GIT_FLEX_ARRAY]; } git_pkt_data; typedef git_pkt_data git_pkt_progress; typedef struct { git_pkt_type type; size_t len; char error[GIT_FLEX_ARRAY]; } git_pkt_err; typedef struct { git_pkt_type type; char *ref; } git_pkt_ok; typedef struct { git_pkt_type type; char *ref; char *msg; } git_pkt_ng; typedef struct { git_pkt_type type; int unpack_ok; } git_pkt_unpack; typedef struct transport_smart_caps { int common:1, ofs_delta:1, multi_ack: 1, multi_ack_detailed: 1, side_band:1, side_band_64k:1, include_tag:1, delete_refs:1, report_status:1, thin_pack:1; } transport_smart_caps; typedef int (*packetsize_cb)(size_t received, void *payload); typedef struct { git_transport parent; git_remote *owner; char *url; git_credential_acquire_cb cred_acquire_cb; void *cred_acquire_payload; git_proxy_options proxy; int direction; int flags; git_transport_message_cb progress_cb; git_transport_message_cb error_cb; git_transport_certificate_check_cb certificate_check_cb; void *message_cb_payload; git_strarray custom_headers; git_smart_subtransport *wrapped; git_smart_subtransport_stream *current_stream; transport_smart_caps caps; git_vector refs; git_vector heads; git_vector common; git_atomic32 cancelled; packetsize_cb packetsize_cb; void *packetsize_payload; unsigned rpc : 1, have_refs : 1, connected : 1; gitno_buffer buffer; char buffer_data[65536]; } transport_smart; /* smart_protocol.c */ int git_smart__store_refs(transport_smart *t, int flushes); int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs); int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs); int git_smart__negotiate_fetch( git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count); int git_smart__download_pack( git_transport *transport, git_repository *repo, git_indexer_progress *stats, git_indexer_progress_cb progress_cb, void *progress_payload); /* smart.c */ int git_smart__negotiation_step(git_transport *transport, void *data, size_t len); int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out); int git_smart__update_heads(transport_smart *t, git_vector *symrefs); /* smart_pkt.c */ int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen); int git_pkt_buffer_flush(git_buf *buf); int git_pkt_send_flush(GIT_SOCKET s); int git_pkt_buffer_done(git_buf *buf); int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_buf *buf); int git_pkt_buffer_have(git_oid *oid, git_buf *buf); void git_pkt_free(git_pkt *pkt); #endif git2r/src/libgit2/src/revwalk.h0000644000175000017500000000312514125111754016173 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_revwalk_h__ #define INCLUDE_revwalk_h__ #include "common.h" #include "git2/revwalk.h" #include "oidmap.h" #include "commit_list.h" #include "pqueue.h" #include "pool.h" #include "vector.h" #include "oidmap.h" struct git_revwalk { git_repository *repo; git_odb *odb; git_oidmap *commits; git_pool commit_pool; git_commit_list *iterator_topo; git_commit_list *iterator_rand; git_commit_list *iterator_reverse; git_pqueue iterator_time; int (*get_next)(git_commit_list_node **, git_revwalk *); int (*enqueue)(git_revwalk *, git_commit_list_node *); unsigned walking:1, first_parent: 1, did_hide: 1, did_push: 1, limited: 1; unsigned int sorting; /* the pushes and hides */ git_commit_list *user_input; /* hide callback */ git_revwalk_hide_cb hide_cb; void *hide_cb_payload; }; git_commit_list_node *git_revwalk__commit_lookup(git_revwalk *walk, const git_oid *oid); typedef struct { int uninteresting; int from_glob; int insert_by_date; } git_revwalk__push_options; #define GIT_REVWALK__PUSH_OPTIONS_INIT { 0 } int git_revwalk__push_commit(git_revwalk *walk, const git_oid *oid, const git_revwalk__push_options *opts); int git_revwalk__push_ref(git_revwalk *walk, const char *refname, const git_revwalk__push_options *opts); int git_revwalk__push_glob(git_revwalk *walk, const char *glob, const git_revwalk__push_options *given_opts); #endif git2r/src/libgit2/src/signature.h0000644000175000017500000000142614125111754016523 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_signature_h__ #define INCLUDE_signature_h__ #include "common.h" #include "git2/common.h" #include "git2/signature.h" #include "repository.h" #include int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender); void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig); bool git_signature__equal(const git_signature *one, const git_signature *two); int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool); #endif git2r/src/libgit2/src/rebase.c0000644000175000017500000010753214125111754015763 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "buffer.h" #include "repository.h" #include "posix.h" #include "filebuf.h" #include "merge.h" #include "array.h" #include "config.h" #include "annotated_commit.h" #include "index.h" #include #include #include #include #include #include #include #define REBASE_APPLY_DIR "rebase-apply" #define REBASE_MERGE_DIR "rebase-merge" #define HEAD_NAME_FILE "head-name" #define ORIG_HEAD_FILE "orig-head" #define HEAD_FILE "head" #define ONTO_FILE "onto" #define ONTO_NAME_FILE "onto_name" #define QUIET_FILE "quiet" #define MSGNUM_FILE "msgnum" #define END_FILE "end" #define CMT_FILE_FMT "cmt.%" PRIuZ #define CURRENT_FILE "current" #define REWRITTEN_FILE "rewritten" #define ORIG_DETACHED_HEAD "detached HEAD" #define NOTES_DEFAULT_REF NULL #define REBASE_DIR_MODE 0777 #define REBASE_FILE_MODE 0666 typedef enum { GIT_REBASE_NONE = 0, GIT_REBASE_APPLY = 1, GIT_REBASE_MERGE = 2, GIT_REBASE_INTERACTIVE = 3, } git_rebase_t; struct git_rebase { git_repository *repo; git_rebase_options options; git_rebase_t type; char *state_path; int head_detached : 1, inmemory : 1, quiet : 1, started : 1; git_array_t(git_rebase_operation) operations; size_t current; /* Used by in-memory rebase */ git_index *index; git_commit *last_commit; /* Used by regular (not in-memory) merge-style rebase */ git_oid orig_head_id; char *orig_head_name; git_oid onto_id; char *onto_name; }; #define GIT_REBASE_STATE_INIT {0} static int rebase_state_type( git_rebase_t *type_out, char **path_out, git_repository *repo) { git_buf path = GIT_BUF_INIT; git_rebase_t type = GIT_REBASE_NONE; if (git_buf_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0) return -1; if (git_path_isdir(git_buf_cstr(&path))) { type = GIT_REBASE_APPLY; goto done; } git_buf_clear(&path); if (git_buf_joinpath(&path, repo->gitdir, REBASE_MERGE_DIR) < 0) return -1; if (git_path_isdir(git_buf_cstr(&path))) { type = GIT_REBASE_MERGE; goto done; } done: *type_out = type; if (type != GIT_REBASE_NONE && path_out) *path_out = git_buf_detach(&path); git_buf_dispose(&path); return 0; } GIT_INLINE(int) rebase_readfile( git_buf *out, git_buf *state_path, const char *filename) { size_t state_path_len = state_path->size; int error; git_buf_clear(out); if ((error = git_buf_joinpath(state_path, state_path->ptr, filename)) < 0 || (error = git_futils_readbuffer(out, state_path->ptr)) < 0) goto done; git_buf_rtrim(out); done: git_buf_truncate(state_path, state_path_len); return error; } GIT_INLINE(int) rebase_readint( size_t *out, git_buf *asc_out, git_buf *state_path, const char *filename) { int32_t num; const char *eol; int error = 0; if ((error = rebase_readfile(asc_out, state_path, filename)) < 0) return error; if (git__strntol32(&num, asc_out->ptr, asc_out->size, &eol, 10) < 0 || num < 0 || *eol) { git_error_set(GIT_ERROR_REBASE, "the file '%s' contains an invalid numeric value", filename); return -1; } *out = (size_t) num; return 0; } GIT_INLINE(int) rebase_readoid( git_oid *out, git_buf *str_out, git_buf *state_path, const char *filename) { int error; if ((error = rebase_readfile(str_out, state_path, filename)) < 0) return error; if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) { git_error_set(GIT_ERROR_REBASE, "the file '%s' contains an invalid object ID", filename); return -1; } return 0; } static git_rebase_operation *rebase_operation_alloc( git_rebase *rebase, git_rebase_operation_t type, git_oid *id, const char *exec) { git_rebase_operation *operation; GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !id, NULL); GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !!exec, NULL); if ((operation = git_array_alloc(rebase->operations)) == NULL) return NULL; operation->type = type; git_oid_cpy((git_oid *)&operation->id, id); operation->exec = exec; return operation; } static int rebase_open_merge(git_rebase *rebase) { git_buf state_path = GIT_BUF_INIT, buf = GIT_BUF_INIT, cmt = GIT_BUF_INIT; git_oid id; git_rebase_operation *operation; size_t i, msgnum = 0, end; int error; if ((error = git_buf_puts(&state_path, rebase->state_path)) < 0) goto done; /* Read 'msgnum' if it exists (otherwise, let msgnum = 0) */ if ((error = rebase_readint(&msgnum, &buf, &state_path, MSGNUM_FILE)) < 0 && error != GIT_ENOTFOUND) goto done; if (msgnum) { rebase->started = 1; rebase->current = msgnum - 1; } /* Read 'end' */ if ((error = rebase_readint(&end, &buf, &state_path, END_FILE)) < 0) goto done; /* Read 'current' if it exists */ if ((error = rebase_readoid(&id, &buf, &state_path, CURRENT_FILE)) < 0 && error != GIT_ENOTFOUND) goto done; /* Read cmt.* */ git_array_init_to_size(rebase->operations, end); GIT_ERROR_CHECK_ARRAY(rebase->operations); for (i = 0; i < end; i++) { git_buf_clear(&cmt); if ((error = git_buf_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 || (error = rebase_readoid(&id, &buf, &state_path, cmt.ptr)) < 0) goto done; operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL); GIT_ERROR_CHECK_ALLOC(operation); } /* Read 'onto_name' */ if ((error = rebase_readfile(&buf, &state_path, ONTO_NAME_FILE)) < 0) goto done; rebase->onto_name = git_buf_detach(&buf); done: git_buf_dispose(&cmt); git_buf_dispose(&state_path); git_buf_dispose(&buf); return error; } static int rebase_alloc(git_rebase **out, const git_rebase_options *rebase_opts) { git_rebase *rebase = git__calloc(1, sizeof(git_rebase)); GIT_ERROR_CHECK_ALLOC(rebase); *out = NULL; if (rebase_opts) memcpy(&rebase->options, rebase_opts, sizeof(git_rebase_options)); else git_rebase_options_init(&rebase->options, GIT_REBASE_OPTIONS_VERSION); if (rebase_opts && rebase_opts->rewrite_notes_ref) { rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref); GIT_ERROR_CHECK_ALLOC(rebase->options.rewrite_notes_ref); } *out = rebase; return 0; } static int rebase_check_versions(const git_rebase_options *given_opts) { GIT_ERROR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options"); if (given_opts) GIT_ERROR_CHECK_VERSION(&given_opts->checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options"); return 0; } int git_rebase_open( git_rebase **out, git_repository *repo, const git_rebase_options *given_opts) { git_rebase *rebase; git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT, orig_head_id = GIT_BUF_INIT, onto_id = GIT_BUF_INIT; size_t state_path_len; int error; GIT_ASSERT_ARG(repo); if ((error = rebase_check_versions(given_opts)) < 0) return error; if (rebase_alloc(&rebase, given_opts) < 0) return -1; rebase->repo = repo; if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0) goto done; if (rebase->type == GIT_REBASE_NONE) { git_error_set(GIT_ERROR_REBASE, "there is no rebase in progress"); error = GIT_ENOTFOUND; goto done; } if ((error = git_buf_puts(&path, rebase->state_path)) < 0) goto done; state_path_len = git_buf_len(&path); if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 || (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0) goto done; git_buf_rtrim(&orig_head_name); if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0) rebase->head_detached = 1; git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0) goto done; if (!git_path_isfile(path.ptr)) { /* Previous versions of git.git used 'head' here; support that. */ git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0) goto done; } if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0) goto done; git_buf_rtrim(&orig_head_id); if ((error = git_oid_fromstr(&rebase->orig_head_id, orig_head_id.ptr)) < 0) goto done; git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, ONTO_FILE)) < 0 || (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0) goto done; git_buf_rtrim(&onto_id); if ((error = git_oid_fromstr(&rebase->onto_id, onto_id.ptr)) < 0) goto done; if (!rebase->head_detached) rebase->orig_head_name = git_buf_detach(&orig_head_name); switch (rebase->type) { case GIT_REBASE_INTERACTIVE: git_error_set(GIT_ERROR_REBASE, "interactive rebase is not supported"); error = -1; break; case GIT_REBASE_MERGE: error = rebase_open_merge(rebase); break; case GIT_REBASE_APPLY: git_error_set(GIT_ERROR_REBASE, "patch application rebase is not supported"); error = -1; break; default: abort(); } done: if (error == 0) *out = rebase; else git_rebase_free(rebase); git_buf_dispose(&path); git_buf_dispose(&orig_head_name); git_buf_dispose(&orig_head_id); git_buf_dispose(&onto_id); return error; } static int rebase_cleanup(git_rebase *rebase) { if (!rebase || rebase->inmemory) return 0; return git_path_isdir(rebase->state_path) ? git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : 0; } static int rebase_setupfile(git_rebase *rebase, const char *filename, int flags, const char *fmt, ...) { git_buf path = GIT_BUF_INIT, contents = GIT_BUF_INIT; va_list ap; int error; va_start(ap, fmt); git_buf_vprintf(&contents, fmt, ap); va_end(ap); if ((error = git_buf_joinpath(&path, rebase->state_path, filename)) == 0) error = git_futils_writebuffer(&contents, path.ptr, flags, REBASE_FILE_MODE); git_buf_dispose(&path); git_buf_dispose(&contents); return error; } static const char *rebase_onto_name(const git_annotated_commit *onto) { if (onto->ref_name && git__strncmp(onto->ref_name, "refs/heads/", 11) == 0) return onto->ref_name + 11; else if (onto->ref_name) return onto->ref_name; else return onto->id_str; } static int rebase_setupfiles_merge(git_rebase *rebase) { git_buf commit_filename = GIT_BUF_INIT; char id_str[GIT_OID_HEXSZ]; git_rebase_operation *operation; size_t i; int error = 0; if ((error = rebase_setupfile(rebase, END_FILE, 0, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 || (error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0) goto done; for (i = 0; i < git_array_size(rebase->operations); i++) { operation = git_array_get(rebase->operations, i); git_buf_clear(&commit_filename); git_buf_printf(&commit_filename, CMT_FILE_FMT, i+1); git_oid_fmt(id_str, &operation->id); if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0, "%.*s\n", GIT_OID_HEXSZ, id_str)) < 0) goto done; } done: git_buf_dispose(&commit_filename); return error; } static int rebase_setupfiles(git_rebase *rebase) { char onto[GIT_OID_HEXSZ], orig_head[GIT_OID_HEXSZ]; const char *orig_head_name; git_oid_fmt(onto, &rebase->onto_id); git_oid_fmt(orig_head, &rebase->orig_head_id); if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) { git_error_set(GIT_ERROR_OS, "failed to create rebase directory '%s'", rebase->state_path); return -1; } orig_head_name = rebase->head_detached ? ORIG_DETACHED_HEAD : rebase->orig_head_name; if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 || rebase_setupfile(rebase, HEAD_NAME_FILE, 0, "%s\n", orig_head_name) < 0 || rebase_setupfile(rebase, ONTO_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || rebase_setupfile(rebase, QUIET_FILE, 0, rebase->quiet ? "t\n" : "\n") < 0) return -1; return rebase_setupfiles_merge(rebase); } int git_rebase_options_init(git_rebase_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_rebase_options, GIT_REBASE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_rebase_init_options(git_rebase_options *opts, unsigned int version) { return git_rebase_options_init(opts, version); } #endif static int rebase_ensure_not_in_progress(git_repository *repo) { int error; git_rebase_t type; if ((error = rebase_state_type(&type, NULL, repo)) < 0) return error; if (type != GIT_REBASE_NONE) { git_error_set(GIT_ERROR_REBASE, "there is an existing rebase in progress"); return -1; } return 0; } static int rebase_ensure_not_dirty( git_repository *repo, bool check_index, bool check_workdir, int fail_with) { git_tree *head = NULL; git_index *index = NULL; git_diff *diff = NULL; int error = 0; if (check_index) { if ((error = git_repository_head_tree(&head, repo)) < 0 || (error = git_repository_index(&index, repo)) < 0 || (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0) goto done; if (git_diff_num_deltas(diff) > 0) { git_error_set(GIT_ERROR_REBASE, "uncommitted changes exist in index"); error = fail_with; goto done; } git_diff_free(diff); diff = NULL; } if (check_workdir) { git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; diff_opts.ignore_submodules = GIT_SUBMODULE_IGNORE_UNTRACKED; if ((error = git_diff_index_to_workdir(&diff, repo, index, &diff_opts)) < 0) goto done; if (git_diff_num_deltas(diff) > 0) { git_error_set(GIT_ERROR_REBASE, "unstaged changes exist in workdir"); error = fail_with; goto done; } } done: git_diff_free(diff); git_index_free(index); git_tree_free(head); return error; } static int rebase_init_operations( git_rebase *rebase, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto) { git_revwalk *revwalk = NULL; git_commit *commit; git_oid id; bool merge; git_rebase_operation *operation; int error; if (!upstream) upstream = onto; if ((error = git_revwalk_new(&revwalk, rebase->repo)) < 0 || (error = git_revwalk_push(revwalk, git_annotated_commit_id(branch))) < 0 || (error = git_revwalk_hide(revwalk, git_annotated_commit_id(upstream))) < 0) goto done; git_revwalk_sorting(revwalk, GIT_SORT_REVERSE); while ((error = git_revwalk_next(&id, revwalk)) == 0) { if ((error = git_commit_lookup(&commit, repo, &id)) < 0) goto done; merge = (git_commit_parentcount(commit) > 1); git_commit_free(commit); if (merge) continue; operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL); GIT_ERROR_CHECK_ALLOC(operation); } error = 0; done: git_revwalk_free(revwalk); return error; } static int rebase_init_merge( git_rebase *rebase, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto) { git_reference *head_ref = NULL; git_commit *onto_commit = NULL; git_buf reflog = GIT_BUF_INIT; git_buf state_path = GIT_BUF_INIT; int error; GIT_UNUSED(upstream); if ((error = git_buf_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0) goto done; rebase->state_path = git_buf_detach(&state_path); GIT_ERROR_CHECK_ALLOC(rebase->state_path); if (branch->ref_name && strcmp(branch->ref_name, "HEAD")) { rebase->orig_head_name = git__strdup(branch->ref_name); GIT_ERROR_CHECK_ALLOC(rebase->orig_head_name); } else { rebase->head_detached = 1; } rebase->onto_name = git__strdup(rebase_onto_name(onto)); GIT_ERROR_CHECK_ALLOC(rebase->onto_name); rebase->quiet = rebase->options.quiet; git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); if ((error = rebase_setupfiles(rebase)) < 0 || (error = git_buf_printf(&reflog, "rebase: checkout %s", rebase_onto_name(onto))) < 0 || (error = git_commit_lookup( &onto_commit, repo, git_annotated_commit_id(onto))) < 0 || (error = git_checkout_tree(repo, (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) goto done; done: git_reference_free(head_ref); git_commit_free(onto_commit); git_buf_dispose(&reflog); git_buf_dispose(&state_path); return error; } static int rebase_init_inmemory( git_rebase *rebase, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto) { GIT_UNUSED(branch); GIT_UNUSED(upstream); return git_commit_lookup( &rebase->last_commit, repo, git_annotated_commit_id(onto)); } int git_rebase_init( git_rebase **out, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto, const git_rebase_options *given_opts) { git_rebase *rebase = NULL; git_annotated_commit *head_branch = NULL; git_reference *head_ref = NULL; bool inmemory = (given_opts && given_opts->inmemory); int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(upstream || onto); *out = NULL; if (!onto) onto = upstream; if ((error = rebase_check_versions(given_opts)) < 0) goto done; if (!inmemory) { if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || (error = rebase_ensure_not_in_progress(repo)) < 0 || (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0) goto done; } if (!branch) { if ((error = git_repository_head(&head_ref, repo)) < 0 || (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) goto done; branch = head_branch; } if (rebase_alloc(&rebase, given_opts) < 0) return -1; rebase->repo = repo; rebase->inmemory = inmemory; rebase->type = GIT_REBASE_MERGE; if ((error = rebase_init_operations(rebase, repo, branch, upstream, onto)) < 0) goto done; if (inmemory) error = rebase_init_inmemory(rebase, repo, branch, upstream, onto); else error = rebase_init_merge(rebase, repo, branch ,upstream, onto); if (error == 0) *out = rebase; done: git_reference_free(head_ref); git_annotated_commit_free(head_branch); if (error < 0) { rebase_cleanup(rebase); git_rebase_free(rebase); } return error; } static void normalize_checkout_options_for_apply( git_checkout_options *checkout_opts, git_rebase *rebase, git_commit *current_commit) { memcpy(checkout_opts, &rebase->options.checkout_options, sizeof(git_checkout_options)); if (!checkout_opts->ancestor_label) checkout_opts->ancestor_label = "ancestor"; if (rebase->type == GIT_REBASE_MERGE) { if (!checkout_opts->our_label) checkout_opts->our_label = rebase->onto_name; if (!checkout_opts->their_label) checkout_opts->their_label = git_commit_summary(current_commit); } else { abort(); } } GIT_INLINE(int) rebase_movenext(git_rebase *rebase) { size_t next = rebase->started ? rebase->current + 1 : 0; if (next == git_array_size(rebase->operations)) return GIT_ITEROVER; rebase->started = 1; rebase->current = next; return 0; } static int rebase_next_merge( git_rebase_operation **out, git_rebase *rebase) { git_buf path = GIT_BUF_INIT; git_commit *current_commit = NULL, *parent_commit = NULL; git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; git_index *index = NULL; git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; git_rebase_operation *operation; git_checkout_options checkout_opts; char current_idstr[GIT_OID_HEXSZ]; unsigned int parent_count; int error; *out = NULL; operation = git_array_get(rebase->operations, rebase->current); if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || (error = git_commit_tree(¤t_tree, current_commit)) < 0 || (error = git_repository_head_tree(&head_tree, rebase->repo)) < 0) goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { git_error_set(GIT_ERROR_REBASE, "cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0) goto done; } git_oid_fmt(current_idstr, &operation->id); normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit); if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || (error = rebase_setupfile(rebase, MSGNUM_FILE, 0, "%" PRIuZ "\n", rebase->current+1)) < 0 || (error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || (error = git_indexwriter_commit(&indexwriter)) < 0) goto done; *out = operation; done: git_indexwriter_cleanup(&indexwriter); git_index_free(index); git_tree_free(current_tree); git_tree_free(head_tree); git_tree_free(parent_tree); git_commit_free(parent_commit); git_commit_free(current_commit); git_buf_dispose(&path); return error; } static int rebase_next_inmemory( git_rebase_operation **out, git_rebase *rebase) { git_commit *current_commit = NULL, *parent_commit = NULL; git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; git_rebase_operation *operation; git_index *index = NULL; unsigned int parent_count; int error; *out = NULL; operation = git_array_get(rebase->operations, rebase->current); if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || (error = git_commit_tree(¤t_tree, current_commit)) < 0) goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { git_error_set(GIT_ERROR_REBASE, "cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0) goto done; } if ((error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 || (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) goto done; if (!rebase->index) { rebase->index = index; index = NULL; } else { if ((error = git_index_read_index(rebase->index, index)) < 0) goto done; } *out = operation; done: git_commit_free(current_commit); git_commit_free(parent_commit); git_tree_free(current_tree); git_tree_free(head_tree); git_tree_free(parent_tree); git_index_free(index); return error; } int git_rebase_next( git_rebase_operation **out, git_rebase *rebase) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(rebase); if ((error = rebase_movenext(rebase)) < 0) return error; if (rebase->inmemory) error = rebase_next_inmemory(out, rebase); else if (rebase->type == GIT_REBASE_MERGE) error = rebase_next_merge(out, rebase); else abort(); return error; } int git_rebase_inmemory_index( git_index **out, git_rebase *rebase) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(rebase); GIT_ASSERT_ARG(rebase->index); GIT_REFCOUNT_INC(rebase->index); *out = rebase->index; return 0; } #ifndef GIT_DEPRECATE_HARD static int create_signed( git_oid *out, git_rebase *rebase, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, git_tree *tree, size_t parent_count, const git_commit **parents) { git_buf commit_content = GIT_BUF_INIT, commit_signature = GIT_BUF_INIT, signature_field = GIT_BUF_INIT; int error; git_error_clear(); if ((error = git_commit_create_buffer(&commit_content, rebase->repo, author, committer, message_encoding, message, tree, parent_count, parents)) < 0) goto done; error = rebase->options.signing_cb(&commit_signature, &signature_field, commit_content.ptr, rebase->options.payload); if (error) { if (error != GIT_PASSTHROUGH) git_error_set_after_callback_function(error, "signing_cb"); goto done; } error = git_commit_create_with_signature(out, rebase->repo, commit_content.ptr, commit_signature.size > 0 ? commit_signature.ptr : NULL, signature_field.size > 0 ? signature_field.ptr : NULL); done: git_buf_dispose(&commit_signature); git_buf_dispose(&signature_field); git_buf_dispose(&commit_content); return error; } #endif static int rebase_commit__create( git_commit **out, git_rebase *rebase, git_index *index, git_commit *parent_commit, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message) { git_rebase_operation *operation; git_commit *current_commit = NULL, *commit = NULL; git_tree *parent_tree = NULL, *tree = NULL; git_oid tree_id, commit_id; int error; operation = git_array_get(rebase->operations, rebase->current); if (git_index_has_conflicts(index)) { git_error_set(GIT_ERROR_REBASE, "conflicts have not been resolved"); error = GIT_EUNMERGED; goto done; } if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || (error = git_index_write_tree_to(&tree_id, index, rebase->repo)) < 0 || (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) goto done; if (git_oid_equal(&tree_id, git_tree_id(parent_tree))) { git_error_set(GIT_ERROR_REBASE, "this patch has already been applied"); error = GIT_EAPPLIED; goto done; } if (!author) author = git_commit_author(current_commit); if (!message) { message_encoding = git_commit_message_encoding(current_commit); message = git_commit_message(current_commit); } git_error_clear(); error = GIT_PASSTHROUGH; if (rebase->options.commit_create_cb) { error = rebase->options.commit_create_cb(&commit_id, author, committer, message_encoding, message, tree, 1, (const git_commit **)&parent_commit, rebase->options.payload); git_error_set_after_callback_function(error, "commit_create_cb"); } #ifndef GIT_DEPRECATE_HARD else if (rebase->options.signing_cb) { error = create_signed(&commit_id, rebase, author, committer, message_encoding, message, tree, 1, (const git_commit **)&parent_commit); } #endif if (error == GIT_PASSTHROUGH) error = git_commit_create(&commit_id, rebase->repo, NULL, author, committer, message_encoding, message, tree, 1, (const git_commit **)&parent_commit); if (error) goto done; if ((error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0) goto done; *out = commit; done: if (error < 0) git_commit_free(commit); git_commit_free(current_commit); git_tree_free(parent_tree); git_tree_free(tree); return error; } static int rebase_commit_merge( git_oid *commit_id, git_rebase *rebase, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message) { git_rebase_operation *operation; git_reference *head = NULL; git_commit *head_commit = NULL, *commit = NULL; git_index *index = NULL; char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; int error; operation = git_array_get(rebase->operations, rebase->current); GIT_ASSERT(operation); if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 || (error = git_repository_head(&head, rebase->repo)) < 0 || (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT)) < 0 || (error = git_repository_index(&index, rebase->repo)) < 0 || (error = rebase_commit__create(&commit, rebase, index, head_commit, author, committer, message_encoding, message)) < 0 || (error = git_reference__update_for_commit( rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0) goto done; git_oid_fmt(old_idstr, &operation->id); git_oid_fmt(new_idstr, git_commit_id(commit)); if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr)) < 0) goto done; git_oid_cpy(commit_id, git_commit_id(commit)); done: git_index_free(index); git_reference_free(head); git_commit_free(head_commit); git_commit_free(commit); return error; } static int rebase_commit_inmemory( git_oid *commit_id, git_rebase *rebase, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message) { git_commit *commit = NULL; int error = 0; GIT_ASSERT_ARG(rebase->index); GIT_ASSERT_ARG(rebase->last_commit); GIT_ASSERT_ARG(rebase->current < rebase->operations.size); if ((error = rebase_commit__create(&commit, rebase, rebase->index, rebase->last_commit, author, committer, message_encoding, message)) < 0) goto done; git_commit_free(rebase->last_commit); rebase->last_commit = commit; git_oid_cpy(commit_id, git_commit_id(commit)); done: if (error < 0) git_commit_free(commit); return error; } int git_rebase_commit( git_oid *id, git_rebase *rebase, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message) { int error; GIT_ASSERT_ARG(rebase); GIT_ASSERT_ARG(committer); if (rebase->inmemory) error = rebase_commit_inmemory( id, rebase, author, committer, message_encoding, message); else if (rebase->type == GIT_REBASE_MERGE) error = rebase_commit_merge( id, rebase, author, committer, message_encoding, message); else abort(); return error; } int git_rebase_abort(git_rebase *rebase) { git_reference *orig_head_ref = NULL; git_commit *orig_head_commit = NULL; int error; GIT_ASSERT_ARG(rebase); if (rebase->inmemory) return 0; error = rebase->head_detached ? git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE, &rebase->orig_head_id, 1, "rebase: aborting") : git_reference_symbolic_create( &orig_head_ref, rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, "rebase: aborting"); if (error < 0) goto done; if ((error = git_commit_lookup( &orig_head_commit, rebase->repo, &rebase->orig_head_id)) < 0 || (error = git_reset(rebase->repo, (git_object *)orig_head_commit, GIT_RESET_HARD, &rebase->options.checkout_options)) < 0) goto done; error = rebase_cleanup(rebase); done: git_commit_free(orig_head_commit); git_reference_free(orig_head_ref); return error; } static int notes_ref_lookup(git_buf *out, git_rebase *rebase) { git_config *config = NULL; int do_rewrite, error; if (rebase->options.rewrite_notes_ref) { git_buf_attach_notowned(out, rebase->options.rewrite_notes_ref, strlen(rebase->options.rewrite_notes_ref)); return 0; } if ((error = git_repository_config(&config, rebase->repo)) < 0 || (error = git_config_get_bool(&do_rewrite, config, "notes.rewrite.rebase")) < 0) { if (error != GIT_ENOTFOUND) goto done; git_error_clear(); do_rewrite = 1; } error = do_rewrite ? git_config_get_string_buf(out, config, "notes.rewriteref") : GIT_ENOTFOUND; done: git_config_free(config); return error; } static int rebase_copy_note( git_rebase *rebase, const char *notes_ref, git_oid *from, git_oid *to, const git_signature *committer) { git_note *note = NULL; git_oid note_id; git_signature *who = NULL; int error; if ((error = git_note_read(¬e, rebase->repo, notes_ref, from)) < 0) { if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } goto done; } if (!committer) { if((error = git_signature_default(&who, rebase->repo)) < 0) { if (error != GIT_ENOTFOUND || (error = git_signature_now(&who, "unknown", "unknown")) < 0) goto done; git_error_clear(); } committer = who; } error = git_note_create(¬e_id, rebase->repo, notes_ref, git_note_author(note), committer, to, git_note_message(note), 0); done: git_note_free(note); git_signature_free(who); return error; } static int rebase_copy_notes( git_rebase *rebase, const git_signature *committer) { git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT, notes_ref = GIT_BUF_INIT; char *pair_list, *fromstr, *tostr, *end; git_oid from, to; unsigned int linenum = 1; int error = 0; if ((error = notes_ref_lookup(¬es_ref, rebase)) < 0) { if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } goto done; } if ((error = git_buf_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 || (error = git_futils_readbuffer(&rewritten, path.ptr)) < 0) goto done; pair_list = rewritten.ptr; while (*pair_list) { fromstr = pair_list; if ((end = strchr(fromstr, '\n')) == NULL) goto on_error; pair_list = end+1; *end = '\0'; if ((end = strchr(fromstr, ' ')) == NULL) goto on_error; tostr = end+1; *end = '\0'; if (strlen(fromstr) != GIT_OID_HEXSZ || strlen(tostr) != GIT_OID_HEXSZ || git_oid_fromstr(&from, fromstr) < 0 || git_oid_fromstr(&to, tostr) < 0) goto on_error; if ((error = rebase_copy_note(rebase, notes_ref.ptr, &from, &to, committer)) < 0) goto done; linenum++; } goto done; on_error: git_error_set(GIT_ERROR_REBASE, "invalid rewritten file at line %d", linenum); error = -1; done: git_buf_dispose(&rewritten); git_buf_dispose(&path); git_buf_dispose(¬es_ref); return error; } static int return_to_orig_head(git_rebase *rebase) { git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL; git_commit *terminal_commit = NULL; git_buf branch_msg = GIT_BUF_INIT, head_msg = GIT_BUF_INIT; char onto[GIT_OID_HEXSZ]; int error = 0; git_oid_fmt(onto, &rebase->onto_id); if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s", rebase->orig_head_name, GIT_OID_HEXSZ, onto)) == 0 && (error = git_buf_printf(&head_msg, "rebase finished: returning to %s", rebase->orig_head_name)) == 0 && (error = git_repository_head(&terminal_ref, rebase->repo)) == 0 && (error = git_reference_peel((git_object **)&terminal_commit, terminal_ref, GIT_OBJECT_COMMIT)) == 0 && (error = git_reference_create_matching(&branch_ref, rebase->repo, rebase->orig_head_name, git_commit_id(terminal_commit), 1, &rebase->orig_head_id, branch_msg.ptr)) == 0) error = git_reference_symbolic_create(&head_ref, rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, head_msg.ptr); git_buf_dispose(&head_msg); git_buf_dispose(&branch_msg); git_commit_free(terminal_commit); git_reference_free(head_ref); git_reference_free(branch_ref); git_reference_free(terminal_ref); return error; } int git_rebase_finish( git_rebase *rebase, const git_signature *signature) { int error = 0; GIT_ASSERT_ARG(rebase); if (rebase->inmemory) return 0; if (!rebase->head_detached) error = return_to_orig_head(rebase); if (error == 0 && (error = rebase_copy_notes(rebase, signature)) == 0) error = rebase_cleanup(rebase); return error; } const char *git_rebase_orig_head_name(git_rebase *rebase) { GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); return rebase->orig_head_name; } const git_oid *git_rebase_orig_head_id(git_rebase *rebase) { GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); return &rebase->orig_head_id; } const char *git_rebase_onto_name(git_rebase *rebase) { GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); return rebase->onto_name; } const git_oid *git_rebase_onto_id(git_rebase *rebase) { return &rebase->onto_id; } size_t git_rebase_operation_entrycount(git_rebase *rebase) { GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0); return git_array_size(rebase->operations); } size_t git_rebase_operation_current(git_rebase *rebase) { GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0); return rebase->started ? rebase->current : GIT_REBASE_NO_OPERATION; } git_rebase_operation *git_rebase_operation_byindex(git_rebase *rebase, size_t idx) { GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); return git_array_get(rebase->operations, idx); } void git_rebase_free(git_rebase *rebase) { if (rebase == NULL) return; git_index_free(rebase->index); git_commit_free(rebase->last_commit); git__free(rebase->onto_name); git__free(rebase->orig_head_name); git__free(rebase->state_path); git_array_clear(rebase->operations); git__free((char *)rebase->options.rewrite_notes_ref); git__free(rebase); } git2r/src/libgit2/src/fetch.h0000644000175000017500000000110114125111754015601 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_fetch_h__ #define INCLUDE_fetch_h__ #include "common.h" #include "git2/remote.h" #include "netops.h" int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts); int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks); int git_fetch_setup_walk(git_revwalk **out, git_repository *repo); #endif git2r/src/libgit2/src/runtime.c0000644000175000017500000000621414125111754016200 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "runtime.h" static git_runtime_shutdown_fn shutdown_callback[32]; static git_atomic32 shutdown_callback_count; static git_atomic32 init_count; static int init_common(git_runtime_init_fn init_fns[], size_t cnt) { size_t i; int ret; /* Initialize subsystems that have global state */ for (i = 0; i < cnt; i++) { if ((ret = init_fns[i]()) != 0) break; } GIT_MEMORY_BARRIER; return ret; } static void shutdown_common(void) { git_runtime_shutdown_fn cb; int pos; for (pos = git_atomic32_get(&shutdown_callback_count); pos > 0; pos = git_atomic32_dec(&shutdown_callback_count)) { cb = git_atomic_swap(shutdown_callback[pos - 1], NULL); if (cb != NULL) cb(); } } int git_runtime_shutdown_register(git_runtime_shutdown_fn callback) { int count = git_atomic32_inc(&shutdown_callback_count); if (count > (int)ARRAY_SIZE(shutdown_callback) || count == 0) { git_error_set(GIT_ERROR_INVALID, "too many shutdown callbacks registered"); git_atomic32_dec(&shutdown_callback_count); return -1; } shutdown_callback[count - 1] = callback; return 0; } #if defined(GIT_THREADS) && defined(GIT_WIN32) /* * On Win32, we use a spinlock to provide locking semantics. This is * lighter-weight than a proper critical section. */ static volatile LONG init_spinlock = 0; GIT_INLINE(int) init_lock(void) { while (InterlockedCompareExchange(&init_spinlock, 1, 0)) { Sleep(0); } return 0; } GIT_INLINE(int) init_unlock(void) { InterlockedExchange(&init_spinlock, 0); return 0; } #elif defined(GIT_THREADS) && defined(_POSIX_THREADS) /* * On POSIX, we need to use a proper mutex for locking. We might prefer * a spinlock here, too, but there's no static initializer for a * pthread_spinlock_t. */ static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; GIT_INLINE(int) init_lock(void) { return pthread_mutex_lock(&init_mutex) == 0 ? 0 : -1; } GIT_INLINE(int) init_unlock(void) { return pthread_mutex_unlock(&init_mutex) == 0 ? 0 : -1; } #elif defined(GIT_THREADS) # error unknown threading model #else # define init_lock() git__noop() # define init_unlock() git__noop() #endif int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt) { int ret; if (init_lock() < 0) return -1; /* Only do work on a 0 -> 1 transition of the refcount */ if ((ret = git_atomic32_inc(&init_count)) == 1) { if (init_common(init_fns, cnt) < 0) ret = -1; } if (init_unlock() < 0) return -1; return ret; } int git_runtime_init_count(void) { int ret; if (init_lock() < 0) return -1; ret = git_atomic32_get(&init_count); if (init_unlock() < 0) return -1; return ret; } int git_runtime_shutdown(void) { int ret; /* Enter the lock */ if (init_lock() < 0) return -1; /* Only do work on a 1 -> 0 transition of the refcount */ if ((ret = git_atomic32_dec(&init_count)) == 0) shutdown_common(); /* Exit the lock */ if (init_unlock() < 0) return -1; return ret; } git2r/src/libgit2/src/checkout.c0000644000175000017500000022340214125111754016322 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "checkout.h" #include "git2/repository.h" #include "git2/refs.h" #include "git2/tree.h" #include "git2/blob.h" #include "git2/config.h" #include "git2/diff.h" #include "git2/submodule.h" #include "git2/sys/index.h" #include "git2/sys/filter.h" #include "git2/merge.h" #include "refs.h" #include "repository.h" #include "index.h" #include "filter.h" #include "blob.h" #include "diff.h" #include "diff_generate.h" #include "pathspec.h" #include "diff_xdiff.h" #include "path.h" #include "attr.h" #include "pool.h" #include "strmap.h" /* See docs/checkout-internals.md for more information */ enum { CHECKOUT_ACTION__NONE = 0, CHECKOUT_ACTION__REMOVE = 1, CHECKOUT_ACTION__UPDATE_BLOB = 2, CHECKOUT_ACTION__UPDATE_SUBMODULE = 4, CHECKOUT_ACTION__CONFLICT = 8, CHECKOUT_ACTION__REMOVE_CONFLICT = 16, CHECKOUT_ACTION__UPDATE_CONFLICT = 32, CHECKOUT_ACTION__MAX = 32, CHECKOUT_ACTION__REMOVE_AND_UPDATE = (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE), }; typedef struct { git_repository *repo; git_iterator *target; git_diff *diff; git_checkout_options opts; bool opts_free_baseline; char *pfx; git_index *index; git_pool pool; git_vector removes; git_vector remove_conflicts; git_vector update_conflicts; git_vector *update_reuc; git_vector *update_names; git_buf target_path; size_t target_len; git_buf tmp; unsigned int strategy; int can_symlink; int respect_filemode; bool reload_submodules; size_t total_steps; size_t completed_steps; git_checkout_perfdata perfdata; git_strmap *mkdir_map; git_attr_session attr_session; } checkout_data; typedef struct { const git_index_entry *ancestor; const git_index_entry *ours; const git_index_entry *theirs; int name_collision:1, directoryfile:1, one_to_two:1, binary:1, submodule:1; } checkout_conflictdata; static int checkout_notify( checkout_data *data, git_checkout_notify_t why, const git_diff_delta *delta, const git_index_entry *wditem) { git_diff_file wdfile; const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL; const char *path = NULL; if (!data->opts.notify_cb || (why & data->opts.notify_flags) == 0) return 0; if (wditem) { memset(&wdfile, 0, sizeof(wdfile)); git_oid_cpy(&wdfile.id, &wditem->id); wdfile.path = wditem->path; wdfile.size = wditem->file_size; wdfile.flags = GIT_DIFF_FLAG_VALID_ID; wdfile.mode = wditem->mode; workdir = &wdfile; path = wditem->path; } if (delta) { switch (delta->status) { case GIT_DELTA_UNMODIFIED: case GIT_DELTA_MODIFIED: case GIT_DELTA_TYPECHANGE: default: baseline = &delta->old_file; target = &delta->new_file; break; case GIT_DELTA_ADDED: case GIT_DELTA_IGNORED: case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNREADABLE: target = &delta->new_file; break; case GIT_DELTA_DELETED: baseline = &delta->old_file; break; } path = delta->old_file.path; } { int error = data->opts.notify_cb( why, path, baseline, target, workdir, data->opts.notify_payload); return git_error_set_after_callback_function( error, "git_checkout notification"); } } GIT_INLINE(bool) is_workdir_base_or_new( const git_oid *workdir_id, const git_diff_file *baseitem, const git_diff_file *newitem) { return (git_oid__cmp(&baseitem->id, workdir_id) == 0 || git_oid__cmp(&newitem->id, workdir_id) == 0); } GIT_INLINE(bool) is_filemode_changed(git_filemode_t a, git_filemode_t b, int respect_filemode) { /* If core.filemode = false, ignore links in the repository and executable bit changes */ if (!respect_filemode) { if (a == S_IFLNK) a = GIT_FILEMODE_BLOB; if (b == S_IFLNK) b = GIT_FILEMODE_BLOB; a &= ~0111; b &= ~0111; } return (a != b); } static bool checkout_is_workdir_modified( checkout_data *data, const git_diff_file *baseitem, const git_diff_file *newitem, const git_index_entry *wditem) { git_oid oid; const git_index_entry *ie; /* handle "modified" submodule */ if (wditem->mode == GIT_FILEMODE_COMMIT) { git_submodule *sm; unsigned int sm_status = 0; const git_oid *sm_oid = NULL; bool rval = false; if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) { git_error_clear(); return true; } if (git_submodule_status(&sm_status, data->repo, wditem->path, GIT_SUBMODULE_IGNORE_UNSPECIFIED) < 0 || GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) rval = true; else if ((sm_oid = git_submodule_wd_id(sm)) == NULL) rval = false; else rval = (git_oid__cmp(&baseitem->id, sm_oid) != 0); git_submodule_free(sm); return rval; } /* * Look at the cache to decide if the workdir is modified: if the * cache contents match the workdir contents, then we do not need * to examine the working directory directly, instead we can * examine the cache to see if _it_ has been modified. This allows * us to avoid touching the disk. */ ie = git_index_get_bypath(data->index, wditem->path, 0); if (ie != NULL && !git_index_entry_newer_than_index(ie, data->index) && git_index_time_eq(&wditem->mtime, &ie->mtime) && wditem->file_size == ie->file_size && !is_filemode_changed(wditem->mode, ie->mode, data->respect_filemode)) { /* The workdir is modified iff the index entry is modified */ return !is_workdir_base_or_new(&ie->id, baseitem, newitem) || is_filemode_changed(baseitem->mode, ie->mode, data->respect_filemode); } /* depending on where base is coming from, we may or may not know * the actual size of the data, so we can't rely on this shortcut. */ if (baseitem->size && wditem->file_size != baseitem->size) return true; /* if the workdir item is a directory, it cannot be a modified file */ if (S_ISDIR(wditem->mode)) return false; if (is_filemode_changed(baseitem->mode, wditem->mode, data->respect_filemode)) return true; if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0) return false; /* Allow the checkout if the workdir is not modified *or* if the checkout * target's contents are already in the working directory. */ return !is_workdir_base_or_new(&oid, baseitem, newitem); } #define CHECKOUT_ACTION_IF(FLAG,YES,NO) \ ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO) static int checkout_action_common( int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) *action = (*action & ~CHECKOUT_ACTION__REMOVE); if ((*action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) { if (S_ISGITLINK(delta->new_file.mode)) *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB) | CHECKOUT_ACTION__UPDATE_SUBMODULE; /* to "update" a symlink, we must remove the old one first */ if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL) *action |= CHECKOUT_ACTION__REMOVE; /* if the file is on disk and doesn't match our mode, force update */ if (wd && GIT_PERMS_IS_EXEC(wd->mode) != GIT_PERMS_IS_EXEC(delta->new_file.mode)) *action |= CHECKOUT_ACTION__REMOVE; notify = GIT_CHECKOUT_NOTIFY_UPDATED; } if ((*action & CHECKOUT_ACTION__CONFLICT) != 0) notify = GIT_CHECKOUT_NOTIFY_CONFLICT; return checkout_notify(data, notify, delta, wd); } static int checkout_action_no_wd( int *action, checkout_data *data, const git_diff_delta *delta) { int error = 0; *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 12 */ error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL); if (error) return error; *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, NONE); break; case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */ *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/ if (delta->new_file.mode == GIT_FILEMODE_TREE) *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_DELETED: /* case 8 or 25 */ *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); break; default: /* impossible */ break; } return checkout_action_common(action, data, delta, NULL); } static int checkout_target_fullpath( git_buf **out, checkout_data *data, const char *path) { git_buf_truncate(&data->target_path, data->target_len); if (path && git_buf_puts(&data->target_path, path) < 0) return -1; if (git_path_validate_workdir_buf(data->repo, &data->target_path) < 0) return -1; *out = &data->target_path; return 0; } static bool wd_item_is_removable( checkout_data *data, const git_index_entry *wd) { git_buf *full; if (wd->mode != GIT_FILEMODE_TREE) return true; if (checkout_target_fullpath(&full, data, wd->path) < 0) return false; return !full || !git_path_contains(full, DOT_GIT); } static int checkout_queue_remove(checkout_data *data, const char *path) { char *copy = git_pool_strdup(&data->pool, path); GIT_ERROR_CHECK_ALLOC(copy); return git_vector_insert(&data->removes, copy); } /* note that this advances the iterator over the wd item */ static int checkout_action_wd_only( checkout_data *data, git_iterator *workdir, const git_index_entry **wditem, git_vector *pathspec) { int error = 0; bool remove = false; git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; const git_index_entry *wd = *wditem; if (!git_pathspec__match( pathspec, wd->path, (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0, git_iterator_ignore_case(workdir), NULL, NULL)) { if (wd->mode == GIT_FILEMODE_TREE) return git_iterator_advance_into(wditem, workdir); else return git_iterator_advance(wditem, workdir); } /* check if item is tracked in the index but not in the checkout diff */ if (data->index != NULL) { size_t pos; error = git_index__find_pos( &pos, data->index, wd->path, 0, GIT_INDEX_STAGE_ANY); if (wd->mode != GIT_FILEMODE_TREE) { if (!error) { /* found by git_index__find_pos call */ notify = GIT_CHECKOUT_NOTIFY_DIRTY; remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); } else if (error != GIT_ENOTFOUND) return error; else error = 0; /* git_index__find_pos does not set error msg */ } else { /* for tree entries, we have to see if there are any index * entries that are contained inside that tree */ const git_index_entry *e = git_index_get_byindex(data->index, pos); if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) return git_iterator_advance_into(wditem, workdir); } } if (notify != GIT_CHECKOUT_NOTIFY_NONE) { /* if we found something in the index, notify and advance */ if ((error = checkout_notify(data, notify, NULL, wd)) != 0) return error; if (remove && wd_item_is_removable(data, wd)) error = checkout_queue_remove(data, wd->path); if (!error) error = git_iterator_advance(wditem, workdir); } else { /* untracked or ignored - can't know which until we advance through */ bool over = false, removable = wd_item_is_removable(data, wd); git_iterator_status_t untracked_state; /* copy the entry for issuing notification callback later */ git_index_entry saved_wd = *wd; git_buf_sets(&data->tmp, wd->path); saved_wd.path = data->tmp.ptr; error = git_iterator_advance_over( wditem, &untracked_state, workdir); if (error == GIT_ITEROVER) over = true; else if (error < 0) return error; if (untracked_state == GIT_ITERATOR_STATUS_IGNORED) { notify = GIT_CHECKOUT_NOTIFY_IGNORED; remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0); } else { notify = GIT_CHECKOUT_NOTIFY_UNTRACKED; remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0); } if ((error = checkout_notify(data, notify, NULL, &saved_wd)) != 0) return error; if (remove && removable) error = checkout_queue_remove(data, saved_wd.path); if (!error && over) /* restore ITEROVER if needed */ error = GIT_ITEROVER; } return error; } static bool submodule_is_config_only( checkout_data *data, const char *path) { git_submodule *sm = NULL; unsigned int sm_loc = 0; bool rval = false; if (git_submodule_lookup(&sm, data->repo, path) < 0) return true; if (git_submodule_location(&sm_loc, sm) < 0 || sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG) rval = true; git_submodule_free(sm); return rval; } static bool checkout_is_empty_dir(checkout_data *data, const char *path) { git_buf *fullpath; if (checkout_target_fullpath(&fullpath, data, path) < 0) return false; return git_path_is_empty_dir(fullpath->ptr); } static int checkout_action_with_wd( int *action, checkout_data *data, const git_diff_delta *delta, git_iterator *workdir, const git_index_entry *wd) { *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) { GIT_ERROR_CHECK_ERROR( checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); } break; case GIT_DELTA_ADDED: /* case 3, 4 or 6 */ if (git_iterator_current_is_ignored(workdir)) *action = CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, UPDATE_BLOB); else *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); else *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); break; case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */ if (wd->mode != GIT_FILEMODE_COMMIT && checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); else *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { if (wd->mode == GIT_FILEMODE_TREE) /* either deleting items in old tree will delete the wd dir, * or we'll get a conflict when we attempt blob update... */ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else if (wd->mode == GIT_FILEMODE_COMMIT) { /* workdir is possibly a "phantom" submodule - treat as a * tree if the only submodule info came from the config */ if (submodule_is_config_only(data, wd->path)) *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); } else *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); } else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); else *action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); /* don't update if the typechange is to a tree */ if (delta->new_file.mode == GIT_FILEMODE_TREE) *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB); break; default: /* impossible */ break; } return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_blocker( int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* should show delta as dirty / deleted */ GIT_ERROR_CHECK_ERROR( checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); break; case GIT_DELTA_ADDED: case GIT_DELTA_MODIFIED: *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* not 100% certain about this... */ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_dir( int *action, checkout_data *data, const git_diff_delta *delta, git_iterator *workdir, const git_index_entry *wd) { *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */ GIT_ERROR_CHECK_ERROR( checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)); GIT_ERROR_CHECK_ERROR( checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); break; case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */ case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */ if (delta->old_file.mode == GIT_FILEMODE_COMMIT) /* expected submodule (and maybe found one) */; else if (delta->new_file.mode != GIT_FILEMODE_TREE) *action = git_iterator_current_is_ignored(workdir) ? CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, REMOVE_AND_UPDATE) : CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */ if (delta->old_file.mode != GIT_FILEMODE_TREE) GIT_ERROR_CHECK_ERROR( checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); break; case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { /* For typechange from dir, remove dir and add blob, but it is * not safe to remove dir if it contains modified files. * However, safely removing child files will remove the parent * directory if is it left empty, so we can defer removing the * dir and it will succeed if no children are left. */ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); } else if (delta->new_file.mode != GIT_FILEMODE_TREE) /* For typechange to dir, dir is already created so no action */ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_dir_empty( int *action, checkout_data *data, const git_diff_delta *delta) { int error = checkout_action_no_wd(action, data, delta); /* We can always safely remove an empty directory. */ if (error == 0 && *action != CHECKOUT_ACTION__NONE) *action |= CHECKOUT_ACTION__REMOVE; return error; } static int checkout_action( int *action, checkout_data *data, git_diff_delta *delta, git_iterator *workdir, const git_index_entry **wditem, git_vector *pathspec) { int cmp = -1, error; int (*strcomp)(const char *, const char *) = data->diff->strcomp; int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp; int (*advance)(const git_index_entry **, git_iterator *) = NULL; /* move workdir iterator to follow along with deltas */ while (1) { const git_index_entry *wd = *wditem; if (!wd) return checkout_action_no_wd(action, data, delta); cmp = strcomp(wd->path, delta->old_file.path); /* 1. wd before delta ("a/a" before "a/b") * 2. wd prefixes delta & should expand ("a/" before "a/b") * 3. wd prefixes delta & cannot expand ("a/b" before "a/b/c") * 4. wd equals delta ("a/b" and "a/b") * 5. wd after delta & delta prefixes wd ("a/b/c" after "a/b/" or "a/b") * 6. wd after delta ("a/c" after "a/b") */ if (cmp < 0) { cmp = pfxcomp(delta->old_file.path, wd->path); if (cmp == 0) { if (wd->mode == GIT_FILEMODE_TREE) { /* case 2 - entry prefixed by workdir tree */ error = git_iterator_advance_into(wditem, workdir); if (error < 0 && error != GIT_ITEROVER) goto done; continue; } /* case 3 maybe - wd contains non-dir where dir expected */ if (delta->old_file.path[strlen(wd->path)] == '/') { error = checkout_action_with_wd_blocker( action, data, delta, wd); advance = git_iterator_advance; goto done; } } /* case 1 - handle wd item (if it matches pathspec) */ error = checkout_action_wd_only(data, workdir, wditem, pathspec); if (error && error != GIT_ITEROVER) goto done; continue; } if (cmp == 0) { /* case 4 */ error = checkout_action_with_wd(action, data, delta, workdir, wd); advance = git_iterator_advance; goto done; } cmp = pfxcomp(wd->path, delta->old_file.path); if (cmp == 0) { /* case 5 */ if (wd->path[strlen(delta->old_file.path)] != '/') return checkout_action_no_wd(action, data, delta); if (delta->status == GIT_DELTA_TYPECHANGE) { if (delta->old_file.mode == GIT_FILEMODE_TREE) { error = checkout_action_with_wd(action, data, delta, workdir, wd); advance = git_iterator_advance_into; goto done; } if (delta->new_file.mode == GIT_FILEMODE_TREE || delta->new_file.mode == GIT_FILEMODE_COMMIT || delta->old_file.mode == GIT_FILEMODE_COMMIT) { error = checkout_action_with_wd(action, data, delta, workdir, wd); advance = git_iterator_advance; goto done; } } return checkout_is_empty_dir(data, wd->path) ? checkout_action_with_wd_dir_empty(action, data, delta) : checkout_action_with_wd_dir(action, data, delta, workdir, wd); } /* case 6 - wd is after delta */ return checkout_action_no_wd(action, data, delta); } done: if (!error && advance != NULL && (error = advance(wditem, workdir)) < 0) { *wditem = NULL; if (error == GIT_ITEROVER) error = 0; } return error; } static int checkout_remaining_wd_items( checkout_data *data, git_iterator *workdir, const git_index_entry *wd, git_vector *spec) { int error = 0; while (wd && !error) error = checkout_action_wd_only(data, workdir, &wd, spec); if (error == GIT_ITEROVER) error = 0; return error; } GIT_INLINE(int) checkout_idxentry_cmp( const git_index_entry *a, const git_index_entry *b) { if (!a && !b) return 0; else if (!a && b) return -1; else if(a && !b) return 1; else return strcmp(a->path, b->path); } static int checkout_conflictdata_cmp(const void *a, const void *b) { const checkout_conflictdata *ca = a; const checkout_conflictdata *cb = b; int diff; if ((diff = checkout_idxentry_cmp(ca->ancestor, cb->ancestor)) == 0 && (diff = checkout_idxentry_cmp(ca->ours, cb->theirs)) == 0) diff = checkout_idxentry_cmp(ca->theirs, cb->theirs); return diff; } static int checkout_conflictdata_empty( const git_vector *conflicts, size_t idx, void *payload) { checkout_conflictdata *conflict; GIT_UNUSED(payload); if ((conflict = git_vector_get(conflicts, idx)) == NULL) return -1; if (conflict->ancestor || conflict->ours || conflict->theirs) return 0; git__free(conflict); return 1; } GIT_INLINE(bool) conflict_pathspec_match( checkout_data *data, git_iterator *workdir, git_vector *pathspec, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs) { /* if the pathspec matches ours *or* theirs, proceed */ if (ours && git_pathspec__match(pathspec, ours->path, (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0, git_iterator_ignore_case(workdir), NULL, NULL)) return true; if (theirs && git_pathspec__match(pathspec, theirs->path, (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0, git_iterator_ignore_case(workdir), NULL, NULL)) return true; if (ancestor && git_pathspec__match(pathspec, ancestor->path, (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0, git_iterator_ignore_case(workdir), NULL, NULL)) return true; return false; } GIT_INLINE(int) checkout_conflict_detect_submodule(checkout_conflictdata *conflict) { conflict->submodule = ((conflict->ancestor && S_ISGITLINK(conflict->ancestor->mode)) || (conflict->ours && S_ISGITLINK(conflict->ours->mode)) || (conflict->theirs && S_ISGITLINK(conflict->theirs->mode))); return 0; } GIT_INLINE(int) checkout_conflict_detect_binary(git_repository *repo, checkout_conflictdata *conflict) { git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL; int error = 0; if (conflict->submodule) return 0; if (conflict->ancestor) { if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor->id)) < 0) goto done; conflict->binary = git_blob_is_binary(ancestor_blob); } if (!conflict->binary && conflict->ours) { if ((error = git_blob_lookup(&our_blob, repo, &conflict->ours->id)) < 0) goto done; conflict->binary = git_blob_is_binary(our_blob); } if (!conflict->binary && conflict->theirs) { if ((error = git_blob_lookup(&their_blob, repo, &conflict->theirs->id)) < 0) goto done; conflict->binary = git_blob_is_binary(their_blob); } done: git_blob_free(ancestor_blob); git_blob_free(our_blob); git_blob_free(their_blob); return error; } static int checkout_conflict_append_update( const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, void *payload) { checkout_data *data = payload; checkout_conflictdata *conflict; int error; conflict = git__calloc(1, sizeof(checkout_conflictdata)); GIT_ERROR_CHECK_ALLOC(conflict); conflict->ancestor = ancestor; conflict->ours = ours; conflict->theirs = theirs; if ((error = checkout_conflict_detect_submodule(conflict)) < 0 || (error = checkout_conflict_detect_binary(data->repo, conflict)) < 0) { git__free(conflict); return error; } if (git_vector_insert(&data->update_conflicts, conflict)) return -1; return 0; } static int checkout_conflicts_foreach( checkout_data *data, git_index *index, git_iterator *workdir, git_vector *pathspec, int (*cb)(const git_index_entry *, const git_index_entry *, const git_index_entry *, void *), void *payload) { git_index_conflict_iterator *iterator = NULL; const git_index_entry *ancestor, *ours, *theirs; int error = 0; if ((error = git_index_conflict_iterator_new(&iterator, index)) < 0) goto done; /* Collect the conflicts */ while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) { if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs)) continue; if ((error = cb(ancestor, ours, theirs, payload)) < 0) goto done; } if (error == GIT_ITEROVER) error = 0; done: git_index_conflict_iterator_free(iterator); return error; } static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec) { git_index *index; /* Only write conficts from sources that have them: indexes. */ if ((index = git_iterator_index(data->target)) == NULL) return 0; data->update_conflicts._cmp = checkout_conflictdata_cmp; if (checkout_conflicts_foreach(data, index, workdir, pathspec, checkout_conflict_append_update, data) < 0) return -1; /* Collect the REUC and NAME entries */ data->update_reuc = &index->reuc; data->update_names = &index->names; return 0; } GIT_INLINE(int) checkout_conflicts_cmp_entry( const char *path, const git_index_entry *entry) { return strcmp((const char *)path, entry->path); } static int checkout_conflicts_cmp_ancestor(const void *p, const void *c) { const char *path = p; const checkout_conflictdata *conflict = c; if (!conflict->ancestor) return 1; return checkout_conflicts_cmp_entry(path, conflict->ancestor); } static checkout_conflictdata *checkout_conflicts_search_ancestor( checkout_data *data, const char *path) { size_t pos; if (git_vector_bsearch2(&pos, &data->update_conflicts, checkout_conflicts_cmp_ancestor, path) < 0) return NULL; return git_vector_get(&data->update_conflicts, pos); } static checkout_conflictdata *checkout_conflicts_search_branch( checkout_data *data, const char *path) { checkout_conflictdata *conflict; size_t i; git_vector_foreach(&data->update_conflicts, i, conflict) { int cmp = -1; if (conflict->ancestor) break; if (conflict->ours) cmp = checkout_conflicts_cmp_entry(path, conflict->ours); else if (conflict->theirs) cmp = checkout_conflicts_cmp_entry(path, conflict->theirs); if (cmp == 0) return conflict; } return NULL; } static int checkout_conflicts_load_byname_entry( checkout_conflictdata **ancestor_out, checkout_conflictdata **ours_out, checkout_conflictdata **theirs_out, checkout_data *data, const git_index_name_entry *name_entry) { checkout_conflictdata *ancestor, *ours = NULL, *theirs = NULL; int error = 0; *ancestor_out = NULL; *ours_out = NULL; *theirs_out = NULL; if (!name_entry->ancestor) { git_error_set(GIT_ERROR_INDEX, "a NAME entry exists without an ancestor"); error = -1; goto done; } if (!name_entry->ours && !name_entry->theirs) { git_error_set(GIT_ERROR_INDEX, "a NAME entry exists without an ours or theirs"); error = -1; goto done; } if ((ancestor = checkout_conflicts_search_ancestor(data, name_entry->ancestor)) == NULL) { git_error_set(GIT_ERROR_INDEX, "a NAME entry referenced ancestor entry '%s' which does not exist in the main index", name_entry->ancestor); error = -1; goto done; } if (name_entry->ours) { if (strcmp(name_entry->ancestor, name_entry->ours) == 0) ours = ancestor; else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL || ours->ours == NULL) { git_error_set(GIT_ERROR_INDEX, "a NAME entry referenced our entry '%s' which does not exist in the main index", name_entry->ours); error = -1; goto done; } } if (name_entry->theirs) { if (strcmp(name_entry->ancestor, name_entry->theirs) == 0) theirs = ancestor; else if (name_entry->ours && strcmp(name_entry->ours, name_entry->theirs) == 0) theirs = ours; else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL || theirs->theirs == NULL) { git_error_set(GIT_ERROR_INDEX, "a NAME entry referenced their entry '%s' which does not exist in the main index", name_entry->theirs); error = -1; goto done; } } *ancestor_out = ancestor; *ours_out = ours; *theirs_out = theirs; done: return error; } static int checkout_conflicts_coalesce_renames( checkout_data *data) { git_index *index; const git_index_name_entry *name_entry; checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict; size_t i, names; int error = 0; if ((index = git_iterator_index(data->target)) == NULL) return 0; /* Juggle entries based on renames */ names = git_index_name_entrycount(index); for (i = 0; i < names; i++) { name_entry = git_index_name_get_byindex(index, i); if ((error = checkout_conflicts_load_byname_entry( &ancestor_conflict, &our_conflict, &their_conflict, data, name_entry)) < 0) goto done; if (our_conflict && our_conflict != ancestor_conflict) { ancestor_conflict->ours = our_conflict->ours; our_conflict->ours = NULL; if (our_conflict->theirs) our_conflict->name_collision = 1; if (our_conflict->name_collision) ancestor_conflict->name_collision = 1; } if (their_conflict && their_conflict != ancestor_conflict) { ancestor_conflict->theirs = their_conflict->theirs; their_conflict->theirs = NULL; if (their_conflict->ours) their_conflict->name_collision = 1; if (their_conflict->name_collision) ancestor_conflict->name_collision = 1; } if (our_conflict && our_conflict != ancestor_conflict && their_conflict && their_conflict != ancestor_conflict) ancestor_conflict->one_to_two = 1; } git_vector_remove_matching( &data->update_conflicts, checkout_conflictdata_empty, NULL); done: return error; } static int checkout_conflicts_mark_directoryfile( checkout_data *data) { git_index *index; checkout_conflictdata *conflict; const git_index_entry *entry; size_t i, j, len; const char *path; int prefixed, error = 0; if ((index = git_iterator_index(data->target)) == NULL) return 0; len = git_index_entrycount(index); /* Find d/f conflicts */ git_vector_foreach(&data->update_conflicts, i, conflict) { if ((conflict->ours && conflict->theirs) || (!conflict->ours && !conflict->theirs)) continue; path = conflict->ours ? conflict->ours->path : conflict->theirs->path; if ((error = git_index_find(&j, index, path)) < 0) { if (error == GIT_ENOTFOUND) git_error_set(GIT_ERROR_INDEX, "index inconsistency, could not find entry for expected conflict '%s'", path); goto done; } for (; j < len; j++) { if ((entry = git_index_get_byindex(index, j)) == NULL) { git_error_set(GIT_ERROR_INDEX, "index inconsistency, truncated index while loading expected conflict '%s'", path); error = -1; goto done; } prefixed = git_path_equal_or_prefixed(path, entry->path, NULL); if (prefixed == GIT_PATH_EQUAL) continue; if (prefixed == GIT_PATH_PREFIX) conflict->directoryfile = 1; break; } } done: return error; } static int checkout_get_update_conflicts( checkout_data *data, git_iterator *workdir, git_vector *pathspec) { int error = 0; if (data->strategy & GIT_CHECKOUT_SKIP_UNMERGED) return 0; if ((error = checkout_conflicts_load(data, workdir, pathspec)) < 0 || (error = checkout_conflicts_coalesce_renames(data)) < 0 || (error = checkout_conflicts_mark_directoryfile(data)) < 0) goto done; done: return error; } static int checkout_conflict_append_remove( const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, void *payload) { checkout_data *data = payload; const char *name; GIT_ASSERT_ARG(ancestor || ours || theirs); if (ancestor) name = git__strdup(ancestor->path); else if (ours) name = git__strdup(ours->path); else if (theirs) name = git__strdup(theirs->path); else abort(); GIT_ERROR_CHECK_ALLOC(name); return git_vector_insert(&data->remove_conflicts, (char *)name); } static int checkout_get_remove_conflicts( checkout_data *data, git_iterator *workdir, git_vector *pathspec) { if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0) return 0; return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data); } static int checkout_verify_paths( git_repository *repo, int action, git_diff_delta *delta) { unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS; if (action & CHECKOUT_ACTION__REMOVE) { if (!git_path_validate(repo, delta->old_file.path, delta->old_file.mode, flags)) { git_error_set(GIT_ERROR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path); return -1; } } if (action & ~CHECKOUT_ACTION__REMOVE) { if (!git_path_validate(repo, delta->new_file.path, delta->new_file.mode, flags)) { git_error_set(GIT_ERROR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path); return -1; } } return 0; } static int checkout_get_actions( uint32_t **actions_ptr, size_t **counts_ptr, checkout_data *data, git_iterator *workdir) { int error = 0, act; const git_index_entry *wditem; git_vector pathspec = GIT_VECTOR_INIT, *deltas; git_pool pathpool; git_diff_delta *delta; size_t i, *counts = NULL; uint32_t *actions = NULL; if (git_pool_init(&pathpool, 1) < 0) return -1; if (data->opts.paths.count > 0 && git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0) return -1; if ((error = git_iterator_current(&wditem, workdir)) < 0 && error != GIT_ITEROVER) goto fail; deltas = &data->diff->deltas; *counts_ptr = counts = git__calloc(CHECKOUT_ACTION__MAX+1, sizeof(size_t)); *actions_ptr = actions = git__calloc( deltas->length ? deltas->length : 1, sizeof(uint32_t)); if (!counts || !actions) { error = -1; goto fail; } git_vector_foreach(deltas, i, delta) { if ((error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec)) == 0) error = checkout_verify_paths(data->repo, act, delta); if (error != 0) goto fail; actions[i] = act; if (act & CHECKOUT_ACTION__REMOVE) counts[CHECKOUT_ACTION__REMOVE]++; if (act & CHECKOUT_ACTION__UPDATE_BLOB) counts[CHECKOUT_ACTION__UPDATE_BLOB]++; if (act & CHECKOUT_ACTION__UPDATE_SUBMODULE) counts[CHECKOUT_ACTION__UPDATE_SUBMODULE]++; if (act & CHECKOUT_ACTION__CONFLICT) counts[CHECKOUT_ACTION__CONFLICT]++; } error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec); if (error) goto fail; counts[CHECKOUT_ACTION__REMOVE] += data->removes.length; if (counts[CHECKOUT_ACTION__CONFLICT] > 0 && (data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0) { git_error_set(GIT_ERROR_CHECKOUT, "%"PRIuZ" %s checkout", counts[CHECKOUT_ACTION__CONFLICT], counts[CHECKOUT_ACTION__CONFLICT] == 1 ? "conflict prevents" : "conflicts prevent"); error = GIT_ECONFLICT; goto fail; } if ((error = checkout_get_remove_conflicts(data, workdir, &pathspec)) < 0 || (error = checkout_get_update_conflicts(data, workdir, &pathspec)) < 0) goto fail; counts[CHECKOUT_ACTION__REMOVE_CONFLICT] = git_vector_length(&data->remove_conflicts); counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->update_conflicts); git_pathspec__vfree(&pathspec); git_pool_clear(&pathpool); return 0; fail: *counts_ptr = NULL; git__free(counts); *actions_ptr = NULL; git__free(actions); git_pathspec__vfree(&pathspec); git_pool_clear(&pathpool); return error; } static bool should_remove_existing(checkout_data *data) { int ignorecase; if (git_repository__configmap_lookup(&ignorecase, data->repo, GIT_CONFIGMAP_IGNORECASE) < 0) { ignorecase = 0; } return (ignorecase && (data->strategy & GIT_CHECKOUT_DONT_REMOVE_EXISTING) == 0); } #define MKDIR_NORMAL \ GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR #define MKDIR_REMOVE_EXISTING \ MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS static int checkout_mkdir( checkout_data *data, const char *path, const char *base, mode_t mode, unsigned int flags) { struct git_futils_mkdir_options mkdir_opts = {0}; int error; mkdir_opts.dir_map = data->mkdir_map; mkdir_opts.pool = &data->pool; error = git_futils_mkdir_relative( path, base, mode, flags, &mkdir_opts); data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls; data->perfdata.stat_calls += mkdir_opts.perfdata.stat_calls; data->perfdata.chmod_calls += mkdir_opts.perfdata.chmod_calls; return error; } static int mkpath2file( checkout_data *data, const char *path, unsigned int mode) { struct stat st; bool remove_existing = should_remove_existing(data); unsigned int flags = (remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL) | GIT_MKDIR_SKIP_LAST; int error; if ((error = checkout_mkdir( data, path, data->opts.target_directory, mode, flags)) < 0) return error; if (remove_existing) { data->perfdata.stat_calls++; if (p_lstat(path, &st) == 0) { /* Some file, symlink or folder already exists at this name. * We would have removed it in remove_the_old unless we're on * a case inensitive filesystem (or the user has asked us not * to). Remove the similarly named file to write the new. */ error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES); } else if (errno != ENOENT) { git_error_set(GIT_ERROR_OS, "failed to stat '%s'", path); return GIT_EEXISTS; } else { git_error_clear(); } } return error; } struct checkout_stream { git_writestream base; const char *path; int fd; int open; }; static int checkout_stream_write( git_writestream *s, const char *buffer, size_t len) { struct checkout_stream *stream = (struct checkout_stream *)s; int ret; if ((ret = p_write(stream->fd, buffer, len)) < 0) git_error_set(GIT_ERROR_OS, "could not write to '%s'", stream->path); return ret; } static int checkout_stream_close(git_writestream *s) { struct checkout_stream *stream = (struct checkout_stream *)s; GIT_ASSERT_ARG(stream); GIT_ASSERT_ARG(stream->open); stream->open = 0; return p_close(stream->fd); } static void checkout_stream_free(git_writestream *s) { GIT_UNUSED(s); } static int blob_content_to_file( checkout_data *data, struct stat *st, git_blob *blob, const char *path, const char *hint_path, mode_t entry_filemode) { int flags = data->opts.file_open_flags; mode_t file_mode = data->opts.file_mode ? data->opts.file_mode : entry_filemode; git_filter_session filter_session = GIT_FILTER_SESSION_INIT; struct checkout_stream writer; mode_t mode; git_filter_list *fl = NULL; int fd; int error = 0; GIT_ASSERT(hint_path != NULL); if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0) return error; if (flags <= 0) flags = O_CREAT | O_TRUNC | O_WRONLY; if (!(mode = file_mode)) mode = GIT_FILEMODE_BLOB; if ((fd = p_open(path, flags, mode)) < 0) { git_error_set(GIT_ERROR_OS, "could not open '%s' for writing", path); return fd; } filter_session.attr_session = &data->attr_session; filter_session.temp_buf = &data->tmp; if (!data->opts.disable_filters && (error = git_filter_list__load( &fl, data->repo, blob, hint_path, GIT_FILTER_TO_WORKTREE, &filter_session))) { p_close(fd); return error; } /* setup the writer */ memset(&writer, 0, sizeof(struct checkout_stream)); writer.base.write = checkout_stream_write; writer.base.close = checkout_stream_close; writer.base.free = checkout_stream_free; writer.path = path; writer.fd = fd; writer.open = 1; error = git_filter_list_stream_blob(fl, blob, &writer.base); GIT_ASSERT(writer.open == 0); git_filter_list_free(fl); if (error < 0) return error; if (st) { data->perfdata.stat_calls++; if ((error = p_stat(path, st)) < 0) { git_error_set(GIT_ERROR_OS, "failed to stat '%s'", path); return error; } st->st_mode = entry_filemode; } return 0; } static int blob_content_to_link( checkout_data *data, struct stat *st, git_blob *blob, const char *path) { git_buf linktarget = GIT_BUF_INIT; int error; if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0) return error; if ((error = git_blob__getbuf(&linktarget, blob)) < 0) return error; if (data->can_symlink) { if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0) git_error_set(GIT_ERROR_OS, "could not create symlink %s", path); } else { error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path); } if (!error) { data->perfdata.stat_calls++; if ((error = p_lstat(path, st)) < 0) git_error_set(GIT_ERROR_CHECKOUT, "could not stat symlink %s", path); st->st_mode = GIT_FILEMODE_LINK; } git_buf_dispose(&linktarget); return error; } static int checkout_update_index( checkout_data *data, const git_diff_file *file, struct stat *st) { git_index_entry entry; if (!data->index) return 0; memset(&entry, 0, sizeof(entry)); entry.path = (char *)file->path; /* cast to prevent warning */ git_index_entry__init_from_stat(&entry, st, true); git_oid_cpy(&entry.id, &file->id); return git_index_add(data->index, &entry); } static int checkout_submodule_update_index( checkout_data *data, const git_diff_file *file) { git_buf *fullpath; struct stat st; /* update the index unless prevented */ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0) return 0; if (checkout_target_fullpath(&fullpath, data, file->path) < 0) return -1; data->perfdata.stat_calls++; if (p_stat(fullpath->ptr, &st) < 0) { git_error_set( GIT_ERROR_CHECKOUT, "could not stat submodule %s\n", file->path); return GIT_ENOTFOUND; } st.st_mode = GIT_FILEMODE_COMMIT; return checkout_update_index(data, file, &st); } static int checkout_submodule( checkout_data *data, const git_diff_file *file) { bool remove_existing = should_remove_existing(data); int error = 0; /* Until submodules are supported, UPDATE_ONLY means do nothing here */ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) return 0; if ((error = checkout_mkdir( data, file->path, data->opts.target_directory, data->opts.dir_mode, remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0) return error; if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) { /* I've observed repos with submodules in the tree that do not * have a .gitmodules - core Git just makes an empty directory */ if (error == GIT_ENOTFOUND) { git_error_clear(); return checkout_submodule_update_index(data, file); } return error; } /* TODO: Support checkout_strategy options. Two circumstances: * 1 - submodule already checked out, but we need to move the HEAD * to the new OID, or * 2 - submodule not checked out and we should recursively check it out * * Checkout will not execute a pull on the submodule, but a clone * command should probably be able to. Do we need a submodule callback? */ return checkout_submodule_update_index(data, file); } static void report_progress( checkout_data *data, const char *path) { if (data->opts.progress_cb) data->opts.progress_cb( path, data->completed_steps, data->total_steps, data->opts.progress_payload); } static int checkout_safe_for_update_only( checkout_data *data, const char *path, mode_t expected_mode) { struct stat st; data->perfdata.stat_calls++; if (p_lstat(path, &st) < 0) { /* if doesn't exist, then no error and no update */ if (errno == ENOENT || errno == ENOTDIR) return 0; /* otherwise, stat error and no update */ git_error_set(GIT_ERROR_OS, "failed to stat '%s'", path); return -1; } /* only safe for update if this is the same type of file */ if ((st.st_mode & ~0777) == (expected_mode & ~0777)) return 1; return 0; } static int checkout_write_content( checkout_data *data, const git_oid *oid, const char *full_path, const char *hint_path, unsigned int mode, struct stat *st) { int error = 0; git_blob *blob; if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0) return error; if (S_ISLNK(mode)) error = blob_content_to_link(data, st, blob, full_path); else error = blob_content_to_file(data, st, blob, full_path, hint_path, mode); git_blob_free(blob); /* if we try to create the blob and an existing directory blocks it from * being written, then there must have been a typechange conflict in a * parent directory - suppress the error and try to continue. */ if ((data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) != 0 && (error == GIT_ENOTFOUND || error == GIT_EEXISTS)) { git_error_clear(); error = 0; } return error; } static int checkout_blob( checkout_data *data, const git_diff_file *file) { git_buf *fullpath; struct stat st; int error = 0; if (checkout_target_fullpath(&fullpath, data, file->path) < 0) return -1; if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) { int rval = checkout_safe_for_update_only( data, fullpath->ptr, file->mode); if (rval <= 0) return rval; } error = checkout_write_content( data, &file->id, fullpath->ptr, file->path, file->mode, &st); /* update the index unless prevented */ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) error = checkout_update_index(data, file, &st); /* update the submodule data if this was a new .gitmodules file */ if (!error && strcmp(file->path, ".gitmodules") == 0) data->reload_submodules = true; return error; } static int checkout_remove_the_old( unsigned int *actions, checkout_data *data) { int error = 0; git_diff_delta *delta; const char *str; size_t i; git_buf *fullpath; uint32_t flg = GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS; if (data->opts.checkout_strategy & GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES) flg |= GIT_RMDIR_SKIP_NONEMPTY; if (checkout_target_fullpath(&fullpath, data, NULL) < 0) return -1; git_vector_foreach(&data->diff->deltas, i, delta) { if (actions[i] & CHECKOUT_ACTION__REMOVE) { error = git_futils_rmdir_r( delta->old_file.path, fullpath->ptr, flg); if (error < 0) return error; data->completed_steps++; report_progress(data, delta->old_file.path); if ((actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) == 0 && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 && data->index != NULL) { (void)git_index_remove(data->index, delta->old_file.path, 0); } } } git_vector_foreach(&data->removes, i, str) { error = git_futils_rmdir_r(str, fullpath->ptr, flg); if (error < 0) return error; data->completed_steps++; report_progress(data, str); if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 && data->index != NULL) { if (str[strlen(str) - 1] == '/') (void)git_index_remove_directory(data->index, str, 0); else (void)git_index_remove(data->index, str, 0); } } return 0; } static int checkout_create_the_new( unsigned int *actions, checkout_data *data) { int error = 0; git_diff_delta *delta; size_t i; git_vector_foreach(&data->diff->deltas, i, delta) { if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB && !S_ISLNK(delta->new_file.mode)) { if ((error = checkout_blob(data, &delta->new_file)) < 0) return error; data->completed_steps++; report_progress(data, delta->new_file.path); } } git_vector_foreach(&data->diff->deltas, i, delta) { if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB && S_ISLNK(delta->new_file.mode)) { if ((error = checkout_blob(data, &delta->new_file)) < 0) return error; data->completed_steps++; report_progress(data, delta->new_file.path); } } return 0; } static int checkout_create_submodules( unsigned int *actions, checkout_data *data) { git_diff_delta *delta; size_t i; git_vector_foreach(&data->diff->deltas, i, delta) { if (actions[i] & CHECKOUT_ACTION__UPDATE_SUBMODULE) { int error = checkout_submodule(data, &delta->new_file); if (error < 0) return error; data->completed_steps++; report_progress(data, delta->new_file.path); } } return 0; } static int checkout_lookup_head_tree(git_tree **out, git_repository *repo) { int error = 0; git_reference *ref = NULL; git_object *head; if (!(error = git_repository_head(&ref, repo)) && !(error = git_reference_peel(&head, ref, GIT_OBJECT_TREE))) *out = (git_tree *)head; git_reference_free(ref); return error; } static int conflict_entry_name( git_buf *out, const char *side_name, const char *filename) { if (git_buf_puts(out, side_name) < 0 || git_buf_putc(out, ':') < 0 || git_buf_puts(out, filename) < 0) return -1; return 0; } static int checkout_path_suffixed(git_buf *path, const char *suffix) { size_t path_len; int i = 0, error = 0; if ((error = git_buf_putc(path, '~')) < 0 || (error = git_buf_puts(path, suffix)) < 0) return -1; path_len = git_buf_len(path); while (git_path_exists(git_buf_cstr(path)) && i < INT_MAX) { git_buf_truncate(path, path_len); if ((error = git_buf_putc(path, '_')) < 0 || (error = git_buf_printf(path, "%d", i)) < 0) return error; i++; } if (i == INT_MAX) { git_buf_truncate(path, path_len); git_error_set(GIT_ERROR_CHECKOUT, "could not write '%s': working directory file exists", path->ptr); return GIT_EEXISTS; } return 0; } static int checkout_write_entry( checkout_data *data, checkout_conflictdata *conflict, const git_index_entry *side) { const char *hint_path, *suffix; git_buf *fullpath; struct stat st; int error; GIT_ASSERT(side == conflict->ours || side == conflict->theirs); if (checkout_target_fullpath(&fullpath, data, side->path) < 0) return -1; if ((conflict->name_collision || conflict->directoryfile) && (data->strategy & GIT_CHECKOUT_USE_OURS) == 0 && (data->strategy & GIT_CHECKOUT_USE_THEIRS) == 0) { if (side == conflict->ours) suffix = data->opts.our_label ? data->opts.our_label : "ours"; else suffix = data->opts.their_label ? data->opts.their_label : "theirs"; if (checkout_path_suffixed(fullpath, suffix) < 0) return -1; } hint_path = side->path; if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 && (error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0) return error; if (!S_ISGITLINK(side->mode)) return checkout_write_content(data, &side->id, fullpath->ptr, hint_path, side->mode, &st); return 0; } static int checkout_write_entries( checkout_data *data, checkout_conflictdata *conflict) { int error = 0; if ((error = checkout_write_entry(data, conflict, conflict->ours)) >= 0) error = checkout_write_entry(data, conflict, conflict->theirs); return error; } static int checkout_merge_path( git_buf *out, checkout_data *data, checkout_conflictdata *conflict, git_merge_file_result *result) { const char *our_label_raw, *their_label_raw, *suffix; int error = 0; if ((error = git_buf_joinpath(out, data->opts.target_directory, result->path)) < 0 || (error = git_path_validate_workdir_buf(data->repo, out)) < 0) return error; /* Most conflicts simply use the filename in the index */ if (!conflict->name_collision) return 0; /* Rename 2->1 conflicts need the branch name appended */ our_label_raw = data->opts.our_label ? data->opts.our_label : "ours"; their_label_raw = data->opts.their_label ? data->opts.their_label : "theirs"; suffix = strcmp(result->path, conflict->ours->path) == 0 ? our_label_raw : their_label_raw; if ((error = checkout_path_suffixed(out, suffix)) < 0) return error; return 0; } static int checkout_write_merge( checkout_data *data, checkout_conflictdata *conflict) { git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT, path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT, in_data = GIT_BUF_INIT, out_data = GIT_BUF_INIT; git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_file_result result = {0}; git_filebuf output = GIT_FILEBUF_INIT; git_filter_list *fl = NULL; git_filter_session filter_session = GIT_FILTER_SESSION_INIT; int error = 0; if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3) opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3; opts.ancestor_label = data->opts.ancestor_label ? data->opts.ancestor_label : "ancestor"; opts.our_label = data->opts.our_label ? data->opts.our_label : "ours"; opts.their_label = data->opts.their_label ? data->opts.their_label : "theirs"; /* If all the paths are identical, decorate the diff3 file with the branch * names. Otherwise, append branch_name:path. */ if (conflict->ours && conflict->theirs && strcmp(conflict->ours->path, conflict->theirs->path) != 0) { if ((error = conflict_entry_name( &our_label, opts.our_label, conflict->ours->path)) < 0 || (error = conflict_entry_name( &their_label, opts.their_label, conflict->theirs->path)) < 0) goto done; opts.our_label = git_buf_cstr(&our_label); opts.their_label = git_buf_cstr(&their_label); } if ((error = git_merge_file_from_index(&result, data->repo, conflict->ancestor, conflict->ours, conflict->theirs, &opts)) < 0) goto done; if (result.path == NULL || result.mode == 0) { git_error_set(GIT_ERROR_CHECKOUT, "could not merge contents of file"); error = GIT_ECONFLICT; goto done; } if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0) goto done; if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 && (error = checkout_safe_for_update_only(data, git_buf_cstr(&path_workdir), result.mode)) <= 0) goto done; if (!data->opts.disable_filters) { in_data.ptr = (char *)result.ptr; in_data.size = result.len; filter_session.attr_session = &data->attr_session; filter_session.temp_buf = &data->tmp; if ((error = git_filter_list__load( &fl, data->repo, NULL, result.path, GIT_FILTER_TO_WORKTREE, &filter_session)) < 0 || (error = git_filter_list__convert_buf(&out_data, fl, &in_data)) < 0) goto done; } else { out_data.ptr = (char *)result.ptr; out_data.size = result.len; } if ((error = mkpath2file(data, path_workdir.ptr, data->opts.dir_mode)) < 0 || (error = git_filebuf_open(&output, git_buf_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 || (error = git_filebuf_write(&output, out_data.ptr, out_data.size)) < 0 || (error = git_filebuf_commit(&output)) < 0) goto done; done: git_filter_list_free(fl); git_buf_dispose(&out_data); git_buf_dispose(&our_label); git_buf_dispose(&their_label); git_merge_file_result_free(&result); git_buf_dispose(&path_workdir); git_buf_dispose(&path_suffixed); return error; } static int checkout_conflict_add( checkout_data *data, const git_index_entry *conflict) { int error = git_index_remove(data->index, conflict->path, 0); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) return error; return git_index_add(data->index, conflict); } static int checkout_conflict_update_index( checkout_data *data, checkout_conflictdata *conflict) { int error = 0; if (conflict->ancestor) error = checkout_conflict_add(data, conflict->ancestor); if (!error && conflict->ours) error = checkout_conflict_add(data, conflict->ours); if (!error && conflict->theirs) error = checkout_conflict_add(data, conflict->theirs); return error; } static int checkout_create_conflicts(checkout_data *data) { checkout_conflictdata *conflict; size_t i; int error = 0; git_vector_foreach(&data->update_conflicts, i, conflict) { /* Both deleted: nothing to do */ if (conflict->ours == NULL && conflict->theirs == NULL) error = 0; else if ((data->strategy & GIT_CHECKOUT_USE_OURS) && conflict->ours) error = checkout_write_entry(data, conflict, conflict->ours); else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) && conflict->theirs) error = checkout_write_entry(data, conflict, conflict->theirs); /* Ignore the other side of name collisions. */ else if ((data->strategy & GIT_CHECKOUT_USE_OURS) && !conflict->ours && conflict->name_collision) error = 0; else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) && !conflict->theirs && conflict->name_collision) error = 0; /* For modify/delete, name collisions and d/f conflicts, write * the file (potentially with the name mangled. */ else if (conflict->ours != NULL && conflict->theirs == NULL) error = checkout_write_entry(data, conflict, conflict->ours); else if (conflict->ours == NULL && conflict->theirs != NULL) error = checkout_write_entry(data, conflict, conflict->theirs); /* Add/add conflicts and rename 1->2 conflicts, write the * ours/theirs sides (potentially name mangled). */ else if (conflict->one_to_two) error = checkout_write_entries(data, conflict); /* If all sides are links, write the ours side */ else if (S_ISLNK(conflict->ours->mode) && S_ISLNK(conflict->theirs->mode)) error = checkout_write_entry(data, conflict, conflict->ours); /* Link/file conflicts, write the file side */ else if (S_ISLNK(conflict->ours->mode)) error = checkout_write_entry(data, conflict, conflict->theirs); else if (S_ISLNK(conflict->theirs->mode)) error = checkout_write_entry(data, conflict, conflict->ours); /* If any side is a gitlink, do nothing. */ else if (conflict->submodule) error = 0; /* If any side is binary, write the ours side */ else if (conflict->binary) error = checkout_write_entry(data, conflict, conflict->ours); else if (!error) error = checkout_write_merge(data, conflict); /* Update the index extensions (REUC and NAME) if we're checking * out a different index. (Otherwise just leave them there.) */ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) error = checkout_conflict_update_index(data, conflict); if (error) break; data->completed_steps++; report_progress(data, conflict->ours ? conflict->ours->path : (conflict->theirs ? conflict->theirs->path : conflict->ancestor->path)); } return error; } static int checkout_remove_conflicts(checkout_data *data) { const char *conflict; size_t i; git_vector_foreach(&data->remove_conflicts, i, conflict) { if (git_index_conflict_remove(data->index, conflict) < 0) return -1; data->completed_steps++; } return 0; } static int checkout_extensions_update_index(checkout_data *data) { const git_index_reuc_entry *reuc_entry; const git_index_name_entry *name_entry; size_t i; int error = 0; if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) return 0; if (data->update_reuc) { git_vector_foreach(data->update_reuc, i, reuc_entry) { if ((error = git_index_reuc_add(data->index, reuc_entry->path, reuc_entry->mode[0], &reuc_entry->oid[0], reuc_entry->mode[1], &reuc_entry->oid[1], reuc_entry->mode[2], &reuc_entry->oid[2])) < 0) goto done; } } if (data->update_names) { git_vector_foreach(data->update_names, i, name_entry) { if ((error = git_index_name_add(data->index, name_entry->ancestor, name_entry->ours, name_entry->theirs)) < 0) goto done; } } done: return error; } static void checkout_data_clear(checkout_data *data) { if (data->opts_free_baseline) { git_tree_free(data->opts.baseline); data->opts.baseline = NULL; } git_vector_free(&data->removes); git_pool_clear(&data->pool); git_vector_free_deep(&data->remove_conflicts); git_vector_free_deep(&data->update_conflicts); git__free(data->pfx); data->pfx = NULL; git_buf_dispose(&data->target_path); git_buf_dispose(&data->tmp); git_index_free(data->index); data->index = NULL; git_strmap_free(data->mkdir_map); data->mkdir_map = NULL; git_attr_session__free(&data->attr_session); } static int validate_target_directory(checkout_data *data) { int error; if ((error = git_path_validate_workdir(data->repo, data->opts.target_directory)) < 0) return error; if (git_path_isdir(data->opts.target_directory)) return 0; error = checkout_mkdir(data, data->opts.target_directory, NULL, GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR); return error; } static int checkout_data_init( checkout_data *data, git_iterator *target, const git_checkout_options *proposed) { int error = 0; git_repository *repo = git_iterator_owner(target); memset(data, 0, sizeof(*data)); if (!repo) { git_error_set(GIT_ERROR_CHECKOUT, "cannot checkout nothing"); return -1; } if ((!proposed || !proposed->target_directory) && (error = git_repository__ensure_not_bare(repo, "checkout")) < 0) return error; data->repo = repo; data->target = target; GIT_ERROR_CHECK_VERSION( proposed, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options"); if (!proposed) GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTIONS_VERSION); else memmove(&data->opts, proposed, sizeof(git_checkout_options)); if (!data->opts.target_directory) data->opts.target_directory = git_repository_workdir(repo); else if ((error = validate_target_directory(data)) < 0) goto cleanup; if ((error = git_repository_index(&data->index, data->repo)) < 0) goto cleanup; /* refresh config and index content unless NO_REFRESH is given */ if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) { git_config *cfg; if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) goto cleanup; /* Reload the repository index (unless we're checking out the * index; then it has the changes we're trying to check out * and those should not be overwritten.) */ if (data->index != git_iterator_index(target)) { if (data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) { /* When forcing, we can blindly re-read the index */ if ((error = git_index_read(data->index, false)) < 0) goto cleanup; } else { /* * When not being forced, we need to check for unresolved * conflicts and unsaved changes in the index before * proceeding. */ if (git_index_has_conflicts(data->index)) { error = GIT_ECONFLICT; git_error_set(GIT_ERROR_CHECKOUT, "unresolved conflicts exist in the index"); goto cleanup; } if ((error = git_index_read_safely(data->index)) < 0) goto cleanup; } /* clean conflict data in the current index */ git_index_name_clear(data->index); git_index_reuc_clear(data->index); } } /* if you are forcing, allow all safe updates, plus recreate missing */ if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0) data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING; /* if the repository does not actually have an index file, then this * is an initial checkout (perhaps from clone), so we allow safe updates */ if (!data->index->on_disk && (data->opts.checkout_strategy & GIT_CHECKOUT_SAFE) != 0) data->opts.checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING; data->strategy = data->opts.checkout_strategy; /* opts->disable_filters is false by default */ if (!data->opts.dir_mode) data->opts.dir_mode = GIT_DIR_MODE; if (!data->opts.file_open_flags) data->opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY; data->pfx = git_pathspec_prefix(&data->opts.paths); if ((error = git_repository__configmap_lookup( &data->can_symlink, repo, GIT_CONFIGMAP_SYMLINKS)) < 0) goto cleanup; if ((error = git_repository__configmap_lookup( &data->respect_filemode, repo, GIT_CONFIGMAP_FILEMODE)) < 0) goto cleanup; if (!data->opts.baseline && !data->opts.baseline_index) { data->opts_free_baseline = true; error = 0; /* if we don't have an index, this is an initial checkout and * should be against an empty baseline */ if (data->index->on_disk) error = checkout_lookup_head_tree(&data->opts.baseline, repo); if (error == GIT_EUNBORNBRANCH) { error = 0; git_error_clear(); } if (error < 0) goto cleanup; } if ((data->opts.checkout_strategy & (GIT_CHECKOUT_CONFLICT_STYLE_MERGE | GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)) == 0) { git_config_entry *conflict_style = NULL; git_config *cfg = NULL; if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 || (error = git_config_get_entry(&conflict_style, cfg, "merge.conflictstyle")) < 0 || error == GIT_ENOTFOUND) ; else if (error) goto cleanup; else if (strcmp(conflict_style->value, "merge") == 0) data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE; else if (strcmp(conflict_style->value, "diff3") == 0) data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; else { git_error_set(GIT_ERROR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'", conflict_style->value); error = -1; git_config_entry_free(conflict_style); goto cleanup; } git_config_entry_free(conflict_style); } if ((error = git_pool_init(&data->pool, 1)) < 0 || (error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 || (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 || (error = git_buf_puts(&data->target_path, data->opts.target_directory)) < 0 || (error = git_path_to_dir(&data->target_path)) < 0 || (error = git_strmap_new(&data->mkdir_map)) < 0) goto cleanup; data->target_len = git_buf_len(&data->target_path); git_attr_session__init(&data->attr_session, data->repo); cleanup: if (error < 0) checkout_data_clear(data); return error; } #define CHECKOUT_INDEX_DONT_WRITE_MASK \ (GIT_CHECKOUT_DONT_UPDATE_INDEX | GIT_CHECKOUT_DONT_WRITE_INDEX) GIT_INLINE(void) setup_pathspecs( git_iterator_options *iter_opts, const git_checkout_options *checkout_opts) { if (checkout_opts && (checkout_opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) { iter_opts->pathlist.count = checkout_opts->paths.count; iter_opts->pathlist.strings = checkout_opts->paths.strings; } } int git_checkout_iterator( git_iterator *target, git_index *index, const git_checkout_options *opts) { int error = 0; git_iterator *baseline = NULL, *workdir = NULL; git_iterator_options baseline_opts = GIT_ITERATOR_OPTIONS_INIT, workdir_opts = GIT_ITERATOR_OPTIONS_INIT; checkout_data data = {0}; git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; uint32_t *actions = NULL; size_t *counts = NULL; /* initialize structures and options */ error = checkout_data_init(&data, target, opts); if (error < 0) return error; diff_opts.flags = GIT_DIFF_INCLUDE_UNMODIFIED | GIT_DIFF_INCLUDE_UNREADABLE | GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */ GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_INCLUDE_TYPECHANGE_TREES | GIT_DIFF_SKIP_BINARY_CHECK | GIT_DIFF_INCLUDE_CASECHANGE; if (data.opts.checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) diff_opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; if (data.opts.paths.count > 0) diff_opts.pathspec = data.opts.paths; /* set up iterators */ workdir_opts.flags = git_iterator_ignore_case(target) ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; workdir_opts.flags |= GIT_ITERATOR_DONT_AUTOEXPAND; workdir_opts.start = data.pfx; workdir_opts.end = data.pfx; setup_pathspecs(&workdir_opts, opts); if ((error = git_iterator_reset_range(target, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_workdir_ext( &workdir, data.repo, data.opts.target_directory, index, NULL, &workdir_opts)) < 0) goto cleanup; baseline_opts.flags = git_iterator_ignore_case(target) ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; baseline_opts.start = data.pfx; baseline_opts.end = data.pfx; setup_pathspecs(&baseline_opts, opts); if (data.opts.baseline_index) { if ((error = git_iterator_for_index( &baseline, git_index_owner(data.opts.baseline_index), data.opts.baseline_index, &baseline_opts)) < 0) goto cleanup; } else { if ((error = git_iterator_for_tree( &baseline, data.opts.baseline, &baseline_opts)) < 0) goto cleanup; } /* Should not have case insensitivity mismatch */ GIT_ASSERT(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline)); /* Generate baseline-to-target diff which will include an entry for * every possible update that might need to be made. */ if ((error = git_diff__from_iterators( &data.diff, data.repo, baseline, target, &diff_opts)) < 0) goto cleanup; /* Loop through diff (and working directory iterator) building a list of * actions to be taken, plus look for conflicts and send notifications, * then loop through conflicts. */ if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0) goto cleanup; if (data.strategy & GIT_CHECKOUT_DRY_RUN) goto cleanup; data.total_steps = counts[CHECKOUT_ACTION__REMOVE] + counts[CHECKOUT_ACTION__REMOVE_CONFLICT] + counts[CHECKOUT_ACTION__UPDATE_BLOB] + counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] + counts[CHECKOUT_ACTION__UPDATE_CONFLICT]; report_progress(&data, NULL); /* establish 0 baseline */ /* To deal with some order dependencies, perform remaining checkout * in three passes: removes, then update blobs, then update submodules. */ if (counts[CHECKOUT_ACTION__REMOVE] > 0 && (error = checkout_remove_the_old(actions, &data)) < 0) goto cleanup; if (counts[CHECKOUT_ACTION__REMOVE_CONFLICT] > 0 && (error = checkout_remove_conflicts(&data)) < 0) goto cleanup; if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 && (error = checkout_create_the_new(actions, &data)) < 0) goto cleanup; if (counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] > 0 && (error = checkout_create_submodules(actions, &data)) < 0) goto cleanup; if (counts[CHECKOUT_ACTION__UPDATE_CONFLICT] > 0 && (error = checkout_create_conflicts(&data)) < 0) goto cleanup; if (data.index != git_iterator_index(target) && (error = checkout_extensions_update_index(&data)) < 0) goto cleanup; GIT_ASSERT(data.completed_steps == data.total_steps); if (data.opts.perfdata_cb) data.opts.perfdata_cb(&data.perfdata, data.opts.perfdata_payload); cleanup: if (!error && data.index != NULL && (data.strategy & CHECKOUT_INDEX_DONT_WRITE_MASK) == 0) error = git_index_write(data.index); git_diff_free(data.diff); git_iterator_free(workdir); git_iterator_free(baseline); git__free(actions); git__free(counts); checkout_data_clear(&data); return error; } int git_checkout_index( git_repository *repo, git_index *index, const git_checkout_options *opts) { git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error, owned = 0; git_iterator *index_i; if (!index && !repo) { git_error_set(GIT_ERROR_CHECKOUT, "must provide either repository or index to checkout"); return -1; } if (index && repo && git_index_owner(index) && git_index_owner(index) != repo) { git_error_set(GIT_ERROR_CHECKOUT, "index to checkout does not match repository"); return -1; } else if(index && repo && !git_index_owner(index)) { GIT_REFCOUNT_OWN(index, repo); owned = 1; } if (!repo) repo = git_index_owner(index); if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0) return error; GIT_REFCOUNT_INC(index); setup_pathspecs(&iter_opts, opts); if (!(error = git_iterator_for_index(&index_i, repo, index, &iter_opts))) error = git_checkout_iterator(index_i, index, opts); if (owned) GIT_REFCOUNT_OWN(index, NULL); git_iterator_free(index_i); git_index_free(index); return error; } int git_checkout_tree( git_repository *repo, const git_object *treeish, const git_checkout_options *opts) { int error; git_index *index; git_tree *tree = NULL; git_iterator *tree_i = NULL; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; if (!treeish && !repo) { git_error_set(GIT_ERROR_CHECKOUT, "must provide either repository or tree to checkout"); return -1; } if (treeish && repo && git_object_owner(treeish) != repo) { git_error_set(GIT_ERROR_CHECKOUT, "object to checkout does not match repository"); return -1; } if (!repo) repo = git_object_owner(treeish); if (treeish) { if (git_object_peel((git_object **)&tree, treeish, GIT_OBJECT_TREE) < 0) { git_error_set( GIT_ERROR_CHECKOUT, "provided object cannot be peeled to a tree"); return -1; } } else { if ((error = checkout_lookup_head_tree(&tree, repo)) < 0) { if (error != GIT_EUNBORNBRANCH) git_error_set( GIT_ERROR_CHECKOUT, "HEAD could not be peeled to a tree and no treeish given"); return error; } } if ((error = git_repository_index(&index, repo)) < 0) return error; setup_pathspecs(&iter_opts, opts); if (!(error = git_iterator_for_tree(&tree_i, tree, &iter_opts))) error = git_checkout_iterator(tree_i, index, opts); git_iterator_free(tree_i); git_index_free(index); git_tree_free(tree); return error; } int git_checkout_head( git_repository *repo, const git_checkout_options *opts) { GIT_ASSERT_ARG(repo); return git_checkout_tree(repo, NULL, opts); } int git_checkout_options_init(git_checkout_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_checkout_options, GIT_CHECKOUT_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_checkout_init_options(git_checkout_options *opts, unsigned int version) { return git_checkout_options_init(opts, version); } #endif git2r/src/libgit2/src/pack-objects.h0000644000175000017500000000431514125111754017067 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_pack_objects_h__ #define INCLUDE_pack_objects_h__ #include "common.h" #include "buffer.h" #include "hash.h" #include "oidmap.h" #include "netops.h" #include "zstream.h" #include "pool.h" #include "indexer.h" #include "git2/oid.h" #include "git2/pack.h" #define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */ #define GIT_PACK_DEPTH 50 /* max delta depth */ #define GIT_PACK_DELTA_CACHE_SIZE (256 * 1024 * 1024) #define GIT_PACK_DELTA_CACHE_LIMIT 1000 #define GIT_PACK_BIG_FILE_THRESHOLD (512 * 1024 * 1024) typedef struct git_pobject { git_oid id; git_object_t type; off64_t offset; size_t size; unsigned int hash; /* name hint hash */ struct git_pobject *delta; /* delta base object */ struct git_pobject *delta_child; /* deltified objects who bases me */ struct git_pobject *delta_sibling; /* other deltified objects * who uses the same base as * me */ void *delta_data; size_t delta_size; size_t z_delta_size; int written:1, recursing:1, tagged:1, filled:1; } git_pobject; struct git_packbuilder { git_repository *repo; /* associated repository */ git_odb *odb; /* associated object database */ git_hash_ctx ctx; git_zstream zstream; uint32_t nr_objects, nr_deltified, nr_written, nr_remaining; size_t nr_alloc; git_pobject *object_list; git_oidmap *object_ix; git_oidmap *walk_objects; git_pool object_pool; git_oid pack_oid; /* hash of written pack */ /* synchronization objects */ git_mutex cache_mutex; git_mutex progress_mutex; git_cond progress_cond; /* configs */ size_t delta_cache_size; size_t max_delta_cache_size; size_t cache_max_small_delta_size; size_t big_file_threshold; size_t window_memory_limit; unsigned int nr_threads; /* nr of threads to use */ git_packbuilder_progress progress_cb; void *progress_cb_payload; double last_progress_report_time; /* the time progress was last reported */ bool done; }; int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb); #endif git2r/src/libgit2/src/date.c0000644000175000017500000004666414125111754015447 0ustar nileshnilesh/* * GIT - The information manager from hell * * Copyright (C) Linus Torvalds, 2005 */ #include "common.h" #ifndef GIT_WIN32 #include #endif #include "util.h" #include "cache.h" #include "posix.h" #include #include typedef enum { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT, DATE_LOCAL, DATE_ISO8601, DATE_RFC2822, DATE_RAW } date_mode; /* * This is like mktime, but without normalization of tm_wday and tm_yday. */ static git_time_t tm_to_time_t(const struct tm *tm) { static const int mdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; int year = tm->tm_year - 70; int month = tm->tm_mon; int day = tm->tm_mday; if (year < 0 || year > 129) /* algo only works for 1970-2099 */ return -1; if (month < 0 || month > 11) /* array bounds */ return -1; if (month < 2 || (year + 2) % 4) day--; if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0) return -1; return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL + tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec; } static const char *month_names[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static const char *weekday_names[] = { "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays" }; /* * Check these. And note how it doesn't do the summer-time conversion. * * In my world, it's always summer, and things are probably a bit off * in other ways too. */ static const struct { const char *name; int offset; int dst; } timezone_names[] = { { "IDLW", -12, 0, }, /* International Date Line West */ { "NT", -11, 0, }, /* Nome */ { "CAT", -10, 0, }, /* Central Alaska */ { "HST", -10, 0, }, /* Hawaii Standard */ { "HDT", -10, 1, }, /* Hawaii Daylight */ { "YST", -9, 0, }, /* Yukon Standard */ { "YDT", -9, 1, }, /* Yukon Daylight */ { "PST", -8, 0, }, /* Pacific Standard */ { "PDT", -8, 1, }, /* Pacific Daylight */ { "MST", -7, 0, }, /* Mountain Standard */ { "MDT", -7, 1, }, /* Mountain Daylight */ { "CST", -6, 0, }, /* Central Standard */ { "CDT", -6, 1, }, /* Central Daylight */ { "EST", -5, 0, }, /* Eastern Standard */ { "EDT", -5, 1, }, /* Eastern Daylight */ { "AST", -3, 0, }, /* Atlantic Standard */ { "ADT", -3, 1, }, /* Atlantic Daylight */ { "WAT", -1, 0, }, /* West Africa */ { "GMT", 0, 0, }, /* Greenwich Mean */ { "UTC", 0, 0, }, /* Universal (Coordinated) */ { "Z", 0, 0, }, /* Zulu, alias for UTC */ { "WET", 0, 0, }, /* Western European */ { "BST", 0, 1, }, /* British Summer */ { "CET", +1, 0, }, /* Central European */ { "MET", +1, 0, }, /* Middle European */ { "MEWT", +1, 0, }, /* Middle European Winter */ { "MEST", +1, 1, }, /* Middle European Summer */ { "CEST", +1, 1, }, /* Central European Summer */ { "MESZ", +1, 1, }, /* Middle European Summer */ { "FWT", +1, 0, }, /* French Winter */ { "FST", +1, 1, }, /* French Summer */ { "EET", +2, 0, }, /* Eastern Europe */ { "EEST", +2, 1, }, /* Eastern European Daylight */ { "WAST", +7, 0, }, /* West Australian Standard */ { "WADT", +7, 1, }, /* West Australian Daylight */ { "CCT", +8, 0, }, /* China Coast */ { "JST", +9, 0, }, /* Japan Standard */ { "EAST", +10, 0, }, /* Eastern Australian Standard */ { "EADT", +10, 1, }, /* Eastern Australian Daylight */ { "GST", +10, 0, }, /* Guam Standard */ { "NZT", +12, 0, }, /* New Zealand */ { "NZST", +12, 0, }, /* New Zealand Standard */ { "NZDT", +12, 1, }, /* New Zealand Daylight */ { "IDLE", +12, 0, }, /* International Date Line East */ }; static size_t match_string(const char *date, const char *str) { size_t i = 0; for (i = 0; *date; date++, str++, i++) { if (*date == *str) continue; if (toupper(*date) == toupper(*str)) continue; if (!isalnum(*date)) break; return 0; } return i; } static int skip_alpha(const char *date) { int i = 0; do { i++; } while (isalpha(date[i])); return i; } /* * Parse month, weekday, or timezone name */ static size_t match_alpha(const char *date, struct tm *tm, int *offset) { unsigned int i; for (i = 0; i < 12; i++) { size_t match = match_string(date, month_names[i]); if (match >= 3) { tm->tm_mon = i; return match; } } for (i = 0; i < 7; i++) { size_t match = match_string(date, weekday_names[i]); if (match >= 3) { tm->tm_wday = i; return match; } } for (i = 0; i < ARRAY_SIZE(timezone_names); i++) { size_t match = match_string(date, timezone_names[i].name); if (match >= 3 || match == strlen(timezone_names[i].name)) { int off = timezone_names[i].offset; /* This is bogus, but we like summer */ off += timezone_names[i].dst; /* Only use the tz name offset if we don't have anything better */ if (*offset == -1) *offset = 60*off; return match; } } if (match_string(date, "PM") == 2) { tm->tm_hour = (tm->tm_hour % 12) + 12; return 2; } if (match_string(date, "AM") == 2) { tm->tm_hour = (tm->tm_hour % 12) + 0; return 2; } /* BAD */ return skip_alpha(date); } static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm) { if (month > 0 && month < 13 && day > 0 && day < 32) { struct tm check = *tm; struct tm *r = (now_tm ? &check : tm); git_time_t specified; r->tm_mon = month - 1; r->tm_mday = day; if (year == -1) { if (!now_tm) return 1; r->tm_year = now_tm->tm_year; } else if (year >= 1970 && year < 2100) r->tm_year = year - 1900; else if (year > 70 && year < 100) r->tm_year = year; else if (year < 38) r->tm_year = year + 100; else return 0; if (!now_tm) return 1; specified = tm_to_time_t(r); /* Be it commit time or author time, it does not make * sense to specify timestamp way into the future. Make * sure it is not later than ten days from now... */ if (now + 10*24*3600 < specified) return 0; tm->tm_mon = r->tm_mon; tm->tm_mday = r->tm_mday; if (year != -1) tm->tm_year = r->tm_year; return 1; } return 0; } static size_t match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm) { time_t now; struct tm now_tm; struct tm *refuse_future; long num2, num3; num2 = strtol(end+1, &end, 10); num3 = -1; if (*end == c && isdigit(end[1])) num3 = strtol(end+1, &end, 10); /* Time? Date? */ switch (c) { case ':': if (num3 < 0) num3 = 0; if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) { tm->tm_hour = num; tm->tm_min = num2; tm->tm_sec = num3; break; } return 0; case '-': case '/': case '.': now = time(NULL); refuse_future = NULL; if (p_gmtime_r(&now, &now_tm)) refuse_future = &now_tm; if (num > 70) { /* yyyy-mm-dd? */ if (is_date(num, num2, num3, refuse_future, now, tm)) break; /* yyyy-dd-mm? */ if (is_date(num, num3, num2, refuse_future, now, tm)) break; } /* Our eastern European friends say dd.mm.yy[yy] * is the norm there, so giving precedence to * mm/dd/yy[yy] form only when separator is not '.' */ if (c != '.' && is_date(num3, num, num2, refuse_future, now, tm)) break; /* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */ if (is_date(num3, num2, num, refuse_future, now, tm)) break; /* Funny European mm.dd.yy */ if (c == '.' && is_date(num3, num, num2, refuse_future, now, tm)) break; return 0; } return end - date; } /* * Have we filled in any part of the time/date yet? * We just do a binary 'and' to see if the sign bit * is set in all the values. */ static int nodate(struct tm *tm) { return (tm->tm_year & tm->tm_mon & tm->tm_mday & tm->tm_hour & tm->tm_min & tm->tm_sec) < 0; } /* * We've seen a digit. Time? Year? Date? */ static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt) { size_t n; char *end; unsigned long num; num = strtoul(date, &end, 10); /* * Seconds since 1970? We trigger on that for any numbers with * more than 8 digits. This is because we don't want to rule out * numbers like 20070606 as a YYYYMMDD date. */ if (num >= 100000000 && nodate(tm)) { time_t time = num; if (p_gmtime_r(&time, tm)) { *tm_gmt = 1; return end - date; } } /* * Check for special formats: num[-.:/]num[same]num */ switch (*end) { case ':': case '.': case '/': case '-': if (isdigit(end[1])) { size_t match = match_multi_number(num, *end, date, end, tm); if (match) return match; } } /* * None of the special formats? Try to guess what * the number meant. We use the number of digits * to make a more educated guess.. */ n = 0; do { n++; } while (isdigit(date[n])); /* Four-digit year or a timezone? */ if (n == 4) { if (num <= 1400 && *offset == -1) { unsigned int minutes = num % 100; unsigned int hours = num / 100; *offset = hours*60 + minutes; } else if (num > 1900 && num < 2100) tm->tm_year = num - 1900; return n; } /* * Ignore lots of numerals. We took care of 4-digit years above. * Days or months must be one or two digits. */ if (n > 2) return n; /* * NOTE! We will give precedence to day-of-month over month or * year numbers in the 1-12 range. So 05 is always "mday 5", * unless we already have a mday.. * * IOW, 01 Apr 05 parses as "April 1st, 2005". */ if (num > 0 && num < 32 && tm->tm_mday < 0) { tm->tm_mday = num; return n; } /* Two-digit year? */ if (n == 2 && tm->tm_year < 0) { if (num < 10 && tm->tm_mday >= 0) { tm->tm_year = num + 100; return n; } if (num >= 70) { tm->tm_year = num; return n; } } if (num > 0 && num < 13 && tm->tm_mon < 0) tm->tm_mon = num-1; return n; } static size_t match_tz(const char *date, int *offp) { char *end; int hour = strtoul(date + 1, &end, 10); size_t n = end - (date + 1); int min = 0; if (n == 4) { /* hhmm */ min = hour % 100; hour = hour / 100; } else if (n != 2) { min = 99; /* random stuff */ } else if (*end == ':') { /* hh:mm? */ min = strtoul(end + 1, &end, 10); if (end - (date + 1) != 5) min = 99; /* random stuff */ } /* otherwise we parsed "hh" */ /* * Don't accept any random stuff. Even though some places have * offset larger than 12 hours (e.g. Pacific/Kiritimati is at * UTC+14), there is something wrong if hour part is much * larger than that. We might also want to check that the * minutes are divisible by 15 or something too. (Offset of * Kathmandu, Nepal is UTC+5:45) */ if (min < 60 && hour < 24) { int offset = hour * 60 + min; if (*date == '-') offset = -offset; *offp = offset; } return end - date; } /* * Parse a string like "0 +0000" as ancient timestamp near epoch, but * only when it appears not as part of any other string. */ static int match_object_header_date(const char *date, git_time_t *timestamp, int *offset) { char *end; unsigned long stamp; int ofs; if (*date < '0' || '9' <= *date) return -1; stamp = strtoul(date, &end, 10); if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-')) return -1; date = end + 2; ofs = strtol(date, &end, 10); if ((*end != '\0' && (*end != '\n')) || end != date + 4) return -1; ofs = (ofs / 100) * 60 + (ofs % 100); if (date[-1] == '-') ofs = -ofs; *timestamp = stamp; *offset = ofs; return 0; } /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822 (i.e. English) day/month names, and it doesn't work correctly with %z. */ static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset) { struct tm tm; int tm_gmt; git_time_t dummy_timestamp; int dummy_offset; if (!timestamp) timestamp = &dummy_timestamp; if (!offset) offset = &dummy_offset; memset(&tm, 0, sizeof(tm)); tm.tm_year = -1; tm.tm_mon = -1; tm.tm_mday = -1; tm.tm_isdst = -1; tm.tm_hour = -1; tm.tm_min = -1; tm.tm_sec = -1; *offset = -1; tm_gmt = 0; if (*date == '@' && !match_object_header_date(date + 1, timestamp, offset)) return 0; /* success */ for (;;) { size_t match = 0; unsigned char c = *date; /* Stop at end of string or newline */ if (!c || c == '\n') break; if (isalpha(c)) match = match_alpha(date, &tm, offset); else if (isdigit(c)) match = match_digit(date, &tm, offset, &tm_gmt); else if ((c == '-' || c == '+') && isdigit(date[1])) match = match_tz(date, offset); if (!match) { /* BAD */ match = 1; } date += match; } /* mktime uses local timezone */ *timestamp = tm_to_time_t(&tm); if (*offset == -1) *offset = (int)((time_t)*timestamp - mktime(&tm)) / 60; if (*timestamp == (git_time_t)-1) return -1; if (!tm_gmt) *timestamp -= *offset * 60; return 0; /* success */ } /* * Relative time update (eg "2 days ago"). If we haven't set the time * yet, we need to set it from current time. */ static git_time_t update_tm(struct tm *tm, struct tm *now, unsigned long sec) { time_t n; if (tm->tm_mday < 0) tm->tm_mday = now->tm_mday; if (tm->tm_mon < 0) tm->tm_mon = now->tm_mon; if (tm->tm_year < 0) { tm->tm_year = now->tm_year; if (tm->tm_mon > now->tm_mon) tm->tm_year--; } n = mktime(tm) - sec; p_localtime_r(&n, tm); return n; } static void date_now(struct tm *tm, struct tm *now, int *num) { GIT_UNUSED(num); update_tm(tm, now, 0); } static void date_yesterday(struct tm *tm, struct tm *now, int *num) { GIT_UNUSED(num); update_tm(tm, now, 24*60*60); } static void date_time(struct tm *tm, struct tm *now, int hour) { if (tm->tm_hour < hour) date_yesterday(tm, now, NULL); tm->tm_hour = hour; tm->tm_min = 0; tm->tm_sec = 0; } static void date_midnight(struct tm *tm, struct tm *now, int *num) { GIT_UNUSED(num); date_time(tm, now, 0); } static void date_noon(struct tm *tm, struct tm *now, int *num) { GIT_UNUSED(num); date_time(tm, now, 12); } static void date_tea(struct tm *tm, struct tm *now, int *num) { GIT_UNUSED(num); date_time(tm, now, 17); } static void date_pm(struct tm *tm, struct tm *now, int *num) { int hour, n = *num; *num = 0; GIT_UNUSED(now); hour = tm->tm_hour; if (n) { hour = n; tm->tm_min = 0; tm->tm_sec = 0; } tm->tm_hour = (hour % 12) + 12; } static void date_am(struct tm *tm, struct tm *now, int *num) { int hour, n = *num; *num = 0; GIT_UNUSED(now); hour = tm->tm_hour; if (n) { hour = n; tm->tm_min = 0; tm->tm_sec = 0; } tm->tm_hour = (hour % 12); } static void date_never(struct tm *tm, struct tm *now, int *num) { time_t n = 0; GIT_UNUSED(now); GIT_UNUSED(num); p_localtime_r(&n, tm); } static const struct special { const char *name; void (*fn)(struct tm *, struct tm *, int *); } special[] = { { "yesterday", date_yesterday }, { "noon", date_noon }, { "midnight", date_midnight }, { "tea", date_tea }, { "PM", date_pm }, { "AM", date_am }, { "never", date_never }, { "now", date_now }, { NULL } }; static const char *number_name[] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", }; static const struct typelen { const char *type; int length; } typelen[] = { { "seconds", 1 }, { "minutes", 60 }, { "hours", 60*60 }, { "days", 24*60*60 }, { "weeks", 7*24*60*60 }, { NULL } }; static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched) { const struct typelen *tl; const struct special *s; const char *end = date; int i; while (isalpha(*++end)) /* scan to non-alpha */; for (i = 0; i < 12; i++) { size_t match = match_string(date, month_names[i]); if (match >= 3) { tm->tm_mon = i; *touched = 1; return end; } } for (s = special; s->name; s++) { size_t len = strlen(s->name); if (match_string(date, s->name) == len) { s->fn(tm, now, num); *touched = 1; return end; } } if (!*num) { for (i = 1; i < 11; i++) { size_t len = strlen(number_name[i]); if (match_string(date, number_name[i]) == len) { *num = i; *touched = 1; return end; } } if (match_string(date, "last") == 4) { *num = 1; *touched = 1; } return end; } tl = typelen; while (tl->type) { size_t len = strlen(tl->type); if (match_string(date, tl->type) >= len-1) { update_tm(tm, now, tl->length * (unsigned long)*num); *num = 0; *touched = 1; return end; } tl++; } for (i = 0; i < 7; i++) { size_t match = match_string(date, weekday_names[i]); if (match >= 3) { int diff, n = *num -1; *num = 0; diff = tm->tm_wday - i; if (diff <= 0) n++; diff += 7*n; update_tm(tm, now, diff * 24 * 60 * 60); *touched = 1; return end; } } if (match_string(date, "months") >= 5) { int n; update_tm(tm, now, 0); /* fill in date fields if needed */ n = tm->tm_mon - *num; *num = 0; while (n < 0) { n += 12; tm->tm_year--; } tm->tm_mon = n; *touched = 1; return end; } if (match_string(date, "years") >= 4) { update_tm(tm, now, 0); /* fill in date fields if needed */ tm->tm_year -= *num; *num = 0; *touched = 1; return end; } return end; } static const char *approxidate_digit(const char *date, struct tm *tm, int *num) { char *end; unsigned long number = strtoul(date, &end, 10); switch (*end) { case ':': case '.': case '/': case '-': if (isdigit(end[1])) { size_t match = match_multi_number(number, *end, date, end, tm); if (match) return date + match; } } /* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */ if (date[0] != '0' || end - date <= 2) *num = number; return end; } /* * Do we have a pending number at the end, or when * we see a new one? Let's assume it's a month day, * as in "Dec 6, 1992" */ static void pending_number(struct tm *tm, int *num) { int number = *num; if (number) { *num = 0; if (tm->tm_mday < 0 && number < 32) tm->tm_mday = number; else if (tm->tm_mon < 0 && number < 13) tm->tm_mon = number-1; else if (tm->tm_year < 0) { if (number > 1969 && number < 2100) tm->tm_year = number - 1900; else if (number > 69 && number < 100) tm->tm_year = number; else if (number < 38) tm->tm_year = 100 + number; /* We mess up for number = 00 ? */ } } } static git_time_t approxidate_str(const char *date, time_t time_sec, int *error_ret) { int number = 0; int touched = 0; struct tm tm = {0}, now; p_localtime_r(&time_sec, &tm); now = tm; tm.tm_year = -1; tm.tm_mon = -1; tm.tm_mday = -1; for (;;) { unsigned char c = *date; if (!c) break; date++; if (isdigit(c)) { pending_number(&tm, &number); date = approxidate_digit(date-1, &tm, &number); touched = 1; continue; } if (isalpha(c)) date = approxidate_alpha(date-1, &tm, &now, &number, &touched); } pending_number(&tm, &number); if (!touched) *error_ret = 1; return update_tm(&tm, &now, 0); } int git__date_parse(git_time_t *out, const char *date) { time_t time_sec; git_time_t timestamp; int offset, error_ret=0; if (!parse_date_basic(date, ×tamp, &offset)) { *out = timestamp; return 0; } if (time(&time_sec) == -1) return -1; *out = approxidate_str(date, time_sec, &error_ret); return error_ret; } int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date) { int written; struct tm gmt; time_t t; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(date); t = (time_t) (date->time + date->offset * 60); if (p_gmtime_r (&t, &gmt) == NULL) return -1; written = p_snprintf(out, len, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d", weekday_names[gmt.tm_wday], gmt.tm_mday, month_names[gmt.tm_mon], gmt.tm_year + 1900, gmt.tm_hour, gmt.tm_min, gmt.tm_sec, date->offset / 60, date->offset % 60); if (written < 0 || (written > (int) len - 1)) return -1; return 0; } git2r/src/libgit2/src/diff_generate.h0000644000175000017500000000742114125111754017305 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_diff_generate_h__ #define INCLUDE_diff_generate_h__ #include "common.h" #include "diff.h" #include "pool.h" #include "index.h" enum { GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */ GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */ GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */ }; #define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY) #define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA) enum { GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */ GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */ GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */ GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */ GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */ GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */ GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */ GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */ GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18), GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19), GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20), }; #define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF) #define GIT_DIFF__VERBOSE (1 << 30) extern void git_diff_addref(git_diff *diff); extern bool git_diff_delta__should_skip( const git_diff_options *opts, const git_diff_delta *delta); extern int git_diff__from_iterators( git_diff **diff_ptr, git_repository *repo, git_iterator *old_iter, git_iterator *new_iter, const git_diff_options *opts); extern int git_diff__commit( git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts); extern int git_diff__paired_foreach( git_diff *idx2head, git_diff *wd2idx, int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload), void *payload); /* Merge two `git_diff`s according to the callback given by `cb`. */ typedef git_diff_delta *(*git_diff__merge_cb)( const git_diff_delta *left, const git_diff_delta *right, git_pool *pool); extern int git_diff__merge( git_diff *onto, const git_diff *from, git_diff__merge_cb cb); extern git_diff_delta *git_diff__merge_like_cgit( const git_diff_delta *a, const git_diff_delta *b, git_pool *pool); /* Duplicate a `git_diff_delta` out of the `git_pool` */ extern git_diff_delta *git_diff__delta_dup( const git_diff_delta *d, git_pool *pool); extern int git_diff__oid_for_file( git_oid *out, git_diff *diff, const char *path, uint16_t mode, git_object_size_t size); extern int git_diff__oid_for_entry( git_oid *out, git_diff *diff, const git_index_entry *src, uint16_t mode, const git_oid *update_match); /* * Sometimes a git_diff_file will have a zero size; this attempts to * fill in the size without loading the blob if possible. If that is * not possible, then it will return the git_odb_object that had to be * loaded and the caller can use it or dispose of it as needed. */ GIT_INLINE(int) git_diff_file__resolve_zero_size( git_diff_file *file, git_odb_object **odb_obj, git_repository *repo) { int error; git_odb *odb; size_t len; git_object_t type; if ((error = git_repository_odb(&odb, repo)) < 0) return error; error = git_odb__read_header_or_object( odb_obj, &len, &type, odb, &file->id); git_odb_free(odb); if (!error) file->size = (git_object_size_t)len; return error; } #endif git2r/src/libgit2/src/diff_print.c0000644000175000017500000005051114125111754016640 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "diff.h" #include "diff_file.h" #include "patch_generate.h" #include "futils.h" #include "zstream.h" #include "blob.h" #include "delta.h" #include "git2/sys/diff.h" typedef struct { git_diff_format_t format; git_diff_line_cb print_cb; void *payload; git_buf *buf; git_diff_line line; const char *old_prefix; const char *new_prefix; uint32_t flags; int id_strlen; int (*strcomp)(const char *, const char *); } diff_print_info; static int diff_print_info_init__common( diff_print_info *pi, git_buf *out, git_repository *repo, git_diff_format_t format, git_diff_line_cb cb, void *payload) { pi->format = format; pi->print_cb = cb; pi->payload = payload; pi->buf = out; if (!pi->id_strlen) { if (!repo) pi->id_strlen = GIT_ABBREV_DEFAULT; else if (git_repository__configmap_lookup(&pi->id_strlen, repo, GIT_CONFIGMAP_ABBREV) < 0) return -1; } if (pi->id_strlen > GIT_OID_HEXSZ) pi->id_strlen = GIT_OID_HEXSZ; memset(&pi->line, 0, sizeof(pi->line)); pi->line.old_lineno = -1; pi->line.new_lineno = -1; pi->line.num_lines = 1; return 0; } static int diff_print_info_init_fromdiff( diff_print_info *pi, git_buf *out, git_diff *diff, git_diff_format_t format, git_diff_line_cb cb, void *payload) { git_repository *repo = diff ? diff->repo : NULL; memset(pi, 0, sizeof(diff_print_info)); if (diff) { pi->flags = diff->opts.flags; pi->id_strlen = diff->opts.id_abbrev; pi->old_prefix = diff->opts.old_prefix; pi->new_prefix = diff->opts.new_prefix; pi->strcomp = diff->strcomp; } return diff_print_info_init__common(pi, out, repo, format, cb, payload); } static int diff_print_info_init_frompatch( diff_print_info *pi, git_buf *out, git_patch *patch, git_diff_format_t format, git_diff_line_cb cb, void *payload) { GIT_ASSERT_ARG(patch); memset(pi, 0, sizeof(diff_print_info)); pi->flags = patch->diff_opts.flags; pi->id_strlen = patch->diff_opts.id_abbrev; pi->old_prefix = patch->diff_opts.old_prefix; pi->new_prefix = patch->diff_opts.new_prefix; return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload); } static char diff_pick_suffix(int mode) { if (S_ISDIR(mode)) return '/'; else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */ /* in git, modes are very regular, so we must have 0100755 mode */ return '*'; else return ' '; } char git_diff_status_char(git_delta_t status) { char code; switch (status) { case GIT_DELTA_ADDED: code = 'A'; break; case GIT_DELTA_DELETED: code = 'D'; break; case GIT_DELTA_MODIFIED: code = 'M'; break; case GIT_DELTA_RENAMED: code = 'R'; break; case GIT_DELTA_COPIED: code = 'C'; break; case GIT_DELTA_IGNORED: code = 'I'; break; case GIT_DELTA_UNTRACKED: code = '?'; break; case GIT_DELTA_TYPECHANGE: code = 'T'; break; case GIT_DELTA_UNREADABLE: code = 'X'; break; default: code = ' '; break; } return code; } static int diff_print_one_name_only( const git_diff_delta *delta, float progress, void *data) { diff_print_info *pi = data; git_buf *out = pi->buf; GIT_UNUSED(progress); if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && delta->status == GIT_DELTA_UNMODIFIED) return 0; git_buf_clear(out); git_buf_puts(out, delta->new_file.path); git_buf_putc(out, '\n'); if (git_buf_oom(out)) return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_name_status( const git_diff_delta *delta, float progress, void *data) { diff_print_info *pi = data; git_buf *out = pi->buf; char old_suffix, new_suffix, code = git_diff_status_char(delta->status); int(*strcomp)(const char *, const char *) = pi->strcomp ? pi->strcomp : git__strcmp; GIT_UNUSED(progress); if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') return 0; old_suffix = diff_pick_suffix(delta->old_file.mode); new_suffix = diff_pick_suffix(delta->new_file.mode); git_buf_clear(out); if (delta->old_file.path != delta->new_file.path && strcomp(delta->old_file.path,delta->new_file.path) != 0) git_buf_printf(out, "%c\t%s%c %s%c\n", code, delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); else if (delta->old_file.mode != delta->new_file.mode && delta->old_file.mode != 0 && delta->new_file.mode != 0) git_buf_printf(out, "%c\t%s%c %s%c\n", code, delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); else if (old_suffix != ' ') git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); else git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); if (git_buf_oom(out)) return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_raw( const git_diff_delta *delta, float progress, void *data) { diff_print_info *pi = data; git_buf *out = pi->buf; int id_abbrev; char code = git_diff_status_char(delta->status); char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; GIT_UNUSED(progress); if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') return 0; git_buf_clear(out); id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev : delta->new_file.id_abbrev; if (pi->id_strlen > id_abbrev) { git_error_set(GIT_ERROR_PATCH, "the patch input contains %d id characters (cannot print %d)", id_abbrev, pi->id_strlen); return -1; } git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id); git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id); git_buf_printf( out, (pi->id_strlen <= GIT_OID_HEXSZ) ? ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c", delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); if (delta->similarity > 0) git_buf_printf(out, "%03u", delta->similarity); if (delta->old_file.path != delta->new_file.path) git_buf_printf( out, "\t%s %s\n", delta->old_file.path, delta->new_file.path); else git_buf_printf( out, "\t%s\n", delta->old_file.path ? delta->old_file.path : delta->new_file.path); if (git_buf_oom(out)) return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_modes( git_buf *out, const git_diff_delta *delta) { git_buf_printf(out, "old mode %o\n", delta->old_file.mode); git_buf_printf(out, "new mode %o\n", delta->new_file.mode); return git_buf_oom(out) ? -1 : 0; } static int diff_print_oid_range( git_buf *out, const git_diff_delta *delta, int id_strlen, bool print_index) { char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; if (delta->old_file.mode && id_strlen > delta->old_file.id_abbrev) { git_error_set(GIT_ERROR_PATCH, "the patch input contains %d id characters (cannot print %d)", delta->old_file.id_abbrev, id_strlen); return -1; } if ((delta->new_file.mode && id_strlen > delta->new_file.id_abbrev)) { git_error_set(GIT_ERROR_PATCH, "the patch input contains %d id characters (cannot print %d)", delta->new_file.id_abbrev, id_strlen); return -1; } git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id); git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id); if (delta->old_file.mode == delta->new_file.mode) { if (print_index) git_buf_printf(out, "index %s..%s %o\n", start_oid, end_oid, delta->old_file.mode); } else { if (delta->old_file.mode == 0) git_buf_printf(out, "new file mode %o\n", delta->new_file.mode); else if (delta->new_file.mode == 0) git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode); else diff_print_modes(out, delta); if (print_index) git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); } return git_buf_oom(out) ? -1 : 0; } static int diff_delta_format_path( git_buf *out, const char *prefix, const char *filename) { if (git_buf_joinpath(out, prefix, filename) < 0) return -1; return git_buf_quote(out); } static int diff_delta_format_with_paths( git_buf *out, const git_diff_delta *delta, const char *template, const char *oldpath, const char *newpath) { if (git_oid_is_zero(&delta->old_file.id)) oldpath = "/dev/null"; if (git_oid_is_zero(&delta->new_file.id)) newpath = "/dev/null"; return git_buf_printf(out, template, oldpath, newpath); } static int diff_delta_format_similarity_header( git_buf *out, const git_diff_delta *delta) { git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; const char *type; int error = 0; if (delta->similarity > 100) { git_error_set(GIT_ERROR_PATCH, "invalid similarity %d", delta->similarity); error = -1; goto done; } GIT_ASSERT(delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED); if (delta->status == GIT_DELTA_RENAMED) type = "rename"; else type = "copy"; if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 || (error = git_buf_puts(&new_path, delta->new_file.path)) < 0 || (error = git_buf_quote(&old_path)) < 0 || (error = git_buf_quote(&new_path)) < 0) goto done; git_buf_printf(out, "similarity index %d%%\n" "%s from %s\n" "%s to %s\n", delta->similarity, type, old_path.ptr, type, new_path.ptr); if (git_buf_oom(out)) error = -1; done: git_buf_dispose(&old_path); git_buf_dispose(&new_path); return error; } static bool delta_is_unchanged(const git_diff_delta *delta) { if (git_oid_is_zero(&delta->old_file.id) && git_oid_is_zero(&delta->new_file.id)) return true; if (delta->old_file.mode == GIT_FILEMODE_COMMIT || delta->new_file.mode == GIT_FILEMODE_COMMIT) return false; if (git_oid_equal(&delta->old_file.id, &delta->new_file.id)) return true; return false; } int git_diff_delta__format_file_header( git_buf *out, const git_diff_delta *delta, const char *oldpfx, const char *newpfx, int id_strlen, bool print_index) { git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; bool unchanged = delta_is_unchanged(delta); int error = 0; if (!oldpfx) oldpfx = DIFF_OLD_PREFIX_DEFAULT; if (!newpfx) newpfx = DIFF_NEW_PREFIX_DEFAULT; if (!id_strlen) id_strlen = GIT_ABBREV_DEFAULT; if ((error = diff_delta_format_path( &old_path, oldpfx, delta->old_file.path)) < 0 || (error = diff_delta_format_path( &new_path, newpfx, delta->new_file.path)) < 0) goto done; git_buf_clear(out); git_buf_printf(out, "diff --git %s %s\n", old_path.ptr, new_path.ptr); if (unchanged && delta->old_file.mode != delta->new_file.mode) diff_print_modes(out, delta); if (delta->status == GIT_DELTA_RENAMED || (delta->status == GIT_DELTA_COPIED && unchanged)) { if ((error = diff_delta_format_similarity_header(out, delta)) < 0) goto done; } if (!unchanged) { if ((error = diff_print_oid_range(out, delta, id_strlen, print_index)) < 0) goto done; if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) diff_delta_format_with_paths(out, delta, "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr); } if (git_buf_oom(out)) error = -1; done: git_buf_dispose(&old_path); git_buf_dispose(&new_path); return error; } static int format_binary( diff_print_info *pi, git_diff_binary_t type, const char *data, size_t datalen, size_t inflatedlen) { const char *typename = type == GIT_DIFF_BINARY_DELTA ? "delta" : "literal"; const char *scan, *end; git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen); pi->line.num_lines++; for (scan = data, end = data + datalen; scan < end; ) { size_t chunk_len = end - scan; if (chunk_len > 52) chunk_len = 52; if (chunk_len <= 26) git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); else git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); git_buf_encode_base85(pi->buf, scan, chunk_len); git_buf_putc(pi->buf, '\n'); if (git_buf_oom(pi->buf)) return -1; scan += chunk_len; pi->line.num_lines++; } git_buf_putc(pi->buf, '\n'); if (git_buf_oom(pi->buf)) return -1; return 0; } static int diff_print_patch_file_binary_noshow( diff_print_info *pi, git_diff_delta *delta, const char *old_pfx, const char *new_pfx) { git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; int error; if ((error = diff_delta_format_path(&old_path, old_pfx, delta->old_file.path)) < 0 || (error = diff_delta_format_path(&new_path, new_pfx, delta->new_file.path)) < 0 || (error = diff_delta_format_with_paths(pi->buf, delta, "Binary files %s and %s differ\n", old_path.ptr, new_path.ptr)) < 0) goto done; pi->line.num_lines = 1; done: git_buf_dispose(&old_path); git_buf_dispose(&new_path); return error; } static int diff_print_patch_file_binary( diff_print_info *pi, git_diff_delta *delta, const char *old_pfx, const char *new_pfx, const git_diff_binary *binary) { size_t pre_binary_size; int error; if (delta->status == GIT_DELTA_UNMODIFIED) return 0; if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data) return diff_print_patch_file_binary_noshow( pi, delta, old_pfx, new_pfx); pre_binary_size = pi->buf->size; git_buf_printf(pi->buf, "GIT binary patch\n"); pi->line.num_lines++; if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || (error = format_binary(pi, binary->old_file.type, binary->old_file.data, binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { if (error == GIT_EBUFS) { git_error_clear(); git_buf_truncate(pi->buf, pre_binary_size); return diff_print_patch_file_binary_noshow( pi, delta, old_pfx, new_pfx); } } pi->line.num_lines++; return error; } static int diff_print_patch_file( const git_diff_delta *delta, float progress, void *data) { int error; diff_print_info *pi = data; const char *oldpfx = pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; const char *newpfx = pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) || (pi->flags & GIT_DIFF_FORCE_BINARY); bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); int id_strlen = pi->id_strlen; bool print_index = (pi->format != GIT_DIFF_FORMAT_PATCH_ID); if (binary && show_binary) id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev : delta->new_file.id_abbrev; GIT_UNUSED(progress); if (S_ISDIR(delta->new_file.mode) || delta->status == GIT_DELTA_UNMODIFIED || delta->status == GIT_DELTA_IGNORED || delta->status == GIT_DELTA_UNREADABLE || (delta->status == GIT_DELTA_UNTRACKED && (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) return 0; if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx, id_strlen, print_index)) < 0) return error; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_patch_binary( const git_diff_delta *delta, const git_diff_binary *binary, void *data) { diff_print_info *pi = data; const char *old_pfx = pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; const char *new_pfx = pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; int error; git_buf_clear(pi->buf); if ((error = diff_print_patch_file_binary( pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0) return error; pi->line.origin = GIT_DIFF_LINE_BINARY; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_patch_hunk( const git_diff_delta *d, const git_diff_hunk *h, void *data) { diff_print_info *pi = data; if (S_ISDIR(d->new_file.mode)) return 0; pi->line.origin = GIT_DIFF_LINE_HUNK_HDR; pi->line.content = h->header; pi->line.content_len = h->header_len; return pi->print_cb(d, h, &pi->line, pi->payload); } static int diff_print_patch_line( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *data) { diff_print_info *pi = data; if (S_ISDIR(delta->new_file.mode)) return 0; return pi->print_cb(delta, hunk, line, pi->payload); } /* print a git_diff to an output callback */ int git_diff_print( git_diff *diff, git_diff_format_t format, git_diff_line_cb print_cb, void *payload) { int error; git_buf buf = GIT_BUF_INIT; diff_print_info pi; git_diff_file_cb print_file = NULL; git_diff_binary_cb print_binary = NULL; git_diff_hunk_cb print_hunk = NULL; git_diff_line_cb print_line = NULL; switch (format) { case GIT_DIFF_FORMAT_PATCH: print_file = diff_print_patch_file; print_binary = diff_print_patch_binary; print_hunk = diff_print_patch_hunk; print_line = diff_print_patch_line; break; case GIT_DIFF_FORMAT_PATCH_ID: print_file = diff_print_patch_file; print_binary = diff_print_patch_binary; print_line = diff_print_patch_line; break; case GIT_DIFF_FORMAT_PATCH_HEADER: print_file = diff_print_patch_file; break; case GIT_DIFF_FORMAT_RAW: print_file = diff_print_one_raw; break; case GIT_DIFF_FORMAT_NAME_ONLY: print_file = diff_print_one_name_only; break; case GIT_DIFF_FORMAT_NAME_STATUS: print_file = diff_print_one_name_status; break; default: git_error_set(GIT_ERROR_INVALID, "unknown diff output format (%d)", format); return -1; } if ((error = diff_print_info_init_fromdiff(&pi, &buf, diff, format, print_cb, payload)) < 0) goto out; if ((error = git_diff_foreach(diff, print_file, print_binary, print_hunk, print_line, &pi)) != 0) { git_error_set_after_callback_function(error, "git_diff_print"); goto out; } out: git_buf_dispose(&buf); return error; } int git_diff_print_callback__to_buf( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { git_buf *output = payload; GIT_UNUSED(delta); GIT_UNUSED(hunk); if (!output) { git_error_set(GIT_ERROR_INVALID, "buffer pointer must be provided"); return -1; } if (line->origin == GIT_DIFF_LINE_ADDITION || line->origin == GIT_DIFF_LINE_DELETION || line->origin == GIT_DIFF_LINE_CONTEXT) git_buf_putc(output, line->origin); return git_buf_put(output, line->content, line->content_len); } int git_diff_print_callback__to_file_handle( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { FILE *fp = payload ? payload : stdout; int error; GIT_UNUSED(delta); GIT_UNUSED(hunk); if (line->origin == GIT_DIFF_LINE_CONTEXT || line->origin == GIT_DIFF_LINE_ADDITION || line->origin == GIT_DIFF_LINE_DELETION) { while ((error = fputc(line->origin, fp)) == EINTR) continue; if (error) { git_error_set(GIT_ERROR_OS, "could not write status"); return -1; } } if (fwrite(line->content, line->content_len, 1, fp) != 1) { git_error_set(GIT_ERROR_OS, "could not write line"); return -1; } return 0; } /* print a git_diff to a git_buf */ int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(diff); if ((error = git_buf_sanitize(out)) < 0) return error; return git_diff_print(diff, format, git_diff_print_callback__to_buf, out); } /* print a git_patch to an output callback */ int git_patch_print( git_patch *patch, git_diff_line_cb print_cb, void *payload) { git_buf temp = GIT_BUF_INIT; diff_print_info pi; int error; GIT_ASSERT_ARG(patch); GIT_ASSERT_ARG(print_cb); if ((error = diff_print_info_init_frompatch(&pi, &temp, patch, GIT_DIFF_FORMAT_PATCH, print_cb, payload)) < 0) goto out; if ((error = git_patch__invoke_callbacks(patch, diff_print_patch_file, diff_print_patch_binary, diff_print_patch_hunk, diff_print_patch_line, &pi)) < 0) { git_error_set_after_callback_function(error, "git_patch_print"); goto out; } out: git_buf_dispose(&temp); return error; } /* print a git_patch to a git_buf */ int git_patch_to_buf(git_buf *out, git_patch *patch) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(patch); if ((error = git_buf_sanitize(out)) < 0) return error; return git_patch_print(patch, git_diff_print_callback__to_buf, out); } git2r/src/libgit2/src/hash.c0000644000175000017500000000371714125111754015445 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "hash.h" int git_hash_global_init(void) { return git_hash_sha1_global_init(); } int git_hash_ctx_init(git_hash_ctx *ctx) { int error; if ((error = git_hash_sha1_ctx_init(&ctx->ctx.sha1)) < 0) return error; ctx->algo = GIT_HASH_ALGO_SHA1; return 0; } void git_hash_ctx_cleanup(git_hash_ctx *ctx) { switch (ctx->algo) { case GIT_HASH_ALGO_SHA1: git_hash_sha1_ctx_cleanup(&ctx->ctx.sha1); return; default: /* unreachable */ ; } } int git_hash_init(git_hash_ctx *ctx) { switch (ctx->algo) { case GIT_HASH_ALGO_SHA1: return git_hash_sha1_init(&ctx->ctx.sha1); default: /* unreachable */ ; } GIT_ASSERT(0); return -1; } int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) { switch (ctx->algo) { case GIT_HASH_ALGO_SHA1: return git_hash_sha1_update(&ctx->ctx.sha1, data, len); default: /* unreachable */ ; } GIT_ASSERT(0); return -1; } int git_hash_final(git_oid *out, git_hash_ctx *ctx) { switch (ctx->algo) { case GIT_HASH_ALGO_SHA1: return git_hash_sha1_final(out, &ctx->ctx.sha1); default: /* unreachable */ ; } GIT_ASSERT(0); return -1; } int git_hash_buf(git_oid *out, const void *data, size_t len) { git_hash_ctx ctx; int error = 0; if (git_hash_ctx_init(&ctx) < 0) return -1; if ((error = git_hash_update(&ctx, data, len)) >= 0) error = git_hash_final(out, &ctx); git_hash_ctx_cleanup(&ctx); return error; } int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n) { git_hash_ctx ctx; size_t i; int error = 0; if (git_hash_ctx_init(&ctx) < 0) return -1; for (i = 0; i < n; i++) { if ((error = git_hash_update(&ctx, vec[i].data, vec[i].len)) < 0) goto done; } error = git_hash_final(out, &ctx); done: git_hash_ctx_cleanup(&ctx); return error; } git2r/src/libgit2/src/tree.c0000644000175000017500000007703514125111754015465 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "tree.h" #include "commit.h" #include "git2/repository.h" #include "git2/object.h" #include "futils.h" #include "tree-cache.h" #include "index.h" #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 #define TREE_ENTRY_CHECK_NAMELEN(n) \ if (n > UINT16_MAX) { git_error_set(GIT_ERROR_INVALID, "tree entry path too long"); } static bool valid_filemode(const int filemode) { return (filemode == GIT_FILEMODE_TREE || filemode == GIT_FILEMODE_BLOB || filemode == GIT_FILEMODE_BLOB_EXECUTABLE || filemode == GIT_FILEMODE_LINK || filemode == GIT_FILEMODE_COMMIT); } GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode) { /* Tree bits set, but it's not a commit */ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_TREE) return GIT_FILEMODE_TREE; /* If any of the x bits are set */ if (GIT_PERMS_IS_EXEC(filemode)) return GIT_FILEMODE_BLOB_EXECUTABLE; /* 16XXXX means commit */ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_COMMIT) return GIT_FILEMODE_COMMIT; /* 12XXXX means symlink */ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_LINK) return GIT_FILEMODE_LINK; /* Otherwise, return a blob */ return GIT_FILEMODE_BLOB; } static int valid_entry_name(git_repository *repo, const char *filename) { return *filename != '\0' && git_path_validate(repo, filename, 0, GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH); } static int entry_sort_cmp(const void *a, const void *b) { const git_tree_entry *e1 = (const git_tree_entry *)a; const git_tree_entry *e2 = (const git_tree_entry *)b; return git_path_cmp( e1->filename, e1->filename_len, git_tree_entry__is_tree(e1), e2->filename, e2->filename_len, git_tree_entry__is_tree(e2), git__strncmp); } int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2) { return entry_sort_cmp(e1, e2); } /** * Allocate a new self-contained entry, with enough space after it to * store the filename and the id. */ static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, const git_oid *id) { git_tree_entry *entry = NULL; size_t tree_len; TREE_ENTRY_CHECK_NAMELEN(filename_len); if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) || GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) || GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, GIT_OID_RAWSZ)) return NULL; entry = git__calloc(1, tree_len); if (!entry) return NULL; { char *filename_ptr; void *id_ptr; filename_ptr = ((char *) entry) + sizeof(git_tree_entry); memcpy(filename_ptr, filename, filename_len); entry->filename = filename_ptr; id_ptr = filename_ptr + filename_len + 1; git_oid_cpy(id_ptr, id); entry->oid = id_ptr; } entry->filename_len = (uint16_t)filename_len; return entry; } struct tree_key_search { const char *filename; uint16_t filename_len; }; static int homing_search_cmp(const void *key, const void *array_member) { const struct tree_key_search *ksearch = key; const git_tree_entry *entry = array_member; const uint16_t len1 = ksearch->filename_len; const uint16_t len2 = entry->filename_len; return memcmp( ksearch->filename, entry->filename, len1 < len2 ? len1 : len2 ); } /* * Search for an entry in a given tree. * * Note that this search is performed in two steps because * of the way tree entries are sorted internally in git: * * Entries in a tree are not sorted alphabetically; two entries * with the same root prefix will have different positions * depending on whether they are folders (subtrees) or normal files. * * Consequently, it is not possible to find an entry on the tree * with a binary search if you don't know whether the filename * you're looking for is a folder or a normal file. * * To work around this, we first perform a homing binary search * on the tree, using the minimal length root prefix of our filename. * Once the comparisons for this homing search start becoming * ambiguous because of folder vs file sorting, we look linearly * around the area for our target file. */ static int tree_key_search( size_t *at_pos, const git_tree *tree, const char *filename, size_t filename_len) { struct tree_key_search ksearch; const git_tree_entry *entry; size_t homing, i; TREE_ENTRY_CHECK_NAMELEN(filename_len); ksearch.filename = filename; ksearch.filename_len = (uint16_t)filename_len; /* Initial homing search; find an entry on the tree with * the same prefix as the filename we're looking for */ if (git_array_search(&homing, tree->entries, &homing_search_cmp, &ksearch) < 0) return GIT_ENOTFOUND; /* just a signal error; not passed back to user */ /* We found a common prefix. Look forward as long as * there are entries that share the common prefix */ for (i = homing; i < tree->entries.size; ++i) { entry = git_array_get(tree->entries, i); if (homing_search_cmp(&ksearch, entry) < 0) break; if (entry->filename_len == filename_len && memcmp(filename, entry->filename, filename_len) == 0) { if (at_pos) *at_pos = i; return 0; } } /* If we haven't found our filename yet, look backwards * too as long as we have entries with the same prefix */ if (homing > 0) { i = homing - 1; do { entry = git_array_get(tree->entries, i); if (homing_search_cmp(&ksearch, entry) > 0) break; if (entry->filename_len == filename_len && memcmp(filename, entry->filename, filename_len) == 0) { if (at_pos) *at_pos = i; return 0; } } while (i-- > 0); } /* The filename doesn't exist at all */ return GIT_ENOTFOUND; } void git_tree_entry_free(git_tree_entry *entry) { if (entry == NULL) return; git__free(entry); } int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source) { git_tree_entry *cpy; GIT_ASSERT_ARG(source); cpy = alloc_entry(source->filename, source->filename_len, source->oid); if (cpy == NULL) return -1; cpy->attr = source->attr; *dest = cpy; return 0; } void git_tree__free(void *_tree) { git_tree *tree = _tree; git_odb_object_free(tree->odb_obj); git_array_clear(tree->entries); git__free(tree); } git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry) { return normalize_filemode(entry->attr); } git_filemode_t git_tree_entry_filemode_raw(const git_tree_entry *entry) { return entry->attr; } const char *git_tree_entry_name(const git_tree_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL); return entry->filename; } const git_oid *git_tree_entry_id(const git_tree_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL); return entry->oid; } git_object_t git_tree_entry_type(const git_tree_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, GIT_OBJECT_INVALID); if (S_ISGITLINK(entry->attr)) return GIT_OBJECT_COMMIT; else if (S_ISDIR(entry->attr)) return GIT_OBJECT_TREE; else return GIT_OBJECT_BLOB; } int git_tree_entry_to_object( git_object **object_out, git_repository *repo, const git_tree_entry *entry) { GIT_ASSERT_ARG(entry); GIT_ASSERT_ARG(object_out); return git_object_lookup(object_out, repo, entry->oid, GIT_OBJECT_ANY); } static const git_tree_entry *entry_fromname( const git_tree *tree, const char *name, size_t name_len) { size_t idx; if (tree_key_search(&idx, tree, name, name_len) < 0) return NULL; return git_array_get(tree->entries, idx); } const git_tree_entry *git_tree_entry_byname( const git_tree *tree, const char *filename) { GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL); GIT_ASSERT_ARG_WITH_RETVAL(filename, NULL); return entry_fromname(tree, filename, strlen(filename)); } const git_tree_entry *git_tree_entry_byindex( const git_tree *tree, size_t idx) { GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL); return git_array_get(tree->entries, idx); } const git_tree_entry *git_tree_entry_byid( const git_tree *tree, const git_oid *id) { size_t i; const git_tree_entry *e; GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL); git_array_foreach(tree->entries, i, e) { if (memcmp(&e->oid->id, &id->id, sizeof(id->id)) == 0) return e; } return NULL; } size_t git_tree_entrycount(const git_tree *tree) { GIT_ASSERT_ARG_WITH_RETVAL(tree, 0); return tree->entries.size; } size_t git_treebuilder_entrycount(git_treebuilder *bld) { GIT_ASSERT_ARG_WITH_RETVAL(bld, 0); return git_strmap_size(bld->map); } static int tree_error(const char *str, const char *path) { if (path) git_error_set(GIT_ERROR_TREE, "%s - %s", str, path); else git_error_set(GIT_ERROR_TREE, "%s", str); return -1; } static int parse_mode(uint16_t *mode_out, const char *buffer, size_t buffer_len, const char **buffer_out) { int32_t mode; int error; if (!buffer_len || git__isspace(*buffer)) return -1; if ((error = git__strntol32(&mode, buffer, buffer_len, buffer_out, 8)) < 0) return error; if (mode < 0 || mode > UINT16_MAX) return -1; *mode_out = mode; return 0; } int git_tree__parse_raw(void *_tree, const char *data, size_t size) { git_tree *tree = _tree; const char *buffer; const char *buffer_end; buffer = data; buffer_end = buffer + size; tree->odb_obj = NULL; git_array_init_to_size(tree->entries, DEFAULT_TREE_SIZE); GIT_ERROR_CHECK_ARRAY(tree->entries); while (buffer < buffer_end) { git_tree_entry *entry; size_t filename_len; const char *nul; uint16_t attr; if (parse_mode(&attr, buffer, buffer_end - buffer, &buffer) < 0 || !buffer) return tree_error("failed to parse tree: can't parse filemode", NULL); if (buffer >= buffer_end || (*buffer++) != ' ') return tree_error("failed to parse tree: missing space after filemode", NULL); if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL) return tree_error("failed to parse tree: object is corrupted", NULL); if ((filename_len = nul - buffer) == 0 || filename_len > UINT16_MAX) return tree_error("failed to parse tree: can't parse filename", NULL); if ((buffer_end - (nul + 1)) < GIT_OID_RAWSZ) return tree_error("failed to parse tree: can't parse OID", NULL); /* Allocate the entry */ { entry = git_array_alloc(tree->entries); GIT_ERROR_CHECK_ALLOC(entry); entry->attr = attr; entry->filename_len = (uint16_t)filename_len; entry->filename = buffer; entry->oid = (git_oid *) ((char *) buffer + filename_len + 1); } buffer += filename_len + 1; buffer += GIT_OID_RAWSZ; } return 0; } int git_tree__parse(void *_tree, git_odb_object *odb_obj) { git_tree *tree = _tree; if ((git_tree__parse_raw(tree, git_odb_object_data(odb_obj), git_odb_object_size(odb_obj))) < 0) return -1; if (git_odb_object_dup(&tree->odb_obj, odb_obj) < 0) return -1; return 0; } static size_t find_next_dir(const char *dirname, git_index *index, size_t start) { size_t dirlen, i, entries = git_index_entrycount(index); dirlen = strlen(dirname); for (i = start; i < entries; ++i) { const git_index_entry *entry = git_index_get_byindex(index, i); if (strlen(entry->path) < dirlen || memcmp(entry->path, dirname, dirlen) || (dirlen > 0 && entry->path[dirlen] != '/')) { break; } } return i; } static git_object_t otype_from_mode(git_filemode_t filemode) { switch (filemode) { case GIT_FILEMODE_TREE: return GIT_OBJECT_TREE; case GIT_FILEMODE_COMMIT: return GIT_OBJECT_COMMIT; default: return GIT_OBJECT_BLOB; } } static int check_entry(git_repository *repo, const char *filename, const git_oid *id, git_filemode_t filemode) { if (!valid_filemode(filemode)) return tree_error("failed to insert entry: invalid filemode for file", filename); if (!valid_entry_name(repo, filename)) return tree_error("failed to insert entry: invalid name for a tree entry", filename); if (git_oid_is_zero(id)) return tree_error("failed to insert entry: invalid null OID", filename); if (filemode != GIT_FILEMODE_COMMIT && !git_object__is_valid(repo, id, otype_from_mode(filemode))) return tree_error("failed to insert entry: invalid object specified", filename); return 0; } static int git_treebuilder__write_with_buffer( git_oid *oid, git_treebuilder *bld, git_buf *buf) { int error = 0; size_t i, entrycount; git_odb *odb; git_tree_entry *entry; git_vector entries = GIT_VECTOR_INIT; git_buf_clear(buf); entrycount = git_strmap_size(bld->map); if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0) goto out; if (buf->asize == 0 && (error = git_buf_grow(buf, entrycount * 72)) < 0) goto out; git_strmap_foreach_value(bld->map, entry, { if ((error = git_vector_insert(&entries, entry)) < 0) goto out; }); git_vector_sort(&entries); for (i = 0; i < entries.length && !error; ++i) { entry = git_vector_get(&entries, i); git_buf_printf(buf, "%o ", entry->attr); git_buf_put(buf, entry->filename, entry->filename_len + 1); git_buf_put(buf, (char *)entry->oid->id, GIT_OID_RAWSZ); if (git_buf_oom(buf)) { error = -1; goto out; } } if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0) error = git_odb_write(oid, odb, buf->ptr, buf->size, GIT_OBJECT_TREE); out: git_vector_free(&entries); return error; } static int append_entry( git_treebuilder *bld, const char *filename, const git_oid *id, git_filemode_t filemode, bool validate) { git_tree_entry *entry; int error = 0; if (validate && ((error = check_entry(bld->repo, filename, id, filemode)) < 0)) return error; entry = alloc_entry(filename, strlen(filename), id); GIT_ERROR_CHECK_ALLOC(entry); entry->attr = (uint16_t)filemode; if ((error = git_strmap_set(bld->map, entry->filename, entry)) < 0) { git_tree_entry_free(entry); git_error_set(GIT_ERROR_TREE, "failed to append entry %s to the tree builder", filename); return -1; } return 0; } static int write_tree( git_oid *oid, git_repository *repo, git_index *index, const char *dirname, size_t start, git_buf *shared_buf) { git_treebuilder *bld = NULL; size_t i, entries = git_index_entrycount(index); int error; size_t dirname_len = strlen(dirname); const git_tree_cache *cache; cache = git_tree_cache_get(index->tree, dirname); if (cache != NULL && cache->entry_count >= 0){ git_oid_cpy(oid, &cache->oid); return (int)find_next_dir(dirname, index, start); } if ((error = git_treebuilder_new(&bld, repo, NULL)) < 0 || bld == NULL) return -1; /* * This loop is unfortunate, but necessary. The index doesn't have * any directores, so we need to handle that manually, and we * need to keep track of the current position. */ for (i = start; i < entries; ++i) { const git_index_entry *entry = git_index_get_byindex(index, i); const char *filename, *next_slash; /* * If we've left our (sub)tree, exit the loop and return. The * first check is an early out (and security for the * third). The second check is a simple prefix comparison. The * third check catches situations where there is a directory * win32/sys and a file win32mmap.c. Without it, the following * code believes there is a file win32/mmap.c */ if (strlen(entry->path) < dirname_len || memcmp(entry->path, dirname, dirname_len) || (dirname_len > 0 && entry->path[dirname_len] != '/')) { break; } filename = entry->path + dirname_len; if (*filename == '/') filename++; next_slash = strchr(filename, '/'); if (next_slash) { git_oid sub_oid; int written; char *subdir, *last_comp; subdir = git__strndup(entry->path, next_slash - entry->path); GIT_ERROR_CHECK_ALLOC(subdir); /* Write out the subtree */ written = write_tree(&sub_oid, repo, index, subdir, i, shared_buf); if (written < 0) { git__free(subdir); goto on_error; } else { i = written - 1; /* -1 because of the loop increment */ } /* * We need to figure out what we want toinsert * into this tree. If we're traversing * deps/zlib/, then we only want to write * 'zlib' into the tree. */ last_comp = strrchr(subdir, '/'); if (last_comp) { last_comp++; /* Get rid of the '/' */ } else { last_comp = subdir; } error = append_entry(bld, last_comp, &sub_oid, S_IFDIR, true); git__free(subdir); if (error < 0) goto on_error; } else { error = append_entry(bld, filename, &entry->id, entry->mode, true); if (error < 0) goto on_error; } } if (git_treebuilder__write_with_buffer(oid, bld, shared_buf) < 0) goto on_error; git_treebuilder_free(bld); return (int)i; on_error: git_treebuilder_free(bld); return -1; } int git_tree__write_index( git_oid *oid, git_index *index, git_repository *repo) { int ret; git_tree *tree; git_buf shared_buf = GIT_BUF_INIT; bool old_ignore_case = false; GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(repo); if (git_index_has_conflicts(index)) { git_error_set(GIT_ERROR_INDEX, "cannot create a tree from a not fully merged index."); return GIT_EUNMERGED; } if (index->tree != NULL && index->tree->entry_count >= 0) { git_oid_cpy(oid, &index->tree->oid); return 0; } /* The tree cache didn't help us; we'll have to write * out a tree. If the index is ignore_case, we must * make it case-sensitive for the duration of the tree-write * operation. */ if (index->ignore_case) { old_ignore_case = true; git_index__set_ignore_case(index, false); } ret = write_tree(oid, repo, index, "", 0, &shared_buf); git_buf_dispose(&shared_buf); if (old_ignore_case) git_index__set_ignore_case(index, true); index->tree = NULL; if (ret < 0) return ret; git_pool_clear(&index->tree_pool); if ((ret = git_tree_lookup(&tree, repo, oid)) < 0) return ret; /* Read the tree cache into the index */ ret = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool); git_tree_free(tree); return ret; } int git_treebuilder_new( git_treebuilder **builder_p, git_repository *repo, const git_tree *source) { git_treebuilder *bld; size_t i; GIT_ASSERT_ARG(builder_p); GIT_ASSERT_ARG(repo); bld = git__calloc(1, sizeof(git_treebuilder)); GIT_ERROR_CHECK_ALLOC(bld); bld->repo = repo; if (git_strmap_new(&bld->map) < 0) { git__free(bld); return -1; } if (source != NULL) { git_tree_entry *entry_src; git_array_foreach(source->entries, i, entry_src) { if (append_entry( bld, entry_src->filename, entry_src->oid, entry_src->attr, false) < 0) goto on_error; } } *builder_p = bld; return 0; on_error: git_treebuilder_free(bld); return -1; } int git_treebuilder_insert( const git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, git_filemode_t filemode) { git_tree_entry *entry; int error; GIT_ASSERT_ARG(bld); GIT_ASSERT_ARG(id); GIT_ASSERT_ARG(filename); if ((error = check_entry(bld->repo, filename, id, filemode)) < 0) return error; if ((entry = git_strmap_get(bld->map, filename)) != NULL) { git_oid_cpy((git_oid *) entry->oid, id); } else { entry = alloc_entry(filename, strlen(filename), id); GIT_ERROR_CHECK_ALLOC(entry); if ((error = git_strmap_set(bld->map, entry->filename, entry)) < 0) { git_tree_entry_free(entry); git_error_set(GIT_ERROR_TREE, "failed to insert %s", filename); return -1; } } entry->attr = filemode; if (entry_out) *entry_out = entry; return 0; } static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename) { GIT_ASSERT_ARG_WITH_RETVAL(bld, NULL); GIT_ASSERT_ARG_WITH_RETVAL(filename, NULL); return git_strmap_get(bld->map, filename); } const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename) { return treebuilder_get(bld, filename); } int git_treebuilder_remove(git_treebuilder *bld, const char *filename) { git_tree_entry *entry = treebuilder_get(bld, filename); if (entry == NULL) return tree_error("failed to remove entry: file isn't in the tree", filename); git_strmap_delete(bld->map, filename); git_tree_entry_free(entry); return 0; } int git_treebuilder_write(git_oid *oid, git_treebuilder *bld) { GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(bld); return git_treebuilder__write_with_buffer(oid, bld, &bld->write_cache); } int git_treebuilder_filter( git_treebuilder *bld, git_treebuilder_filter_cb filter, void *payload) { const char *filename; git_tree_entry *entry; GIT_ASSERT_ARG(bld); GIT_ASSERT_ARG(filter); git_strmap_foreach(bld->map, filename, entry, { if (filter(entry, payload)) { git_strmap_delete(bld->map, filename); git_tree_entry_free(entry); } }); return 0; } int git_treebuilder_clear(git_treebuilder *bld) { git_tree_entry *e; GIT_ASSERT_ARG(bld); git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e)); git_strmap_clear(bld->map); return 0; } void git_treebuilder_free(git_treebuilder *bld) { if (bld == NULL) return; git_buf_dispose(&bld->write_cache); git_treebuilder_clear(bld); git_strmap_free(bld->map); git__free(bld); } static size_t subpath_len(const char *path) { const char *slash_pos = strchr(path, '/'); if (slash_pos == NULL) return strlen(path); return slash_pos - path; } int git_tree_entry_bypath( git_tree_entry **entry_out, const git_tree *root, const char *path) { int error = 0; git_tree *subtree; const git_tree_entry *entry; size_t filename_len; /* Find how long is the current path component (i.e. * the filename between two slashes */ filename_len = subpath_len(path); if (filename_len == 0) { git_error_set(GIT_ERROR_TREE, "invalid tree path given"); return GIT_ENOTFOUND; } entry = entry_fromname(root, path, filename_len); if (entry == NULL) { git_error_set(GIT_ERROR_TREE, "the path '%.*s' does not exist in the given tree", (int) filename_len, path); return GIT_ENOTFOUND; } switch (path[filename_len]) { case '/': /* If there are more components in the path... * then this entry *must* be a tree */ if (!git_tree_entry__is_tree(entry)) { git_error_set(GIT_ERROR_TREE, "the path '%.*s' exists but is not a tree", (int) filename_len, path); return GIT_ENOTFOUND; } /* If there's only a slash left in the path, we * return the current entry; otherwise, we keep * walking down the path */ if (path[filename_len + 1] != '\0') break; /* fall through */ case '\0': /* If there are no more components in the path, return * this entry */ return git_tree_entry_dup(entry_out, entry); } if (git_tree_lookup(&subtree, root->object.repo, entry->oid) < 0) return -1; error = git_tree_entry_bypath( entry_out, subtree, path + filename_len + 1 ); git_tree_free(subtree); return error; } static int tree_walk( const git_tree *tree, git_treewalk_cb callback, git_buf *path, void *payload, bool preorder) { int error = 0; size_t i; const git_tree_entry *entry; git_array_foreach(tree->entries, i, entry) { if (preorder) { error = callback(path->ptr, entry, payload); if (error < 0) { /* negative value stops iteration */ git_error_set_after_callback_function(error, "git_tree_walk"); break; } if (error > 0) { /* positive value skips this entry */ error = 0; continue; } } if (git_tree_entry__is_tree(entry)) { git_tree *subtree; size_t path_len = git_buf_len(path); error = git_tree_lookup(&subtree, tree->object.repo, entry->oid); if (error < 0) break; /* append the next entry to the path */ git_buf_puts(path, entry->filename); git_buf_putc(path, '/'); if (git_buf_oom(path)) error = -1; else error = tree_walk(subtree, callback, path, payload, preorder); git_tree_free(subtree); if (error != 0) break; git_buf_truncate(path, path_len); } if (!preorder) { error = callback(path->ptr, entry, payload); if (error < 0) { /* negative value stops iteration */ git_error_set_after_callback_function(error, "git_tree_walk"); break; } error = 0; } } return error; } int git_tree_walk( const git_tree *tree, git_treewalk_mode mode, git_treewalk_cb callback, void *payload) { int error = 0; git_buf root_path = GIT_BUF_INIT; if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) { git_error_set(GIT_ERROR_INVALID, "invalid walking mode for tree walk"); return -1; } error = tree_walk( tree, callback, &root_path, payload, (mode == GIT_TREEWALK_PRE)); git_buf_dispose(&root_path); return error; } static int compare_entries(const void *_a, const void *_b) { const git_tree_update *a = (git_tree_update *) _a; const git_tree_update *b = (git_tree_update *) _b; return strcmp(a->path, b->path); } static int on_dup_entry(void **old, void *new) { GIT_UNUSED(old); GIT_UNUSED(new); git_error_set(GIT_ERROR_TREE, "duplicate entries given for update"); return -1; } /* * We keep the previous tree and the new one at each level of the * stack. When we leave a level we're done with that tree and we can * write it out to the odb. */ typedef struct { git_treebuilder *bld; git_tree *tree; char *name; } tree_stack_entry; /** Count how many slashes (i.e. path components) there are in this string */ GIT_INLINE(size_t) count_slashes(const char *path) { size_t count = 0; const char *slash; while ((slash = strchr(path, '/')) != NULL) { count++; path = slash + 1; } return count; } static bool next_component(git_buf *out, const char *in) { const char *slash = strchr(in, '/'); git_buf_clear(out); if (slash) git_buf_put(out, in, slash - in); return !!slash; } static int create_popped_tree(tree_stack_entry *current, tree_stack_entry *popped, git_buf *component) { int error; git_oid new_tree; git_tree_free(popped->tree); /* If the tree would be empty, remove it from the one higher up */ if (git_treebuilder_entrycount(popped->bld) == 0) { git_treebuilder_free(popped->bld); error = git_treebuilder_remove(current->bld, popped->name); git__free(popped->name); return error; } error = git_treebuilder_write(&new_tree, popped->bld); git_treebuilder_free(popped->bld); if (error < 0) { git__free(popped->name); return error; } /* We've written out the tree, now we have to put the new value into its parent */ git_buf_clear(component); git_buf_puts(component, popped->name); git__free(popped->name); GIT_ERROR_CHECK_ALLOC(component->ptr); /* Error out if this would create a D/F conflict in this update */ if (current->tree) { const git_tree_entry *to_replace; to_replace = git_tree_entry_byname(current->tree, component->ptr); if (to_replace && git_tree_entry_type(to_replace) != GIT_OBJECT_TREE) { git_error_set(GIT_ERROR_TREE, "D/F conflict when updating tree"); return -1; } } return git_treebuilder_insert(NULL, current->bld, component->ptr, &new_tree, GIT_FILEMODE_TREE); } int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates) { git_array_t(tree_stack_entry) stack = GIT_ARRAY_INIT; tree_stack_entry *root_elem; git_vector entries; int error; size_t i; git_buf component = GIT_BUF_INIT; if ((error = git_vector_init(&entries, nupdates, compare_entries)) < 0) return error; /* Sort the entries for treversal */ for (i = 0 ; i < nupdates; i++) { if ((error = git_vector_insert_sorted(&entries, (void *) &updates[i], on_dup_entry)) < 0) goto cleanup; } root_elem = git_array_alloc(stack); GIT_ERROR_CHECK_ALLOC(root_elem); memset(root_elem, 0, sizeof(*root_elem)); if (baseline && (error = git_tree_dup(&root_elem->tree, baseline)) < 0) goto cleanup; if ((error = git_treebuilder_new(&root_elem->bld, repo, root_elem->tree)) < 0) goto cleanup; for (i = 0; i < nupdates; i++) { const git_tree_update *last_update = i == 0 ? NULL : git_vector_get(&entries, i-1); const git_tree_update *update = git_vector_get(&entries, i); size_t common_prefix = 0, steps_up, j; const char *path; /* Figure out how much we need to change from the previous tree */ if (last_update) common_prefix = git_path_common_dirlen(last_update->path, update->path); /* * The entries are sorted, so when we find we're no * longer in the same directory, we need to abandon * the old tree (steps up) and dive down to the next * one. */ steps_up = last_update == NULL ? 0 : count_slashes(&last_update->path[common_prefix]); for (j = 0; j < steps_up; j++) { tree_stack_entry *current, *popped = git_array_pop(stack); GIT_ASSERT(popped); current = git_array_last(stack); GIT_ASSERT(current); if ((error = create_popped_tree(current, popped, &component)) < 0) goto cleanup; } /* Now that we've created the trees we popped from the stack, let's go back down */ path = &update->path[common_prefix]; while (next_component(&component, path)) { tree_stack_entry *last, *new_entry; const git_tree_entry *entry; last = git_array_last(stack); entry = last->tree ? git_tree_entry_byname(last->tree, component.ptr) : NULL; if (!entry) entry = treebuilder_get(last->bld, component.ptr); if (entry && git_tree_entry_type(entry) != GIT_OBJECT_TREE) { git_error_set(GIT_ERROR_TREE, "D/F conflict when updating tree"); error = -1; goto cleanup; } new_entry = git_array_alloc(stack); GIT_ERROR_CHECK_ALLOC(new_entry); memset(new_entry, 0, sizeof(*new_entry)); new_entry->tree = NULL; if (entry && (error = git_tree_lookup(&new_entry->tree, repo, git_tree_entry_id(entry))) < 0) goto cleanup; if ((error = git_treebuilder_new(&new_entry->bld, repo, new_entry->tree)) < 0) goto cleanup; new_entry->name = git__strdup(component.ptr); GIT_ERROR_CHECK_ALLOC(new_entry->name); /* Get to the start of the next component */ path += component.size + 1; } /* After all that, we're finally at the place where we want to perform the update */ switch (update->action) { case GIT_TREE_UPDATE_UPSERT: { /* Make sure we're replacing something of the same type */ tree_stack_entry *last = git_array_last(stack); char *basename = git_path_basename(update->path); const git_tree_entry *e = git_treebuilder_get(last->bld, basename); if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) { git__free(basename); git_error_set(GIT_ERROR_TREE, "cannot replace '%s' with '%s' at '%s'", git_object_type2string(git_tree_entry_type(e)), git_object_type2string(git_object__type_from_filemode(update->filemode)), update->path); error = -1; goto cleanup; } error = git_treebuilder_insert(NULL, last->bld, basename, &update->id, update->filemode); git__free(basename); break; } case GIT_TREE_UPDATE_REMOVE: { tree_stack_entry *last = git_array_last(stack); char *basename = git_path_basename(update->path); error = git_treebuilder_remove(last->bld, basename); git__free(basename); break; } default: git_error_set(GIT_ERROR_TREE, "unknown action for update"); error = -1; goto cleanup; } if (error < 0) goto cleanup; } /* We're done, go up the stack again and write out the tree */ { tree_stack_entry *current = NULL, *popped = NULL; while ((popped = git_array_pop(stack)) != NULL) { current = git_array_last(stack); /* We've reached the top, current is the root tree */ if (!current) break; if ((error = create_popped_tree(current, popped, &component)) < 0) goto cleanup; } /* Write out the root tree */ git__free(popped->name); git_tree_free(popped->tree); error = git_treebuilder_write(out, popped->bld); git_treebuilder_free(popped->bld); if (error < 0) goto cleanup; } cleanup: { tree_stack_entry *e; while ((e = git_array_pop(stack)) != NULL) { git_treebuilder_free(e->bld); git_tree_free(e->tree); git__free(e->name); } } git_buf_dispose(&component); git_array_clear(stack); git_vector_free(&entries); return error; } /* Deprecated Functions */ #ifndef GIT_DEPRECATE_HARD int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *buf) { GIT_UNUSED(buf); return git_treebuilder_write(oid, bld); } #endif git2r/src/libgit2/src/wildmatch.c0000644000175000017500000002235114125111754016471 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. * * Do shell-style pattern matching for ?, \, [], and * characters. * It is 8bit clean. * * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. * Rich $alz is now . * * Modified by Wayne Davison to special-case '/' matching, to make '**' * work differently than '*', and to fix the character-class code. * * Imported from git.git. */ #include "wildmatch.h" #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 #define GIT_ALPHA 0x04 #define GIT_GLOB_SPECIAL 0x08 #define GIT_REGEX_SPECIAL 0x10 #define GIT_PATHSPEC_MAGIC 0x20 #define GIT_CNTRL 0x40 #define GIT_PUNCT 0x80 enum { S = GIT_SPACE, A = GIT_ALPHA, D = GIT_DIGIT, G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */ P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */ X = GIT_CNTRL, U = GIT_PUNCT, Z = GIT_CNTRL | GIT_SPACE }; static const unsigned char sane_ctype[256] = { X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */ S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */ A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */ A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */ /* Nothing in the 128.. range */ }; #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL) typedef unsigned char uchar; /* What character marks an inverted character class? */ #define NEGATE_CLASS '!' #define NEGATE_CLASS2 '^' #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \ && *(class) == *(litmatch) \ && strncmp((char*)class, litmatch, len) == 0) #if defined STDC_HEADERS || !defined isascii # define ISASCII(c) 1 #else # define ISASCII(c) isascii(c) #endif #ifdef isblank # define ISBLANK(c) (ISASCII(c) && isblank(c)) #else # define ISBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef isgraph # define ISGRAPH(c) (ISASCII(c) && isgraph(c)) #else # define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c)) #endif #define ISPRINT(c) (ISASCII(c) && isprint(c)) #define ISDIGIT(c) (ISASCII(c) && isdigit(c)) #define ISALNUM(c) (ISASCII(c) && isalnum(c)) #define ISALPHA(c) (ISASCII(c) && isalpha(c)) #define ISCNTRL(c) (ISASCII(c) && iscntrl(c)) #define ISLOWER(c) (ISASCII(c) && islower(c)) #define ISPUNCT(c) (ISASCII(c) && ispunct(c)) #define ISSPACE(c) (ISASCII(c) && isspace(c)) #define ISUPPER(c) (ISASCII(c) && isupper(c)) #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) /* Match pattern "p" against "text" */ static int dowild(const uchar *p, const uchar *text, unsigned int flags) { uchar p_ch; const uchar *pattern = p; for ( ; (p_ch = *p) != '\0'; text++, p++) { int matched, match_slash, negated; uchar t_ch, prev_ch; if ((t_ch = *text) == '\0' && p_ch != '*') return WM_ABORT_ALL; if ((flags & WM_CASEFOLD) && ISUPPER(t_ch)) t_ch = tolower(t_ch); if ((flags & WM_CASEFOLD) && ISUPPER(p_ch)) p_ch = tolower(p_ch); switch (p_ch) { case '\\': /* Literal match with following character. Note that the test * in "default" handles the p[1] == '\0' failure case. */ p_ch = *++p; /* FALLTHROUGH */ default: if (t_ch != p_ch) return WM_NOMATCH; continue; case '?': /* Match anything but '/'. */ if ((flags & WM_PATHNAME) && t_ch == '/') return WM_NOMATCH; continue; case '*': if (*++p == '*') { const uchar *prev_p = p - 2; while (*++p == '*') {} if (!(flags & WM_PATHNAME)) /* without WM_PATHNAME, '*' == '**' */ match_slash = 1; else if ((prev_p < pattern || *prev_p == '/') && (*p == '\0' || *p == '/' || (p[0] == '\\' && p[1] == '/'))) { /* * Assuming we already match 'foo/' and are at * , just assume it matches * nothing and go ahead match the rest of the * pattern with the remaining string. This * helps make foo/<*><*>/bar (<> because * otherwise it breaks C comment syntax) match * both foo/bar and foo/a/bar. */ if (p[0] == '/' && dowild(p + 1, text, flags) == WM_MATCH) return WM_MATCH; match_slash = 1; } else /* WM_PATHNAME is set */ match_slash = 0; } else /* without WM_PATHNAME, '*' == '**' */ match_slash = flags & WM_PATHNAME ? 0 : 1; if (*p == '\0') { /* Trailing "**" matches everything. Trailing "*" matches * only if there are no more slash characters. */ if (!match_slash) { if (strchr((char*)text, '/') != NULL) return WM_NOMATCH; } return WM_MATCH; } else if (!match_slash && *p == '/') { /* * _one_ asterisk followed by a slash * with WM_PATHNAME matches the next * directory */ const char *slash = strchr((char*)text, '/'); if (!slash) return WM_NOMATCH; text = (const uchar*)slash; /* the slash is consumed by the top-level for loop */ break; } while (1) { if (t_ch == '\0') break; /* * Try to advance faster when an asterisk is * followed by a literal. We know in this case * that the string before the literal * must belong to "*". * If match_slash is false, do not look past * the first slash as it cannot belong to '*'. */ if (!is_glob_special(*p)) { p_ch = *p; if ((flags & WM_CASEFOLD) && ISUPPER(p_ch)) p_ch = tolower(p_ch); while ((t_ch = *text) != '\0' && (match_slash || t_ch != '/')) { if ((flags & WM_CASEFOLD) && ISUPPER(t_ch)) t_ch = tolower(t_ch); if (t_ch == p_ch) break; text++; } if (t_ch != p_ch) return WM_NOMATCH; } if ((matched = dowild(p, text, flags)) != WM_NOMATCH) { if (!match_slash || matched != WM_ABORT_TO_STARSTAR) return matched; } else if (!match_slash && t_ch == '/') return WM_ABORT_TO_STARSTAR; t_ch = *++text; } return WM_ABORT_ALL; case '[': p_ch = *++p; #ifdef NEGATE_CLASS2 if (p_ch == NEGATE_CLASS2) p_ch = NEGATE_CLASS; #endif /* Assign literal 1/0 because of "matched" comparison. */ negated = p_ch == NEGATE_CLASS ? 1 : 0; if (negated) { /* Inverted character class. */ p_ch = *++p; } prev_ch = 0; matched = 0; do { if (!p_ch) return WM_ABORT_ALL; if (p_ch == '\\') { p_ch = *++p; if (!p_ch) return WM_ABORT_ALL; if (t_ch == p_ch) matched = 1; } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { p_ch = *++p; if (p_ch == '\\') { p_ch = *++p; if (!p_ch) return WM_ABORT_ALL; } if (t_ch <= p_ch && t_ch >= prev_ch) matched = 1; else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) { uchar t_ch_upper = toupper(t_ch); if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch) matched = 1; } p_ch = 0; /* This makes "prev_ch" get set to 0. */ } else if (p_ch == '[' && p[1] == ':') { const uchar *s; int i; for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/ if (!p_ch) return WM_ABORT_ALL; i = (int)(p - s - 1); if (i < 0 || p[-1] != ':') { /* Didn't find ":]", so treat like a normal set. */ p = s - 2; p_ch = '['; if (t_ch == p_ch) matched = 1; continue; } if (CC_EQ(s,i, "alnum")) { if (ISALNUM(t_ch)) matched = 1; } else if (CC_EQ(s,i, "alpha")) { if (ISALPHA(t_ch)) matched = 1; } else if (CC_EQ(s,i, "blank")) { if (ISBLANK(t_ch)) matched = 1; } else if (CC_EQ(s,i, "cntrl")) { if (ISCNTRL(t_ch)) matched = 1; } else if (CC_EQ(s,i, "digit")) { if (ISDIGIT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "graph")) { if (ISGRAPH(t_ch)) matched = 1; } else if (CC_EQ(s,i, "lower")) { if (ISLOWER(t_ch)) matched = 1; } else if (CC_EQ(s,i, "print")) { if (ISPRINT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "punct")) { if (ISPUNCT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "space")) { if (ISSPACE(t_ch)) matched = 1; } else if (CC_EQ(s,i, "upper")) { if (ISUPPER(t_ch)) matched = 1; else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) matched = 1; } else if (CC_EQ(s,i, "xdigit")) { if (ISXDIGIT(t_ch)) matched = 1; } else /* malformed [:class:] string */ return WM_ABORT_ALL; p_ch = 0; /* This makes "prev_ch" get set to 0. */ } else if (t_ch == p_ch) matched = 1; } while (prev_ch = p_ch, (p_ch = *++p) != ']'); if (matched == negated || ((flags & WM_PATHNAME) && t_ch == '/')) return WM_NOMATCH; continue; } } return *text ? WM_NOMATCH : WM_MATCH; } /* Match the "pattern" against the "text" string. */ int wildmatch(const char *pattern, const char *text, unsigned int flags) { return dowild((const uchar*)pattern, (const uchar*)text, flags); } git2r/src/libgit2/src/annotated_commit.h0000644000175000017500000000236214125111754020047 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_annotated_commit_h__ #define INCLUDE_annotated_commit_h__ #include "common.h" #include "oidarray.h" #include "git2/oid.h" typedef enum { GIT_ANNOTATED_COMMIT_REAL = 1, GIT_ANNOTATED_COMMIT_VIRTUAL = 2, } git_annotated_commit_t; /** * Internal structure for merge inputs. An annotated commit is generally * "real" and backed by an actual commit in the repository, but merge will * internally create "virtual" commits that are in-memory intermediate * commits backed by an index. */ struct git_annotated_commit { git_annotated_commit_t type; /* real commit */ git_commit *commit; git_tree *tree; /* virtual commit structure */ git_index *index; git_array_oid_t parents; /* how this commit was looked up */ const char *description; const char *ref_name; const char *remote_url; char id_str[GIT_OID_HEXSZ+1]; }; extern int git_annotated_commit_from_head(git_annotated_commit **out, git_repository *repo); extern int git_annotated_commit_from_commit(git_annotated_commit **out, git_commit *commit); #endif git2r/src/libgit2/src/futils.c0000644000175000017500000006600314125111754016025 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "futils.h" #include "runtime.h" #include "strmap.h" #include "hash.h" #include #if GIT_WIN32 #include "win32/findfile.h" #endif int git_futils_mkpath2file(const char *file_path, const mode_t mode) { return git_futils_mkdir( file_path, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR); } int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode) { int fd; mode_t mask; p_umask(mask = p_umask(0)); git_buf_sets(path_out, filename); git_buf_puts(path_out, "_git2_XXXXXX"); if (git_buf_oom(path_out)) return -1; if ((fd = p_mkstemp(path_out->ptr)) < 0) { git_error_set(GIT_ERROR_OS, "failed to create temporary file '%s'", path_out->ptr); return -1; } if (p_chmod(path_out->ptr, (mode & ~mask))) { git_error_set(GIT_ERROR_OS, "failed to set permissions on file '%s'", path_out->ptr); return -1; } return fd; } int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode) { int fd; if (git_futils_mkpath2file(path, dirmode) < 0) return -1; fd = p_creat(path, mode); if (fd < 0) { git_error_set(GIT_ERROR_OS, "failed to create file '%s'", path); return -1; } return fd; } int git_futils_creat_locked(const char *path, const mode_t mode) { int fd = p_open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode); if (fd < 0) { int error = errno; git_error_set(GIT_ERROR_OS, "failed to create locked file '%s'", path); switch (error) { case EEXIST: return GIT_ELOCKED; case ENOENT: return GIT_ENOTFOUND; default: return -1; } } return fd; } int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode) { if (git_futils_mkpath2file(path, dirmode) < 0) return -1; return git_futils_creat_locked(path, mode); } int git_futils_open_ro(const char *path) { int fd = p_open(path, O_RDONLY); if (fd < 0) return git_path_set_error(errno, path, "open"); return fd; } int git_futils_truncate(const char *path, int mode) { int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); if (fd < 0) return git_path_set_error(errno, path, "open"); close(fd); return 0; } int git_futils_filesize(uint64_t *out, git_file fd) { struct stat sb; if (p_fstat(fd, &sb)) { git_error_set(GIT_ERROR_OS, "failed to stat file descriptor"); return -1; } if (sb.st_size < 0) { git_error_set(GIT_ERROR_INVALID, "invalid file size"); return -1; } *out = sb.st_size; return 0; } mode_t git_futils_canonical_mode(mode_t raw_mode) { if (S_ISREG(raw_mode)) return S_IFREG | GIT_PERMS_CANONICAL(raw_mode); else if (S_ISLNK(raw_mode)) return S_IFLNK; else if (S_ISGITLINK(raw_mode)) return S_IFGITLINK; else if (S_ISDIR(raw_mode)) return S_IFDIR; else return 0; } int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) { ssize_t read_size = 0; size_t alloc_len; git_buf_clear(buf); if (!git__is_ssizet(len)) { git_error_set(GIT_ERROR_INVALID, "read too large"); return -1; } GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, len, 1); if (git_buf_grow(buf, alloc_len) < 0) return -1; /* p_read loops internally to read len bytes */ read_size = p_read(fd, buf->ptr, len); if (read_size != (ssize_t)len) { git_error_set(GIT_ERROR_OS, "failed to read descriptor"); git_buf_dispose(buf); return -1; } buf->ptr[read_size] = '\0'; buf->size = read_size; return 0; } int git_futils_readbuffer_updated( git_buf *out, const char *path, git_oid *checksum, int *updated) { int error; git_file fd; struct stat st; git_buf buf = GIT_BUF_INIT; git_oid checksum_new; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(path && *path); if (updated != NULL) *updated = 0; if (p_stat(path, &st) < 0) return git_path_set_error(errno, path, "stat"); if (S_ISDIR(st.st_mode)) { git_error_set(GIT_ERROR_INVALID, "requested file is a directory"); return GIT_ENOTFOUND; } if (!git__is_sizet(st.st_size+1)) { git_error_set(GIT_ERROR_OS, "invalid regular file stat for '%s'", path); return -1; } if ((fd = git_futils_open_ro(path)) < 0) return fd; if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) { p_close(fd); return -1; } p_close(fd); if (checksum) { if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { git_buf_dispose(&buf); return error; } /* * If we were given a checksum, we only want to use it if it's different */ if (!git_oid__cmp(checksum, &checksum_new)) { git_buf_dispose(&buf); if (updated) *updated = 0; return 0; } git_oid_cpy(checksum, &checksum_new); } /* * If we're here, the file did change, or the user didn't have an old version */ if (updated != NULL) *updated = 1; git_buf_swap(out, &buf); git_buf_dispose(&buf); return 0; } int git_futils_readbuffer(git_buf *buf, const char *path) { return git_futils_readbuffer_updated(buf, path, NULL, NULL); } int git_futils_writebuffer( const git_buf *buf, const char *path, int flags, mode_t mode) { int fd, do_fsync = 0, error = 0; if (!flags) flags = O_CREAT | O_TRUNC | O_WRONLY; if ((flags & O_FSYNC) != 0) do_fsync = 1; flags &= ~O_FSYNC; if (!mode) mode = GIT_FILEMODE_BLOB; if ((fd = p_open(path, flags, mode)) < 0) { git_error_set(GIT_ERROR_OS, "could not open '%s' for writing", path); return fd; } if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) { git_error_set(GIT_ERROR_OS, "could not write to '%s'", path); (void)p_close(fd); return error; } if (do_fsync && (error = p_fsync(fd)) < 0) { git_error_set(GIT_ERROR_OS, "could not fsync '%s'", path); p_close(fd); return error; } if ((error = p_close(fd)) < 0) { git_error_set(GIT_ERROR_OS, "error while closing '%s'", path); return error; } if (do_fsync && (flags & O_CREAT)) error = git_futils_fsync_parent(path); return error; } int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode) { if (git_futils_mkpath2file(to, dirmode) < 0) return -1; if (p_rename(from, to) < 0) { git_error_set(GIT_ERROR_OS, "failed to rename '%s' to '%s'", from, to); return -1; } return 0; } int git_futils_mmap_ro(git_map *out, git_file fd, off64_t begin, size_t len) { return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin); } int git_futils_mmap_ro_file(git_map *out, const char *path) { git_file fd = git_futils_open_ro(path); uint64_t len; int result; if (fd < 0) return fd; if ((result = git_futils_filesize(&len, fd)) < 0) goto out; if (!git__is_sizet(len)) { git_error_set(GIT_ERROR_OS, "file `%s` too large to mmap", path); result = -1; goto out; } result = git_futils_mmap_ro(out, fd, 0, (size_t)len); out: p_close(fd); return result; } void git_futils_mmap_free(git_map *out) { p_munmap(out); } GIT_INLINE(int) mkdir_validate_dir( const char *path, struct stat *st, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts) { /* with exclusive create, existing dir is an error */ if ((flags & GIT_MKDIR_EXCL) != 0) { git_error_set(GIT_ERROR_FILESYSTEM, "failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) || (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) { if (p_unlink(path) < 0) { git_error_set(GIT_ERROR_OS, "failed to remove %s '%s'", S_ISLNK(st->st_mode) ? "symlink" : "file", path); return GIT_EEXISTS; } opts->perfdata.mkdir_calls++; if (p_mkdir(path, mode) < 0) { git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (S_ISLNK(st->st_mode)) { /* Re-stat the target, make sure it's a directory */ opts->perfdata.stat_calls++; if (p_stat(path, st) < 0) { git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (!S_ISDIR(st->st_mode)) { git_error_set(GIT_ERROR_FILESYSTEM, "failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } return 0; } GIT_INLINE(int) mkdir_validate_mode( const char *path, struct stat *st, bool terminal_path, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts) { if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) || (flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) { opts->perfdata.chmod_calls++; if (p_chmod(path, mode) < 0) { git_error_set(GIT_ERROR_OS, "failed to set permissions on '%s'", path); return -1; } } return 0; } GIT_INLINE(int) mkdir_canonicalize( git_buf *path, uint32_t flags) { ssize_t root_len; if (path->size == 0) { git_error_set(GIT_ERROR_OS, "attempt to create empty path"); return -1; } /* Trim trailing slashes (except the root) */ if ((root_len = git_path_root(path->ptr)) < 0) root_len = 0; else root_len++; while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/') path->ptr[--path->size] = '\0'; /* if we are not supposed to made the last element, truncate it */ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { git_path_dirname_r(path, path->ptr); flags |= GIT_MKDIR_SKIP_LAST; } if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { git_path_dirname_r(path, path->ptr); } /* We were either given the root path (or trimmed it to * the root), we don't have anything to do. */ if (path->size <= (size_t)root_len) git_buf_clear(path); return 0; } int git_futils_mkdir( const char *path, mode_t mode, uint32_t flags) { git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT; const char *relative; struct git_futils_mkdir_options opts = { 0 }; struct stat st; size_t depth = 0; int len = 0, root_len, error; if ((error = git_buf_puts(&make_path, path)) < 0 || (error = mkdir_canonicalize(&make_path, flags)) < 0 || (error = git_buf_puts(&parent_path, make_path.ptr)) < 0 || make_path.size == 0) goto done; root_len = git_path_root(make_path.ptr); /* find the first parent directory that exists. this will be used * as the base to dirname_relative. */ for (relative = make_path.ptr; parent_path.size; ) { error = p_lstat(parent_path.ptr, &st); if (error == 0) { break; } else if (errno != ENOENT) { git_error_set(GIT_ERROR_OS, "failed to stat '%s'", parent_path.ptr); error = -1; goto done; } depth++; /* examine the parent of the current path */ if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) { error = len; goto done; } GIT_ASSERT(len); /* * We've walked all the given path's parents and it's either relative * (the parent is simply '.') or rooted (the length is less than or * equal to length of the root path). The path may be less than the * root path length on Windows, where `C:` == `C:/`. */ if ((len == 1 && parent_path.ptr[0] == '.') || (len == 1 && parent_path.ptr[0] == '/') || len <= root_len) { relative = make_path.ptr; break; } relative = make_path.ptr + len + 1; /* not recursive? just make this directory relative to its parent. */ if ((flags & GIT_MKDIR_PATH) == 0) break; } /* we found an item at the location we're trying to create, * validate it. */ if (depth == 0) { error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts); if (!error) error = mkdir_validate_mode( make_path.ptr, &st, true, mode, flags, &opts); goto done; } /* we already took `SKIP_LAST` and `SKIP_LAST2` into account when * canonicalizing `make_path`. */ flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST); error = git_futils_mkdir_relative(relative, parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts); done: git_buf_dispose(&make_path); git_buf_dispose(&parent_path); return error; } int git_futils_mkdir_r(const char *path, const mode_t mode) { return git_futils_mkdir(path, mode, GIT_MKDIR_PATH); } int git_futils_mkdir_relative( const char *relative_path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts) { git_buf make_path = GIT_BUF_INIT; ssize_t root = 0, min_root_len; char lastch = '/', *tail; struct stat st; struct git_futils_mkdir_options empty_opts = {0}; int error; if (!opts) opts = &empty_opts; /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0) return -1; if ((error = mkdir_canonicalize(&make_path, flags)) < 0 || make_path.size == 0) goto done; /* if we are not supposed to make the whole path, reset root */ if ((flags & GIT_MKDIR_PATH) == 0) root = git_buf_rfind(&make_path, '/'); /* advance root past drive name or network mount prefix */ min_root_len = git_path_root(make_path.ptr); if (root < min_root_len) root = min_root_len; while (root >= 0 && make_path.ptr[root] == '/') ++root; /* clip root to make_path length */ if (root > (ssize_t)make_path.size) root = (ssize_t)make_path.size; /* i.e. NUL byte of string */ if (root < 0) root = 0; /* walk down tail of path making each directory */ for (tail = &make_path.ptr[root]; *tail; *tail = lastch) { bool mkdir_attempted = false; /* advance tail to include next path component */ while (*tail == '/') tail++; while (*tail && *tail != '/') tail++; /* truncate path at next component */ lastch = *tail; *tail = '\0'; st.st_mode = 0; if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr)) continue; /* See what's going on with this path component */ opts->perfdata.stat_calls++; retry_lstat: if (p_lstat(make_path.ptr, &st) < 0) { if (mkdir_attempted || errno != ENOENT) { git_error_set(GIT_ERROR_OS, "cannot access component in path '%s'", make_path.ptr); error = -1; goto done; } git_error_clear(); opts->perfdata.mkdir_calls++; mkdir_attempted = true; if (p_mkdir(make_path.ptr, mode) < 0) { if (errno == EEXIST) goto retry_lstat; git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", make_path.ptr); error = -1; goto done; } } else { if ((error = mkdir_validate_dir( make_path.ptr, &st, mode, flags, opts)) < 0) goto done; } /* chmod if requested and necessary */ if ((error = mkdir_validate_mode( make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0) goto done; if (opts->dir_map && opts->pool) { char *cache_path; size_t alloc_size; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1); cache_path = git_pool_malloc(opts->pool, alloc_size); GIT_ERROR_CHECK_ALLOC(cache_path); memcpy(cache_path, make_path.ptr, make_path.size + 1); if ((error = git_strmap_set(opts->dir_map, cache_path, cache_path)) < 0) goto done; } } error = 0; /* check that full path really is a directory if requested & needed */ if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && lastch != '\0') { opts->perfdata.stat_calls++; if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { git_error_set(GIT_ERROR_OS, "path is not a directory '%s'", make_path.ptr); error = GIT_ENOTFOUND; } } done: git_buf_dispose(&make_path); return error; } typedef struct { const char *base; size_t baselen; uint32_t flags; int depth; } futils__rmdir_data; #define FUTILS_MAX_DEPTH 100 static int futils__error_cannot_rmdir(const char *path, const char *filemsg) { if (filemsg) git_error_set(GIT_ERROR_OS, "could not remove directory '%s': %s", path, filemsg); else git_error_set(GIT_ERROR_OS, "could not remove directory '%s'", path); return -1; } static int futils__rm_first_parent(git_buf *path, const char *ceiling) { int error = GIT_ENOTFOUND; struct stat st; while (error == GIT_ENOTFOUND) { git_buf_rtruncate_at_char(path, '/'); if (!path->size || git__prefixcmp(path->ptr, ceiling) != 0) error = 0; else if (p_lstat_posixly(path->ptr, &st) == 0) { if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) error = p_unlink(path->ptr); else if (!S_ISDIR(st.st_mode)) error = -1; /* fail to remove non-regular file */ } else if (errno != ENOTDIR) error = -1; } if (error) futils__error_cannot_rmdir(path->ptr, "cannot remove parent"); return error; } static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) { int error = 0; futils__rmdir_data *data = opaque; struct stat st; if (data->depth > FUTILS_MAX_DEPTH) error = futils__error_cannot_rmdir( path->ptr, "directory nesting too deep"); else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) { if (errno == ENOENT) error = 0; else if (errno == ENOTDIR) { /* asked to remove a/b/c/d/e and a/b is a normal file */ if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0) error = futils__rm_first_parent(path, data->base); else futils__error_cannot_rmdir( path->ptr, "parent is not directory"); } else error = git_path_set_error(errno, path->ptr, "rmdir"); } else if (S_ISDIR(st.st_mode)) { data->depth++; error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); data->depth--; if (error < 0) return error; if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) return error; if ((error = p_rmdir(path->ptr)) < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY)) error = 0; else error = git_path_set_error(errno, path->ptr, "rmdir"); } } else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) { if (p_unlink(path->ptr) < 0) error = git_path_set_error(errno, path->ptr, "remove"); } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) error = futils__error_cannot_rmdir(path->ptr, "still present"); return error; } static int futils__rmdir_empty_parent(void *opaque, const char *path) { futils__rmdir_data *data = opaque; int error = 0; if (strlen(path) <= data->baselen) error = GIT_ITEROVER; else if (p_rmdir(path) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { /* do nothing */ } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 && en == EBUSY) { error = git_path_set_error(errno, path, "rmdir"); } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { error = GIT_ITEROVER; } else { error = git_path_set_error(errno, path, "rmdir"); } } return error; } int git_futils_rmdir_r( const char *path, const char *base, uint32_t flags) { int error; git_buf fullpath = GIT_BUF_INIT; futils__rmdir_data data; /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0) return -1; memset(&data, 0, sizeof(data)); data.base = base ? base : ""; data.baselen = base ? strlen(base) : 0; data.flags = flags; error = futils__rmdir_recurs_foreach(&data, &fullpath); /* remove now-empty parents if requested */ if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) error = git_path_walk_up( &fullpath, base, futils__rmdir_empty_parent, &data); if (error == GIT_ITEROVER) { git_error_clear(); error = 0; } git_buf_dispose(&fullpath); return error; } int git_futils_fake_symlink(const char *target, const char *path) { int retcode = GIT_ERROR; int fd = git_futils_creat_withpath(path, 0755, 0644); if (fd >= 0) { retcode = p_write(fd, target, strlen(target)); p_close(fd); } return retcode; } static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done) { int error = 0; char buffer[FILEIO_BUFSIZE]; ssize_t len = 0; while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0) /* p_write() does not have the same semantics as write(). It loops * internally and will return 0 when it has completed writing. */ error = p_write(ofd, buffer, len); if (len < 0) { git_error_set(GIT_ERROR_OS, "read error while copying file"); error = (int)len; } if (error < 0) git_error_set(GIT_ERROR_OS, "write error while copying file"); if (close_fd_when_done) { p_close(ifd); p_close(ofd); } return error; } int git_futils_cp(const char *from, const char *to, mode_t filemode) { int ifd, ofd; if ((ifd = git_futils_open_ro(from)) < 0) return ifd; if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) { p_close(ifd); return git_path_set_error(errno, to, "open for writing"); } return cp_by_fd(ifd, ofd, true); } int git_futils_touch(const char *path, time_t *when) { struct p_timeval times[2]; int ret; times[0].tv_sec = times[1].tv_sec = when ? *when : time(NULL); times[0].tv_usec = times[1].tv_usec = 0; ret = p_utimes(path, times); return (ret < 0) ? git_path_set_error(errno, path, "touch") : 0; } static int cp_link(const char *from, const char *to, size_t link_size) { int error = 0; ssize_t read_len; char *link_data; size_t alloc_size; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, link_size, 1); link_data = git__malloc(alloc_size); GIT_ERROR_CHECK_ALLOC(link_data); read_len = p_readlink(from, link_data, link_size); if (read_len != (ssize_t)link_size) { git_error_set(GIT_ERROR_OS, "failed to read symlink data for '%s'", from); error = -1; } else { link_data[read_len] = '\0'; if (p_symlink(link_data, to) < 0) { git_error_set(GIT_ERROR_OS, "could not symlink '%s' as '%s'", link_data, to); error = -1; } } git__free(link_data); return error; } typedef struct { const char *to_root; git_buf to; ssize_t from_prefix; uint32_t flags; uint32_t mkdir_flags; mode_t dirmode; } cp_r_info; #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10) static int _cp_r_mkdir(cp_r_info *info, git_buf *from) { int error = 0; /* create root directory the first time we need to create a directory */ if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) { error = git_futils_mkdir( info->to_root, info->dirmode, (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0); info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT; } /* create directory with root as base to prevent excess chmods */ if (!error) error = git_futils_mkdir_relative( from->ptr + info->from_prefix, info->to_root, info->dirmode, info->mkdir_flags, NULL); return error; } static int _cp_r_callback(void *ref, git_buf *from) { int error = 0; cp_r_info *info = ref; struct stat from_st, to_st; bool exists = false; if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 && from->ptr[git_path_basename_offset(from)] == '.') return 0; if ((error = git_buf_joinpath( &info->to, info->to_root, from->ptr + info->from_prefix)) < 0) return error; if (!(error = git_path_lstat(info->to.ptr, &to_st))) exists = true; else if (error != GIT_ENOTFOUND) return error; else { git_error_clear(); error = 0; } if ((error = git_path_lstat(from->ptr, &from_st)) < 0) return error; if (S_ISDIR(from_st.st_mode)) { mode_t oldmode = info->dirmode; /* if we are not chmod'ing, then overwrite dirmode */ if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0) info->dirmode = from_st.st_mode; /* make directory now if CREATE_EMPTY_DIRS is requested and needed */ if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0) error = _cp_r_mkdir(info, from); /* recurse onto target directory */ if (!error && (!exists || S_ISDIR(to_st.st_mode))) error = git_path_direach(from, 0, _cp_r_callback, info); if (oldmode != 0) info->dirmode = oldmode; return error; } if (exists) { if ((info->flags & GIT_CPDIR_OVERWRITE) == 0) return 0; if (p_unlink(info->to.ptr) < 0) { git_error_set(GIT_ERROR_OS, "cannot overwrite existing file '%s'", info->to.ptr); return GIT_EEXISTS; } } /* Done if this isn't a regular file or a symlink */ if (!S_ISREG(from_st.st_mode) && (!S_ISLNK(from_st.st_mode) || (info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0)) return 0; /* Make container directory on demand if needed */ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 && (error = _cp_r_mkdir(info, from)) < 0) return error; /* make symlink or regular file */ if (info->flags & GIT_CPDIR_LINK_FILES) { if ((error = p_link(from->ptr, info->to.ptr)) < 0) git_error_set(GIT_ERROR_OS, "failed to link '%s'", from->ptr); } else if (S_ISLNK(from_st.st_mode)) { error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size); } else { mode_t usemode = from_st.st_mode; if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0) usemode = GIT_PERMS_FOR_WRITE(usemode); error = git_futils_cp(from->ptr, info->to.ptr, usemode); } return error; } int git_futils_cp_r( const char *from, const char *to, uint32_t flags, mode_t dirmode) { int error; git_buf path = GIT_BUF_INIT; cp_r_info info; if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */ return -1; memset(&info, 0, sizeof(info)); info.to_root = to; info.flags = flags; info.dirmode = dirmode; info.from_prefix = path.size; git_buf_init(&info.to, 0); /* precalculate mkdir flags */ if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) { /* if not creating empty dirs, then use mkdir to create the path on * demand right before files are copied. */ info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST; if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0) info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH; } else { /* otherwise, we will do simple mkdir as directories are encountered */ info.mkdir_flags = ((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0; } error = _cp_r_callback(&info, &path); git_buf_dispose(&path); git_buf_dispose(&info.to); return error; } int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path) { struct stat st; /* if the stamp is NULL, then always reload */ if (stamp == NULL) return 1; if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; if (stamp->mtime.tv_sec == st.st_mtime && #if defined(GIT_USE_NSEC) stamp->mtime.tv_nsec == st.st_mtime_nsec && #endif stamp->size == (uint64_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; stamp->mtime.tv_sec = st.st_mtime; #if defined(GIT_USE_NSEC) stamp->mtime.tv_nsec = st.st_mtime_nsec; #endif stamp->size = (uint64_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; return 1; } void git_futils_filestamp_set( git_futils_filestamp *target, const git_futils_filestamp *source) { if (source) memcpy(target, source, sizeof(*target)); else memset(target, 0, sizeof(*target)); } void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { if (st) { stamp->mtime.tv_sec = st->st_mtime; #if defined(GIT_USE_NSEC) stamp->mtime.tv_nsec = st->st_mtime_nsec; #else stamp->mtime.tv_nsec = 0; #endif stamp->size = (uint64_t)st->st_size; stamp->ino = (unsigned int)st->st_ino; } else { memset(stamp, 0, sizeof(*stamp)); } } int git_futils_fsync_dir(const char *path) { #ifdef GIT_WIN32 GIT_UNUSED(path); return 0; #else int fd, error = -1; if ((fd = p_open(path, O_RDONLY)) < 0) { git_error_set(GIT_ERROR_OS, "failed to open directory '%s' for fsync", path); return -1; } if ((error = p_fsync(fd)) < 0) git_error_set(GIT_ERROR_OS, "failed to fsync directory '%s'", path); p_close(fd); return error; #endif } int git_futils_fsync_parent(const char *path) { char *parent; int error; if ((parent = git_path_dirname(path)) == NULL) return -1; error = git_futils_fsync_dir(parent); git__free(parent); return error; } git2r/src/libgit2/src/email.h0000644000175000017500000000110214125111754015600 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_email_h__ #define INCLUDE_email_h__ #include "common.h" #include "git2/email.h" extern int git_email__append_from_diff( git_buf *out, git_diff *diff, size_t patch_idx, size_t patch_count, const git_oid *commit_id, const char *summary, const char *body, const git_signature *author, const git_email_create_options *given_opts); #endif git2r/src/libgit2/src/diff_file.c0000644000175000017500000002723314125111754016430 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "diff_file.h" #include "git2/blob.h" #include "git2/submodule.h" #include "diff.h" #include "diff_generate.h" #include "odb.h" #include "futils.h" #include "filter.h" #define DIFF_MAX_FILESIZE 0x20000000 static bool diff_file_content_binary_by_size(git_diff_file_content *fc) { /* if we have diff opts, check max_size vs file size */ if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) == 0 && fc->opts_max_size > 0 && fc->file->size > fc->opts_max_size) fc->file->flags |= GIT_DIFF_FLAG_BINARY; return ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0); } static void diff_file_content_binary_by_content(git_diff_file_content *fc) { if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) != 0) return; switch (git_diff_driver_content_is_binary( fc->driver, fc->map.data, fc->map.len)) { case 0: fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; break; case 1: fc->file->flags |= GIT_DIFF_FLAG_BINARY; break; default: break; } } static int diff_file_content_init_common( git_diff_file_content *fc, const git_diff_options *opts) { fc->opts_flags = opts ? opts->flags : GIT_DIFF_NORMAL; if (opts && opts->max_size >= 0) fc->opts_max_size = opts->max_size ? opts->max_size : DIFF_MAX_FILESIZE; if (fc->src == GIT_ITERATOR_EMPTY) fc->src = GIT_ITERATOR_TREE; if (!fc->driver && git_diff_driver_lookup(&fc->driver, fc->repo, NULL, fc->file->path) < 0) return -1; /* give driver a chance to modify options */ git_diff_driver_update_options(&fc->opts_flags, fc->driver); /* make sure file is conceivable mmap-able */ if ((size_t)fc->file->size != fc->file->size) fc->file->flags |= GIT_DIFF_FLAG_BINARY; /* check if user is forcing text diff the file */ else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) { fc->file->flags &= ~GIT_DIFF_FLAG_BINARY; fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; } /* check if user is forcing binary diff the file */ else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) { fc->file->flags &= ~GIT_DIFF_FLAG_NOT_BINARY; fc->file->flags |= GIT_DIFF_FLAG_BINARY; } diff_file_content_binary_by_size(fc); if ((fc->flags & GIT_DIFF_FLAG__NO_DATA) != 0) { fc->flags |= GIT_DIFF_FLAG__LOADED; fc->map.len = 0; fc->map.data = ""; } if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0) diff_file_content_binary_by_content(fc); return 0; } int git_diff_file_content__init_from_diff( git_diff_file_content *fc, git_diff *diff, git_diff_delta *delta, bool use_old) { bool has_data = true; memset(fc, 0, sizeof(*fc)); fc->repo = diff->repo; fc->file = use_old ? &delta->old_file : &delta->new_file; fc->src = use_old ? diff->old_src : diff->new_src; if (git_diff_driver_lookup(&fc->driver, fc->repo, &diff->attrsession, fc->file->path) < 0) return -1; switch (delta->status) { case GIT_DELTA_ADDED: has_data = !use_old; break; case GIT_DELTA_DELETED: has_data = use_old; break; case GIT_DELTA_UNTRACKED: has_data = !use_old && (diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0; break; case GIT_DELTA_UNREADABLE: case GIT_DELTA_MODIFIED: case GIT_DELTA_COPIED: case GIT_DELTA_RENAMED: break; default: has_data = false; break; } if (!has_data) fc->flags |= GIT_DIFF_FLAG__NO_DATA; return diff_file_content_init_common(fc, &diff->opts); } int git_diff_file_content__init_from_src( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, const git_diff_file_content_src *src, git_diff_file *as_file) { memset(fc, 0, sizeof(*fc)); fc->repo = repo; fc->file = as_file; if (!src->blob && !src->buf) { fc->flags |= GIT_DIFF_FLAG__NO_DATA; } else { fc->flags |= GIT_DIFF_FLAG__LOADED; fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; fc->file->mode = GIT_FILEMODE_BLOB; if (src->blob) { git_blob_dup((git_blob **)&fc->blob, (git_blob *) src->blob); fc->file->size = git_blob_rawsize(src->blob); git_oid_cpy(&fc->file->id, git_blob_id(src->blob)); fc->file->id_abbrev = GIT_OID_HEXSZ; fc->map.len = (size_t)fc->file->size; fc->map.data = (char *)git_blob_rawcontent(src->blob); fc->flags |= GIT_DIFF_FLAG__FREE_BLOB; } else { int error; if ((error = git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJECT_BLOB)) < 0) return error; fc->file->size = src->buflen; fc->file->id_abbrev = GIT_OID_HEXSZ; fc->map.len = src->buflen; fc->map.data = (char *)src->buf; } } return diff_file_content_init_common(fc, opts); } static int diff_file_content_commit_to_str( git_diff_file_content *fc, bool check_status) { char oid[GIT_OID_HEXSZ+1]; git_buf content = GIT_BUF_INIT; const char *status = ""; if (check_status) { int error = 0; git_submodule *sm = NULL; unsigned int sm_status = 0; const git_oid *sm_head; if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) { /* GIT_EEXISTS means a "submodule" that has not been git added */ if (error == GIT_EEXISTS) { git_error_clear(); error = 0; } return error; } if ((error = git_submodule_status(&sm_status, fc->repo, fc->file->path, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0) { git_submodule_free(sm); return error; } /* update OID if we didn't have it previously */ if ((fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0 && ((sm_head = git_submodule_wd_id(sm)) != NULL || (sm_head = git_submodule_head_id(sm)) != NULL)) { git_oid_cpy(&fc->file->id, sm_head); fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; } if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) status = "-dirty"; git_submodule_free(sm); } git_oid_tostr(oid, sizeof(oid), &fc->file->id); if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0) return -1; fc->map.len = git_buf_len(&content); fc->map.data = git_buf_detach(&content); fc->flags |= GIT_DIFF_FLAG__FREE_DATA; return 0; } static int diff_file_content_load_blob( git_diff_file_content *fc, git_diff_options *opts) { int error = 0; git_odb_object *odb_obj = NULL; if (git_oid_is_zero(&fc->file->id)) return 0; if (fc->file->mode == GIT_FILEMODE_COMMIT) return diff_file_content_commit_to_str(fc, false); /* if we don't know size, try to peek at object header first */ if (!fc->file->size) { if ((error = git_diff_file__resolve_zero_size( fc->file, &odb_obj, fc->repo)) < 0) return error; } if ((opts->flags & GIT_DIFF_SHOW_BINARY) == 0 && diff_file_content_binary_by_size(fc)) return 0; if (odb_obj != NULL) { error = git_object__from_odb_object( (git_object **)&fc->blob, fc->repo, odb_obj, GIT_OBJECT_BLOB); git_odb_object_free(odb_obj); } else { error = git_blob_lookup( (git_blob **)&fc->blob, fc->repo, &fc->file->id); } if (!error) { fc->flags |= GIT_DIFF_FLAG__FREE_BLOB; fc->map.data = (void *)git_blob_rawcontent(fc->blob); fc->map.len = (size_t)git_blob_rawsize(fc->blob); } return error; } static int diff_file_content_load_workdir_symlink_fake( git_diff_file_content *fc, git_buf *path) { git_buf target = GIT_BUF_INIT; int error; if ((error = git_futils_readbuffer(&target, path->ptr)) < 0) return error; fc->map.len = git_buf_len(&target); fc->map.data = git_buf_detach(&target); fc->flags |= GIT_DIFF_FLAG__FREE_DATA; git_buf_dispose(&target); return error; } static int diff_file_content_load_workdir_symlink( git_diff_file_content *fc, git_buf *path) { ssize_t alloc_len, read_len; int symlink_supported, error; if ((error = git_repository__configmap_lookup( &symlink_supported, fc->repo, GIT_CONFIGMAP_SYMLINKS)) < 0) return -1; if (!symlink_supported) return diff_file_content_load_workdir_symlink_fake(fc, path); /* link path on disk could be UTF-16, so prepare a buffer that is * big enough to handle some UTF-8 data expansion */ alloc_len = (ssize_t)(fc->file->size * 2) + 1; fc->map.data = git__calloc(alloc_len, sizeof(char)); GIT_ERROR_CHECK_ALLOC(fc->map.data); fc->flags |= GIT_DIFF_FLAG__FREE_DATA; read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len); if (read_len < 0) { git_error_set(GIT_ERROR_OS, "failed to read symlink '%s'", fc->file->path); return -1; } fc->map.len = read_len; return 0; } static int diff_file_content_load_workdir_file( git_diff_file_content *fc, git_buf *path, git_diff_options *diff_opts) { int error = 0; git_filter_list *fl = NULL; git_file fd = git_futils_open_ro(git_buf_cstr(path)); git_buf raw = GIT_BUF_INIT; if (fd < 0) return fd; if (!fc->file->size) error = git_futils_filesize(&fc->file->size, fd); if (error < 0 || !fc->file->size) goto cleanup; if ((diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0 && diff_file_content_binary_by_size(fc)) goto cleanup; if ((error = git_filter_list_load( &fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE)) < 0) goto cleanup; /* if there are no filters, try to mmap the file */ if (fl == NULL) { if (!(error = git_futils_mmap_ro( &fc->map, fd, 0, (size_t)fc->file->size))) { fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA; goto cleanup; } /* if mmap failed, fall through to try readbuffer below */ git_error_clear(); } if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) { git_buf out = GIT_BUF_INIT; error = git_filter_list__convert_buf(&out, fl, &raw); if (!error) { fc->map.len = out.size; fc->map.data = out.ptr; fc->flags |= GIT_DIFF_FLAG__FREE_DATA; } } cleanup: git_filter_list_free(fl); p_close(fd); return error; } static int diff_file_content_load_workdir( git_diff_file_content *fc, git_diff_options *diff_opts) { int error = 0; git_buf path = GIT_BUF_INIT; if (fc->file->mode == GIT_FILEMODE_COMMIT) return diff_file_content_commit_to_str(fc, true); if (fc->file->mode == GIT_FILEMODE_TREE) return 0; if (git_repository_workdir_path(&path, fc->repo, fc->file->path) < 0) return -1; if (S_ISLNK(fc->file->mode)) error = diff_file_content_load_workdir_symlink(fc, &path); else error = diff_file_content_load_workdir_file(fc, &path, diff_opts); /* once data is loaded, update OID if we didn't have it previously */ if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0) { error = git_odb_hash( &fc->file->id, fc->map.data, fc->map.len, GIT_OBJECT_BLOB); fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; } git_buf_dispose(&path); return error; } int git_diff_file_content__load( git_diff_file_content *fc, git_diff_options *diff_opts) { int error = 0; if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0) return 0; if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0 && (diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0) return 0; if (fc->src == GIT_ITERATOR_WORKDIR) error = diff_file_content_load_workdir(fc, diff_opts); else error = diff_file_content_load_blob(fc, diff_opts); if (error) return error; fc->flags |= GIT_DIFF_FLAG__LOADED; diff_file_content_binary_by_content(fc); return 0; } void git_diff_file_content__unload(git_diff_file_content *fc) { if ((fc->flags & GIT_DIFF_FLAG__LOADED) == 0) return; if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) { git__free(fc->map.data); fc->map.data = ""; fc->map.len = 0; fc->flags &= ~GIT_DIFF_FLAG__FREE_DATA; } else if (fc->flags & GIT_DIFF_FLAG__UNMAP_DATA) { git_futils_mmap_free(&fc->map); fc->map.data = ""; fc->map.len = 0; fc->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA; } if (fc->flags & GIT_DIFF_FLAG__FREE_BLOB) { git_blob_free((git_blob *)fc->blob); fc->blob = NULL; fc->flags &= ~GIT_DIFF_FLAG__FREE_BLOB; } fc->flags &= ~GIT_DIFF_FLAG__LOADED; } void git_diff_file_content__clear(git_diff_file_content *fc) { git_diff_file_content__unload(fc); /* for now, nothing else to do */ } git2r/src/libgit2/src/pqueue.h0000644000175000017500000000272614125111754016032 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_pqueue_h__ #define INCLUDE_pqueue_h__ #include "common.h" #include "vector.h" typedef git_vector git_pqueue; enum { /* flag meaning: don't grow heap, keep highest values only */ GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1), }; /** * Initialize priority queue * * @param pq The priority queue struct to initialize * @param flags Flags (see above) to control queue behavior * @param init_size The initial queue size * @param cmp The entry priority comparison function * @return 0 on success, <0 on error */ extern int git_pqueue_init( git_pqueue *pq, uint32_t flags, size_t init_size, git_vector_cmp cmp); #define git_pqueue_free git_vector_free #define git_pqueue_clear git_vector_clear #define git_pqueue_size git_vector_length #define git_pqueue_get git_vector_get #define git_pqueue_reverse git_vector_reverse /** * Insert a new item into the queue * * @param pq The priority queue * @param item Pointer to the item data * @return 0 on success, <0 on failure */ extern int git_pqueue_insert(git_pqueue *pq, void *item); /** * Remove the top item in the priority queue * * @param pq The priority queue * @return item from heap on success, NULL if queue is empty */ extern void *git_pqueue_pop(git_pqueue *pq); #endif git2r/src/libgit2/src/oid.h0000644000175000017500000000230614125111754015273 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_oid_h__ #define INCLUDE_oid_h__ #include "common.h" #include "git2/oid.h" /** * Format a git_oid into a newly allocated c-string. * * The c-string is owned by the caller and needs to be manually freed. * * @param id the oid structure to format * @return the c-string; NULL if memory is exhausted. Caller must * deallocate the string with git__free(). */ char *git_oid_allocfmt(const git_oid *id); GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2) { return memcmp(sha1, sha2, GIT_OID_RAWSZ); } /* * Compare two oid structures. * * @param a first oid structure. * @param b second oid structure. * @return <0, 0, >0 if a < b, a == b, a > b. */ GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b) { return git_oid__hashcmp(a->id, b->id); } GIT_INLINE(void) git_oid__cpy_prefix( git_oid *out, const git_oid *id, size_t len) { memcpy(&out->id, id->id, (len + 1) / 2); if (len & 1) out->id[len / 2] &= 0xF0; } #endif git2r/src/libgit2/src/diff_tform.c0000644000175000017500000007276414125111754016651 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "diff_tform.h" #include "git2/config.h" #include "git2/blob.h" #include "git2/sys/hashsig.h" #include "diff.h" #include "diff_generate.h" #include "path.h" #include "futils.h" #include "config.h" git_diff_delta *git_diff__delta_dup( const git_diff_delta *d, git_pool *pool) { git_diff_delta *delta = git__malloc(sizeof(git_diff_delta)); if (!delta) return NULL; memcpy(delta, d, sizeof(git_diff_delta)); GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags); if (d->old_file.path != NULL) { delta->old_file.path = git_pool_strdup(pool, d->old_file.path); if (delta->old_file.path == NULL) goto fail; } if (d->new_file.path != d->old_file.path && d->new_file.path != NULL) { delta->new_file.path = git_pool_strdup(pool, d->new_file.path); if (delta->new_file.path == NULL) goto fail; } else { delta->new_file.path = delta->old_file.path; } return delta; fail: git__free(delta); return NULL; } git_diff_delta *git_diff__merge_like_cgit( const git_diff_delta *a, const git_diff_delta *b, git_pool *pool) { git_diff_delta *dup; /* Emulate C git for merging two diffs (a la 'git diff '). * * When C git does a diff between the work dir and a tree, it actually * diffs with the index but uses the workdir contents. This emulates * those choices so we can emulate the type of diff. * * We have three file descriptions here, let's call them: * f1 = a->old_file * f2 = a->new_file AND b->old_file * f3 = b->new_file */ /* If one of the diffs is a conflict, just dup it */ if (b->status == GIT_DELTA_CONFLICTED) return git_diff__delta_dup(b, pool); if (a->status == GIT_DELTA_CONFLICTED) return git_diff__delta_dup(a, pool); /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */ if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED) return git_diff__delta_dup(a, pool); /* otherwise, base this diff on the 'b' diff */ if ((dup = git_diff__delta_dup(b, pool)) == NULL) return NULL; /* If 'a' status is uninteresting, then we're done */ if (a->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_UNTRACKED || a->status == GIT_DELTA_UNREADABLE) return dup; GIT_ASSERT_WITH_RETVAL(b->status != GIT_DELTA_UNMODIFIED, NULL); /* A cgit exception is that the diff of a file that is only in the * index (i.e. not in HEAD nor workdir) is given as empty. */ if (dup->status == GIT_DELTA_DELETED) { if (a->status == GIT_DELTA_ADDED) { dup->status = GIT_DELTA_UNMODIFIED; dup->nfiles = 2; } /* else don't overwrite DELETE status */ } else { dup->status = a->status; dup->nfiles = a->nfiles; } git_oid_cpy(&dup->old_file.id, &a->old_file.id); dup->old_file.mode = a->old_file.mode; dup->old_file.size = a->old_file.size; dup->old_file.flags = a->old_file.flags; return dup; } int git_diff__merge( git_diff *onto, const git_diff *from, git_diff__merge_cb cb) { int error = 0; git_pool onto_pool; git_vector onto_new; git_diff_delta *delta; bool ignore_case, reversed; unsigned int i, j; GIT_ASSERT_ARG(onto); GIT_ASSERT_ARG(from); if (!from->deltas.length) return 0; ignore_case = ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0); reversed = ((onto->opts.flags & GIT_DIFF_REVERSE) != 0); if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) || reversed != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) { git_error_set(GIT_ERROR_INVALID, "attempt to merge diffs created with conflicting options"); return -1; } if (git_vector_init(&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 || git_pool_init(&onto_pool, 1) < 0) return -1; for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) { git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i); const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j); int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path); if (cmp < 0) { delta = git_diff__delta_dup(o, &onto_pool); i++; } else if (cmp > 0) { delta = git_diff__delta_dup(f, &onto_pool); j++; } else { const git_diff_delta *left = reversed ? f : o; const git_diff_delta *right = reversed ? o : f; delta = cb(left, right, &onto_pool); i++; j++; } /* the ignore rules for the target may not match the source * or the result of a merged delta could be skippable... */ if (delta && git_diff_delta__should_skip(&onto->opts, delta)) { git__free(delta); continue; } if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0) break; } if (!error) { git_vector_swap(&onto->deltas, &onto_new); git_pool_swap(&onto->pool, &onto_pool); if ((onto->opts.flags & GIT_DIFF_REVERSE) != 0) onto->old_src = from->old_src; else onto->new_src = from->new_src; /* prefix strings also come from old pool, so recreate those.*/ onto->opts.old_prefix = git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix); onto->opts.new_prefix = git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix); } git_vector_free_deep(&onto_new); git_pool_clear(&onto_pool); return error; } int git_diff_merge(git_diff *onto, const git_diff *from) { return git_diff__merge(onto, from, git_diff__merge_like_cgit); } int git_diff_find_similar__hashsig_for_file( void **out, const git_diff_file *f, const char *path, void *p) { git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p; GIT_UNUSED(f); return git_hashsig_create_fromfile((git_hashsig **)out, path, opt); } int git_diff_find_similar__hashsig_for_buf( void **out, const git_diff_file *f, const char *buf, size_t len, void *p) { git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p; GIT_UNUSED(f); return git_hashsig_create((git_hashsig **)out, buf, len, opt); } void git_diff_find_similar__hashsig_free(void *sig, void *payload) { GIT_UNUSED(payload); git_hashsig_free(sig); } int git_diff_find_similar__calc_similarity( int *score, void *siga, void *sigb, void *payload) { int error; GIT_UNUSED(payload); error = git_hashsig_compare(siga, sigb); if (error < 0) return error; *score = error; return 0; } #define DEFAULT_THRESHOLD 50 #define DEFAULT_BREAK_REWRITE_THRESHOLD 60 #define DEFAULT_RENAME_LIMIT 200 static int normalize_find_opts( git_diff *diff, git_diff_find_options *opts, const git_diff_find_options *given) { git_config *cfg = NULL; git_hashsig_option_t hashsig_opts; GIT_ERROR_CHECK_VERSION(given, GIT_DIFF_FIND_OPTIONS_VERSION, "git_diff_find_options"); if (diff->repo != NULL && git_repository_config__weakptr(&cfg, diff->repo) < 0) return -1; if (given) memcpy(opts, given, sizeof(*opts)); if (!given || (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG) { if (cfg) { char *rule = git_config__get_string_force(cfg, "diff.renames", "true"); int boolval; if (!git__parse_bool(&boolval, rule) && !boolval) /* don't set FIND_RENAMES if bool value is false */; else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; else opts->flags |= GIT_DIFF_FIND_RENAMES; git__free(rule); } else { /* set default flag */ opts->flags |= GIT_DIFF_FIND_RENAMES; } } /* some flags imply others */ if (opts->flags & GIT_DIFF_FIND_EXACT_MATCH_ONLY) { /* if we are only looking for exact matches, then don't turn * MODIFIED items into ADD/DELETE pairs because it's too picky */ opts->flags &= ~(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES); /* similarly, don't look for self-rewrites to split */ opts->flags &= ~GIT_DIFF_FIND_RENAMES_FROM_REWRITES; } if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES) opts->flags |= GIT_DIFF_FIND_RENAMES; if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) opts->flags |= GIT_DIFF_FIND_COPIES; if (opts->flags & GIT_DIFF_BREAK_REWRITES) opts->flags |= GIT_DIFF_FIND_REWRITES; #define USE_DEFAULT(X) ((X) == 0 || (X) > 100) if (USE_DEFAULT(opts->rename_threshold)) opts->rename_threshold = DEFAULT_THRESHOLD; if (USE_DEFAULT(opts->rename_from_rewrite_threshold)) opts->rename_from_rewrite_threshold = DEFAULT_THRESHOLD; if (USE_DEFAULT(opts->copy_threshold)) opts->copy_threshold = DEFAULT_THRESHOLD; if (USE_DEFAULT(opts->break_rewrite_threshold)) opts->break_rewrite_threshold = DEFAULT_BREAK_REWRITE_THRESHOLD; #undef USE_DEFAULT if (!opts->rename_limit) { if (cfg) { opts->rename_limit = git_config__get_int_force( cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT); } if (opts->rename_limit <= 0) opts->rename_limit = DEFAULT_RENAME_LIMIT; } /* assign the internal metric with whitespace flag as payload */ if (!opts->metric) { opts->metric = git__malloc(sizeof(git_diff_similarity_metric)); GIT_ERROR_CHECK_ALLOC(opts->metric); opts->metric->file_signature = git_diff_find_similar__hashsig_for_file; opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf; opts->metric->free_signature = git_diff_find_similar__hashsig_free; opts->metric->similarity = git_diff_find_similar__calc_similarity; if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE) hashsig_opts = GIT_HASHSIG_IGNORE_WHITESPACE; else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE) hashsig_opts = GIT_HASHSIG_NORMAL; else hashsig_opts = GIT_HASHSIG_SMART_WHITESPACE; hashsig_opts |= GIT_HASHSIG_ALLOW_SMALL_FILES; opts->metric->payload = (void *)hashsig_opts; } return 0; } static int insert_delete_side_of_split( git_diff *diff, git_vector *onto, const git_diff_delta *delta) { /* make new record for DELETED side of split */ git_diff_delta *deleted = git_diff__delta_dup(delta, &diff->pool); GIT_ERROR_CHECK_ALLOC(deleted); deleted->status = GIT_DELTA_DELETED; deleted->nfiles = 1; memset(&deleted->new_file, 0, sizeof(deleted->new_file)); deleted->new_file.path = deleted->old_file.path; deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; return git_vector_insert(onto, deleted); } static int apply_splits_and_deletes( git_diff *diff, size_t expected_size, bool actually_split) { git_vector onto = GIT_VECTOR_INIT; size_t i; git_diff_delta *delta; if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0) return -1; /* build new delta list without TO_DELETE and splitting TO_SPLIT */ git_vector_foreach(&diff->deltas, i, delta) { if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0) continue; if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) { delta->similarity = 0; if (insert_delete_side_of_split(diff, &onto, delta) < 0) goto on_error; if (diff->new_src == GIT_ITERATOR_WORKDIR) delta->status = GIT_DELTA_UNTRACKED; else delta->status = GIT_DELTA_ADDED; delta->nfiles = 1; memset(&delta->old_file, 0, sizeof(delta->old_file)); delta->old_file.path = delta->new_file.path; delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; } /* clean up delta before inserting into new list */ GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags); if (delta->status != GIT_DELTA_COPIED && delta->status != GIT_DELTA_RENAMED && (delta->status != GIT_DELTA_MODIFIED || actually_split)) delta->similarity = 0; /* insert into new list */ if (git_vector_insert(&onto, delta) < 0) goto on_error; } /* cannot return an error past this point */ /* free deltas from old list that didn't make it to the new one */ git_vector_foreach(&diff->deltas, i, delta) { if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0) git__free(delta); } /* swap new delta list into place */ git_vector_swap(&diff->deltas, &onto); git_vector_free(&onto); git_vector_sort(&diff->deltas); return 0; on_error: git_vector_free_deep(&onto); return -1; } GIT_INLINE(git_diff_file *) similarity_get_file(git_diff *diff, size_t idx) { git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2); return (idx & 1) ? &delta->new_file : &delta->old_file; } typedef struct { size_t idx; git_iterator_t src; git_repository *repo; git_diff_file *file; git_buf data; git_odb_object *odb_obj; git_blob *blob; } similarity_info; static int similarity_init( similarity_info *info, git_diff *diff, size_t file_idx) { info->idx = file_idx; info->src = (file_idx & 1) ? diff->new_src : diff->old_src; info->repo = diff->repo; info->file = similarity_get_file(diff, file_idx); info->odb_obj = NULL; info->blob = NULL; git_buf_init(&info->data, 0); if (info->file->size > 0 || info->src == GIT_ITERATOR_WORKDIR) return 0; return git_diff_file__resolve_zero_size( info->file, &info->odb_obj, info->repo); } static int similarity_sig( similarity_info *info, const git_diff_find_options *opts, void **cache) { int error = 0; git_diff_file *file = info->file; if (info->src == GIT_ITERATOR_WORKDIR) { if ((error = git_repository_workdir_path( &info->data, info->repo, file->path)) < 0) return error; /* if path is not a regular file, just skip this item */ if (!git_path_isfile(info->data.ptr)) return 0; /* TODO: apply wd-to-odb filters to file data if necessary */ error = opts->metric->file_signature( &cache[info->idx], info->file, info->data.ptr, opts->metric->payload); } else { /* if we didn't initially know the size, we might have an odb_obj * around from earlier, so convert that, otherwise load the blob now */ if (info->odb_obj != NULL) error = git_object__from_odb_object( (git_object **)&info->blob, info->repo, info->odb_obj, GIT_OBJECT_BLOB); else error = git_blob_lookup(&info->blob, info->repo, &file->id); if (error < 0) { /* if lookup fails, just skip this item in similarity calc */ git_error_clear(); } else { size_t sz; /* index size may not be actual blob size if filtered */ if (file->size != git_blob_rawsize(info->blob)) file->size = git_blob_rawsize(info->blob); sz = git__is_sizet(file->size) ? (size_t)file->size : (size_t)-1; error = opts->metric->buffer_signature( &cache[info->idx], info->file, git_blob_rawcontent(info->blob), sz, opts->metric->payload); } } return error; } static void similarity_unload(similarity_info *info) { if (info->odb_obj) git_odb_object_free(info->odb_obj); if (info->blob) git_blob_free(info->blob); else git_buf_dispose(&info->data); } #define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0) /* - score < 0 means files cannot be compared * - score >= 100 means files are exact match * - score == 0 means files are completely different */ static int similarity_measure( int *score, git_diff *diff, const git_diff_find_options *opts, void **cache, size_t a_idx, size_t b_idx) { git_diff_file *a_file = similarity_get_file(diff, a_idx); git_diff_file *b_file = similarity_get_file(diff, b_idx); bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY); int error = 0; similarity_info a_info, b_info; *score = -1; /* don't try to compare things that aren't files */ if (!GIT_MODE_ISBLOB(a_file->mode) || !GIT_MODE_ISBLOB(b_file->mode)) return 0; /* if exact match is requested, force calculation of missing OIDs now */ if (exact_match) { if (git_oid_is_zero(&a_file->id) && diff->old_src == GIT_ITERATOR_WORKDIR && !git_diff__oid_for_file(&a_file->id, diff, a_file->path, a_file->mode, a_file->size)) a_file->flags |= GIT_DIFF_FLAG_VALID_ID; if (git_oid_is_zero(&b_file->id) && diff->new_src == GIT_ITERATOR_WORKDIR && !git_diff__oid_for_file(&b_file->id, diff, b_file->path, b_file->mode, b_file->size)) b_file->flags |= GIT_DIFF_FLAG_VALID_ID; } /* check OID match as a quick test */ if (git_oid__cmp(&a_file->id, &b_file->id) == 0) { *score = 100; return 0; } /* don't calculate signatures if we are doing exact match */ if (exact_match) { *score = 0; return 0; } memset(&a_info, 0, sizeof(a_info)); memset(&b_info, 0, sizeof(b_info)); /* set up similarity data (will try to update missing file sizes) */ if (!cache[a_idx] && (error = similarity_init(&a_info, diff, a_idx)) < 0) return error; if (!cache[b_idx] && (error = similarity_init(&b_info, diff, b_idx)) < 0) goto cleanup; /* check if file sizes are nowhere near each other */ if (a_file->size > 127 && b_file->size > 127 && (a_file->size > (b_file->size << 3) || b_file->size > (a_file->size << 3))) goto cleanup; /* update signature cache if needed */ if (!cache[a_idx]) { if ((error = similarity_sig(&a_info, opts, cache)) < 0) goto cleanup; } if (!cache[b_idx]) { if ((error = similarity_sig(&b_info, opts, cache)) < 0) goto cleanup; } /* calculate similarity provided that the metric choose to process * both the a and b files (some may not if file is too big, etc). */ if (cache[a_idx] && cache[b_idx]) error = opts->metric->similarity( score, cache[a_idx], cache[b_idx], opts->metric->payload); cleanup: similarity_unload(&a_info); similarity_unload(&b_info); return error; } static int calc_self_similarity( git_diff *diff, const git_diff_find_options *opts, size_t delta_idx, void **cache) { int error, similarity = -1; git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx); if ((delta->flags & GIT_DIFF_FLAG__HAS_SELF_SIMILARITY) != 0) return 0; error = similarity_measure( &similarity, diff, opts, cache, 2 * delta_idx, 2 * delta_idx + 1); if (error < 0) return error; if (similarity >= 0) { delta->similarity = (uint16_t)similarity; delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY; } return 0; } static bool is_rename_target( git_diff *diff, const git_diff_find_options *opts, size_t delta_idx, void **cache) { git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx); /* skip things that aren't plain blobs */ if (!GIT_MODE_ISBLOB(delta->new_file.mode)) return false; /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as * targets; maybe include UNTRACKED if requested. */ switch (delta->status) { case GIT_DELTA_UNMODIFIED: case GIT_DELTA_DELETED: case GIT_DELTA_IGNORED: case GIT_DELTA_CONFLICTED: return false; case GIT_DELTA_MODIFIED: if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) && !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES)) return false; if (calc_self_similarity(diff, opts, delta_idx, cache) < 0) return false; if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) && delta->similarity < opts->break_rewrite_threshold) { delta->flags |= GIT_DIFF_FLAG__TO_SPLIT; break; } if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) && delta->similarity < opts->rename_from_rewrite_threshold) { delta->flags |= GIT_DIFF_FLAG__TO_SPLIT; break; } return false; case GIT_DELTA_UNTRACKED: if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED)) return false; break; default: /* all other status values should be checked */ break; } delta->flags |= GIT_DIFF_FLAG__IS_RENAME_TARGET; return true; } static bool is_rename_source( git_diff *diff, const git_diff_find_options *opts, size_t delta_idx, void **cache) { git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx); /* skip things that aren't blobs */ if (!GIT_MODE_ISBLOB(delta->old_file.mode)) return false; switch (delta->status) { case GIT_DELTA_ADDED: case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNREADABLE: case GIT_DELTA_IGNORED: case GIT_DELTA_CONFLICTED: return false; case GIT_DELTA_DELETED: case GIT_DELTA_TYPECHANGE: break; case GIT_DELTA_UNMODIFIED: if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)) return false; if (FLAG_SET(opts, GIT_DIFF_FIND_REMOVE_UNMODIFIED)) delta->flags |= GIT_DIFF_FLAG__TO_DELETE; break; default: /* MODIFIED, RENAMED, COPIED */ /* if we're finding copies, this could be a source */ if (FLAG_SET(opts, GIT_DIFF_FIND_COPIES)) break; /* otherwise, this is only a source if we can split it */ if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) && !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES)) return false; if (calc_self_similarity(diff, opts, delta_idx, cache) < 0) return false; if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) && delta->similarity < opts->break_rewrite_threshold) { delta->flags |= GIT_DIFF_FLAG__TO_SPLIT; break; } if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) && delta->similarity < opts->rename_from_rewrite_threshold) break; return false; } delta->flags |= GIT_DIFF_FLAG__IS_RENAME_SOURCE; return true; } GIT_INLINE(bool) delta_is_split(git_diff_delta *delta) { return (delta->status == GIT_DELTA_TYPECHANGE || (delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0); } GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta) { return (delta->status == GIT_DELTA_ADDED || delta->status == GIT_DELTA_UNTRACKED || delta->status == GIT_DELTA_UNREADABLE || delta->status == GIT_DELTA_IGNORED); } GIT_INLINE(void) delta_make_rename( git_diff_delta *to, const git_diff_delta *from, uint16_t similarity) { to->status = GIT_DELTA_RENAMED; to->similarity = similarity; to->nfiles = 2; memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; } typedef struct { size_t idx; uint16_t similarity; } diff_find_match; int git_diff_find_similar( git_diff *diff, const git_diff_find_options *given_opts) { size_t s, t; int error = 0, result; uint16_t similarity; git_diff_delta *src, *tgt; git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; size_t num_deltas, num_srcs = 0, num_tgts = 0; size_t tried_srcs = 0, tried_tgts = 0; size_t num_rewrites = 0, num_updates = 0, num_bumped = 0; size_t sigcache_size; void **sigcache = NULL; /* cache of similarity metric file signatures */ diff_find_match *tgt2src = NULL; diff_find_match *src2tgt = NULL; diff_find_match *tgt2src_copy = NULL; diff_find_match *best_match; git_diff_file swap; GIT_ASSERT_ARG(diff); if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0) return error; num_deltas = diff->deltas.length; /* TODO: maybe abort if deltas.length > rename_limit ??? */ if (!num_deltas || !git__is_uint32(num_deltas)) goto cleanup; /* No flags set; nothing to do */ if ((opts.flags & GIT_DIFF_FIND_ALL) == 0) goto cleanup; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&sigcache_size, num_deltas, 2); sigcache = git__calloc(sigcache_size, sizeof(void *)); GIT_ERROR_CHECK_ALLOC(sigcache); /* Label rename sources and targets * * This will also set self-similarity scores for MODIFIED files and * mark them for splitting if break-rewrites is enabled */ git_vector_foreach(&diff->deltas, t, tgt) { if (is_rename_source(diff, &opts, t, sigcache)) ++num_srcs; if (is_rename_target(diff, &opts, t, sigcache)) ++num_tgts; if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) num_rewrites++; } /* if there are no candidate srcs or tgts, we're done */ if (!num_srcs || !num_tgts) goto cleanup; src2tgt = git__calloc(num_deltas, sizeof(diff_find_match)); GIT_ERROR_CHECK_ALLOC(src2tgt); tgt2src = git__calloc(num_deltas, sizeof(diff_find_match)); GIT_ERROR_CHECK_ALLOC(tgt2src); if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) { tgt2src_copy = git__calloc(num_deltas, sizeof(diff_find_match)); GIT_ERROR_CHECK_ALLOC(tgt2src_copy); } /* * Find best-fit matches for rename / copy candidates */ find_best_matches: tried_tgts = num_bumped = 0; git_vector_foreach(&diff->deltas, t, tgt) { /* skip things that are not rename targets */ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0) continue; tried_srcs = 0; git_vector_foreach(&diff->deltas, s, src) { /* skip things that are not rename sources */ if ((src->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0) continue; /* calculate similarity for this pair and find best match */ if (s == t) result = -1; /* don't measure self-similarity here */ else if ((error = similarity_measure( &result, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0) goto cleanup; if (result < 0) continue; similarity = (uint16_t)result; /* is this a better rename? */ if (tgt2src[t].similarity < similarity && src2tgt[s].similarity < similarity) { /* eject old mapping */ if (src2tgt[s].similarity > 0) { tgt2src[src2tgt[s].idx].similarity = 0; num_bumped++; } if (tgt2src[t].similarity > 0) { src2tgt[tgt2src[t].idx].similarity = 0; num_bumped++; } /* write new mapping */ tgt2src[t].idx = s; tgt2src[t].similarity = similarity; src2tgt[s].idx = t; src2tgt[s].similarity = similarity; } /* keep best absolute match for copies */ if (tgt2src_copy != NULL && tgt2src_copy[t].similarity < similarity) { tgt2src_copy[t].idx = s; tgt2src_copy[t].similarity = similarity; } if (++tried_srcs >= num_srcs) break; /* cap on maximum targets we'll examine (per "tgt" file) */ if (tried_srcs > opts.rename_limit) break; } if (++tried_tgts >= num_tgts) break; } if (num_bumped > 0) /* try again if we bumped some items */ goto find_best_matches; /* * Rewrite the diffs with renames / copies */ git_vector_foreach(&diff->deltas, t, tgt) { /* skip things that are not rename targets */ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0) continue; /* check if this delta was the target of a similarity */ if (tgt2src[t].similarity) best_match = &tgt2src[t]; else if (tgt2src_copy && tgt2src_copy[t].similarity) best_match = &tgt2src_copy[t]; else continue; s = best_match->idx; src = GIT_VECTOR_GET(&diff->deltas, s); /* possible scenarios: * 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME * 2. from DELETE to SPLIT/TYPECHANGE = RENAME + DELETE * 3. from SPLIT/TYPECHANGE to ADD/UNTRACK/IGNORE = ADD + RENAME * 4. from SPLIT/TYPECHANGE to SPLIT/TYPECHANGE = RENAME + SPLIT * 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY */ if (src->status == GIT_DELTA_DELETED) { if (delta_is_new_only(tgt)) { if (best_match->similarity < opts.rename_threshold) continue; delta_make_rename(tgt, src, best_match->similarity); src->flags |= GIT_DIFF_FLAG__TO_DELETE; num_rewrites++; } else { GIT_ASSERT(delta_is_split(tgt)); if (best_match->similarity < opts.rename_from_rewrite_threshold) continue; memcpy(&swap, &tgt->old_file, sizeof(swap)); delta_make_rename(tgt, src, best_match->similarity); num_rewrites--; GIT_ASSERT(src->status == GIT_DELTA_DELETED); memcpy(&src->old_file, &swap, sizeof(src->old_file)); memset(&src->new_file, 0, sizeof(src->new_file)); src->new_file.path = src->old_file.path; src->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; num_updates++; if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) { /* what used to be at src t is now at src s */ tgt2src[src2tgt[t].idx].idx = s; } } } else if (delta_is_split(src)) { if (delta_is_new_only(tgt)) { if (best_match->similarity < opts.rename_threshold) continue; delta_make_rename(tgt, src, best_match->similarity); src->status = (diff->new_src == GIT_ITERATOR_WORKDIR) ? GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED; src->nfiles = 1; memset(&src->old_file, 0, sizeof(src->old_file)); src->old_file.path = src->new_file.path; src->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; num_rewrites--; num_updates++; } else { GIT_ASSERT(delta_is_split(src)); if (best_match->similarity < opts.rename_from_rewrite_threshold) continue; memcpy(&swap, &tgt->old_file, sizeof(swap)); delta_make_rename(tgt, src, best_match->similarity); num_rewrites--; num_updates++; memcpy(&src->old_file, &swap, sizeof(src->old_file)); /* if we've just swapped the new element into the correct * place, clear the SPLIT and RENAME_TARGET flags */ if (tgt2src[s].idx == t && tgt2src[s].similarity > opts.rename_from_rewrite_threshold) { src->status = GIT_DELTA_RENAMED; src->similarity = tgt2src[s].similarity; tgt2src[s].similarity = 0; src->flags &= ~(GIT_DIFF_FLAG__TO_SPLIT | GIT_DIFF_FLAG__IS_RENAME_TARGET); num_rewrites--; } /* otherwise, if we just overwrote a source, update mapping */ else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) { /* what used to be at src t is now at src s */ tgt2src[src2tgt[t].idx].idx = s; } num_updates++; } } else if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) { if (tgt2src_copy[t].similarity < opts.copy_threshold) continue; /* always use best possible source for copy */ best_match = &tgt2src_copy[t]; src = GIT_VECTOR_GET(&diff->deltas, best_match->idx); if (delta_is_split(tgt)) { error = insert_delete_side_of_split(diff, &diff->deltas, tgt); if (error < 0) goto cleanup; num_rewrites--; } if (!delta_is_split(tgt) && !delta_is_new_only(tgt)) continue; tgt->status = GIT_DELTA_COPIED; tgt->similarity = best_match->similarity; tgt->nfiles = 2; memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file)); tgt->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; num_updates++; } } /* * Actually split and delete entries as needed */ if (num_rewrites > 0 || num_updates > 0) error = apply_splits_and_deletes( diff, diff->deltas.length - num_rewrites, FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) && !FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY)); cleanup: git__free(tgt2src); git__free(src2tgt); git__free(tgt2src_copy); if (sigcache) { for (t = 0; t < num_deltas * 2; ++t) { if (sigcache[t] != NULL) opts.metric->free_signature(sigcache[t], opts.metric->payload); } git__free(sigcache); } if (!given_opts || !given_opts->metric) git__free(opts.metric); return error; } #undef FLAG_SET git2r/src/libgit2/src/commit_graph.h0000644000175000017500000001134314125111754017172 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_commit_graph_h__ #define INCLUDE_commit_graph_h__ #include "common.h" #include "git2/types.h" #include "git2/sys/commit_graph.h" #include "map.h" #include "vector.h" /** * A commit-graph file. * * This file contains metadata about commits, particularly the generation * number for each one. This can help speed up graph operations without * requiring a full graph traversal. * * Support for this feature was added in git 2.19. */ typedef struct git_commit_graph_file { git_map graph_map; /* The OID Fanout table. */ const uint32_t *oid_fanout; /* The total number of commits in the graph. */ uint32_t num_commits; /* The OID Lookup table. */ git_oid *oid_lookup; /* * The Commit Data table. Each entry contains the OID of the commit followed * by two 8-byte fields in network byte order: * - The indices of the first two parents (32 bits each). * - The generation number (first 30 bits) and commit time in seconds since * UNIX epoch (34 bits). */ const unsigned char *commit_data; /* * The Extra Edge List table. Each 4-byte entry is a network byte order index * of one of the i-th (i > 0) parents of commits in the `commit_data` table, * when the commit has more than 2 parents. */ const unsigned char *extra_edge_list; /* The number of entries in the Extra Edge List table. Each entry is 4 bytes wide. */ size_t num_extra_edge_list; /* The trailer of the file. Contains the SHA1-checksum of the whole file. */ git_oid checksum; } git_commit_graph_file; /** * An entry in the commit-graph file. Provides a subset of the information that * can be obtained from the commit header. */ typedef struct git_commit_graph_entry { /* The generation number of the commit within the graph */ size_t generation; /* Time in seconds from UNIX epoch. */ git_time_t commit_time; /* The number of parents of the commit. */ size_t parent_count; /* * The indices of the parent commits within the Commit Data table. The value * of `GIT_COMMIT_GRAPH_MISSING_PARENT` indicates that no parent is in that * position. */ size_t parent_indices[2]; /* The index within the Extra Edge List of any parent after the first two. */ size_t extra_parents_index; /* The SHA-1 hash of the root tree of the commit. */ git_oid tree_oid; /* The SHA-1 hash of the requested commit. */ git_oid sha1; } git_commit_graph_entry; /* A wrapper for git_commit_graph_file to enable lazy loading in the ODB. */ struct git_commit_graph { /* The path to the commit-graph file. Something like ".git/objects/info/commit-graph". */ git_buf filename; /* The underlying commit-graph file. */ git_commit_graph_file *file; /* Whether the commit-graph file was already checked for validity. */ bool checked; }; /** Create a new commit-graph, optionally opening the underlying file. */ int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file); /** Open and validate a commit-graph file. */ int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path); /* * Attempt to get the git_commit_graph's commit-graph file. This object is * still owned by the git_commit_graph. If the repository does not contain a commit graph, * it will return GIT_ENOTFOUND. * * This function is not thread-safe. */ int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph *cgraph); /* Marks the commit-graph file as needing a refresh. */ void git_commit_graph_refresh(git_commit_graph *cgraph); /* * A writer for `commit-graph` files. */ struct git_commit_graph_writer { /* * The path of the `objects/info` directory where the `commit-graph` will be * stored. */ git_buf objects_info_dir; /* The list of packed commits. */ git_vector commits; }; /* * Returns whether the git_commit_graph_file needs to be reloaded since the * contents of the commit-graph file have changed on disk. */ bool git_commit_graph_file_needs_refresh( const git_commit_graph_file *file, const char *path); int git_commit_graph_entry_find( git_commit_graph_entry *e, const git_commit_graph_file *file, const git_oid *short_oid, size_t len); int git_commit_graph_entry_parent( git_commit_graph_entry *parent, const git_commit_graph_file *file, const git_commit_graph_entry *entry, size_t n); int git_commit_graph_file_close(git_commit_graph_file *cgraph); void git_commit_graph_file_free(git_commit_graph_file *cgraph); /* This is exposed for use in the fuzzers. */ int git_commit_graph_file_parse( git_commit_graph_file *file, const unsigned char *data, size_t size); #endif git2r/src/libgit2/src/merge.c0000644000175000017500000026150514125111754015622 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "merge.h" #include "posix.h" #include "buffer.h" #include "repository.h" #include "revwalk.h" #include "commit_list.h" #include "path.h" #include "refs.h" #include "object.h" #include "iterator.h" #include "refs.h" #include "diff.h" #include "diff_generate.h" #include "diff_tform.h" #include "checkout.h" #include "tree.h" #include "blob.h" #include "oid.h" #include "index.h" #include "filebuf.h" #include "config.h" #include "oidarray.h" #include "annotated_commit.h" #include "commit.h" #include "oidarray.h" #include "merge_driver.h" #include "oidmap.h" #include "array.h" #include "git2/types.h" #include "git2/repository.h" #include "git2/object.h" #include "git2/commit.h" #include "git2/merge.h" #include "git2/refs.h" #include "git2/reset.h" #include "git2/checkout.h" #include "git2/signature.h" #include "git2/config.h" #include "git2/tree.h" #include "git2/oidarray.h" #include "git2/annotated_commit.h" #include "git2/sys/index.h" #include "git2/sys/hashsig.h" #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) #define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode) typedef enum { TREE_IDX_ANCESTOR = 0, TREE_IDX_OURS = 1, TREE_IDX_THEIRS = 2 } merge_tree_index_t; /* Tracks D/F conflicts */ struct merge_diff_df_data { const char *df_path; const char *prev_path; git_merge_diff *prev_conflict; }; /* * This acts as a negative cache entry marker. In case we've tried to calculate * similarity metrics for a given blob already but `git_hashsig` determined * that it's too small in order to have a meaningful hash signature, we will * insert the address of this marker instead of `NULL`. Like this, we can * easily check whether we have checked a gien entry already and skip doing the * calculation again and again. */ static int cache_invalid_marker; /* Merge base computation */ static int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[]) { git_revwalk *walk = NULL; git_vector list; git_commit_list *result = NULL; git_commit_list_node *commit; int error = -1; unsigned int i; if (length < 2) { git_error_set(GIT_ERROR_INVALID, "at least two commits are required to find an ancestor"); return -1; } if (git_vector_init(&list, length - 1, NULL) < 0) return -1; if (git_revwalk_new(&walk, repo) < 0) goto on_error; for (i = 1; i < length; i++) { commit = git_revwalk__commit_lookup(walk, &input_array[i]); if (commit == NULL) goto on_error; git_vector_insert(&list, commit); } commit = git_revwalk__commit_lookup(walk, &input_array[0]); if (commit == NULL) goto on_error; if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0) goto on_error; if (!result) { git_error_set(GIT_ERROR_MERGE, "no merge base found"); error = GIT_ENOTFOUND; goto on_error; } *out = result; *walk_out = walk; git_vector_free(&list); return 0; on_error: git_vector_free(&list); git_revwalk_free(walk); return error; } int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) { git_revwalk *walk; git_commit_list *result = NULL; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(input_array); if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0) return error; git_oid_cpy(out, &result->item->oid); git_commit_list_free(&result); git_revwalk_free(walk); return 0; } int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[]) { git_revwalk *walk; git_commit_list *list, *result = NULL; int error = 0; git_array_oid_t array; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(input_array); if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0) return error; git_array_init(array); list = result; while (list) { git_oid *id = git_array_alloc(array); if (id == NULL) { error = -1; goto cleanup; } git_oid_cpy(id, &list->item->oid); list = list->next; } git_oidarray__from_array(out, &array); cleanup: git_commit_list_free(&result); git_revwalk_free(walk); return error; } int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) { git_oid result; unsigned int i; int error = -1; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(input_array); if (length < 2) { git_error_set(GIT_ERROR_INVALID, "at least two commits are required to find an ancestor"); return -1; } result = input_array[0]; for (i = 1; i < length; i++) { error = git_merge_base(&result, repo, &result, &input_array[i]); if (error < 0) return error; } *out = result; return 0; } static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two) { git_revwalk *walk; git_vector list; git_commit_list *result = NULL; git_commit_list_node *commit; void *contents[1]; if (git_revwalk_new(&walk, repo) < 0) return -1; commit = git_revwalk__commit_lookup(walk, two); if (commit == NULL) goto on_error; /* This is just one value, so we can do it on the stack */ memset(&list, 0x0, sizeof(git_vector)); contents[0] = commit; list.length = 1; list.contents = contents; commit = git_revwalk__commit_lookup(walk, one); if (commit == NULL) goto on_error; if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0) goto on_error; if (!result) { git_revwalk_free(walk); git_error_set(GIT_ERROR_MERGE, "no merge base found"); return GIT_ENOTFOUND; } *out = result; *walk_out = walk; return 0; on_error: git_revwalk_free(walk); return -1; } int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two) { int error; git_revwalk *walk; git_commit_list *result; if ((error = merge_bases(&result, &walk, repo, one, two)) < 0) return error; git_oid_cpy(out, &result->item->oid); git_commit_list_free(&result); git_revwalk_free(walk); return 0; } int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two) { int error; git_revwalk *walk; git_commit_list *result, *list; git_array_oid_t array; git_array_init(array); if ((error = merge_bases(&result, &walk, repo, one, two)) < 0) return error; list = result; while (list) { git_oid *id = git_array_alloc(array); if (id == NULL) goto on_error; git_oid_cpy(id, &list->item->oid); list = list->next; } git_oidarray__from_array(out, &array); git_commit_list_free(&result); git_revwalk_free(walk); return 0; on_error: git_commit_list_free(&result); git_revwalk_free(walk); return -1; } static int interesting(git_pqueue *list) { size_t i; for (i = 0; i < git_pqueue_size(list); i++) { git_commit_list_node *commit = git_pqueue_get(list, i); if ((commit->flags & STALE) == 0) return 1; } return 0; } static int clear_commit_marks_1(git_commit_list **plist, git_commit_list_node *commit, unsigned int mark) { while (commit) { unsigned int i; if (!(mark & commit->flags)) return 0; commit->flags &= ~mark; for (i = 1; i < commit->out_degree; i++) { git_commit_list_node *p = commit->parents[i]; if (git_commit_list_insert(p, plist) == NULL) return -1; } commit = commit->out_degree ? commit->parents[0] : NULL; } return 0; } static int clear_commit_marks_many(git_vector *commits, unsigned int mark) { git_commit_list *list = NULL; git_commit_list_node *c; unsigned int i; git_vector_foreach(commits, i, c) { if (git_commit_list_insert(c, &list) == NULL) return -1; } while (list) if (clear_commit_marks_1(&list, git_commit_list_pop(&list), mark) < 0) return -1; return 0; } static int clear_commit_marks(git_commit_list_node *commit, unsigned int mark) { git_commit_list *list = NULL; if (git_commit_list_insert(commit, &list) == NULL) return -1; while (list) if (clear_commit_marks_1(&list, git_commit_list_pop(&list), mark) < 0) return -1; return 0; } static int paint_down_to_common( git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos, uint32_t minimum_generation) { git_pqueue list; git_commit_list *result = NULL; git_commit_list_node *two; int error; unsigned int i; if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_generation_cmp) < 0) return -1; one->flags |= PARENT1; if (git_pqueue_insert(&list, one) < 0) return -1; git_vector_foreach(twos, i, two) { if (git_commit_list_parse(walk, two) < 0) return -1; two->flags |= PARENT2; if (git_pqueue_insert(&list, two) < 0) return -1; } /* as long as there are non-STALE commits */ while (interesting(&list)) { git_commit_list_node *commit = git_pqueue_pop(&list); int flags; if (commit == NULL) break; flags = commit->flags & (PARENT1 | PARENT2 | STALE); if (flags == (PARENT1 | PARENT2)) { if (!(commit->flags & RESULT)) { commit->flags |= RESULT; if (git_commit_list_insert(commit, &result) == NULL) return -1; } /* we mark the parents of a merge stale */ flags |= STALE; } for (i = 0; i < commit->out_degree; i++) { git_commit_list_node *p = commit->parents[i]; if ((p->flags & flags) == flags) continue; if (p->generation < minimum_generation) continue; if ((error = git_commit_list_parse(walk, p)) < 0) return error; p->flags |= flags; if (git_pqueue_insert(&list, p) < 0) return -1; } } git_pqueue_free(&list); *out = result; return 0; } static int remove_redundant(git_revwalk *walk, git_vector *commits, uint32_t minimum_generation) { git_vector work = GIT_VECTOR_INIT; unsigned char *redundant; unsigned int *filled_index; unsigned int i, j; int error = 0; redundant = git__calloc(commits->length, 1); GIT_ERROR_CHECK_ALLOC(redundant); filled_index = git__calloc((commits->length - 1), sizeof(unsigned int)); GIT_ERROR_CHECK_ALLOC(filled_index); for (i = 0; i < commits->length; ++i) { if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0) goto done; } for (i = 0; i < commits->length; ++i) { git_commit_list *common = NULL; git_commit_list_node *commit = commits->contents[i]; if (redundant[i]) continue; git_vector_clear(&work); for (j = 0; j < commits->length; j++) { if (i == j || redundant[j]) continue; filled_index[work.length] = j; if ((error = git_vector_insert(&work, commits->contents[j])) < 0) goto done; } error = paint_down_to_common(&common, walk, commit, &work, minimum_generation); if (error < 0) goto done; if (commit->flags & PARENT2) redundant[i] = 1; for (j = 0; j < work.length; j++) { git_commit_list_node *w = work.contents[j]; if (w->flags & PARENT1) redundant[filled_index[j]] = 1; } git_commit_list_free(&common); if ((error = clear_commit_marks(commit, ALL_FLAGS)) < 0 || (error = clear_commit_marks_many(&work, ALL_FLAGS)) < 0) goto done; } for (i = 0; i < commits->length; ++i) { if (redundant[i]) commits->contents[i] = NULL; } done: git__free(redundant); git__free(filled_index); git_vector_free(&work); return error; } int git_merge__bases_many( git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos, uint32_t minimum_generation) { int error; unsigned int i; git_commit_list_node *two; git_commit_list *result = NULL, *tmp = NULL; /* If there's only the one commit, there can be no merge bases */ if (twos->length == 0) { *out = NULL; return 0; } /* if the commit is repeated, we have a our merge base already */ git_vector_foreach(twos, i, two) { if (one == two) return git_commit_list_insert(one, out) ? 0 : -1; } if (git_commit_list_parse(walk, one) < 0) return -1; error = paint_down_to_common(&result, walk, one, twos, minimum_generation); if (error < 0) return error; /* filter out any stale commits in the results */ tmp = result; result = NULL; while (tmp) { git_commit_list_node *c = git_commit_list_pop(&tmp); if (!(c->flags & STALE)) if (git_commit_list_insert_by_date(c, &result) == NULL) return -1; } /* * more than one merge base -- see if there are redundant merge * bases and remove them */ if (result && result->next) { git_vector redundant = GIT_VECTOR_INIT; while (result) git_vector_insert(&redundant, git_commit_list_pop(&result)); if ((error = clear_commit_marks(one, ALL_FLAGS)) < 0 || (error = clear_commit_marks_many(twos, ALL_FLAGS)) < 0 || (error = remove_redundant(walk, &redundant, minimum_generation)) < 0) { git_vector_free(&redundant); return error; } git_vector_foreach(&redundant, i, two) { if (two != NULL) git_commit_list_insert_by_date(two, &result); } git_vector_free(&redundant); } *out = result; return 0; } int git_repository_mergehead_foreach( git_repository *repo, git_repository_mergehead_foreach_cb cb, void *payload) { git_buf merge_head_path = GIT_BUF_INIT, merge_head_file = GIT_BUF_INIT; char *buffer, *line; size_t line_num = 1; git_oid oid; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(cb); if ((error = git_buf_joinpath(&merge_head_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0) return error; if ((error = git_futils_readbuffer(&merge_head_file, git_buf_cstr(&merge_head_path))) < 0) goto cleanup; buffer = merge_head_file.ptr; while ((line = git__strsep(&buffer, "\n")) != NULL) { if (strlen(line) != GIT_OID_HEXSZ) { git_error_set(GIT_ERROR_INVALID, "unable to parse OID - invalid length"); error = -1; goto cleanup; } if ((error = git_oid_fromstr(&oid, line)) < 0) goto cleanup; if ((error = cb(&oid, payload)) != 0) { git_error_set_after_callback(error); goto cleanup; } ++line_num; } if (*buffer) { git_error_set(GIT_ERROR_MERGE, "no EOL at line %"PRIuZ, line_num); error = -1; goto cleanup; } cleanup: git_buf_dispose(&merge_head_path); git_buf_dispose(&merge_head_file); return error; } GIT_INLINE(int) index_entry_cmp(const git_index_entry *a, const git_index_entry *b) { int value = 0; if (a->path == NULL) return (b->path == NULL) ? 0 : 1; if ((value = a->mode - b->mode) == 0 && (value = git_oid__cmp(&a->id, &b->id)) == 0) value = strcmp(a->path, b->path); return value; } /* Conflict resolution */ static int merge_conflict_resolve_trivial( int *resolved, git_merge_diff_list *diff_list, const git_merge_diff *conflict) { int ours_empty, theirs_empty; int ours_changed, theirs_changed, ours_theirs_differ; git_index_entry const *result = NULL; int error = 0; GIT_ASSERT_ARG(resolved); GIT_ASSERT_ARG(diff_list); GIT_ASSERT_ARG(conflict); *resolved = 0; if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE || conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) return 0; if (conflict->our_status == GIT_DELTA_RENAMED || conflict->their_status == GIT_DELTA_RENAMED) return 0; ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry); theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry); ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED); theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED); ours_theirs_differ = ours_changed && theirs_changed && index_entry_cmp(&conflict->our_entry, &conflict->their_entry); /* * Note: with only one ancestor, some cases are not distinct: * * 16: ancest:anc1/anc2, head:anc1, remote:anc2 = result:no merge * 3: ancest:(empty)^, head:head, remote:(empty) = result:no merge * 2: ancest:(empty)^, head:(empty), remote:remote = result:no merge * * Note that the two cases that take D/F conflicts into account * specifically do not need to be explicitly tested, as D/F conflicts * would fail the *empty* test: * * 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head * 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote * * Note that many of these cases need not be explicitly tested, as * they simply degrade to "all different" cases (eg, 11): * * 4: ancest:(empty)^, head:head, remote:remote = result:no merge * 7: ancest:ancest+, head:(empty), remote:remote = result:no merge * 9: ancest:ancest+, head:head, remote:(empty) = result:no merge * 11: ancest:ancest+, head:head, remote:remote = result:no merge */ /* 5ALT: ancest:*, head:head, remote:head = result:head */ if (ours_changed && !ours_empty && !ours_theirs_differ) result = &conflict->our_entry; /* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */ else if (ours_changed && ours_empty && theirs_empty) *resolved = 0; /* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */ else if (ours_empty && !theirs_changed) *resolved = 0; /* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */ else if (!ours_changed && theirs_empty) *resolved = 0; /* 13: ancest:ancest+, head:head, remote:ancest = result:head */ else if (ours_changed && !theirs_changed) result = &conflict->our_entry; /* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */ else if (!ours_changed && theirs_changed) result = &conflict->their_entry; else *resolved = 0; if (result != NULL && GIT_MERGE_INDEX_ENTRY_EXISTS(*result) && (error = git_vector_insert(&diff_list->staged, (void *)result)) >= 0) *resolved = 1; /* Note: trivial resolution does not update the REUC. */ return error; } static int merge_conflict_resolve_one_removed( int *resolved, git_merge_diff_list *diff_list, const git_merge_diff *conflict) { int ours_empty, theirs_empty; int ours_changed, theirs_changed; int error = 0; GIT_ASSERT_ARG(resolved); GIT_ASSERT_ARG(diff_list); GIT_ASSERT_ARG(conflict); *resolved = 0; if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE || conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) return 0; ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry); theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry); ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED); theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED); /* Removed in both */ if (ours_changed && ours_empty && theirs_empty) *resolved = 1; /* Removed in ours */ else if (ours_empty && !theirs_changed) *resolved = 1; /* Removed in theirs */ else if (!ours_changed && theirs_empty) *resolved = 1; if (*resolved) git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); return error; } static int merge_conflict_resolve_one_renamed( int *resolved, git_merge_diff_list *diff_list, const git_merge_diff *conflict) { int ours_renamed, theirs_renamed; int ours_changed, theirs_changed; git_index_entry *merged; int error = 0; GIT_ASSERT_ARG(resolved); GIT_ASSERT_ARG(diff_list); GIT_ASSERT_ARG(conflict); *resolved = 0; if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) || !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) return 0; ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED); theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED); if (!ours_renamed && !theirs_renamed) return 0; /* Reject one file in a 2->1 conflict */ if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 || conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 || conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) return 0; ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0) || (conflict->ancestor_entry.mode != conflict->our_entry.mode); theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0) || (conflict->ancestor_entry.mode != conflict->their_entry.mode); /* if both are modified (and not to a common target) require a merge */ if (ours_changed && theirs_changed && git_oid__cmp(&conflict->our_entry.id, &conflict->their_entry.id) != 0) return 0; if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) return -1; if (ours_changed) memcpy(merged, &conflict->our_entry, sizeof(git_index_entry)); else memcpy(merged, &conflict->their_entry, sizeof(git_index_entry)); if (ours_renamed) merged->path = conflict->our_entry.path; else merged->path = conflict->their_entry.path; *resolved = 1; git_vector_insert(&diff_list->staged, merged); git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); return error; } static bool merge_conflict_can_resolve_contents( const git_merge_diff *conflict) { if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) || !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) return false; /* Reject D/F conflicts */ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE) return false; /* Reject submodules. */ if (S_ISGITLINK(conflict->ancestor_entry.mode) || S_ISGITLINK(conflict->our_entry.mode) || S_ISGITLINK(conflict->their_entry.mode)) return false; /* Reject link/file conflicts. */ if ((S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->our_entry.mode)) || (S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->their_entry.mode))) return false; /* Reject name conflicts */ if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 || conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) return false; if ((conflict->our_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED && (conflict->their_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED && strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0) return false; return true; } static int merge_conflict_invoke_driver( git_index_entry **out, const char *name, git_merge_driver *driver, git_merge_diff_list *diff_list, git_merge_driver_source *src) { git_index_entry *result; git_buf buf = GIT_BUF_INIT; const char *path; uint32_t mode; git_odb *odb = NULL; git_oid oid; int error; *out = NULL; if ((error = driver->apply(driver, &path, &mode, &buf, name, src)) < 0 || (error = git_repository_odb(&odb, src->repo)) < 0 || (error = git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJECT_BLOB)) < 0) goto done; result = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry)); GIT_ERROR_CHECK_ALLOC(result); git_oid_cpy(&result->id, &oid); result->mode = mode; result->file_size = (uint32_t)buf.size; result->path = git_pool_strdup(&diff_list->pool, path); GIT_ERROR_CHECK_ALLOC(result->path); *out = result; done: git_buf_dispose(&buf); git_odb_free(odb); return error; } static int merge_conflict_resolve_contents( int *resolved, git_merge_diff_list *diff_list, const git_merge_diff *conflict, const git_merge_options *merge_opts, const git_merge_file_options *file_opts) { git_merge_driver_source source = {0}; git_merge_file_result result = {0}; git_merge_driver *driver; git_merge_driver__builtin builtin = {{0}}; git_index_entry *merge_result; git_odb *odb = NULL; const char *name; bool fallback = false; int error; GIT_ASSERT_ARG(resolved); GIT_ASSERT_ARG(diff_list); GIT_ASSERT_ARG(conflict); *resolved = 0; if (!merge_conflict_can_resolve_contents(conflict)) return 0; source.repo = diff_list->repo; source.default_driver = merge_opts->default_driver; source.file_opts = file_opts; source.ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? &conflict->ancestor_entry : NULL; source.ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? &conflict->our_entry : NULL; source.theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? &conflict->their_entry : NULL; if (file_opts->favor != GIT_MERGE_FILE_FAVOR_NORMAL) { /* if the user requested a particular type of resolution (via the * favor flag) then let that override the gitattributes and use * the builtin driver. */ name = "text"; builtin.base.apply = git_merge_driver__builtin_apply; builtin.favor = file_opts->favor; driver = &builtin.base; } else { /* find the merge driver for this file */ if ((error = git_merge_driver_for_source(&name, &driver, &source)) < 0) goto done; if (driver == NULL) fallback = true; } if (driver) { error = merge_conflict_invoke_driver(&merge_result, name, driver, diff_list, &source); if (error == GIT_PASSTHROUGH) fallback = true; } if (fallback) { error = merge_conflict_invoke_driver(&merge_result, "text", &git_merge_driver__text.base, diff_list, &source); } if (error < 0) { if (error == GIT_EMERGECONFLICT) error = 0; goto done; } git_vector_insert(&diff_list->staged, merge_result); git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); *resolved = 1; done: git_merge_file_result_free(&result); git_odb_free(odb); return error; } static int merge_conflict_resolve( int *out, git_merge_diff_list *diff_list, const git_merge_diff *conflict, const git_merge_options *merge_opts, const git_merge_file_options *file_opts) { int resolved = 0; int error = 0; *out = 0; if ((error = merge_conflict_resolve_trivial( &resolved, diff_list, conflict)) < 0) goto done; if (!resolved && (error = merge_conflict_resolve_one_removed( &resolved, diff_list, conflict)) < 0) goto done; if (!resolved && (error = merge_conflict_resolve_one_renamed( &resolved, diff_list, conflict)) < 0) goto done; if (!resolved && (error = merge_conflict_resolve_contents( &resolved, diff_list, conflict, merge_opts, file_opts)) < 0) goto done; *out = resolved; done: return error; } /* Rename detection and coalescing */ struct merge_diff_similarity { unsigned char similarity; size_t other_idx; }; static int index_entry_similarity_calc( void **out, git_repository *repo, git_index_entry *entry, const git_merge_options *opts) { git_blob *blob; git_diff_file diff_file = {{{0}}}; git_object_size_t blobsize; int error; if (*out || *out == &cache_invalid_marker) return 0; *out = NULL; if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0) return error; git_oid_cpy(&diff_file.id, &entry->id); diff_file.path = entry->path; diff_file.size = entry->file_size; diff_file.mode = entry->mode; diff_file.flags = 0; blobsize = git_blob_rawsize(blob); /* file too big for rename processing */ if (!git__is_sizet(blobsize)) return 0; error = opts->metric->buffer_signature(out, &diff_file, git_blob_rawcontent(blob), (size_t)blobsize, opts->metric->payload); if (error == GIT_EBUFS) *out = &cache_invalid_marker; git_blob_free(blob); return error; } static int index_entry_similarity_inexact( git_repository *repo, git_index_entry *a, size_t a_idx, git_index_entry *b, size_t b_idx, void **cache, const git_merge_options *opts) { int score = 0; int error = 0; if (!GIT_MODE_ISBLOB(a->mode) || !GIT_MODE_ISBLOB(b->mode)) return 0; /* update signature cache if needed */ if ((error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0 || (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0) return error; /* some metrics may not wish to process this file (too big / too small) */ if (cache[a_idx] == &cache_invalid_marker || cache[b_idx] == &cache_invalid_marker) return 0; /* compare signatures */ if (opts->metric->similarity(&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0) return -1; /* clip score */ if (score < 0) score = 0; else if (score > 100) score = 100; return score; } /* Tracks deletes by oid for merge_diff_mark_similarity_exact(). This is a * non-shrinking queue where next_pos is the next position to dequeue. */ typedef struct { git_array_t(size_t) arr; size_t next_pos; size_t first_entry; } deletes_by_oid_queue; static void deletes_by_oid_free(git_oidmap *map) { deletes_by_oid_queue *queue; if (!map) return; git_oidmap_foreach_value(map, queue, { git_array_clear(queue->arr); }); git_oidmap_free(map); } static int deletes_by_oid_enqueue(git_oidmap *map, git_pool *pool, const git_oid *id, size_t idx) { deletes_by_oid_queue *queue; size_t *array_entry; if ((queue = git_oidmap_get(map, id)) == NULL) { queue = git_pool_malloc(pool, sizeof(deletes_by_oid_queue)); GIT_ERROR_CHECK_ALLOC(queue); git_array_init(queue->arr); queue->next_pos = 0; queue->first_entry = idx; if (git_oidmap_set(map, id, queue) < 0) return -1; } else { array_entry = git_array_alloc(queue->arr); GIT_ERROR_CHECK_ALLOC(array_entry); *array_entry = idx; } return 0; } static int deletes_by_oid_dequeue(size_t *idx, git_oidmap *map, const git_oid *id) { deletes_by_oid_queue *queue; size_t *array_entry; if ((queue = git_oidmap_get(map, id)) == NULL) return GIT_ENOTFOUND; if (queue->next_pos == 0) { *idx = queue->first_entry; } else { array_entry = git_array_get(queue->arr, queue->next_pos - 1); if (array_entry == NULL) return GIT_ENOTFOUND; *idx = *array_entry; } queue->next_pos++; return 0; } static int merge_diff_mark_similarity_exact( git_merge_diff_list *diff_list, struct merge_diff_similarity *similarity_ours, struct merge_diff_similarity *similarity_theirs) { size_t i, j; git_merge_diff *conflict_src, *conflict_tgt; git_oidmap *ours_deletes_by_oid = NULL, *theirs_deletes_by_oid = NULL; int error = 0; if (git_oidmap_new(&ours_deletes_by_oid) < 0 || git_oidmap_new(&theirs_deletes_by_oid) < 0) { error = -1; goto done; } /* Build a map of object ids to conflicts */ git_vector_foreach(&diff_list->conflicts, i, conflict_src) { /* Items can be the source of a rename iff they have an item in the * ancestor slot and lack an item in the ours or theirs slot. */ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry)) continue; if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) { error = deletes_by_oid_enqueue(ours_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i); if (error < 0) goto done; } if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) { error = deletes_by_oid_enqueue(theirs_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i); if (error < 0) goto done; } } git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) { if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry)) continue; if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry)) { if (deletes_by_oid_dequeue(&i, ours_deletes_by_oid, &conflict_tgt->our_entry.id) == 0) { similarity_ours[i].similarity = 100; similarity_ours[i].other_idx = j; similarity_ours[j].similarity = 100; similarity_ours[j].other_idx = i; } } if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry)) { if (deletes_by_oid_dequeue(&i, theirs_deletes_by_oid, &conflict_tgt->their_entry.id) == 0) { similarity_theirs[i].similarity = 100; similarity_theirs[i].other_idx = j; similarity_theirs[j].similarity = 100; similarity_theirs[j].other_idx = i; } } } done: deletes_by_oid_free(ours_deletes_by_oid); deletes_by_oid_free(theirs_deletes_by_oid); return error; } static int merge_diff_mark_similarity_inexact( git_repository *repo, git_merge_diff_list *diff_list, struct merge_diff_similarity *similarity_ours, struct merge_diff_similarity *similarity_theirs, void **cache, const git_merge_options *opts) { size_t i, j; git_merge_diff *conflict_src, *conflict_tgt; int similarity; git_vector_foreach(&diff_list->conflicts, i, conflict_src) { /* Items can be the source of a rename iff they have an item in the * ancestor slot and lack an item in the ours or theirs slot. */ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry) || (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) && GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry))) continue; git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) { size_t our_idx = diff_list->conflicts.length + j; size_t their_idx = (diff_list->conflicts.length * 2) + j; if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry)) continue; if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) { similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts); if (similarity == GIT_EBUFS) continue; else if (similarity < 0) return similarity; if (similarity > similarity_ours[i].similarity && similarity > similarity_ours[j].similarity) { /* Clear previous best similarity */ if (similarity_ours[i].similarity > 0) similarity_ours[similarity_ours[i].other_idx].similarity = 0; if (similarity_ours[j].similarity > 0) similarity_ours[similarity_ours[j].other_idx].similarity = 0; similarity_ours[i].similarity = similarity; similarity_ours[i].other_idx = j; similarity_ours[j].similarity = similarity; similarity_ours[j].other_idx = i; } } if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) { similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts); if (similarity > similarity_theirs[i].similarity && similarity > similarity_theirs[j].similarity) { /* Clear previous best similarity */ if (similarity_theirs[i].similarity > 0) similarity_theirs[similarity_theirs[i].other_idx].similarity = 0; if (similarity_theirs[j].similarity > 0) similarity_theirs[similarity_theirs[j].other_idx].similarity = 0; similarity_theirs[i].similarity = similarity; similarity_theirs[i].other_idx = j; similarity_theirs[j].similarity = similarity; similarity_theirs[j].other_idx = i; } } } } return 0; } /* * Rename conflicts: * * Ancestor Ours Theirs * * 0a A A A No rename * b A A* A No rename (ours was rewritten) * c A A A* No rename (theirs rewritten) * 1a A A B[A] Rename or rename/edit * b A B[A] A (automergeable) * 2 A B[A] B[A] Both renamed (automergeable) * 3a A B[A] Rename/delete * b A B[A] (same) * 4a A B[A] B Rename/add [B~ours B~theirs] * b A B B[A] (same) * 5 A B[A] C[A] Both renamed ("1 -> 2") * 6 A C[A] Both renamed ("2 -> 1") * B C[B] [C~ours C~theirs] (automergeable) */ static void merge_diff_mark_rename_conflict( git_merge_diff_list *diff_list, struct merge_diff_similarity *similarity_ours, bool ours_renamed, size_t ours_source_idx, struct merge_diff_similarity *similarity_theirs, bool theirs_renamed, size_t theirs_source_idx, git_merge_diff *target, const git_merge_options *opts) { git_merge_diff *ours_source = NULL, *theirs_source = NULL; if (ours_renamed) ours_source = diff_list->conflicts.contents[ours_source_idx]; if (theirs_renamed) theirs_source = diff_list->conflicts.contents[theirs_source_idx]; /* Detect 2->1 conflicts */ if (ours_renamed && theirs_renamed) { /* Both renamed to the same target name. */ if (ours_source_idx == theirs_source_idx) ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED; else { ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1; theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1; } } else if (ours_renamed) { /* If our source was also renamed in theirs, this is a 1->2 */ if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold) ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2; else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) { ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED; target->type = GIT_MERGE_DIFF_RENAMED_ADDED; } else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry)) ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED; else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED) ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED; } else if (theirs_renamed) { /* If their source was also renamed in ours, this is a 1->2 */ if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold) theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2; else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) { theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED; target->type = GIT_MERGE_DIFF_RENAMED_ADDED; } else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry)) theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED; else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED) theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED; } } GIT_INLINE(void) merge_diff_coalesce_rename( git_index_entry *source_entry, git_delta_t *source_status, git_index_entry *target_entry, git_delta_t *target_status) { /* Coalesce the rename target into the rename source. */ memcpy(source_entry, target_entry, sizeof(git_index_entry)); *source_status = GIT_DELTA_RENAMED; memset(target_entry, 0x0, sizeof(git_index_entry)); *target_status = GIT_DELTA_UNMODIFIED; } static void merge_diff_list_coalesce_renames( git_merge_diff_list *diff_list, struct merge_diff_similarity *similarity_ours, struct merge_diff_similarity *similarity_theirs, const git_merge_options *opts) { size_t i; bool ours_renamed = 0, theirs_renamed = 0; size_t ours_source_idx = 0, theirs_source_idx = 0; git_merge_diff *ours_source, *theirs_source, *target; for (i = 0; i < diff_list->conflicts.length; i++) { target = diff_list->conflicts.contents[i]; ours_renamed = 0; theirs_renamed = 0; if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) && similarity_ours[i].similarity >= opts->rename_threshold) { ours_source_idx = similarity_ours[i].other_idx; ours_source = diff_list->conflicts.contents[ours_source_idx]; merge_diff_coalesce_rename( &ours_source->our_entry, &ours_source->our_status, &target->our_entry, &target->our_status); similarity_ours[ours_source_idx].similarity = 0; similarity_ours[i].similarity = 0; ours_renamed = 1; } /* insufficient to determine direction */ if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) && similarity_theirs[i].similarity >= opts->rename_threshold) { theirs_source_idx = similarity_theirs[i].other_idx; theirs_source = diff_list->conflicts.contents[theirs_source_idx]; merge_diff_coalesce_rename( &theirs_source->their_entry, &theirs_source->their_status, &target->their_entry, &target->their_status); similarity_theirs[theirs_source_idx].similarity = 0; similarity_theirs[i].similarity = 0; theirs_renamed = 1; } merge_diff_mark_rename_conflict(diff_list, similarity_ours, ours_renamed, ours_source_idx, similarity_theirs, theirs_renamed, theirs_source_idx, target, opts); } } static int merge_diff_empty(const git_vector *conflicts, size_t idx, void *p) { git_merge_diff *conflict = conflicts->contents[idx]; GIT_UNUSED(p); return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)); } static void merge_diff_list_count_candidates( git_merge_diff_list *diff_list, size_t *src_count, size_t *tgt_count) { git_merge_diff *entry; size_t i; *src_count = 0; *tgt_count = 0; git_vector_foreach(&diff_list->conflicts, i, entry) { if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) && (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) || !GIT_MERGE_INDEX_ENTRY_EXISTS(entry->their_entry))) (*src_count)++; else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry)) (*tgt_count)++; } } int git_merge_diff_list__find_renames( git_repository *repo, git_merge_diff_list *diff_list, const git_merge_options *opts) { struct merge_diff_similarity *similarity_ours, *similarity_theirs; void **cache = NULL; size_t cache_size = 0; size_t src_count, tgt_count, i; int error = 0; GIT_ASSERT_ARG(diff_list); GIT_ASSERT_ARG(opts); if ((opts->flags & GIT_MERGE_FIND_RENAMES) == 0 || !diff_list->conflicts.length) return 0; similarity_ours = git__calloc(diff_list->conflicts.length, sizeof(struct merge_diff_similarity)); GIT_ERROR_CHECK_ALLOC(similarity_ours); similarity_theirs = git__calloc(diff_list->conflicts.length, sizeof(struct merge_diff_similarity)); GIT_ERROR_CHECK_ALLOC(similarity_theirs); /* Calculate similarity between items that were deleted from the ancestor * and added in the other branch. */ if ((error = merge_diff_mark_similarity_exact(diff_list, similarity_ours, similarity_theirs)) < 0) goto done; if (opts->rename_threshold < 100 && diff_list->conflicts.length <= opts->target_limit) { GIT_ERROR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3); cache = git__calloc(cache_size, sizeof(void *)); GIT_ERROR_CHECK_ALLOC(cache); merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count); if (src_count > opts->target_limit || tgt_count > opts->target_limit) { /* TODO: report! */ } else { if ((error = merge_diff_mark_similarity_inexact( repo, diff_list, similarity_ours, similarity_theirs, cache, opts)) < 0) goto done; } } /* For entries that are appropriately similar, merge the new name's entry * into the old name. */ merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts); /* And remove any entries that were merged and are now empty. */ git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty, NULL); done: if (cache != NULL) { for (i = 0; i < cache_size; ++i) { if (cache[i] != NULL && cache[i] != &cache_invalid_marker) opts->metric->free_signature(cache[i], opts->metric->payload); } git__free(cache); } git__free(similarity_ours); git__free(similarity_theirs); return error; } /* Directory/file conflict handling */ GIT_INLINE(const char *) merge_diff_path( const git_merge_diff *conflict) { if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry)) return conflict->ancestor_entry.path; else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry)) return conflict->our_entry.path; else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) return conflict->their_entry.path; return NULL; } GIT_INLINE(bool) merge_diff_any_side_added_or_modified( const git_merge_diff *conflict) { if (conflict->our_status == GIT_DELTA_ADDED || conflict->our_status == GIT_DELTA_MODIFIED || conflict->their_status == GIT_DELTA_ADDED || conflict->their_status == GIT_DELTA_MODIFIED) return true; return false; } GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child) { size_t child_len = strlen(child); size_t parent_len = strlen(parent); if (child_len < parent_len || strncmp(parent, child, parent_len) != 0) return 0; return (child[parent_len] == '/'); } GIT_INLINE(int) merge_diff_detect_df_conflict( struct merge_diff_df_data *df_data, git_merge_diff *conflict) { const char *cur_path = merge_diff_path(conflict); /* Determine if this is a D/F conflict or the child of one */ if (df_data->df_path && path_is_prefixed(df_data->df_path, cur_path)) conflict->type = GIT_MERGE_DIFF_DF_CHILD; else if(df_data->df_path) df_data->df_path = NULL; else if (df_data->prev_path && merge_diff_any_side_added_or_modified(df_data->prev_conflict) && merge_diff_any_side_added_or_modified(conflict) && path_is_prefixed(df_data->prev_path, cur_path)) { conflict->type = GIT_MERGE_DIFF_DF_CHILD; df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE; df_data->df_path = df_data->prev_path; } df_data->prev_path = cur_path; df_data->prev_conflict = conflict; return 0; } /* Conflict handling */ GIT_INLINE(int) merge_diff_detect_type( git_merge_diff *conflict) { if (conflict->our_status == GIT_DELTA_ADDED && conflict->their_status == GIT_DELTA_ADDED) conflict->type = GIT_MERGE_DIFF_BOTH_ADDED; else if (conflict->our_status == GIT_DELTA_MODIFIED && conflict->their_status == GIT_DELTA_MODIFIED) conflict->type = GIT_MERGE_DIFF_BOTH_MODIFIED; else if (conflict->our_status == GIT_DELTA_DELETED && conflict->their_status == GIT_DELTA_DELETED) conflict->type = GIT_MERGE_DIFF_BOTH_DELETED; else if (conflict->our_status == GIT_DELTA_MODIFIED && conflict->their_status == GIT_DELTA_DELETED) conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED; else if (conflict->our_status == GIT_DELTA_DELETED && conflict->their_status == GIT_DELTA_MODIFIED) conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED; else conflict->type = GIT_MERGE_DIFF_NONE; return 0; } GIT_INLINE(int) index_entry_dup_pool( git_index_entry *out, git_pool *pool, const git_index_entry *src) { if (src != NULL) { memcpy(out, src, sizeof(git_index_entry)); if ((out->path = git_pool_strdup(pool, src->path)) == NULL) return -1; } return 0; } GIT_INLINE(int) merge_delta_type_from_index_entries( const git_index_entry *ancestor, const git_index_entry *other) { if (ancestor == NULL && other == NULL) return GIT_DELTA_UNMODIFIED; else if (ancestor == NULL && other != NULL) return GIT_DELTA_ADDED; else if (ancestor != NULL && other == NULL) return GIT_DELTA_DELETED; else if (S_ISDIR(ancestor->mode) ^ S_ISDIR(other->mode)) return GIT_DELTA_TYPECHANGE; else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode)) return GIT_DELTA_TYPECHANGE; else if (git_oid__cmp(&ancestor->id, &other->id) || ancestor->mode != other->mode) return GIT_DELTA_MODIFIED; return GIT_DELTA_UNMODIFIED; } static git_merge_diff *merge_diff_from_index_entries( git_merge_diff_list *diff_list, const git_index_entry **entries) { git_merge_diff *conflict; git_pool *pool = &diff_list->pool; if ((conflict = git_pool_mallocz(pool, sizeof(git_merge_diff))) == NULL) return NULL; if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || index_entry_dup_pool(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || index_entry_dup_pool(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) return NULL; conflict->our_status = merge_delta_type_from_index_entries( entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]); conflict->their_status = merge_delta_type_from_index_entries( entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]); return conflict; } /* Merge trees */ static int merge_diff_list_insert_conflict( git_merge_diff_list *diff_list, struct merge_diff_df_data *merge_df_data, const git_index_entry *tree_items[3]) { git_merge_diff *conflict; if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL || merge_diff_detect_type(conflict) < 0 || merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 || git_vector_insert(&diff_list->conflicts, conflict) < 0) return -1; return 0; } static int merge_diff_list_insert_unmodified( git_merge_diff_list *diff_list, const git_index_entry *tree_items[3]) { int error = 0; git_index_entry *entry; entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry)); GIT_ERROR_CHECK_ALLOC(entry); if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0) error = git_vector_insert(&diff_list->staged, entry); return error; } struct merge_diff_find_data { git_merge_diff_list *diff_list; struct merge_diff_df_data df_data; }; static int queue_difference(const git_index_entry **entries, void *data) { struct merge_diff_find_data *find_data = data; bool item_modified = false; size_t i; if (!entries[0] || !entries[1] || !entries[2]) { item_modified = true; } else { for (i = 1; i < 3; i++) { if (index_entry_cmp(entries[0], entries[i]) != 0) { item_modified = true; break; } } } return item_modified ? merge_diff_list_insert_conflict( find_data->diff_list, &find_data->df_data, entries) : merge_diff_list_insert_unmodified(find_data->diff_list, entries); } int git_merge_diff_list__find_differences( git_merge_diff_list *diff_list, git_iterator *ancestor_iter, git_iterator *our_iter, git_iterator *their_iter) { git_iterator *iterators[3] = { ancestor_iter, our_iter, their_iter }; struct merge_diff_find_data find_data = { diff_list }; return git_iterator_walk(iterators, 3, queue_difference, &find_data); } git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo) { git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list)); if (diff_list == NULL) return NULL; diff_list->repo = repo; if (git_pool_init(&diff_list->pool, 1) < 0 || git_vector_init(&diff_list->staged, 0, NULL) < 0 || git_vector_init(&diff_list->conflicts, 0, NULL) < 0 || git_vector_init(&diff_list->resolved, 0, NULL) < 0) { git_merge_diff_list__free(diff_list); return NULL; } return diff_list; } void git_merge_diff_list__free(git_merge_diff_list *diff_list) { if (!diff_list) return; git_vector_free(&diff_list->staged); git_vector_free(&diff_list->conflicts); git_vector_free(&diff_list->resolved); git_pool_clear(&diff_list->pool); git__free(diff_list); } static int merge_normalize_opts( git_repository *repo, git_merge_options *opts, const git_merge_options *given) { git_config *cfg = NULL; git_config_entry *entry = NULL; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(opts); if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; if (given != NULL) { memcpy(opts, given, sizeof(git_merge_options)); } else { git_merge_options init = GIT_MERGE_OPTIONS_INIT; memcpy(opts, &init, sizeof(init)); } if ((opts->flags & GIT_MERGE_FIND_RENAMES) && !opts->rename_threshold) opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD; if (given && given->default_driver) { opts->default_driver = git__strdup(given->default_driver); GIT_ERROR_CHECK_ALLOC(opts->default_driver); } else { error = git_config_get_entry(&entry, cfg, "merge.default"); if (error == 0) { opts->default_driver = git__strdup(entry->value); GIT_ERROR_CHECK_ALLOC(opts->default_driver); } else if (error == GIT_ENOTFOUND) { error = 0; } else { goto done; } } if (!opts->target_limit) { int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0); if (!limit) limit = git_config__get_int_force(cfg, "diff.renamelimit", 0); opts->target_limit = (limit <= 0) ? GIT_MERGE_DEFAULT_TARGET_LIMIT : (unsigned int)limit; } /* assign the internal metric with whitespace flag as payload */ if (!opts->metric) { opts->metric = git__malloc(sizeof(git_diff_similarity_metric)); GIT_ERROR_CHECK_ALLOC(opts->metric); opts->metric->file_signature = git_diff_find_similar__hashsig_for_file; opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf; opts->metric->free_signature = git_diff_find_similar__hashsig_free; opts->metric->similarity = git_diff_find_similar__calc_similarity; opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE; } done: git_config_entry_free(entry); return error; } static int merge_index_insert_reuc( git_index *index, size_t idx, const git_index_entry *entry) { const git_index_reuc_entry *reuc; int mode[3] = { 0, 0, 0 }; git_oid const *oid[3] = { NULL, NULL, NULL }; size_t i; if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry)) return 0; if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) { for (i = 0; i < 3; i++) { mode[i] = reuc->mode[i]; oid[i] = &reuc->oid[i]; } } mode[idx] = entry->mode; oid[idx] = &entry->id; return git_index_reuc_add(index, entry->path, mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]); } static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list) { int error; size_t i; git_merge_diff *conflict; /* Add each entry in the resolved conflict to the REUC independently, since * the paths may differ due to renames. */ git_vector_foreach(&diff_list->resolved, i, conflict) { const git_index_entry *ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? &conflict->ancestor_entry : NULL; const git_index_entry *ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? &conflict->our_entry : NULL; const git_index_entry *theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? &conflict->their_entry : NULL; if (ancestor != NULL && (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0) return error; if (ours != NULL && (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0) return error; if (theirs != NULL && (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0) return error; } return 0; } static int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list, bool skip_reuc) { git_index *index; size_t i; git_merge_diff *conflict; int error = 0; *out = NULL; if ((error = git_index_new(&index)) < 0) return error; if ((error = git_index__fill(index, &diff_list->staged)) < 0) goto on_error; git_vector_foreach(&diff_list->conflicts, i, conflict) { const git_index_entry *ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? &conflict->ancestor_entry : NULL; const git_index_entry *ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? &conflict->our_entry : NULL; const git_index_entry *theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? &conflict->their_entry : NULL; if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0) goto on_error; } /* Add each rename entry to the rename portion of the index. */ git_vector_foreach(&diff_list->conflicts, i, conflict) { const char *ancestor_path, *our_path, *their_path; if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry)) continue; ancestor_path = conflict->ancestor_entry.path; our_path = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? conflict->our_entry.path : NULL; their_path = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? conflict->their_entry.path : NULL; if ((our_path && strcmp(ancestor_path, our_path) != 0) || (their_path && strcmp(ancestor_path, their_path) != 0)) { if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0) goto on_error; } } if (!skip_reuc) { if ((error = index_update_reuc(index, diff_list)) < 0) goto on_error; } *out = index; return 0; on_error: git_index_free(index); return error; } static git_iterator *iterator_given_or_empty(git_iterator **empty, git_iterator *given) { git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; if (given) return given; opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if (git_iterator_for_nothing(empty, &opts) < 0) return NULL; return *empty; } int git_merge__iterators( git_index **out, git_repository *repo, git_iterator *ancestor_iter, git_iterator *our_iter, git_iterator *theirs_iter, const git_merge_options *given_opts) { git_iterator *empty_ancestor = NULL, *empty_ours = NULL, *empty_theirs = NULL; git_merge_diff_list *diff_list; git_merge_options opts; git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_diff *conflict; git_vector changes; size_t i; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; GIT_ERROR_CHECK_VERSION( given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options"); if ((error = merge_normalize_opts(repo, &opts, given_opts)) < 0) return error; file_opts.favor = opts.file_favor; file_opts.flags = opts.file_flags; /* use the git-inspired labels when virtual base building */ if (opts.flags & GIT_MERGE__VIRTUAL_BASE) { file_opts.ancestor_label = "merged common ancestors"; file_opts.our_label = "Temporary merge branch 1"; file_opts.their_label = "Temporary merge branch 2"; file_opts.flags |= GIT_MERGE_FILE_FAVOR__CONFLICTED; file_opts.marker_size = GIT_MERGE_CONFLICT_MARKER_SIZE + 2; } diff_list = git_merge_diff_list__alloc(repo); GIT_ERROR_CHECK_ALLOC(diff_list); ancestor_iter = iterator_given_or_empty(&empty_ancestor, ancestor_iter); our_iter = iterator_given_or_empty(&empty_ours, our_iter); theirs_iter = iterator_given_or_empty(&empty_theirs, theirs_iter); if ((error = git_merge_diff_list__find_differences( diff_list, ancestor_iter, our_iter, theirs_iter)) < 0 || (error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0) goto done; memcpy(&changes, &diff_list->conflicts, sizeof(git_vector)); git_vector_clear(&diff_list->conflicts); git_vector_foreach(&changes, i, conflict) { int resolved = 0; if ((error = merge_conflict_resolve( &resolved, diff_list, conflict, &opts, &file_opts)) < 0) goto done; if (!resolved) { if ((opts.flags & GIT_MERGE_FAIL_ON_CONFLICT)) { git_error_set(GIT_ERROR_MERGE, "merge conflicts exist"); error = GIT_EMERGECONFLICT; goto done; } git_vector_insert(&diff_list->conflicts, conflict); } } error = index_from_diff_list(out, diff_list, (opts.flags & GIT_MERGE_SKIP_REUC)); done: if (!given_opts || !given_opts->metric) git__free(opts.metric); git__free((char *)opts.default_driver); git_merge_diff_list__free(diff_list); git_iterator_free(empty_ancestor); git_iterator_free(empty_ours); git_iterator_free(empty_theirs); return error; } int git_merge_trees( git_index **out, git_repository *repo, const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, const git_merge_options *merge_opts) { git_iterator *ancestor_iter = NULL, *our_iter = NULL, *their_iter = NULL; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); /* if one side is treesame to the ancestor, take the other side */ if (ancestor_tree && merge_opts && (merge_opts->flags & GIT_MERGE_SKIP_REUC)) { const git_tree *result = NULL; const git_oid *ancestor_tree_id = git_tree_id(ancestor_tree); if (our_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(our_tree))) result = their_tree; else if (their_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(their_tree))) result = our_tree; if (result) { if ((error = git_index_new(out)) == 0) error = git_index_read_tree(*out, result); return error; } } iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree( &ancestor_iter, (git_tree *)ancestor_tree, &iter_opts)) < 0 || (error = git_iterator_for_tree( &our_iter, (git_tree *)our_tree, &iter_opts)) < 0 || (error = git_iterator_for_tree( &their_iter, (git_tree *)their_tree, &iter_opts)) < 0) goto done; error = git_merge__iterators( out, repo, ancestor_iter, our_iter, their_iter, merge_opts); done: git_iterator_free(ancestor_iter); git_iterator_free(our_iter); git_iterator_free(their_iter); return error; } static int merge_annotated_commits( git_index **index_out, git_annotated_commit **base_out, git_repository *repo, git_annotated_commit *our_commit, git_annotated_commit *their_commit, size_t recursion_level, const git_merge_options *opts); GIT_INLINE(int) insert_head_ids( git_array_oid_t *ids, const git_annotated_commit *annotated_commit) { git_oid *id; size_t i; if (annotated_commit->type == GIT_ANNOTATED_COMMIT_REAL) { id = git_array_alloc(*ids); GIT_ERROR_CHECK_ALLOC(id); git_oid_cpy(id, git_commit_id(annotated_commit->commit)); } else { for (i = 0; i < annotated_commit->parents.size; i++) { id = git_array_alloc(*ids); GIT_ERROR_CHECK_ALLOC(id); git_oid_cpy(id, &annotated_commit->parents.ptr[i]); } } return 0; } static int create_virtual_base( git_annotated_commit **out, git_repository *repo, git_annotated_commit *one, git_annotated_commit *two, const git_merge_options *opts, size_t recursion_level) { git_annotated_commit *result = NULL; git_index *index = NULL; git_merge_options virtual_opts = GIT_MERGE_OPTIONS_INIT; /* Conflicts in the merge base creation do not propagate to conflicts * in the result; the conflicted base will act as the common ancestor. */ if (opts) memcpy(&virtual_opts, opts, sizeof(git_merge_options)); virtual_opts.flags &= ~GIT_MERGE_FAIL_ON_CONFLICT; virtual_opts.flags |= GIT_MERGE__VIRTUAL_BASE; if ((merge_annotated_commits(&index, NULL, repo, one, two, recursion_level + 1, &virtual_opts)) < 0) return -1; result = git__calloc(1, sizeof(git_annotated_commit)); GIT_ERROR_CHECK_ALLOC(result); result->type = GIT_ANNOTATED_COMMIT_VIRTUAL; result->index = index; if (insert_head_ids(&result->parents, one) < 0 || insert_head_ids(&result->parents, two) < 0) { git_annotated_commit_free(result); return -1; } *out = result; return 0; } static int compute_base( git_annotated_commit **out, git_repository *repo, const git_annotated_commit *one, const git_annotated_commit *two, const git_merge_options *given_opts, size_t recursion_level) { git_array_oid_t head_ids = GIT_ARRAY_INIT; git_oidarray bases = {0}; git_annotated_commit *base = NULL, *other = NULL, *new_base = NULL; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; size_t i, base_count; int error; *out = NULL; if (given_opts) memcpy(&opts, given_opts, sizeof(git_merge_options)); /* With more than two commits, merge_bases_many finds the base of * the first commit and a hypothetical merge of the others. Since * "one" may itself be a virtual commit, which insert_head_ids * substitutes multiple ancestors for, it needs to be added * after "two" which is always a single real commit. */ if ((error = insert_head_ids(&head_ids, two)) < 0 || (error = insert_head_ids(&head_ids, one)) < 0 || (error = git_merge_bases_many(&bases, repo, head_ids.size, head_ids.ptr)) < 0) goto done; base_count = (opts.flags & GIT_MERGE_NO_RECURSIVE) ? 0 : bases.count; if (base_count) git_oidarray__reverse(&bases); if ((error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0) goto done; for (i = 1; i < base_count; i++) { recursion_level++; if (opts.recursion_limit && recursion_level > opts.recursion_limit) break; if ((error = git_annotated_commit_lookup(&other, repo, &bases.ids[i])) < 0 || (error = create_virtual_base(&new_base, repo, base, other, &opts, recursion_level)) < 0) goto done; git_annotated_commit_free(base); git_annotated_commit_free(other); base = new_base; new_base = NULL; other = NULL; } done: if (error == 0) *out = base; else git_annotated_commit_free(base); git_annotated_commit_free(other); git_annotated_commit_free(new_base); git_oidarray_dispose(&bases); git_array_clear(head_ids); return error; } static int iterator_for_annotated_commit( git_iterator **out, git_annotated_commit *commit) { git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; int error; opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if (commit == NULL) { error = git_iterator_for_nothing(out, &opts); } else if (commit->type == GIT_ANNOTATED_COMMIT_VIRTUAL) { error = git_iterator_for_index(out, git_index_owner(commit->index), commit->index, &opts); } else { if (!commit->tree && (error = git_commit_tree(&commit->tree, commit->commit)) < 0) goto done; error = git_iterator_for_tree(out, commit->tree, &opts); } done: return error; } static int merge_annotated_commits( git_index **index_out, git_annotated_commit **base_out, git_repository *repo, git_annotated_commit *ours, git_annotated_commit *theirs, size_t recursion_level, const git_merge_options *opts) { git_annotated_commit *base = NULL; git_iterator *base_iter = NULL, *our_iter = NULL, *their_iter = NULL; int error; if ((error = compute_base(&base, repo, ours, theirs, opts, recursion_level)) < 0) { if (error != GIT_ENOTFOUND) goto done; git_error_clear(); } if ((error = iterator_for_annotated_commit(&base_iter, base)) < 0 || (error = iterator_for_annotated_commit(&our_iter, ours)) < 0 || (error = iterator_for_annotated_commit(&their_iter, theirs)) < 0 || (error = git_merge__iterators(index_out, repo, base_iter, our_iter, their_iter, opts)) < 0) goto done; if (base_out) { *base_out = base; base = NULL; } done: git_annotated_commit_free(base); git_iterator_free(base_iter); git_iterator_free(our_iter); git_iterator_free(their_iter); return error; } int git_merge_commits( git_index **out, git_repository *repo, const git_commit *our_commit, const git_commit *their_commit, const git_merge_options *opts) { git_annotated_commit *ours = NULL, *theirs = NULL, *base = NULL; int error = 0; if ((error = git_annotated_commit_from_commit(&ours, (git_commit *)our_commit)) < 0 || (error = git_annotated_commit_from_commit(&theirs, (git_commit *)their_commit)) < 0) goto done; error = merge_annotated_commits(out, &base, repo, ours, theirs, 0, opts); done: git_annotated_commit_free(ours); git_annotated_commit_free(theirs); git_annotated_commit_free(base); return error; } /* Merge setup / cleanup */ static int write_merge_head( git_repository *repo, const git_annotated_commit *heads[], size_t heads_len) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; size_t i; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(heads); if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; for (i = 0; i < heads_len; i++) { if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->id_str)) < 0) goto cleanup; } error = git_filebuf_commit(&file); cleanup: if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } static int write_merge_mode(git_repository *repo) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; int error = 0; GIT_ASSERT_ARG(repo); if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MODE_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0) goto cleanup; error = git_filebuf_commit(&file); cleanup: if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } struct merge_msg_entry { const git_annotated_commit *merge_head; bool written; }; static int msg_entry_is_branch( const struct merge_msg_entry *entry, git_vector *entries) { GIT_UNUSED(entries); return (entry->written == 0 && entry->merge_head->remote_url == NULL && entry->merge_head->ref_name != NULL && git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0); } static int msg_entry_is_tracking( const struct merge_msg_entry *entry, git_vector *entries) { GIT_UNUSED(entries); return (entry->written == 0 && entry->merge_head->remote_url == NULL && entry->merge_head->ref_name != NULL && git__strncmp(GIT_REFS_REMOTES_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_REMOTES_DIR)) == 0); } static int msg_entry_is_tag( const struct merge_msg_entry *entry, git_vector *entries) { GIT_UNUSED(entries); return (entry->written == 0 && entry->merge_head->remote_url == NULL && entry->merge_head->ref_name != NULL && git__strncmp(GIT_REFS_TAGS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_TAGS_DIR)) == 0); } static int msg_entry_is_remote( const struct merge_msg_entry *entry, git_vector *entries) { if (entry->written == 0 && entry->merge_head->remote_url != NULL && entry->merge_head->ref_name != NULL && git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0) { struct merge_msg_entry *existing; /* Match only branches from the same remote */ if (entries->length == 0) return 1; existing = git_vector_get(entries, 0); return (git__strcmp(existing->merge_head->remote_url, entry->merge_head->remote_url) == 0); } return 0; } static int msg_entry_is_oid( const struct merge_msg_entry *merge_msg_entry) { return (merge_msg_entry->written == 0 && merge_msg_entry->merge_head->ref_name == NULL && merge_msg_entry->merge_head->remote_url == NULL); } static int merge_msg_entry_written( const struct merge_msg_entry *merge_msg_entry) { return (merge_msg_entry->written == 1); } static int merge_msg_entries( git_vector *v, const struct merge_msg_entry *entries, size_t len, int (*match)(const struct merge_msg_entry *entry, git_vector *entries)) { size_t i; int matches, total = 0; git_vector_clear(v); for (i = 0; i < len; i++) { if ((matches = match(&entries[i], v)) < 0) return matches; else if (!matches) continue; git_vector_insert(v, (struct merge_msg_entry *)&entries[i]); total++; } return total; } static int merge_msg_write_entries( git_filebuf *file, git_vector *entries, const char *item_name, const char *item_plural_name, size_t ref_name_skip, const char *source, char sep) { struct merge_msg_entry *entry; size_t i; int error = 0; if (entries->length == 0) return 0; if (sep && (error = git_filebuf_printf(file, "%c ", sep)) < 0) goto done; if ((error = git_filebuf_printf(file, "%s ", (entries->length == 1) ? item_name : item_plural_name)) < 0) goto done; git_vector_foreach(entries, i, entry) { if (i > 0 && (error = git_filebuf_printf(file, "%s", (i == entries->length - 1) ? " and " : ", ")) < 0) goto done; if ((error = git_filebuf_printf(file, "'%s'", entry->merge_head->ref_name + ref_name_skip)) < 0) goto done; entry->written = 1; } if (source) error = git_filebuf_printf(file, " of %s", source); done: return error; } static int merge_msg_write_branches( git_filebuf *file, git_vector *entries, char sep) { return merge_msg_write_entries(file, entries, "branch", "branches", strlen(GIT_REFS_HEADS_DIR), NULL, sep); } static int merge_msg_write_tracking( git_filebuf *file, git_vector *entries, char sep) { return merge_msg_write_entries(file, entries, "remote-tracking branch", "remote-tracking branches", 0, NULL, sep); } static int merge_msg_write_tags( git_filebuf *file, git_vector *entries, char sep) { return merge_msg_write_entries(file, entries, "tag", "tags", strlen(GIT_REFS_TAGS_DIR), NULL, sep); } static int merge_msg_write_remotes( git_filebuf *file, git_vector *entries, char sep) { const char *source; if (entries->length == 0) return 0; source = ((struct merge_msg_entry *)entries->contents[0])->merge_head->remote_url; return merge_msg_write_entries(file, entries, "branch", "branches", strlen(GIT_REFS_HEADS_DIR), source, sep); } static int write_merge_msg( git_repository *repo, const git_annotated_commit *heads[], size_t heads_len) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; struct merge_msg_entry *entries; git_vector matching = GIT_VECTOR_INIT; size_t i; char sep = 0; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(heads); entries = git__calloc(heads_len, sizeof(struct merge_msg_entry)); GIT_ERROR_CHECK_ALLOC(entries); if (git_vector_init(&matching, heads_len, NULL) < 0) { git__free(entries); return -1; } for (i = 0; i < heads_len; i++) entries[i].merge_head = heads[i]; if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0 || (error = git_filebuf_write(&file, "Merge ", 6)) < 0) goto cleanup; /* * This is to emulate the format of MERGE_MSG by core git. * * Core git will write all the commits specified by OID, in the order * provided, until the first named branch or tag is reached, at which * point all branches will be written in the order provided, then all * tags, then all remote tracking branches and finally all commits that * were specified by OID that were not already written. * * Yes. Really. */ for (i = 0; i < heads_len; i++) { if (!msg_entry_is_oid(&entries[i])) break; if ((error = git_filebuf_printf(&file, "%scommit '%s'", (i > 0) ? "; " : "", entries[i].merge_head->id_str)) < 0) goto cleanup; entries[i].written = 1; } if (i) sep = ';'; if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_branch)) < 0 || (error = merge_msg_write_branches(&file, &matching, sep)) < 0) goto cleanup; if (matching.length) sep =','; if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tracking)) < 0 || (error = merge_msg_write_tracking(&file, &matching, sep)) < 0) goto cleanup; if (matching.length) sep =','; if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 || (error = merge_msg_write_tags(&file, &matching, sep)) < 0) goto cleanup; if (matching.length) sep =','; /* We should never be called with multiple remote branches, but handle * it in case we are... */ while ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_remote)) > 0) { if ((error = merge_msg_write_remotes(&file, &matching, sep)) < 0) goto cleanup; if (matching.length) sep =','; } if (error < 0) goto cleanup; for (i = 0; i < heads_len; i++) { if (merge_msg_entry_written(&entries[i])) continue; if ((error = git_filebuf_printf(&file, "; commit '%s'", entries[i].merge_head->id_str)) < 0) goto cleanup; } if ((error = git_filebuf_printf(&file, "\n")) < 0 || (error = git_filebuf_commit(&file)) < 0) goto cleanup; cleanup: if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); git_vector_free(&matching); git__free(entries); return error; } int git_merge__setup( git_repository *repo, const git_annotated_commit *our_head, const git_annotated_commit *heads[], size_t heads_len) { int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(our_head); GIT_ASSERT_ARG(heads); if ((error = git_repository__set_orig_head(repo, git_annotated_commit_id(our_head))) == 0 && (error = write_merge_head(repo, heads, heads_len)) == 0 && (error = write_merge_mode(repo)) == 0) { error = write_merge_msg(repo, heads, heads_len); } return error; } /* Merge branches */ static int merge_ancestor_head( git_annotated_commit **ancestor_head, git_repository *repo, const git_annotated_commit *our_head, const git_annotated_commit **their_heads, size_t their_heads_len) { git_oid *oids, ancestor_oid; size_t i, alloc_len; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(our_head); GIT_ASSERT_ARG(their_heads); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, their_heads_len, 1); oids = git__calloc(alloc_len, sizeof(git_oid)); GIT_ERROR_CHECK_ALLOC(oids); git_oid_cpy(&oids[0], git_commit_id(our_head->commit)); for (i = 0; i < their_heads_len; i++) git_oid_cpy(&oids[i + 1], git_annotated_commit_id(their_heads[i])); if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0) goto on_error; error = git_annotated_commit_lookup(ancestor_head, repo, &ancestor_oid); on_error: git__free(oids); return error; } static const char *merge_their_label(const char *branchname) { const char *slash; if ((slash = strrchr(branchname, '/')) == NULL) return branchname; if (*(slash+1) == '\0') return "theirs"; return slash+1; } static int merge_normalize_checkout_opts( git_checkout_options *out, git_repository *repo, const git_checkout_options *given_checkout_opts, unsigned int checkout_strategy, git_annotated_commit *ancestor, const git_annotated_commit *our_head, const git_annotated_commit **their_heads, size_t their_heads_len) { git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; int error = 0; GIT_UNUSED(repo); if (given_checkout_opts != NULL) memcpy(out, given_checkout_opts, sizeof(git_checkout_options)); else memcpy(out, &default_checkout_opts, sizeof(git_checkout_options)); out->checkout_strategy = checkout_strategy; if (!out->ancestor_label) { if (ancestor && ancestor->type == GIT_ANNOTATED_COMMIT_REAL) out->ancestor_label = git_commit_summary(ancestor->commit); else if (ancestor) out->ancestor_label = "merged common ancestors"; else out->ancestor_label = "empty base"; } if (!out->our_label) { if (our_head && our_head->ref_name) out->our_label = our_head->ref_name; else out->our_label = "ours"; } if (!out->their_label) { if (their_heads_len == 1 && their_heads[0]->ref_name) out->their_label = merge_their_label(their_heads[0]->ref_name); else if (their_heads_len == 1) out->their_label = their_heads[0]->id_str; else out->their_label = "theirs"; } return error; } static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths) { git_tree *head_tree = NULL; git_index *index_repo = NULL; git_iterator *iter_repo = NULL, *iter_new = NULL; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; git_diff *staged_diff_list = NULL, *index_diff_list = NULL; git_diff_delta *delta; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_vector staged_paths = GIT_VECTOR_INIT; size_t i; int error = 0; GIT_UNUSED(merged_paths); *conflicts = 0; /* No staged changes may exist unless the change staged is identical to * the result of the merge. This allows one to apply to merge manually, * then run merge. Any other staged change would be overwritten by * a reset merge. */ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || (error = git_repository_index(&index_repo, repo)) < 0 || (error = git_diff_tree_to_index(&staged_diff_list, repo, head_tree, index_repo, &opts)) < 0) goto done; if (staged_diff_list->deltas.length == 0) goto done; git_vector_foreach(&staged_diff_list->deltas, i, delta) { if ((error = git_vector_insert(&staged_paths, (char *)delta->new_file.path)) < 0) goto done; } iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; iter_opts.pathlist.strings = (char **)staged_paths.contents; iter_opts.pathlist.count = staged_paths.length; if ((error = git_iterator_for_index(&iter_repo, repo, index_repo, &iter_opts)) < 0 || (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0) goto done; *conflicts = index_diff_list->deltas.length; done: git_tree_free(head_tree); git_index_free(index_repo); git_iterator_free(iter_repo); git_iterator_free(iter_new); git_diff_free(staged_diff_list); git_diff_free(index_diff_list); git_vector_free(&staged_paths); return error; } static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths) { git_diff *wd_diff_list = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; int error = 0; GIT_UNUSED(index_new); *conflicts = 0; /* We need to have merged at least 1 file for the possibility to exist to * have conflicts with the workdir. Passing 0 as the pathspec count paramter * will consider all files in the working directory, that is, we may detect * a conflict if there were untracked files in the workdir prior to starting * the merge. This typically happens when cherry-picking a commmit whose * changes have already been applied. */ if (merged_paths->length == 0) return 0; opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; /* Workdir changes may exist iff they do not conflict with changes that * will be applied by the merge (including conflicts). Ensure that there * are no changes in the workdir to these paths. */ opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; opts.pathspec.count = merged_paths->length; opts.pathspec.strings = (char **)merged_paths->contents; opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL; if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0) goto done; *conflicts = wd_diff_list->deltas.length; done: git_diff_free(wd_diff_list); return error; } int git_merge__check_result(git_repository *repo, git_index *index_new) { git_tree *head_tree = NULL; git_iterator *iter_head = NULL, *iter_new = NULL; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; git_diff *merged_list = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_delta *delta; git_vector paths = GIT_VECTOR_INIT; size_t i, index_conflicts = 0, wd_conflicts = 0, conflicts; const git_index_entry *e; int error = 0; iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || (error = git_iterator_for_tree(&iter_head, head_tree, &iter_opts)) < 0 || (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0) goto done; git_vector_foreach(&merged_list->deltas, i, delta) { if ((error = git_vector_insert(&paths, (char *)delta->new_file.path)) < 0) goto done; } for (i = 0; i < git_index_entrycount(index_new); i++) { e = git_index_get_byindex(index_new, i); if (git_index_entry_is_conflict(e) && (git_vector_last(&paths) == NULL || strcmp(git_vector_last(&paths), e->path) != 0)) { if ((error = git_vector_insert(&paths, (char *)e->path)) < 0) goto done; } } /* Make sure the index and workdir state do not prevent merging */ if ((error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 || (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0) goto done; if ((conflicts = index_conflicts + wd_conflicts) > 0) { git_error_set(GIT_ERROR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge", conflicts, (conflicts != 1) ? "s" : ""); error = GIT_ECONFLICT; } done: git_vector_free(&paths); git_tree_free(head_tree); git_iterator_free(iter_head); git_iterator_free(iter_new); git_diff_free(merged_list); return error; } int git_merge__append_conflicts_to_merge_msg( git_repository *repo, git_index *index) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; const char *last = NULL; size_t i; int error; if (!git_index_has_conflicts(index)) return 0; if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; git_filebuf_printf(&file, "\nConflicts:\n"); for (i = 0; i < git_index_entrycount(index); i++) { const git_index_entry *e = git_index_get_byindex(index, i); if (!git_index_entry_is_conflict(e)) continue; if (last == NULL || strcmp(e->path, last) != 0) git_filebuf_printf(&file, "\t%s\n", e->path); last = e->path; } error = git_filebuf_commit(&file); cleanup: if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } static int merge_state_cleanup(git_repository *repo) { const char *state_files[] = { GIT_MERGE_HEAD_FILE, GIT_MERGE_MODE_FILE, GIT_MERGE_MSG_FILE, }; return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); } static int merge_heads( git_annotated_commit **ancestor_head_out, git_annotated_commit **our_head_out, git_repository *repo, git_reference *our_ref, const git_annotated_commit **their_heads, size_t their_heads_len) { git_annotated_commit *ancestor_head = NULL, *our_head = NULL; int error = 0; *ancestor_head_out = NULL; *our_head_out = NULL; if ((error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0) goto done; if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) { if (error != GIT_ENOTFOUND) goto done; git_error_clear(); error = 0; } *ancestor_head_out = ancestor_head; *our_head_out = our_head; done: if (error < 0) { git_annotated_commit_free(ancestor_head); git_annotated_commit_free(our_head); } return error; } static int merge_preference(git_merge_preference_t *out, git_repository *repo) { git_config *config; const char *value; int bool_value, error = 0; *out = GIT_MERGE_PREFERENCE_NONE; if ((error = git_repository_config_snapshot(&config, repo)) < 0) goto done; if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) { if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } goto done; } if (git_config_parse_bool(&bool_value, value) == 0) { if (!bool_value) *out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD; } else { if (strcasecmp(value, "only") == 0) *out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY; } done: git_config_free(config); return error; } int git_merge_analysis_for_ref( git_merge_analysis_t *analysis_out, git_merge_preference_t *preference_out, git_repository *repo, git_reference *our_ref, const git_annotated_commit **their_heads, size_t their_heads_len) { git_annotated_commit *ancestor_head = NULL, *our_head = NULL; int error = 0; bool unborn; GIT_ASSERT_ARG(analysis_out); GIT_ASSERT_ARG(preference_out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(their_heads && their_heads_len > 0); if (their_heads_len != 1) { git_error_set(GIT_ERROR_MERGE, "can only merge a single branch"); error = -1; goto done; } *analysis_out = GIT_MERGE_ANALYSIS_NONE; if ((error = merge_preference(preference_out, repo)) < 0) goto done; if ((error = git_reference__is_unborn_head(&unborn, our_ref, repo)) < 0) goto done; if (unborn) { *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN; error = 0; goto done; } if ((error = merge_heads(&ancestor_head, &our_head, repo, our_ref, their_heads, their_heads_len)) < 0) goto done; /* We're up-to-date if we're trying to merge our own common ancestor. */ if (ancestor_head && git_oid_equal( git_annotated_commit_id(ancestor_head), git_annotated_commit_id(their_heads[0]))) *analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE; /* We're fastforwardable if we're our own common ancestor. */ else if (ancestor_head && git_oid_equal( git_annotated_commit_id(ancestor_head), git_annotated_commit_id(our_head))) *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL; /* Otherwise, just a normal merge is possible. */ else *analysis_out |= GIT_MERGE_ANALYSIS_NORMAL; done: git_annotated_commit_free(ancestor_head); git_annotated_commit_free(our_head); return error; } int git_merge_analysis( git_merge_analysis_t *analysis_out, git_merge_preference_t *preference_out, git_repository *repo, const git_annotated_commit **their_heads, size_t their_heads_len) { git_reference *head_ref = NULL; int error = 0; if ((error = git_reference_lookup(&head_ref, repo, GIT_HEAD_FILE)) < 0) { git_error_set(GIT_ERROR_MERGE, "failed to lookup HEAD reference"); return error; } error = git_merge_analysis_for_ref(analysis_out, preference_out, repo, head_ref, their_heads, their_heads_len); git_reference_free(head_ref); return error; } int git_merge( git_repository *repo, const git_annotated_commit **their_heads, size_t their_heads_len, const git_merge_options *merge_opts, const git_checkout_options *given_checkout_opts) { git_reference *our_ref = NULL; git_checkout_options checkout_opts; git_annotated_commit *our_head = NULL, *base = NULL; git_index *repo_index = NULL, *index = NULL; git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; unsigned int checkout_strategy; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(their_heads && their_heads_len > 0); if (their_heads_len != 1) { git_error_set(GIT_ERROR_MERGE, "can only merge a single branch"); return -1; } if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0) goto done; checkout_strategy = given_checkout_opts ? given_checkout_opts->checkout_strategy : GIT_CHECKOUT_SAFE; if ((error = git_indexwriter_init_for_operation(&indexwriter, repo, &checkout_strategy)) < 0) goto done; if ((error = git_repository_index(&repo_index, repo) < 0) || (error = git_index_read(repo_index, 0) < 0)) goto done; /* Write the merge setup files to the repository. */ if ((error = git_annotated_commit_from_head(&our_head, repo)) < 0 || (error = git_merge__setup(repo, our_head, their_heads, their_heads_len)) < 0) goto done; /* TODO: octopus */ if ((error = merge_annotated_commits(&index, &base, repo, our_head, (git_annotated_commit *)their_heads[0], 0, merge_opts)) < 0 || (error = git_merge__check_result(repo, index)) < 0 || (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0) goto done; /* check out the merge results */ if ((error = merge_normalize_checkout_opts(&checkout_opts, repo, given_checkout_opts, checkout_strategy, base, our_head, their_heads, their_heads_len)) < 0 || (error = git_checkout_index(repo, index, &checkout_opts)) < 0) goto done; error = git_indexwriter_commit(&indexwriter); done: if (error < 0) merge_state_cleanup(repo); git_indexwriter_cleanup(&indexwriter); git_index_free(index); git_annotated_commit_free(our_head); git_annotated_commit_free(base); git_reference_free(our_ref); git_index_free(repo_index); return error; } int git_merge_options_init(git_merge_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_merge_options, GIT_MERGE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_merge_init_options(git_merge_options *opts, unsigned int version) { return git_merge_options_init(opts, version); } #endif int git_merge_file_input_init(git_merge_file_input *input, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( input, version, git_merge_file_input, GIT_MERGE_FILE_INPUT_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_merge_file_init_input(git_merge_file_input *input, unsigned int version) { return git_merge_file_input_init(input, version); } #endif int git_merge_file_options_init( git_merge_file_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_merge_file_options, GIT_MERGE_FILE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_merge_file_init_options( git_merge_file_options *opts, unsigned int version) { return git_merge_file_options_init(opts, version); } #endif git2r/src/libgit2/src/revert.c0000644000175000017500000001502714125111754016026 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "repository.h" #include "filebuf.h" #include "merge.h" #include "index.h" #include "git2/types.h" #include "git2/merge.h" #include "git2/revert.h" #include "git2/commit.h" #include "git2/sys/commit.h" #define GIT_REVERT_FILE_MODE 0666 static int write_revert_head( git_repository *repo, const char *commit_oidstr) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; int error = 0; if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_REVERT_HEAD_FILE)) >= 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_REVERT_FILE_MODE)) >= 0 && (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) error = git_filebuf_commit(&file); if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } static int write_merge_msg( git_repository *repo, const char *commit_oidstr, const char *commit_msgline) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; int error = 0; if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_REVERT_FILE_MODE)) < 0 || (error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n", commit_msgline, commit_oidstr)) < 0) goto cleanup; error = git_filebuf_commit(&file); cleanup: if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } static int revert_normalize_opts( git_repository *repo, git_revert_options *opts, const git_revert_options *given, const char *their_label) { int error = 0; unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS; GIT_UNUSED(repo); if (given != NULL) memcpy(opts, given, sizeof(git_revert_options)); else { git_revert_options default_opts = GIT_REVERT_OPTIONS_INIT; memcpy(opts, &default_opts, sizeof(git_revert_options)); } if (!opts->checkout_opts.checkout_strategy) opts->checkout_opts.checkout_strategy = default_checkout_strategy; if (!opts->checkout_opts.our_label) opts->checkout_opts.our_label = "HEAD"; if (!opts->checkout_opts.their_label) opts->checkout_opts.their_label = their_label; return error; } static int revert_state_cleanup(git_repository *repo) { const char *state_files[] = { GIT_REVERT_HEAD_FILE, GIT_MERGE_MSG_FILE }; return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); } static int revert_seterr(git_commit *commit, const char *fmt) { char commit_oidstr[GIT_OID_HEXSZ + 1]; git_oid_fmt(commit_oidstr, git_commit_id(commit)); commit_oidstr[GIT_OID_HEXSZ] = '\0'; git_error_set(GIT_ERROR_REVERT, fmt, commit_oidstr); return -1; } int git_revert_commit( git_index **out, git_repository *repo, git_commit *revert_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_opts) { git_commit *parent_commit = NULL; git_tree *parent_tree = NULL, *our_tree = NULL, *revert_tree = NULL; int parent = 0, error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(revert_commit); GIT_ASSERT_ARG(our_commit); if (git_commit_parentcount(revert_commit) > 1) { if (!mainline) return revert_seterr(revert_commit, "mainline branch is not specified but %s is a merge commit"); parent = mainline; } else { if (mainline) return revert_seterr(revert_commit, "mainline branch specified but %s is not a merge commit"); parent = git_commit_parentcount(revert_commit); } if (parent && ((error = git_commit_parent(&parent_commit, revert_commit, (parent - 1))) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0)) goto done; if ((error = git_commit_tree(&revert_tree, revert_commit)) < 0 || (error = git_commit_tree(&our_tree, our_commit)) < 0) goto done; error = git_merge_trees(out, repo, revert_tree, our_tree, parent_tree, merge_opts); done: git_tree_free(parent_tree); git_tree_free(our_tree); git_tree_free(revert_tree); git_commit_free(parent_commit); return error; } int git_revert( git_repository *repo, git_commit *commit, const git_revert_options *given_opts) { git_revert_options opts; git_reference *our_ref = NULL; git_commit *our_commit = NULL; char commit_oidstr[GIT_OID_HEXSZ + 1]; const char *commit_msg; git_buf their_label = GIT_BUF_INIT; git_index *index = NULL; git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(commit); GIT_ERROR_CHECK_VERSION(given_opts, GIT_REVERT_OPTIONS_VERSION, "git_revert_options"); if ((error = git_repository__ensure_not_bare(repo, "revert")) < 0) return error; git_oid_fmt(commit_oidstr, git_commit_id(commit)); commit_oidstr[GIT_OID_HEXSZ] = '\0'; if ((commit_msg = git_commit_summary(commit)) == NULL) { error = -1; goto on_error; } if ((error = git_buf_printf(&their_label, "parent of %.7s... %s", commit_oidstr, commit_msg)) < 0 || (error = revert_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 || (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 || (error = write_revert_head(repo, commit_oidstr)) < 0 || (error = write_merge_msg(repo, commit_oidstr, commit_msg)) < 0 || (error = git_repository_head(&our_ref, repo)) < 0 || (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJECT_COMMIT)) < 0 || (error = git_revert_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 || (error = git_merge__check_result(repo, index)) < 0 || (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 || (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 || (error = git_indexwriter_commit(&indexwriter)) < 0) goto on_error; goto done; on_error: revert_state_cleanup(repo); done: git_indexwriter_cleanup(&indexwriter); git_index_free(index); git_commit_free(our_commit); git_reference_free(our_ref); git_buf_dispose(&their_label); return error; } int git_revert_options_init(git_revert_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_revert_options, GIT_REVERT_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_revert_init_options(git_revert_options *opts, unsigned int version) { return git_revert_options_init(opts, version); } #endif git2r/src/libgit2/src/fetch.c0000644000175000017500000001010414125111754015577 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "fetch.h" #include "git2/oid.h" #include "git2/refs.h" #include "git2/revwalk.h" #include "git2/transport.h" #include "remote.h" #include "refspec.h" #include "pack.h" #include "netops.h" #include "repository.h" #include "refs.h" static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec, git_remote_autotag_option_t tagopt) { int match = 0, valid; if (git_reference_name_is_valid(&valid, head->name) < 0) return -1; if (!valid) return 0; if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { /* * If tagopt is --tags, always request tags * in addition to the remote's refspecs */ if (git_refspec_src_matches(tagspec, head->name)) match = 1; } if (!match && git_remote__matching_refspec(remote, head->name)) match = 1; if (!match) return 0; /* If we have the object, mark it so we don't ask for it */ if (git_odb_exists(odb, &head->oid)) { head->local = 1; } else remote->need_pack = 1; return git_vector_insert(&remote->refs, head); } static int filter_wants(git_remote *remote, const git_fetch_options *opts) { git_remote_head **heads; git_refspec tagspec, head; int error = 0; git_odb *odb; size_t i, heads_len; git_remote_autotag_option_t tagopt = remote->download_tags; if (opts && opts->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED) tagopt = opts->download_tags; git_vector_clear(&remote->refs); if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0) return error; /* * The fetch refspec can be NULL, and what this means is that the * user didn't specify one. This is fine, as it means that we're * not interested in any particular branch but just the remote's * HEAD, which will be stored in FETCH_HEAD after the fetch. */ if (remote->active_refspecs.length == 0) { if ((error = git_refspec__parse(&head, "HEAD", true)) < 0) goto cleanup; error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs); git_refspec__dispose(&head); if (error < 0) goto cleanup; } if (git_repository_odb__weakptr(&odb, remote->repo) < 0) goto cleanup; if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) goto cleanup; for (i = 0; i < heads_len; i++) { if ((error = maybe_want(remote, heads[i], odb, &tagspec, tagopt)) < 0) break; } cleanup: git_refspec__dispose(&tagspec); return error; } /* * In this first version, we push all our refs in and start sending * them out. When we get an ACK we hide that commit and continue * traversing until we're done */ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts) { git_transport *t = remote->transport; remote->need_pack = 0; if (filter_wants(remote, opts) < 0) { git_error_set(GIT_ERROR_NET, "failed to filter the reference list for wants"); return -1; } /* Don't try to negotiate when we don't want anything */ if (!remote->need_pack) return 0; /* * Now we have everything set up so we can start tell the * server what we want and what we have. */ return t->negotiate_fetch(t, remote->repo, (const git_remote_head * const *)remote->refs.contents, remote->refs.length); } int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks) { git_transport *t = remote->transport; git_indexer_progress_cb progress = NULL; void *payload = NULL; if (!remote->need_pack) return 0; if (callbacks) { progress = callbacks->transfer_progress; payload = callbacks->payload; } return t->download_pack(t, remote->repo, &remote->stats, progress, payload); } int git_fetch_options_init(git_fetch_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_fetch_options, GIT_FETCH_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_fetch_init_options(git_fetch_options *opts, unsigned int version) { return git_fetch_options_init(opts, version); } #endif git2r/src/libgit2/src/fetchhead.c0000644000175000017500000001670414125111754016435 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "fetchhead.h" #include "git2/types.h" #include "git2/oid.h" #include "buffer.h" #include "futils.h" #include "filebuf.h" #include "refs.h" #include "net.h" #include "repository.h" int git_fetchhead_ref_cmp(const void *a, const void *b) { const git_fetchhead_ref *one = (const git_fetchhead_ref *)a; const git_fetchhead_ref *two = (const git_fetchhead_ref *)b; if (one->is_merge && !two->is_merge) return -1; if (two->is_merge && !one->is_merge) return 1; if (one->ref_name && two->ref_name) return strcmp(one->ref_name, two->ref_name); else if (one->ref_name) return -1; else if (two->ref_name) return 1; return 0; } static char *sanitized_remote_url(const char *remote_url) { git_net_url url = GIT_NET_URL_INIT; char *sanitized = NULL; int error; if (git_net_url_parse(&url, remote_url) == 0) { git_buf buf = GIT_BUF_INIT; git__free(url.username); git__free(url.password); url.username = url.password = NULL; if ((error = git_net_url_fmt(&buf, &url)) < 0) goto fallback; sanitized = git_buf_detach(&buf); } fallback: if (!sanitized) sanitized = git__strdup(remote_url); git_net_url_dispose(&url); return sanitized; } int git_fetchhead_ref_create( git_fetchhead_ref **out, git_oid *oid, unsigned int is_merge, const char *ref_name, const char *remote_url) { git_fetchhead_ref *fetchhead_ref; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(oid); *out = NULL; fetchhead_ref = git__malloc(sizeof(git_fetchhead_ref)); GIT_ERROR_CHECK_ALLOC(fetchhead_ref); memset(fetchhead_ref, 0x0, sizeof(git_fetchhead_ref)); git_oid_cpy(&fetchhead_ref->oid, oid); fetchhead_ref->is_merge = is_merge; if (ref_name) { fetchhead_ref->ref_name = git__strdup(ref_name); GIT_ERROR_CHECK_ALLOC(fetchhead_ref->ref_name); } if (remote_url) { fetchhead_ref->remote_url = sanitized_remote_url(remote_url); GIT_ERROR_CHECK_ALLOC(fetchhead_ref->remote_url); } *out = fetchhead_ref; return 0; } static int fetchhead_ref_write( git_filebuf *file, git_fetchhead_ref *fetchhead_ref) { char oid[GIT_OID_HEXSZ + 1]; const char *type, *name; int head = 0; GIT_ASSERT_ARG(file); GIT_ASSERT_ARG(fetchhead_ref); git_oid_fmt(oid, &fetchhead_ref->oid); oid[GIT_OID_HEXSZ] = '\0'; if (git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_HEADS_DIR) == 0) { type = "branch "; name = fetchhead_ref->ref_name + strlen(GIT_REFS_HEADS_DIR); } else if(git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_TAGS_DIR) == 0) { type = "tag "; name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR); } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) { head = 1; } else { type = ""; name = fetchhead_ref->ref_name; } if (head) return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url); return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n", oid, (fetchhead_ref->is_merge) ? "" : "not-for-merge", type, name, fetchhead_ref->remote_url); } int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs) { git_filebuf file = GIT_FILEBUF_INIT; git_buf path = GIT_BUF_INIT; unsigned int i; git_fetchhead_ref *fetchhead_ref; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(fetchhead_refs); if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0) return -1; if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_APPEND, GIT_REFS_FILE_MODE) < 0) { git_buf_dispose(&path); return -1; } git_buf_dispose(&path); git_vector_sort(fetchhead_refs); git_vector_foreach(fetchhead_refs, i, fetchhead_ref) fetchhead_ref_write(&file, fetchhead_ref); return git_filebuf_commit(&file); } static int fetchhead_ref_parse( git_oid *oid, unsigned int *is_merge, git_buf *ref_name, const char **remote_url, char *line, size_t line_num) { char *oid_str, *is_merge_str, *desc, *name = NULL; const char *type = NULL; int error = 0; *remote_url = NULL; if (!*line) { git_error_set(GIT_ERROR_FETCHHEAD, "empty line in FETCH_HEAD line %"PRIuZ, line_num); return -1; } /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */ if ((oid_str = git__strsep(&line, "\t")) == NULL) { oid_str = line; line += strlen(line); *is_merge = 1; } if (strlen(oid_str) != GIT_OID_HEXSZ) { git_error_set(GIT_ERROR_FETCHHEAD, "invalid object ID in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if (git_oid_fromstr(oid, oid_str) < 0) { const git_error *oid_err = git_error_last(); const char *err_msg = oid_err ? oid_err->message : "invalid object ID"; git_error_set(GIT_ERROR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ, err_msg, line_num); return -1; } /* Parse new data from newer git clients */ if (*line) { if ((is_merge_str = git__strsep(&line, "\t")) == NULL) { git_error_set(GIT_ERROR_FETCHHEAD, "invalid description data in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if (*is_merge_str == '\0') *is_merge = 1; else if (strcmp(is_merge_str, "not-for-merge") == 0) *is_merge = 0; else { git_error_set(GIT_ERROR_FETCHHEAD, "invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if ((desc = line) == NULL) { git_error_set(GIT_ERROR_FETCHHEAD, "invalid description in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if (git__prefixcmp(desc, "branch '") == 0) { type = GIT_REFS_HEADS_DIR; name = desc + 8; } else if (git__prefixcmp(desc, "tag '") == 0) { type = GIT_REFS_TAGS_DIR; name = desc + 5; } else if (git__prefixcmp(desc, "'") == 0) name = desc + 1; if (name) { if ((desc = strstr(name, "' ")) == NULL || git__prefixcmp(desc, "' of ") != 0) { git_error_set(GIT_ERROR_FETCHHEAD, "invalid description in FETCH_HEAD line %"PRIuZ, line_num); return -1; } *desc = '\0'; desc += 5; } *remote_url = desc; } git_buf_clear(ref_name); if (type) git_buf_join(ref_name, '/', type, name); else if(name) git_buf_puts(ref_name, name); return error; } int git_repository_fetchhead_foreach(git_repository *repo, git_repository_fetchhead_foreach_cb cb, void *payload) { git_buf path = GIT_BUF_INIT, file = GIT_BUF_INIT, name = GIT_BUF_INIT; const char *ref_name; git_oid oid; const char *remote_url; unsigned int is_merge = 0; char *buffer, *line; size_t line_num = 0; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(cb); if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0) return -1; if ((error = git_futils_readbuffer(&file, git_buf_cstr(&path))) < 0) goto done; buffer = file.ptr; while ((line = git__strsep(&buffer, "\n")) != NULL) { ++line_num; if ((error = fetchhead_ref_parse( &oid, &is_merge, &name, &remote_url, line, line_num)) < 0) goto done; if (git_buf_len(&name) > 0) ref_name = git_buf_cstr(&name); else ref_name = NULL; error = cb(ref_name, remote_url, &oid, is_merge, payload); if (error) { git_error_set_after_callback(error); goto done; } } if (*buffer) { git_error_set(GIT_ERROR_FETCHHEAD, "no EOL at line %"PRIuZ, line_num+1); error = -1; goto done; } done: git_buf_dispose(&file); git_buf_dispose(&path); git_buf_dispose(&name); return error; } void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref) { if (fetchhead_ref == NULL) return; git__free(fetchhead_ref->remote_url); git__free(fetchhead_ref->ref_name); git__free(fetchhead_ref); } git2r/src/libgit2/src/index.c0000644000175000017500000026374514125111754015642 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "index.h" #include #include "repository.h" #include "tree.h" #include "tree-cache.h" #include "hash.h" #include "iterator.h" #include "pathspec.h" #include "ignore.h" #include "blob.h" #include "idxmap.h" #include "diff.h" #include "varint.h" #include "git2/odb.h" #include "git2/oid.h" #include "git2/blob.h" #include "git2/config.h" #include "git2/sys/index.h" static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths, unsigned int flags, git_index_matched_path_cb cb, void *payload); #define minimal_entry_size (offsetof(struct entry_short, path)) static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ; static const size_t INDEX_HEADER_SIZE = 12; static const unsigned int INDEX_VERSION_NUMBER_DEFAULT = 2; static const unsigned int INDEX_VERSION_NUMBER_LB = 2; static const unsigned int INDEX_VERSION_NUMBER_EXT = 3; static const unsigned int INDEX_VERSION_NUMBER_COMP = 4; static const unsigned int INDEX_VERSION_NUMBER_UB = 4; static const unsigned int INDEX_HEADER_SIG = 0x44495243; static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'}; static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'}; static const char INDEX_EXT_CONFLICT_NAME_SIG[] = {'N', 'A', 'M', 'E'}; #define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx))) struct index_header { uint32_t signature; uint32_t version; uint32_t entry_count; }; struct index_extension { char signature[4]; uint32_t extension_size; }; struct entry_time { uint32_t seconds; uint32_t nanoseconds; }; struct entry_short { struct entry_time ctime; struct entry_time mtime; uint32_t dev; uint32_t ino; uint32_t mode; uint32_t uid; uint32_t gid; uint32_t file_size; git_oid oid; uint16_t flags; char path[1]; /* arbitrary length */ }; struct entry_long { struct entry_time ctime; struct entry_time mtime; uint32_t dev; uint32_t ino; uint32_t mode; uint32_t uid; uint32_t gid; uint32_t file_size; git_oid oid; uint16_t flags; uint16_t flags_extended; char path[1]; /* arbitrary length */ }; struct entry_srch_key { const char *path; size_t pathlen; int stage; }; struct entry_internal { git_index_entry entry; size_t pathlen; char path[GIT_FLEX_ARRAY]; }; struct reuc_entry_internal { git_index_reuc_entry entry; size_t pathlen; char path[GIT_FLEX_ARRAY]; }; bool git_index__enforce_unsaved_safety = false; /* local declarations */ static int read_extension(size_t *read_len, git_index *index, const char *buffer, size_t buffer_size); static int read_header(struct index_header *dest, const void *buffer); static int parse_index(git_index *index, const char *buffer, size_t buffer_size); static bool is_index_extended(git_index *index); static int write_index(git_oid *checksum, git_index *index, git_filebuf *file); static void index_entry_free(git_index_entry *entry); static void index_entry_reuc_free(git_index_reuc_entry *reuc); GIT_INLINE(int) index_map_set(git_idxmap *map, git_index_entry *e, bool ignore_case) { if (ignore_case) return git_idxmap_icase_set((git_idxmap_icase *) map, e, e); else return git_idxmap_set(map, e, e); } GIT_INLINE(int) index_map_delete(git_idxmap *map, git_index_entry *e, bool ignore_case) { if (ignore_case) return git_idxmap_icase_delete((git_idxmap_icase *) map, e); else return git_idxmap_delete(map, e); } GIT_INLINE(int) index_map_resize(git_idxmap *map, size_t count, bool ignore_case) { if (ignore_case) return git_idxmap_icase_resize((git_idxmap_icase *) map, count); else return git_idxmap_resize(map, count); } int git_index_entry_srch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; const struct entry_internal *entry = array_member; int cmp; size_t len1, len2, len; len1 = srch_key->pathlen; len2 = entry->pathlen; len = len1 < len2 ? len1 : len2; cmp = memcmp(srch_key->path, entry->path, len); if (cmp) return cmp; if (len1 < len2) return -1; if (len1 > len2) return 1; if (srch_key->stage != GIT_INDEX_STAGE_ANY) return srch_key->stage - GIT_INDEX_ENTRY_STAGE(&entry->entry); return 0; } int git_index_entry_isrch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; const struct entry_internal *entry = array_member; int cmp; size_t len1, len2, len; len1 = srch_key->pathlen; len2 = entry->pathlen; len = len1 < len2 ? len1 : len2; cmp = strncasecmp(srch_key->path, entry->path, len); if (cmp) return cmp; if (len1 < len2) return -1; if (len1 > len2) return 1; if (srch_key->stage != GIT_INDEX_STAGE_ANY) return srch_key->stage - GIT_INDEX_ENTRY_STAGE(&entry->entry); return 0; } static int index_entry_srch_path(const void *path, const void *array_member) { const git_index_entry *entry = array_member; return strcmp((const char *)path, entry->path); } static int index_entry_isrch_path(const void *path, const void *array_member) { const git_index_entry *entry = array_member; return strcasecmp((const char *)path, entry->path); } int git_index_entry_cmp(const void *a, const void *b) { int diff; const git_index_entry *entry_a = a; const git_index_entry *entry_b = b; diff = strcmp(entry_a->path, entry_b->path); if (diff == 0) diff = (GIT_INDEX_ENTRY_STAGE(entry_a) - GIT_INDEX_ENTRY_STAGE(entry_b)); return diff; } int git_index_entry_icmp(const void *a, const void *b) { int diff; const git_index_entry *entry_a = a; const git_index_entry *entry_b = b; diff = strcasecmp(entry_a->path, entry_b->path); if (diff == 0) diff = (GIT_INDEX_ENTRY_STAGE(entry_a) - GIT_INDEX_ENTRY_STAGE(entry_b)); return diff; } static int conflict_name_cmp(const void *a, const void *b) { const git_index_name_entry *name_a = a; const git_index_name_entry *name_b = b; if (name_a->ancestor && !name_b->ancestor) return 1; if (!name_a->ancestor && name_b->ancestor) return -1; if (name_a->ancestor) return strcmp(name_a->ancestor, name_b->ancestor); if (!name_a->ours || !name_b->ours) return 0; return strcmp(name_a->ours, name_b->ours); } /** * TODO: enable this when resolving case insensitive conflicts */ #if 0 static int conflict_name_icmp(const void *a, const void *b) { const git_index_name_entry *name_a = a; const git_index_name_entry *name_b = b; if (name_a->ancestor && !name_b->ancestor) return 1; if (!name_a->ancestor && name_b->ancestor) return -1; if (name_a->ancestor) return strcasecmp(name_a->ancestor, name_b->ancestor); if (!name_a->ours || !name_b->ours) return 0; return strcasecmp(name_a->ours, name_b->ours); } #endif static int reuc_srch(const void *key, const void *array_member) { const git_index_reuc_entry *reuc = array_member; return strcmp(key, reuc->path); } static int reuc_isrch(const void *key, const void *array_member) { const git_index_reuc_entry *reuc = array_member; return strcasecmp(key, reuc->path); } static int reuc_cmp(const void *a, const void *b) { const git_index_reuc_entry *info_a = a; const git_index_reuc_entry *info_b = b; return strcmp(info_a->path, info_b->path); } static int reuc_icmp(const void *a, const void *b) { const git_index_reuc_entry *info_a = a; const git_index_reuc_entry *info_b = b; return strcasecmp(info_a->path, info_b->path); } static void index_entry_reuc_free(git_index_reuc_entry *reuc) { git__free(reuc); } static void index_entry_free(git_index_entry *entry) { if (!entry) return; memset(&entry->id, 0, sizeof(entry->id)); git__free(entry); } unsigned int git_index__create_mode(unsigned int mode) { if (S_ISLNK(mode)) return S_IFLNK; if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR)) return (S_IFLNK | S_IFDIR); return S_IFREG | GIT_PERMS_CANONICAL(mode); } static unsigned int index_merge_mode( git_index *index, git_index_entry *existing, unsigned int mode) { if (index->no_symlinks && S_ISREG(mode) && existing && S_ISLNK(existing->mode)) return existing->mode; if (index->distrust_filemode && S_ISREG(mode)) return (existing && S_ISREG(existing->mode)) ? existing->mode : git_index__create_mode(0666); return git_index__create_mode(mode); } GIT_INLINE(int) index_find_in_entries( size_t *out, git_vector *entries, git_vector_cmp entry_srch, const char *path, size_t path_len, int stage) { struct entry_srch_key srch_key; srch_key.path = path; srch_key.pathlen = !path_len ? strlen(path) : path_len; srch_key.stage = stage; return git_vector_bsearch2(out, entries, entry_srch, &srch_key); } GIT_INLINE(int) index_find( size_t *out, git_index *index, const char *path, size_t path_len, int stage) { git_vector_sort(&index->entries); return index_find_in_entries( out, &index->entries, index->entries_search, path, path_len, stage); } void git_index__set_ignore_case(git_index *index, bool ignore_case) { index->ignore_case = ignore_case; if (ignore_case) { index->entries_cmp_path = git__strcasecmp_cb; index->entries_search = git_index_entry_isrch; index->entries_search_path = index_entry_isrch_path; index->reuc_search = reuc_isrch; } else { index->entries_cmp_path = git__strcmp_cb; index->entries_search = git_index_entry_srch; index->entries_search_path = index_entry_srch_path; index->reuc_search = reuc_srch; } git_vector_set_cmp(&index->entries, ignore_case ? git_index_entry_icmp : git_index_entry_cmp); git_vector_sort(&index->entries); git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp); git_vector_sort(&index->reuc); } int git_index_open(git_index **index_out, const char *index_path) { git_index *index; int error = -1; GIT_ASSERT_ARG(index_out); index = git__calloc(1, sizeof(git_index)); GIT_ERROR_CHECK_ALLOC(index); if (git_pool_init(&index->tree_pool, 1) < 0) goto fail; if (index_path != NULL) { index->index_file_path = git__strdup(index_path); if (!index->index_file_path) goto fail; /* Check if index file is stored on disk already */ if (git_path_exists(index->index_file_path) == true) index->on_disk = 1; } if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 || git_idxmap_new(&index->entries_map) < 0 || git_vector_init(&index->names, 8, conflict_name_cmp) < 0 || git_vector_init(&index->reuc, 8, reuc_cmp) < 0 || git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0) goto fail; index->entries_cmp_path = git__strcmp_cb; index->entries_search = git_index_entry_srch; index->entries_search_path = index_entry_srch_path; index->reuc_search = reuc_srch; index->version = INDEX_VERSION_NUMBER_DEFAULT; if (index_path != NULL && (error = git_index_read(index, true)) < 0) goto fail; *index_out = index; GIT_REFCOUNT_INC(index); return 0; fail: git_pool_clear(&index->tree_pool); git_index_free(index); return error; } int git_index_new(git_index **out) { return git_index_open(out, NULL); } static void index_free(git_index *index) { /* index iterators increment the refcount of the index, so if we * get here then there should be no outstanding iterators. */ if (git_atomic32_get(&index->readers)) return; git_index_clear(index); git_idxmap_free(index->entries_map); git_vector_free(&index->entries); git_vector_free(&index->names); git_vector_free(&index->reuc); git_vector_free(&index->deleted); git__free(index->index_file_path); git__memzero(index, sizeof(*index)); git__free(index); } void git_index_free(git_index *index) { if (index == NULL) return; GIT_REFCOUNT_DEC(index, index_free); } /* call with locked index */ static void index_free_deleted(git_index *index) { int readers = (int)git_atomic32_get(&index->readers); size_t i; if (readers > 0 || !index->deleted.length) return; for (i = 0; i < index->deleted.length; ++i) { git_index_entry *ie = git_atomic_swap(index->deleted.contents[i], NULL); index_entry_free(ie); } git_vector_clear(&index->deleted); } /* call with locked index */ static int index_remove_entry(git_index *index, size_t pos) { int error = 0; git_index_entry *entry = git_vector_get(&index->entries, pos); if (entry != NULL) { git_tree_cache_invalidate_path(index->tree, entry->path); index_map_delete(index->entries_map, entry, index->ignore_case); } error = git_vector_remove(&index->entries, pos); if (!error) { if (git_atomic32_get(&index->readers) > 0) { error = git_vector_insert(&index->deleted, entry); } else { index_entry_free(entry); } index->dirty = 1; } return error; } int git_index_clear(git_index *index) { int error = 0; GIT_ASSERT_ARG(index); index->dirty = 1; index->tree = NULL; git_pool_clear(&index->tree_pool); git_idxmap_clear(index->entries_map); while (!error && index->entries.length > 0) error = index_remove_entry(index, index->entries.length - 1); if (error) goto done; index_free_deleted(index); if ((error = git_index_name_clear(index)) < 0 || (error = git_index_reuc_clear(index)) < 0) goto done; git_futils_filestamp_set(&index->stamp, NULL); done: return error; } static int create_index_error(int error, const char *msg) { git_error_set_str(GIT_ERROR_INDEX, msg); return error; } int git_index_set_caps(git_index *index, int caps) { unsigned int old_ignore_case; GIT_ASSERT_ARG(index); old_ignore_case = index->ignore_case; if (caps == GIT_INDEX_CAPABILITY_FROM_OWNER) { git_repository *repo = INDEX_OWNER(index); int val; if (!repo) return create_index_error( -1, "cannot access repository to set index caps"); if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_IGNORECASE)) index->ignore_case = (val != 0); if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_FILEMODE)) index->distrust_filemode = (val == 0); if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_SYMLINKS)) index->no_symlinks = (val == 0); } else { index->ignore_case = ((caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0); index->distrust_filemode = ((caps & GIT_INDEX_CAPABILITY_NO_FILEMODE) != 0); index->no_symlinks = ((caps & GIT_INDEX_CAPABILITY_NO_SYMLINKS) != 0); } if (old_ignore_case != index->ignore_case) { git_index__set_ignore_case(index, (bool)index->ignore_case); } return 0; } int git_index_caps(const git_index *index) { return ((index->ignore_case ? GIT_INDEX_CAPABILITY_IGNORE_CASE : 0) | (index->distrust_filemode ? GIT_INDEX_CAPABILITY_NO_FILEMODE : 0) | (index->no_symlinks ? GIT_INDEX_CAPABILITY_NO_SYMLINKS : 0)); } const git_oid *git_index_checksum(git_index *index) { return &index->checksum; } /** * Returns 1 for changed, 0 for not changed and <0 for errors */ static int compare_checksum(git_index *index) { int fd; ssize_t bytes_read; git_oid checksum = {{ 0 }}; if ((fd = p_open(index->index_file_path, O_RDONLY)) < 0) return fd; if (p_lseek(fd, -20, SEEK_END) < 0) { p_close(fd); git_error_set(GIT_ERROR_OS, "failed to seek to end of file"); return -1; } bytes_read = p_read(fd, &checksum, GIT_OID_RAWSZ); p_close(fd); if (bytes_read < 0) return -1; return !!git_oid_cmp(&checksum, &index->checksum); } int git_index_read(git_index *index, int force) { int error = 0, updated; git_buf buffer = GIT_BUF_INIT; git_futils_filestamp stamp = index->stamp; if (!index->index_file_path) return create_index_error(-1, "failed to read index: The index is in-memory only"); index->on_disk = git_path_exists(index->index_file_path); if (!index->on_disk) { if (force && (error = git_index_clear(index)) < 0) return error; index->dirty = 0; return 0; } if ((updated = git_futils_filestamp_check(&stamp, index->index_file_path) < 0) || ((updated = compare_checksum(index)) < 0)) { git_error_set( GIT_ERROR_INDEX, "failed to read index: '%s' no longer exists", index->index_file_path); return updated; } if (!updated && !force) return 0; error = git_futils_readbuffer(&buffer, index->index_file_path); if (error < 0) return error; index->tree = NULL; git_pool_clear(&index->tree_pool); error = git_index_clear(index); if (!error) error = parse_index(index, buffer.ptr, buffer.size); if (!error) { git_futils_filestamp_set(&index->stamp, &stamp); index->dirty = 0; } git_buf_dispose(&buffer); return error; } int git_index_read_safely(git_index *index) { if (git_index__enforce_unsaved_safety && index->dirty) { git_error_set(GIT_ERROR_INDEX, "the index has unsaved changes that would be overwritten by this operation"); return GIT_EINDEXDIRTY; } return git_index_read(index, false); } int git_index__changed_relative_to( git_index *index, const git_oid *checksum) { /* attempt to update index (ignoring errors) */ if (git_index_read(index, false) < 0) git_error_clear(); return !!git_oid_cmp(&index->checksum, checksum); } static bool is_racy_entry(git_index *index, const git_index_entry *entry) { /* Git special-cases submodules in the check */ if (S_ISGITLINK(entry->mode)) return false; return git_index_entry_newer_than_index(entry, index); } /* * Force the next diff to take a look at those entries which have the * same timestamp as the current index. */ static int truncate_racily_clean(git_index *index) { size_t i; int error; git_index_entry *entry; git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff = NULL; git_vector paths = GIT_VECTOR_INIT; git_diff_delta *delta; /* Nothing to do if there's no repo to talk about */ if (!INDEX_OWNER(index)) return 0; /* If there's no workdir, we can't know where to even check */ if (!git_repository_workdir(INDEX_OWNER(index))) return 0; diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH; git_vector_foreach(&index->entries, i, entry) { if ((entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE) == 0 && is_racy_entry(index, entry)) git_vector_insert(&paths, (char *)entry->path); } if (paths.length == 0) goto done; diff_opts.pathspec.count = paths.length; diff_opts.pathspec.strings = (char **)paths.contents; if ((error = git_diff_index_to_workdir(&diff, INDEX_OWNER(index), index, &diff_opts)) < 0) return error; git_vector_foreach(&diff->deltas, i, delta) { entry = (git_index_entry *)git_index_get_bypath(index, delta->old_file.path, 0); /* Ensure that we have a stage 0 for this file (ie, it's not a * conflict), otherwise smudging it is quite pointless. */ if (entry) { entry->file_size = 0; index->dirty = 1; } } done: git_diff_free(diff); git_vector_free(&paths); return 0; } unsigned git_index_version(git_index *index) { GIT_ASSERT_ARG(index); return index->version; } int git_index_set_version(git_index *index, unsigned int version) { GIT_ASSERT_ARG(index); if (version < INDEX_VERSION_NUMBER_LB || version > INDEX_VERSION_NUMBER_UB) { git_error_set(GIT_ERROR_INDEX, "invalid version number"); return -1; } index->version = version; return 0; } int git_index_write(git_index *index) { git_indexwriter writer = GIT_INDEXWRITER_INIT; int error; truncate_racily_clean(index); if ((error = git_indexwriter_init(&writer, index)) == 0 && (error = git_indexwriter_commit(&writer)) == 0) index->dirty = 0; git_indexwriter_cleanup(&writer); return error; } const char *git_index_path(const git_index *index) { GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); return index->index_file_path; } int git_index_write_tree(git_oid *oid, git_index *index) { git_repository *repo; GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(index); repo = INDEX_OWNER(index); if (repo == NULL) return create_index_error(-1, "Failed to write tree. " "the index file is not backed up by an existing repository"); return git_tree__write_index(oid, index, repo); } int git_index_write_tree_to( git_oid *oid, git_index *index, git_repository *repo) { GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(repo); return git_tree__write_index(oid, index, repo); } size_t git_index_entrycount(const git_index *index) { GIT_ASSERT_ARG(index); return index->entries.length; } const git_index_entry *git_index_get_byindex( git_index *index, size_t n) { GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); git_vector_sort(&index->entries); return git_vector_get(&index->entries, n); } const git_index_entry *git_index_get_bypath( git_index *index, const char *path, int stage) { git_index_entry key = {{ 0 }}; git_index_entry *value; GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); key.path = path; GIT_INDEX_ENTRY_STAGE_SET(&key, stage); if (index->ignore_case) value = git_idxmap_icase_get((git_idxmap_icase *) index->entries_map, &key); else value = git_idxmap_get(index->entries_map, &key); if (!value) { git_error_set(GIT_ERROR_INDEX, "index does not contain '%s'", path); return NULL; } return value; } void git_index_entry__init_from_stat( git_index_entry *entry, struct stat *st, bool trust_mode) { entry->ctime.seconds = (int32_t)st->st_ctime; entry->mtime.seconds = (int32_t)st->st_mtime; #if defined(GIT_USE_NSEC) entry->mtime.nanoseconds = st->st_mtime_nsec; entry->ctime.nanoseconds = st->st_ctime_nsec; #endif entry->dev = st->st_rdev; entry->ino = st->st_ino; entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ? git_index__create_mode(0666) : git_index__create_mode(st->st_mode); entry->uid = st->st_uid; entry->gid = st->st_gid; entry->file_size = (uint32_t)st->st_size; } static void index_entry_adjust_namemask( git_index_entry *entry, size_t path_length) { entry->flags &= ~GIT_INDEX_ENTRY_NAMEMASK; if (path_length < GIT_INDEX_ENTRY_NAMEMASK) entry->flags |= path_length & GIT_INDEX_ENTRY_NAMEMASK; else entry->flags |= GIT_INDEX_ENTRY_NAMEMASK; } /* When `from_workdir` is true, we will validate the paths to avoid placing * paths that are invalid for the working directory on the current filesystem * (eg, on Windows, we will disallow `GIT~1`, `AUX`, `COM1`, etc). This * function will *always* prevent `.git` and directory traversal `../` from * being added to the index. */ static int index_entry_create( git_index_entry **out, git_repository *repo, const char *path, struct stat *st, bool from_workdir) { size_t pathlen = strlen(path), alloclen; struct entry_internal *entry; unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS; uint16_t mode = 0; /* always reject placing `.git` in the index and directory traversal. * when requested, disallow platform-specific filenames and upgrade to * the platform-specific `.git` tests (eg, `git~1`, etc). */ if (from_workdir) path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS; if (st) mode = st->st_mode; if (!git_path_validate(repo, path, mode, path_valid_flags)) { git_error_set(GIT_ERROR_INDEX, "invalid path: '%s'", path); return -1; } GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(struct entry_internal), pathlen); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); entry = git__calloc(1, alloclen); GIT_ERROR_CHECK_ALLOC(entry); entry->pathlen = pathlen; memcpy(entry->path, path, pathlen); entry->entry.path = entry->path; *out = (git_index_entry *)entry; return 0; } static int index_entry_init( git_index_entry **entry_out, git_index *index, const char *rel_path) { int error = 0; git_index_entry *entry = NULL; git_buf path = GIT_BUF_INIT; struct stat st; git_oid oid; git_repository *repo; if (INDEX_OWNER(index) == NULL) return create_index_error(-1, "could not initialize index entry. " "Index is not backed up by an existing repository."); /* * FIXME: this is duplicated with the work in * git_blob__create_from_paths. It should accept an optional stat * structure so we can pass in the one we have to do here. */ repo = INDEX_OWNER(index); if (git_repository__ensure_not_bare(repo, "create blob from file") < 0) return GIT_EBAREREPO; if (git_repository_workdir_path(&path, repo, rel_path) < 0) return -1; error = git_path_lstat(path.ptr, &st); git_buf_dispose(&path); if (error < 0) return error; if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, &st, true) < 0) return -1; /* write the blob to disk and get the oid and stat info */ error = git_blob__create_from_paths( &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true); if (error < 0) { index_entry_free(entry); return error; } entry->id = oid; git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); *entry_out = (git_index_entry *)entry; return 0; } static git_index_reuc_entry *reuc_entry_alloc(const char *path) { size_t pathlen = strlen(path), structlen = sizeof(struct reuc_entry_internal), alloclen; struct reuc_entry_internal *entry; if (GIT_ADD_SIZET_OVERFLOW(&alloclen, structlen, pathlen) || GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1)) return NULL; entry = git__calloc(1, alloclen); if (!entry) return NULL; entry->pathlen = pathlen; memcpy(entry->path, path, pathlen); entry->entry.path = entry->path; return (git_index_reuc_entry *)entry; } static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, const char *path, int ancestor_mode, const git_oid *ancestor_oid, int our_mode, const git_oid *our_oid, int their_mode, const git_oid *their_oid) { git_index_reuc_entry *reuc = NULL; GIT_ASSERT_ARG(reuc_out); GIT_ASSERT_ARG(path); *reuc_out = reuc = reuc_entry_alloc(path); GIT_ERROR_CHECK_ALLOC(reuc); if ((reuc->mode[0] = ancestor_mode) > 0) { GIT_ASSERT(ancestor_oid); git_oid_cpy(&reuc->oid[0], ancestor_oid); } if ((reuc->mode[1] = our_mode) > 0) { GIT_ASSERT(our_oid); git_oid_cpy(&reuc->oid[1], our_oid); } if ((reuc->mode[2] = their_mode) > 0) { GIT_ASSERT(their_oid); git_oid_cpy(&reuc->oid[2], their_oid); } return 0; } static void index_entry_cpy( git_index_entry *tgt, const git_index_entry *src) { const char *tgt_path = tgt->path; memcpy(tgt, src, sizeof(*tgt)); tgt->path = tgt_path; } static int index_entry_dup( git_index_entry **out, git_index *index, const git_index_entry *src) { if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0) return -1; index_entry_cpy(*out, src); return 0; } static void index_entry_cpy_nocache( git_index_entry *tgt, const git_index_entry *src) { git_oid_cpy(&tgt->id, &src->id); tgt->mode = src->mode; tgt->flags = src->flags; tgt->flags_extended = (src->flags_extended & GIT_INDEX_ENTRY_EXTENDED_FLAGS); } static int index_entry_dup_nocache( git_index_entry **out, git_index *index, const git_index_entry *src) { if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0) return -1; index_entry_cpy_nocache(*out, src); return 0; } static int has_file_name(git_index *index, const git_index_entry *entry, size_t pos, int ok_to_replace) { size_t len = strlen(entry->path); int stage = GIT_INDEX_ENTRY_STAGE(entry); const char *name = entry->path; while (pos < index->entries.length) { struct entry_internal *p = index->entries.contents[pos++]; if (len >= p->pathlen) break; if (memcmp(name, p->path, len)) break; if (GIT_INDEX_ENTRY_STAGE(&p->entry) != stage) continue; if (p->path[len] != '/') continue; if (!ok_to_replace) return -1; if (index_remove_entry(index, --pos) < 0) break; } return 0; } /* * Do we have another file with a pathname that is a proper * subset of the name we're trying to add? */ static int has_dir_name(git_index *index, const git_index_entry *entry, int ok_to_replace) { int stage = GIT_INDEX_ENTRY_STAGE(entry); const char *name = entry->path; const char *slash = name + strlen(name); for (;;) { size_t len, pos; for (;;) { if (*--slash == '/') break; if (slash <= entry->path) return 0; } len = slash - name; if (!index_find(&pos, index, name, len, stage)) { if (!ok_to_replace) return -1; if (index_remove_entry(index, pos) < 0) break; continue; } /* * Trivial optimization: if we find an entry that * already matches the sub-directory, then we know * we're ok, and we can exit. */ for (; pos < index->entries.length; ++pos) { struct entry_internal *p = index->entries.contents[pos]; if (p->pathlen <= len || p->path[len] != '/' || memcmp(p->path, name, len)) break; /* not our subdirectory */ if (GIT_INDEX_ENTRY_STAGE(&p->entry) == stage) return 0; } } return 0; } static int check_file_directory_collision(git_index *index, git_index_entry *entry, size_t pos, int ok_to_replace) { if (has_file_name(index, entry, pos, ok_to_replace) < 0 || has_dir_name(index, entry, ok_to_replace) < 0) { git_error_set(GIT_ERROR_INDEX, "'%s' appears as both a file and a directory", entry->path); return -1; } return 0; } static int canonicalize_directory_path( git_index *index, git_index_entry *entry, git_index_entry *existing) { const git_index_entry *match, *best = NULL; char *search, *sep; size_t pos, search_len, best_len; if (!index->ignore_case) return 0; /* item already exists in the index, simply re-use the existing case */ if (existing) { memcpy((char *)entry->path, existing->path, strlen(existing->path)); return 0; } /* nothing to do */ if (strchr(entry->path, '/') == NULL) return 0; if ((search = git__strdup(entry->path)) == NULL) return -1; /* starting at the parent directory and descending to the root, find the * common parent directory. */ while (!best && (sep = strrchr(search, '/'))) { sep[1] = '\0'; search_len = strlen(search); git_vector_bsearch2( &pos, &index->entries, index->entries_search_path, search); while ((match = git_vector_get(&index->entries, pos))) { if (GIT_INDEX_ENTRY_STAGE(match) != 0) { /* conflicts do not contribute to canonical paths */ } else if (strncmp(search, match->path, search_len) == 0) { /* prefer an exact match to the input filename */ best = match; best_len = search_len; break; } else if (strncasecmp(search, match->path, search_len) == 0) { /* continue walking, there may be a path with an exact * (case sensitive) match later in the index, but use this * as the best match until that happens. */ if (!best) { best = match; best_len = search_len; } } else { break; } pos++; } sep[0] = '\0'; } if (best) memcpy((char *)entry->path, best->path, best_len); git__free(search); return 0; } static int index_no_dups(void **old, void *new) { const git_index_entry *entry = new; GIT_UNUSED(old); git_error_set(GIT_ERROR_INDEX, "'%s' appears multiple times at stage %d", entry->path, GIT_INDEX_ENTRY_STAGE(entry)); return GIT_EEXISTS; } static void index_existing_and_best( git_index_entry **existing, size_t *existing_position, git_index_entry **best, git_index *index, const git_index_entry *entry) { git_index_entry *e; size_t pos; int error; error = index_find(&pos, index, entry->path, 0, GIT_INDEX_ENTRY_STAGE(entry)); if (error == 0) { *existing = index->entries.contents[pos]; *existing_position = pos; *best = index->entries.contents[pos]; return; } *existing = NULL; *existing_position = 0; *best = NULL; if (GIT_INDEX_ENTRY_STAGE(entry) == 0) { for (; pos < index->entries.length; pos++) { int (*strcomp)(const char *a, const char *b) = index->ignore_case ? git__strcasecmp : git__strcmp; e = index->entries.contents[pos]; if (strcomp(entry->path, e->path) != 0) break; if (GIT_INDEX_ENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) { *best = e; continue; } else { *best = e; break; } } } } /* index_insert takes ownership of the new entry - if it can't insert * it, then it will return an error **and also free the entry**. When * it replaces an existing entry, it will update the entry_ptr with the * actual entry in the index (and free the passed in one). * * trust_path is whether we use the given path, or whether (on case * insensitive systems only) we try to canonicalize the given path to * be within an existing directory. * * trust_mode is whether we trust the mode in entry_ptr. * * trust_id is whether we trust the id or it should be validated. */ static int index_insert( git_index *index, git_index_entry **entry_ptr, int replace, bool trust_path, bool trust_mode, bool trust_id) { git_index_entry *existing, *best, *entry; size_t path_length, position; int error; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(entry_ptr); entry = *entry_ptr; /* Make sure that the path length flag is correct */ path_length = ((struct entry_internal *)entry)->pathlen; index_entry_adjust_namemask(entry, path_length); /* This entry is now up-to-date and should not be checked for raciness */ entry->flags_extended |= GIT_INDEX_ENTRY_UPTODATE; git_vector_sort(&index->entries); /* * Look if an entry with this path already exists, either staged, or (if * this entry is a regular staged item) as the "ours" side of a conflict. */ index_existing_and_best(&existing, &position, &best, index, entry); /* Update the file mode */ entry->mode = trust_mode ? git_index__create_mode(entry->mode) : index_merge_mode(index, best, entry->mode); /* Canonicalize the directory name */ if (!trust_path && (error = canonicalize_directory_path(index, entry, best)) < 0) goto out; /* Ensure that the given id exists (unless it's a submodule) */ if (!trust_id && INDEX_OWNER(index) && (entry->mode & GIT_FILEMODE_COMMIT) != GIT_FILEMODE_COMMIT) { if (!git_object__is_valid(INDEX_OWNER(index), &entry->id, git_object__type_from_filemode(entry->mode))) { error = -1; goto out; } } /* Look for tree / blob name collisions, removing conflicts if requested */ if ((error = check_file_directory_collision(index, entry, position, replace)) < 0) goto out; /* * If we are replacing an existing item, overwrite the existing entry * and return it in place of the passed in one. */ if (existing) { if (replace) { index_entry_cpy(existing, entry); if (trust_path) memcpy((char *)existing->path, entry->path, strlen(entry->path)); } index_entry_free(entry); *entry_ptr = existing; } else { /* * If replace is not requested or no existing entry exists, insert * at the sorted position. (Since we re-sort after each insert to * check for dups, this is actually cheaper in the long run.) */ if ((error = git_vector_insert_sorted(&index->entries, entry, index_no_dups)) < 0 || (error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0) goto out; } index->dirty = 1; out: if (error < 0) { index_entry_free(*entry_ptr); *entry_ptr = NULL; } return error; } static int index_conflict_to_reuc(git_index *index, const char *path) { const git_index_entry *conflict_entries[3]; int ancestor_mode, our_mode, their_mode; git_oid const *ancestor_oid, *our_oid, *their_oid; int ret; if ((ret = git_index_conflict_get(&conflict_entries[0], &conflict_entries[1], &conflict_entries[2], index, path)) < 0) return ret; ancestor_mode = conflict_entries[0] == NULL ? 0 : conflict_entries[0]->mode; our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode; their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode; ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->id; our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->id; their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->id; if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) >= 0) ret = git_index_conflict_remove(index, path); return ret; } GIT_INLINE(bool) is_file_or_link(const int filemode) { return (filemode == GIT_FILEMODE_BLOB || filemode == GIT_FILEMODE_BLOB_EXECUTABLE || filemode == GIT_FILEMODE_LINK); } GIT_INLINE(bool) valid_filemode(const int filemode) { return (is_file_or_link(filemode) || filemode == GIT_FILEMODE_COMMIT); } int git_index_add_from_buffer( git_index *index, const git_index_entry *source_entry, const void *buffer, size_t len) { git_index_entry *entry = NULL; int error = 0; git_oid id; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(source_entry && source_entry->path); if (INDEX_OWNER(index) == NULL) return create_index_error(-1, "could not initialize index entry. " "Index is not backed up by an existing repository."); if (!is_file_or_link(source_entry->mode)) { git_error_set(GIT_ERROR_INDEX, "invalid filemode"); return -1; } if (len > UINT32_MAX) { git_error_set(GIT_ERROR_INDEX, "buffer is too large"); return -1; } if (index_entry_dup(&entry, index, source_entry) < 0) return -1; error = git_blob_create_from_buffer(&id, INDEX_OWNER(index), buffer, len); if (error < 0) { index_entry_free(entry); return error; } git_oid_cpy(&entry->id, &id); entry->file_size = (uint32_t)len; if ((error = index_insert(index, &entry, 1, true, true, true)) < 0) return error; /* Adding implies conflict was resolved, move conflict entries to REUC */ if ((error = index_conflict_to_reuc(index, entry->path)) < 0 && error != GIT_ENOTFOUND) return error; git_tree_cache_invalidate_path(index->tree, entry->path); return 0; } static int add_repo_as_submodule(git_index_entry **out, git_index *index, const char *path) { git_repository *sub; git_buf abspath = GIT_BUF_INIT; git_repository *repo = INDEX_OWNER(index); git_reference *head; git_index_entry *entry; struct stat st; int error; if ((error = git_repository_workdir_path(&abspath, repo, path)) < 0) return error; if ((error = p_stat(abspath.ptr, &st)) < 0) { git_error_set(GIT_ERROR_OS, "failed to stat repository dir"); return -1; } if (index_entry_create(&entry, INDEX_OWNER(index), path, &st, true) < 0) return -1; git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); if ((error = git_repository_open(&sub, abspath.ptr)) < 0) return error; if ((error = git_repository_head(&head, sub)) < 0) return error; git_oid_cpy(&entry->id, git_reference_target(head)); entry->mode = GIT_FILEMODE_COMMIT; git_reference_free(head); git_repository_free(sub); git_buf_dispose(&abspath); *out = entry; return 0; } int git_index_add_bypath(git_index *index, const char *path) { git_index_entry *entry = NULL; int ret; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(path); if ((ret = index_entry_init(&entry, index, path)) == 0) ret = index_insert(index, &entry, 1, false, false, true); /* If we were given a directory, let's see if it's a submodule */ if (ret < 0 && ret != GIT_EDIRECTORY) return ret; if (ret == GIT_EDIRECTORY) { git_submodule *sm; git_error_state err; git_error_state_capture(&err, ret); ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path); if (ret == GIT_ENOTFOUND) return git_error_state_restore(&err); git_error_state_free(&err); /* * EEXISTS means that there is a repository at that path, but it's not known * as a submodule. We add its HEAD as an entry and don't register it. */ if (ret == GIT_EEXISTS) { if ((ret = add_repo_as_submodule(&entry, index, path)) < 0) return ret; if ((ret = index_insert(index, &entry, 1, false, false, true)) < 0) return ret; } else if (ret < 0) { return ret; } else { ret = git_submodule_add_to_index(sm, false); git_submodule_free(sm); return ret; } } /* Adding implies conflict was resolved, move conflict entries to REUC */ if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND) return ret; git_tree_cache_invalidate_path(index->tree, entry->path); return 0; } int git_index_remove_bypath(git_index *index, const char *path) { int ret; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(path); if (((ret = git_index_remove(index, path, 0)) < 0 && ret != GIT_ENOTFOUND) || ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)) return ret; if (ret == GIT_ENOTFOUND) git_error_clear(); return 0; } int git_index__fill(git_index *index, const git_vector *source_entries) { const git_index_entry *source_entry = NULL; int error = 0; size_t i; GIT_ASSERT_ARG(index); if (!source_entries->length) return 0; if (git_vector_size_hint(&index->entries, source_entries->length) < 0 || index_map_resize(index->entries_map, (size_t)(source_entries->length * 1.3), index->ignore_case) < 0) return -1; git_vector_foreach(source_entries, i, source_entry) { git_index_entry *entry = NULL; if ((error = index_entry_dup(&entry, index, source_entry)) < 0) break; index_entry_adjust_namemask(entry, ((struct entry_internal *)entry)->pathlen); entry->flags_extended |= GIT_INDEX_ENTRY_UPTODATE; entry->mode = git_index__create_mode(entry->mode); if ((error = git_vector_insert(&index->entries, entry)) < 0) break; if ((error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0) break; index->dirty = 1; } if (!error) git_vector_sort(&index->entries); return error; } int git_index_add(git_index *index, const git_index_entry *source_entry) { git_index_entry *entry = NULL; int ret; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(source_entry && source_entry->path); if (!valid_filemode(source_entry->mode)) { git_error_set(GIT_ERROR_INDEX, "invalid entry mode"); return -1; } if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 || (ret = index_insert(index, &entry, 1, true, true, false)) < 0) return ret; git_tree_cache_invalidate_path(index->tree, entry->path); return 0; } int git_index_remove(git_index *index, const char *path, int stage) { int error; size_t position; git_index_entry remove_key = {{ 0 }}; remove_key.path = path; GIT_INDEX_ENTRY_STAGE_SET(&remove_key, stage); index_map_delete(index->entries_map, &remove_key, index->ignore_case); if (index_find(&position, index, path, 0, stage) < 0) { git_error_set( GIT_ERROR_INDEX, "index does not contain %s at stage %d", path, stage); error = GIT_ENOTFOUND; } else { error = index_remove_entry(index, position); } return error; } int git_index_remove_directory(git_index *index, const char *dir, int stage) { git_buf pfx = GIT_BUF_INIT; int error = 0; size_t pos; git_index_entry *entry; if (!(error = git_buf_sets(&pfx, dir)) && !(error = git_path_to_dir(&pfx))) index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY); while (!error) { entry = git_vector_get(&index->entries, pos); if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0) break; if (GIT_INDEX_ENTRY_STAGE(entry) != stage) { ++pos; continue; } error = index_remove_entry(index, pos); /* removed entry at 'pos' so we don't need to increment */ } git_buf_dispose(&pfx); return error; } int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix) { int error = 0; size_t pos; const git_index_entry *entry; index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY); entry = git_vector_get(&index->entries, pos); if (!entry || git__prefixcmp(entry->path, prefix) != 0) error = GIT_ENOTFOUND; if (!error && at_pos) *at_pos = pos; return error; } int git_index__find_pos( size_t *out, git_index *index, const char *path, size_t path_len, int stage) { GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(path); return index_find(out, index, path, path_len, stage); } int git_index_find(size_t *at_pos, git_index *index, const char *path) { size_t pos; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(path); if (git_vector_bsearch2( &pos, &index->entries, index->entries_search_path, path) < 0) { git_error_set(GIT_ERROR_INDEX, "index does not contain %s", path); return GIT_ENOTFOUND; } /* Since our binary search only looked at path, we may be in the * middle of a list of stages. */ for (; pos > 0; --pos) { const git_index_entry *prev = git_vector_get(&index->entries, pos - 1); if (index->entries_cmp_path(prev->path, path) != 0) break; } if (at_pos) *at_pos = pos; return 0; } int git_index_conflict_add(git_index *index, const git_index_entry *ancestor_entry, const git_index_entry *our_entry, const git_index_entry *their_entry) { git_index_entry *entries[3] = { 0 }; unsigned short i; int ret = 0; GIT_ASSERT_ARG(index); if ((ancestor_entry && (ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0) || (our_entry && (ret = index_entry_dup(&entries[1], index, our_entry)) < 0) || (their_entry && (ret = index_entry_dup(&entries[2], index, their_entry)) < 0)) goto on_error; /* Validate entries */ for (i = 0; i < 3; i++) { if (entries[i] && !valid_filemode(entries[i]->mode)) { git_error_set(GIT_ERROR_INDEX, "invalid filemode for stage %d entry", i + 1); ret = -1; goto on_error; } } /* Remove existing index entries for each path */ for (i = 0; i < 3; i++) { if (entries[i] == NULL) continue; if ((ret = git_index_remove(index, entries[i]->path, 0)) != 0) { if (ret != GIT_ENOTFOUND) goto on_error; git_error_clear(); ret = 0; } } /* Add the conflict entries */ for (i = 0; i < 3; i++) { if (entries[i] == NULL) continue; /* Make sure stage is correct */ GIT_INDEX_ENTRY_STAGE_SET(entries[i], i + 1); if ((ret = index_insert(index, &entries[i], 1, true, true, false)) < 0) goto on_error; entries[i] = NULL; /* don't free if later entry fails */ } return 0; on_error: for (i = 0; i < 3; i++) { if (entries[i] != NULL) index_entry_free(entries[i]); } return ret; } static int index_conflict__get_byindex( const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index *index, size_t n) { const git_index_entry *conflict_entry; const char *path = NULL; size_t count; int stage, len = 0; GIT_ASSERT_ARG(ancestor_out); GIT_ASSERT_ARG(our_out); GIT_ASSERT_ARG(their_out); GIT_ASSERT_ARG(index); *ancestor_out = NULL; *our_out = NULL; *their_out = NULL; for (count = git_index_entrycount(index); n < count; ++n) { conflict_entry = git_vector_get(&index->entries, n); if (path && index->entries_cmp_path(conflict_entry->path, path) != 0) break; stage = GIT_INDEX_ENTRY_STAGE(conflict_entry); path = conflict_entry->path; switch (stage) { case 3: *their_out = conflict_entry; len++; break; case 2: *our_out = conflict_entry; len++; break; case 1: *ancestor_out = conflict_entry; len++; break; default: break; }; } return len; } int git_index_conflict_get( const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index *index, const char *path) { size_t pos; int len = 0; GIT_ASSERT_ARG(ancestor_out); GIT_ASSERT_ARG(our_out); GIT_ASSERT_ARG(their_out); GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(path); *ancestor_out = NULL; *our_out = NULL; *their_out = NULL; if (git_index_find(&pos, index, path) < 0) return GIT_ENOTFOUND; if ((len = index_conflict__get_byindex( ancestor_out, our_out, their_out, index, pos)) < 0) return len; else if (len == 0) return GIT_ENOTFOUND; return 0; } static int index_conflict_remove(git_index *index, const char *path) { size_t pos = 0; git_index_entry *conflict_entry; int error = 0; if (path != NULL && git_index_find(&pos, index, path) < 0) return GIT_ENOTFOUND; while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) { if (path != NULL && index->entries_cmp_path(conflict_entry->path, path) != 0) break; if (GIT_INDEX_ENTRY_STAGE(conflict_entry) == 0) { pos++; continue; } if ((error = index_remove_entry(index, pos)) < 0) break; } return error; } int git_index_conflict_remove(git_index *index, const char *path) { GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(path); return index_conflict_remove(index, path); } int git_index_conflict_cleanup(git_index *index) { GIT_ASSERT_ARG(index); return index_conflict_remove(index, NULL); } int git_index_has_conflicts(const git_index *index) { size_t i; git_index_entry *entry; GIT_ASSERT_ARG(index); git_vector_foreach(&index->entries, i, entry) { if (GIT_INDEX_ENTRY_STAGE(entry) > 0) return 1; } return 0; } int git_index_iterator_new( git_index_iterator **iterator_out, git_index *index) { git_index_iterator *it; int error; GIT_ASSERT_ARG(iterator_out); GIT_ASSERT_ARG(index); it = git__calloc(1, sizeof(git_index_iterator)); GIT_ERROR_CHECK_ALLOC(it); if ((error = git_index_snapshot_new(&it->snap, index)) < 0) { git__free(it); return error; } it->index = index; *iterator_out = it; return 0; } int git_index_iterator_next( const git_index_entry **out, git_index_iterator *it) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(it); if (it->cur >= git_vector_length(&it->snap)) return GIT_ITEROVER; *out = (git_index_entry *)git_vector_get(&it->snap, it->cur++); return 0; } void git_index_iterator_free(git_index_iterator *it) { if (it == NULL) return; git_index_snapshot_release(&it->snap, it->index); git__free(it); } int git_index_conflict_iterator_new( git_index_conflict_iterator **iterator_out, git_index *index) { git_index_conflict_iterator *it = NULL; GIT_ASSERT_ARG(iterator_out); GIT_ASSERT_ARG(index); it = git__calloc(1, sizeof(git_index_conflict_iterator)); GIT_ERROR_CHECK_ALLOC(it); it->index = index; *iterator_out = it; return 0; } int git_index_conflict_next( const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index_conflict_iterator *iterator) { const git_index_entry *entry; int len; GIT_ASSERT_ARG(ancestor_out); GIT_ASSERT_ARG(our_out); GIT_ASSERT_ARG(their_out); GIT_ASSERT_ARG(iterator); *ancestor_out = NULL; *our_out = NULL; *their_out = NULL; while (iterator->cur < iterator->index->entries.length) { entry = git_index_get_byindex(iterator->index, iterator->cur); if (git_index_entry_is_conflict(entry)) { if ((len = index_conflict__get_byindex( ancestor_out, our_out, their_out, iterator->index, iterator->cur)) < 0) return len; iterator->cur += len; return 0; } iterator->cur++; } return GIT_ITEROVER; } void git_index_conflict_iterator_free(git_index_conflict_iterator *iterator) { if (iterator == NULL) return; git__free(iterator); } size_t git_index_name_entrycount(git_index *index) { GIT_ASSERT_ARG(index); return index->names.length; } const git_index_name_entry *git_index_name_get_byindex( git_index *index, size_t n) { GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); git_vector_sort(&index->names); return git_vector_get(&index->names, n); } static void index_name_entry_free(git_index_name_entry *ne) { if (!ne) return; git__free(ne->ancestor); git__free(ne->ours); git__free(ne->theirs); git__free(ne); } int git_index_name_add(git_index *index, const char *ancestor, const char *ours, const char *theirs) { git_index_name_entry *conflict_name; GIT_ASSERT_ARG((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); conflict_name = git__calloc(1, sizeof(git_index_name_entry)); GIT_ERROR_CHECK_ALLOC(conflict_name); if ((ancestor && !(conflict_name->ancestor = git__strdup(ancestor))) || (ours && !(conflict_name->ours = git__strdup(ours))) || (theirs && !(conflict_name->theirs = git__strdup(theirs))) || git_vector_insert(&index->names, conflict_name) < 0) { index_name_entry_free(conflict_name); return -1; } index->dirty = 1; return 0; } int git_index_name_clear(git_index *index) { size_t i; git_index_name_entry *conflict_name; GIT_ASSERT_ARG(index); git_vector_foreach(&index->names, i, conflict_name) index_name_entry_free(conflict_name); git_vector_clear(&index->names); index->dirty = 1; return 0; } size_t git_index_reuc_entrycount(git_index *index) { GIT_ASSERT_ARG(index); return index->reuc.length; } static int index_reuc_on_dup(void **old, void *new) { index_entry_reuc_free(*old); *old = new; return GIT_EEXISTS; } static int index_reuc_insert( git_index *index, git_index_reuc_entry *reuc) { int res; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(reuc && reuc->path != NULL); GIT_ASSERT(git_vector_is_sorted(&index->reuc)); res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup); index->dirty = 1; return res == GIT_EEXISTS ? 0 : res; } int git_index_reuc_add(git_index *index, const char *path, int ancestor_mode, const git_oid *ancestor_oid, int our_mode, const git_oid *our_oid, int their_mode, const git_oid *their_oid) { git_index_reuc_entry *reuc = NULL; int error = 0; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(path); if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || (error = index_reuc_insert(index, reuc)) < 0) index_entry_reuc_free(reuc); return error; } int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path) { return git_vector_bsearch2(at_pos, &index->reuc, index->reuc_search, path); } const git_index_reuc_entry *git_index_reuc_get_bypath( git_index *index, const char *path) { size_t pos; GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); GIT_ASSERT_ARG_WITH_RETVAL(path, NULL); if (!index->reuc.length) return NULL; GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL); if (git_index_reuc_find(&pos, index, path) < 0) return NULL; return git_vector_get(&index->reuc, pos); } const git_index_reuc_entry *git_index_reuc_get_byindex( git_index *index, size_t n) { GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL); return git_vector_get(&index->reuc, n); } int git_index_reuc_remove(git_index *index, size_t position) { int error; git_index_reuc_entry *reuc; GIT_ASSERT_ARG(index); GIT_ASSERT(git_vector_is_sorted(&index->reuc)); reuc = git_vector_get(&index->reuc, position); error = git_vector_remove(&index->reuc, position); if (!error) index_entry_reuc_free(reuc); index->dirty = 1; return error; } int git_index_reuc_clear(git_index *index) { size_t i; GIT_ASSERT_ARG(index); for (i = 0; i < index->reuc.length; ++i) index_entry_reuc_free(git_atomic_swap(index->reuc.contents[i], NULL)); git_vector_clear(&index->reuc); index->dirty = 1; return 0; } static int index_error_invalid(const char *message) { git_error_set(GIT_ERROR_INDEX, "invalid data in index - %s", message); return -1; } static int read_reuc(git_index *index, const char *buffer, size_t size) { const char *endptr; size_t len; int i; /* If called multiple times, the vector might already be initialized */ if (index->reuc._alloc_size == 0 && git_vector_init(&index->reuc, 16, reuc_cmp) < 0) return -1; while (size) { git_index_reuc_entry *lost; len = p_strnlen(buffer, size) + 1; if (size <= len) return index_error_invalid("reading reuc entries"); lost = reuc_entry_alloc(buffer); GIT_ERROR_CHECK_ALLOC(lost); size -= len; buffer += len; /* read 3 ASCII octal numbers for stage entries */ for (i = 0; i < 3; i++) { int64_t tmp; if (git__strntol64(&tmp, buffer, size, &endptr, 8) < 0 || !endptr || endptr == buffer || *endptr || tmp < 0 || tmp > UINT32_MAX) { index_entry_reuc_free(lost); return index_error_invalid("reading reuc entry stage"); } lost->mode[i] = (uint32_t)tmp; len = (endptr + 1) - buffer; if (size <= len) { index_entry_reuc_free(lost); return index_error_invalid("reading reuc entry stage"); } size -= len; buffer += len; } /* read up to 3 OIDs for stage entries */ for (i = 0; i < 3; i++) { if (!lost->mode[i]) continue; if (size < 20) { index_entry_reuc_free(lost); return index_error_invalid("reading reuc entry oid"); } git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer); size -= 20; buffer += 20; } /* entry was read successfully - insert into reuc vector */ if (git_vector_insert(&index->reuc, lost) < 0) return -1; } /* entries are guaranteed to be sorted on-disk */ git_vector_set_sorted(&index->reuc, true); return 0; } static int read_conflict_names(git_index *index, const char *buffer, size_t size) { size_t len; /* This gets called multiple times, the vector might already be initialized */ if (index->names._alloc_size == 0 && git_vector_init(&index->names, 16, conflict_name_cmp) < 0) return -1; #define read_conflict_name(ptr) \ len = p_strnlen(buffer, size) + 1; \ if (size < len) { \ index_error_invalid("reading conflict name entries"); \ goto out_err; \ } \ if (len == 1) \ ptr = NULL; \ else { \ ptr = git__malloc(len); \ GIT_ERROR_CHECK_ALLOC(ptr); \ memcpy(ptr, buffer, len); \ } \ \ buffer += len; \ size -= len; while (size) { git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry)); GIT_ERROR_CHECK_ALLOC(conflict_name); read_conflict_name(conflict_name->ancestor); read_conflict_name(conflict_name->ours); read_conflict_name(conflict_name->theirs); if (git_vector_insert(&index->names, conflict_name) < 0) goto out_err; continue; out_err: git__free(conflict_name->ancestor); git__free(conflict_name->ours); git__free(conflict_name->theirs); git__free(conflict_name); return -1; } #undef read_conflict_name /* entries are guaranteed to be sorted on-disk */ git_vector_set_sorted(&index->names, true); return 0; } static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flags) { if (varint_len) { if (flags & GIT_INDEX_ENTRY_EXTENDED) return offsetof(struct entry_long, path) + path_len + 1 + varint_len; else return offsetof(struct entry_short, path) + path_len + 1 + varint_len; } else { #define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) if (flags & GIT_INDEX_ENTRY_EXTENDED) return entry_size(struct entry_long, path_len); else return entry_size(struct entry_short, path_len); #undef entry_size } } static int read_entry( git_index_entry **out, size_t *out_size, git_index *index, const void *buffer, size_t buffer_size, const char *last) { size_t path_length, entry_size; const char *path_ptr; struct entry_short source; git_index_entry entry = {{0}}; bool compressed = index->version >= INDEX_VERSION_NUMBER_COMP; char *tmp_path = NULL; if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size) return -1; /* buffer is not guaranteed to be aligned */ memcpy(&source, buffer, sizeof(struct entry_short)); entry.ctime.seconds = (git_time_t)ntohl(source.ctime.seconds); entry.ctime.nanoseconds = ntohl(source.ctime.nanoseconds); entry.mtime.seconds = (git_time_t)ntohl(source.mtime.seconds); entry.mtime.nanoseconds = ntohl(source.mtime.nanoseconds); entry.dev = ntohl(source.dev); entry.ino = ntohl(source.ino); entry.mode = ntohl(source.mode); entry.uid = ntohl(source.uid); entry.gid = ntohl(source.gid); entry.file_size = ntohl(source.file_size); git_oid_cpy(&entry.id, &source.oid); entry.flags = ntohs(source.flags); if (entry.flags & GIT_INDEX_ENTRY_EXTENDED) { uint16_t flags_raw; size_t flags_offset; flags_offset = offsetof(struct entry_long, flags_extended); memcpy(&flags_raw, (const char *) buffer + flags_offset, sizeof(flags_raw)); flags_raw = ntohs(flags_raw); memcpy(&entry.flags_extended, &flags_raw, sizeof(flags_raw)); path_ptr = (const char *) buffer + offsetof(struct entry_long, path); } else path_ptr = (const char *) buffer + offsetof(struct entry_short, path); if (!compressed) { path_length = entry.flags & GIT_INDEX_ENTRY_NAMEMASK; /* if this is a very long string, we must find its * real length without overflowing */ if (path_length == 0xFFF) { const char *path_end; path_end = memchr(path_ptr, '\0', buffer_size); if (path_end == NULL) return -1; path_length = path_end - path_ptr; } entry_size = index_entry_size(path_length, 0, entry.flags); entry.path = (char *)path_ptr; } else { size_t varint_len, last_len, prefix_len, suffix_len, path_len; uintmax_t strip_len; strip_len = git_decode_varint((const unsigned char *)path_ptr, &varint_len); last_len = strlen(last); if (varint_len == 0 || last_len < strip_len) return index_error_invalid("incorrect prefix length"); prefix_len = last_len - (size_t)strip_len; suffix_len = strlen(path_ptr + varint_len); GIT_ERROR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len); GIT_ERROR_CHECK_ALLOC_ADD(&path_len, path_len, 1); if (path_len > GIT_PATH_MAX) return index_error_invalid("unreasonable path length"); tmp_path = git__malloc(path_len); GIT_ERROR_CHECK_ALLOC(tmp_path); memcpy(tmp_path, last, prefix_len); memcpy(tmp_path + prefix_len, path_ptr + varint_len, suffix_len + 1); entry_size = index_entry_size(suffix_len, varint_len, entry.flags); entry.path = tmp_path; } if (entry_size == 0) return -1; if (INDEX_FOOTER_SIZE + entry_size > buffer_size) return -1; if (index_entry_dup(out, index, &entry) < 0) { git__free(tmp_path); return -1; } git__free(tmp_path); *out_size = entry_size; return 0; } static int read_header(struct index_header *dest, const void *buffer) { const struct index_header *source = buffer; dest->signature = ntohl(source->signature); if (dest->signature != INDEX_HEADER_SIG) return index_error_invalid("incorrect header signature"); dest->version = ntohl(source->version); if (dest->version < INDEX_VERSION_NUMBER_LB || dest->version > INDEX_VERSION_NUMBER_UB) return index_error_invalid("incorrect header version"); dest->entry_count = ntohl(source->entry_count); return 0; } static int read_extension(size_t *read_len, git_index *index, const char *buffer, size_t buffer_size) { struct index_extension dest; size_t total_size; /* buffer is not guaranteed to be aligned */ memcpy(&dest, buffer, sizeof(struct index_extension)); dest.extension_size = ntohl(dest.extension_size); total_size = dest.extension_size + sizeof(struct index_extension); if (dest.extension_size > total_size || buffer_size < total_size || buffer_size - total_size < INDEX_FOOTER_SIZE) { index_error_invalid("extension is truncated"); return -1; } /* optional extension */ if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') { /* tree cache */ if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) { if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size, &index->tree_pool) < 0) return -1; } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) { if (read_reuc(index, buffer + 8, dest.extension_size) < 0) return -1; } else if (memcmp(dest.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4) == 0) { if (read_conflict_names(index, buffer + 8, dest.extension_size) < 0) return -1; } /* else, unsupported extension. We cannot parse this, but we can skip * it by returning `total_size */ } else { /* we cannot handle non-ignorable extensions; * in fact they aren't even defined in the standard */ git_error_set(GIT_ERROR_INDEX, "unsupported mandatory extension: '%.4s'", dest.signature); return -1; } *read_len = total_size; return 0; } static int parse_index(git_index *index, const char *buffer, size_t buffer_size) { int error = 0; unsigned int i; struct index_header header = { 0 }; git_oid checksum_calculated, checksum_expected; const char *last = NULL; const char *empty = ""; #define seek_forward(_increase) { \ if (_increase >= buffer_size) { \ error = index_error_invalid("ran out of data while parsing"); \ goto done; } \ buffer += _increase; \ buffer_size -= _increase;\ } if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE) return index_error_invalid("insufficient buffer space"); /* Precalculate the SHA1 of the files's contents -- we'll match it to * the provided SHA1 in the footer */ git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE); /* Parse header */ if ((error = read_header(&header, buffer)) < 0) return error; index->version = header.version; if (index->version >= INDEX_VERSION_NUMBER_COMP) last = empty; seek_forward(INDEX_HEADER_SIZE); GIT_ASSERT(!index->entries.length); if ((error = index_map_resize(index->entries_map, header.entry_count, index->ignore_case)) < 0) return error; /* Parse all the entries */ for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { git_index_entry *entry = NULL; size_t entry_size; if ((error = read_entry(&entry, &entry_size, index, buffer, buffer_size, last)) < 0) { error = index_error_invalid("invalid entry"); goto done; } if ((error = git_vector_insert(&index->entries, entry)) < 0) { index_entry_free(entry); goto done; } if ((error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0) { index_entry_free(entry); goto done; } error = 0; if (index->version >= INDEX_VERSION_NUMBER_COMP) last = entry->path; seek_forward(entry_size); } if (i != header.entry_count) { error = index_error_invalid("header entries changed while parsing"); goto done; } /* There's still space for some extensions! */ while (buffer_size > INDEX_FOOTER_SIZE) { size_t extension_size; if ((error = read_extension(&extension_size, index, buffer, buffer_size)) < 0) { goto done; } seek_forward(extension_size); } if (buffer_size != INDEX_FOOTER_SIZE) { error = index_error_invalid( "buffer size does not match index footer size"); goto done; } /* 160-bit SHA-1 over the content of the index file before this checksum. */ git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer); if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) { error = index_error_invalid( "calculated checksum does not match expected"); goto done; } git_oid_cpy(&index->checksum, &checksum_calculated); #undef seek_forward /* Entries are stored case-sensitively on disk, so re-sort now if * in-memory index is supposed to be case-insensitive */ git_vector_set_sorted(&index->entries, !index->ignore_case); git_vector_sort(&index->entries); index->dirty = 0; done: return error; } static bool is_index_extended(git_index *index) { size_t i, extended; git_index_entry *entry; extended = 0; git_vector_foreach(&index->entries, i, entry) { entry->flags &= ~GIT_INDEX_ENTRY_EXTENDED; if (entry->flags_extended & GIT_INDEX_ENTRY_EXTENDED_FLAGS) { extended++; entry->flags |= GIT_INDEX_ENTRY_EXTENDED; } } return (extended > 0); } static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char *last) { void *mem = NULL; struct entry_short ondisk; size_t path_len, disk_size; int varint_len = 0; char *path; const char *path_start = entry->path; size_t same_len = 0; path_len = ((struct entry_internal *)entry)->pathlen; if (last) { const char *last_c = last; while (*path_start == *last_c) { if (!*path_start || !*last_c) break; ++path_start; ++last_c; ++same_len; } path_len -= same_len; varint_len = git_encode_varint(NULL, 0, strlen(last) - same_len); } disk_size = index_entry_size(path_len, varint_len, entry->flags); if (git_filebuf_reserve(file, &mem, disk_size) < 0) return -1; memset(mem, 0x0, disk_size); /** * Yes, we have to truncate. * * The on-disk format for Index entries clearly defines * the time and size fields to be 4 bytes each -- so even if * we store these values with 8 bytes on-memory, they must * be truncated to 4 bytes before writing to disk. * * In 2038 I will be either too dead or too rich to care about this */ ondisk.ctime.seconds = htonl((uint32_t)entry->ctime.seconds); ondisk.mtime.seconds = htonl((uint32_t)entry->mtime.seconds); ondisk.ctime.nanoseconds = htonl(entry->ctime.nanoseconds); ondisk.mtime.nanoseconds = htonl(entry->mtime.nanoseconds); ondisk.dev = htonl(entry->dev); ondisk.ino = htonl(entry->ino); ondisk.mode = htonl(entry->mode); ondisk.uid = htonl(entry->uid); ondisk.gid = htonl(entry->gid); ondisk.file_size = htonl((uint32_t)entry->file_size); git_oid_cpy(&ondisk.oid, &entry->id); ondisk.flags = htons(entry->flags); if (entry->flags & GIT_INDEX_ENTRY_EXTENDED) { const size_t path_offset = offsetof(struct entry_long, path); struct entry_long ondisk_ext; memcpy(&ondisk_ext, &ondisk, sizeof(struct entry_short)); ondisk_ext.flags_extended = htons(entry->flags_extended & GIT_INDEX_ENTRY_EXTENDED_FLAGS); memcpy(mem, &ondisk_ext, path_offset); path = (char *)mem + path_offset; disk_size -= path_offset; } else { const size_t path_offset = offsetof(struct entry_short, path); memcpy(mem, &ondisk, path_offset); path = (char *)mem + path_offset; disk_size -= path_offset; } if (last) { varint_len = git_encode_varint((unsigned char *) path, disk_size, strlen(last) - same_len); GIT_ASSERT(varint_len > 0); path += varint_len; disk_size -= varint_len; /* * If using path compression, we are not allowed * to have additional trailing NULs. */ GIT_ASSERT(disk_size == path_len + 1); } else { /* * If no path compression is used, we do have * NULs as padding. As such, simply assert that * we have enough space left to write the path. */ GIT_ASSERT(disk_size > path_len); } memcpy(path, path_start, path_len + 1); return 0; } static int write_entries(git_index *index, git_filebuf *file) { int error = 0; size_t i; git_vector case_sorted = GIT_VECTOR_INIT, *entries = NULL; git_index_entry *entry; const char *last = NULL; /* If index->entries is sorted case-insensitively, then we need * to re-sort it case-sensitively before writing */ if (index->ignore_case) { if ((error = git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp)) < 0) goto done; git_vector_sort(&case_sorted); entries = &case_sorted; } else { entries = &index->entries; } if (index->version >= INDEX_VERSION_NUMBER_COMP) last = ""; git_vector_foreach(entries, i, entry) { if ((error = write_disk_entry(file, entry, last)) < 0) break; if (index->version >= INDEX_VERSION_NUMBER_COMP) last = entry->path; } done: git_vector_free(&case_sorted); return error; } static int write_extension(git_filebuf *file, struct index_extension *header, git_buf *data) { struct index_extension ondisk; memset(&ondisk, 0x0, sizeof(struct index_extension)); memcpy(&ondisk, header, 4); ondisk.extension_size = htonl(header->extension_size); git_filebuf_write(file, &ondisk, sizeof(struct index_extension)); return git_filebuf_write(file, data->ptr, data->size); } static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *conflict_name) { int error = 0; if (conflict_name->ancestor == NULL) error = git_buf_put(name_buf, "\0", 1); else error = git_buf_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1); if (error != 0) goto on_error; if (conflict_name->ours == NULL) error = git_buf_put(name_buf, "\0", 1); else error = git_buf_put(name_buf, conflict_name->ours, strlen(conflict_name->ours) + 1); if (error != 0) goto on_error; if (conflict_name->theirs == NULL) error = git_buf_put(name_buf, "\0", 1); else error = git_buf_put(name_buf, conflict_name->theirs, strlen(conflict_name->theirs) + 1); on_error: return error; } static int write_name_extension(git_index *index, git_filebuf *file) { git_buf name_buf = GIT_BUF_INIT; git_vector *out = &index->names; git_index_name_entry *conflict_name; struct index_extension extension; size_t i; int error = 0; git_vector_foreach(out, i, conflict_name) { if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0) goto done; } memset(&extension, 0x0, sizeof(struct index_extension)); memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4); extension.extension_size = (uint32_t)name_buf.size; error = write_extension(file, &extension, &name_buf); git_buf_dispose(&name_buf); done: return error; } static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc) { int i; int error = 0; if ((error = git_buf_put(reuc_buf, reuc->path, strlen(reuc->path) + 1)) < 0) return error; for (i = 0; i < 3; i++) { if ((error = git_buf_printf(reuc_buf, "%o", reuc->mode[i])) < 0 || (error = git_buf_put(reuc_buf, "\0", 1)) < 0) return error; } for (i = 0; i < 3; i++) { if (reuc->mode[i] && (error = git_buf_put(reuc_buf, (char *)&reuc->oid[i].id, GIT_OID_RAWSZ)) < 0) return error; } return 0; } static int write_reuc_extension(git_index *index, git_filebuf *file) { git_buf reuc_buf = GIT_BUF_INIT; git_vector *out = &index->reuc; git_index_reuc_entry *reuc; struct index_extension extension; size_t i; int error = 0; git_vector_foreach(out, i, reuc) { if ((error = create_reuc_extension_data(&reuc_buf, reuc)) < 0) goto done; } memset(&extension, 0x0, sizeof(struct index_extension)); memcpy(&extension.signature, INDEX_EXT_UNMERGED_SIG, 4); extension.extension_size = (uint32_t)reuc_buf.size; error = write_extension(file, &extension, &reuc_buf); git_buf_dispose(&reuc_buf); done: return error; } static int write_tree_extension(git_index *index, git_filebuf *file) { struct index_extension extension; git_buf buf = GIT_BUF_INIT; int error; if (index->tree == NULL) return 0; if ((error = git_tree_cache_write(&buf, index->tree)) < 0) return error; memset(&extension, 0x0, sizeof(struct index_extension)); memcpy(&extension.signature, INDEX_EXT_TREECACHE_SIG, 4); extension.extension_size = (uint32_t)buf.size; error = write_extension(file, &extension, &buf); git_buf_dispose(&buf); return error; } static void clear_uptodate(git_index *index) { git_index_entry *entry; size_t i; git_vector_foreach(&index->entries, i, entry) entry->flags_extended &= ~GIT_INDEX_ENTRY_UPTODATE; } static int write_index(git_oid *checksum, git_index *index, git_filebuf *file) { git_oid hash_final; struct index_header header; bool is_extended; uint32_t index_version_number; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(file); if (index->version <= INDEX_VERSION_NUMBER_EXT) { is_extended = is_index_extended(index); index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER_LB; } else { index_version_number = index->version; } header.signature = htonl(INDEX_HEADER_SIG); header.version = htonl(index_version_number); header.entry_count = htonl((uint32_t)index->entries.length); if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0) return -1; if (write_entries(index, file) < 0) return -1; /* write the tree cache extension */ if (index->tree != NULL && write_tree_extension(index, file) < 0) return -1; /* write the rename conflict extension */ if (index->names.length > 0 && write_name_extension(index, file) < 0) return -1; /* write the reuc extension */ if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0) return -1; /* get out the hash for all the contents we've appended to the file */ git_filebuf_hash(&hash_final, file); git_oid_cpy(checksum, &hash_final); /* write it at the end of the file */ if (git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ) < 0) return -1; /* file entries are no longer up to date */ clear_uptodate(index); return 0; } int git_index_entry_stage(const git_index_entry *entry) { return GIT_INDEX_ENTRY_STAGE(entry); } int git_index_entry_is_conflict(const git_index_entry *entry) { return (GIT_INDEX_ENTRY_STAGE(entry) > 0); } typedef struct read_tree_data { git_index *index; git_vector *old_entries; git_vector *new_entries; git_vector_cmp entry_cmp; git_tree_cache *tree; } read_tree_data; static int read_tree_cb( const char *root, const git_tree_entry *tentry, void *payload) { read_tree_data *data = payload; git_index_entry *entry = NULL, *old_entry; git_buf path = GIT_BUF_INIT; size_t pos; if (git_tree_entry__is_tree(tentry)) return 0; if (git_buf_joinpath(&path, root, tentry->filename) < 0) return -1; if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, NULL, false) < 0) return -1; entry->mode = tentry->attr; git_oid_cpy(&entry->id, git_tree_entry_id(tentry)); /* look for corresponding old entry and copy data to new entry */ if (data->old_entries != NULL && !index_find_in_entries( &pos, data->old_entries, data->entry_cmp, path.ptr, 0, 0) && (old_entry = git_vector_get(data->old_entries, pos)) != NULL && entry->mode == old_entry->mode && git_oid_equal(&entry->id, &old_entry->id)) { index_entry_cpy(entry, old_entry); entry->flags_extended = 0; } index_entry_adjust_namemask(entry, path.size); git_buf_dispose(&path); if (git_vector_insert(data->new_entries, entry) < 0) { index_entry_free(entry); return -1; } return 0; } int git_index_read_tree(git_index *index, const git_tree *tree) { int error = 0; git_vector entries = GIT_VECTOR_INIT; git_idxmap *entries_map; read_tree_data data; size_t i; git_index_entry *e; if (git_idxmap_new(&entries_map) < 0) return -1; git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */ data.index = index; data.old_entries = &index->entries; data.new_entries = &entries; data.entry_cmp = index->entries_search; index->tree = NULL; git_pool_clear(&index->tree_pool); git_vector_sort(&index->entries); if ((error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data)) < 0) goto cleanup; if ((error = index_map_resize(entries_map, entries.length, index->ignore_case)) < 0) goto cleanup; git_vector_foreach(&entries, i, e) { if ((error = index_map_set(entries_map, e, index->ignore_case)) < 0) { git_error_set(GIT_ERROR_INDEX, "failed to insert entry into map"); return error; } } error = 0; git_vector_sort(&entries); if ((error = git_index_clear(index)) < 0) { /* well, this isn't good */; } else { git_vector_swap(&entries, &index->entries); entries_map = git_atomic_swap(index->entries_map, entries_map); } index->dirty = 1; cleanup: git_vector_free(&entries); git_idxmap_free(entries_map); if (error < 0) return error; error = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool); return error; } static int git_index_read_iterator( git_index *index, git_iterator *new_iterator, size_t new_length_hint) { git_vector new_entries = GIT_VECTOR_INIT, remove_entries = GIT_VECTOR_INIT; git_idxmap *new_entries_map = NULL; git_iterator *index_iterator = NULL; git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *old_entry, *new_entry; git_index_entry *entry; size_t i; int error; GIT_ASSERT((new_iterator->flags & GIT_ITERATOR_DONT_IGNORE_CASE)); if ((error = git_vector_init(&new_entries, new_length_hint, index->entries._cmp)) < 0 || (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 || (error = git_idxmap_new(&new_entries_map)) < 0) goto done; if (new_length_hint && (error = index_map_resize(new_entries_map, new_length_hint, index->ignore_case)) < 0) goto done; opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_CONFLICTS; if ((error = git_iterator_for_index(&index_iterator, git_index_owner(index), index, &opts)) < 0 || ((error = git_iterator_current(&old_entry, index_iterator)) < 0 && error != GIT_ITEROVER) || ((error = git_iterator_current(&new_entry, new_iterator)) < 0 && error != GIT_ITEROVER)) goto done; while (true) { git_index_entry *dup_entry = NULL, *add_entry = NULL, *remove_entry = NULL; int diff; error = 0; if (old_entry && new_entry) diff = git_index_entry_cmp(old_entry, new_entry); else if (!old_entry && new_entry) diff = 1; else if (old_entry && !new_entry) diff = -1; else break; if (diff < 0) { remove_entry = (git_index_entry *)old_entry; } else if (diff > 0) { dup_entry = (git_index_entry *)new_entry; } else { /* Path and stage are equal, if the OID is equal, keep it to * keep the stat cache data. */ if (git_oid_equal(&old_entry->id, &new_entry->id) && old_entry->mode == new_entry->mode) { add_entry = (git_index_entry *)old_entry; } else { dup_entry = (git_index_entry *)new_entry; remove_entry = (git_index_entry *)old_entry; } } if (dup_entry) { if ((error = index_entry_dup_nocache(&add_entry, index, dup_entry)) < 0) goto done; index_entry_adjust_namemask(add_entry, ((struct entry_internal *)add_entry)->pathlen); } /* invalidate this path in the tree cache if this is new (to * invalidate the parent trees) */ if (dup_entry && !remove_entry && index->tree) git_tree_cache_invalidate_path(index->tree, dup_entry->path); if (add_entry) { if ((error = git_vector_insert(&new_entries, add_entry)) == 0) error = index_map_set(new_entries_map, add_entry, index->ignore_case); } if (remove_entry && error >= 0) error = git_vector_insert(&remove_entries, remove_entry); if (error < 0) { git_error_set(GIT_ERROR_INDEX, "failed to insert entry"); goto done; } if (diff <= 0) { if ((error = git_iterator_advance(&old_entry, index_iterator)) < 0 && error != GIT_ITEROVER) goto done; } if (diff >= 0) { if ((error = git_iterator_advance(&new_entry, new_iterator)) < 0 && error != GIT_ITEROVER) goto done; } } if ((error = git_index_name_clear(index)) < 0 || (error = git_index_reuc_clear(index)) < 0) goto done; git_vector_swap(&new_entries, &index->entries); new_entries_map = git_atomic_swap(index->entries_map, new_entries_map); git_vector_foreach(&remove_entries, i, entry) { if (index->tree) git_tree_cache_invalidate_path(index->tree, entry->path); index_entry_free(entry); } clear_uptodate(index); index->dirty = 1; error = 0; done: git_idxmap_free(new_entries_map); git_vector_free(&new_entries); git_vector_free(&remove_entries); git_iterator_free(index_iterator); return error; } int git_index_read_index( git_index *index, const git_index *new_index) { git_iterator *new_iterator = NULL; git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; int error; opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_CONFLICTS; if ((error = git_iterator_for_index(&new_iterator, git_index_owner(new_index), (git_index *)new_index, &opts)) < 0 || (error = git_index_read_iterator(index, new_iterator, new_index->entries.length)) < 0) goto done; done: git_iterator_free(new_iterator); return error; } git_repository *git_index_owner(const git_index *index) { return INDEX_OWNER(index); } enum { INDEX_ACTION_NONE = 0, INDEX_ACTION_UPDATE = 1, INDEX_ACTION_REMOVE = 2, INDEX_ACTION_ADDALL = 3, }; int git_index_add_all( git_index *index, const git_strarray *paths, unsigned int flags, git_index_matched_path_cb cb, void *payload) { int error; git_repository *repo; git_iterator *wditer = NULL; git_pathspec ps; bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0; GIT_ASSERT_ARG(index); repo = INDEX_OWNER(index); if ((error = git_repository__ensure_not_bare(repo, "index add all")) < 0) return error; if ((error = git_pathspec__init(&ps, paths)) < 0) return error; /* optionally check that pathspec doesn't mention any ignored files */ if ((flags & GIT_INDEX_ADD_CHECK_PATHSPEC) != 0 && (flags & GIT_INDEX_ADD_FORCE) == 0 && (error = git_ignore__check_pathspec_for_exact_ignores( repo, &ps.pathspec, no_fnmatch)) < 0) goto cleanup; error = index_apply_to_wd_diff(index, INDEX_ACTION_ADDALL, paths, flags, cb, payload); if (error) git_error_set_after_callback(error); cleanup: git_iterator_free(wditer); git_pathspec__clear(&ps); return error; } struct foreach_diff_data { git_index *index; const git_pathspec *pathspec; unsigned int flags; git_index_matched_path_cb cb; void *payload; }; static int apply_each_file(const git_diff_delta *delta, float progress, void *payload) { struct foreach_diff_data *data = payload; const char *match, *path; int error = 0; GIT_UNUSED(progress); path = delta->old_file.path; /* We only want those which match the pathspecs */ if (!git_pathspec__match( &data->pathspec->pathspec, path, false, (bool)data->index->ignore_case, &match, NULL)) return 0; if (data->cb) error = data->cb(path, match, data->payload); if (error > 0) /* skip this entry */ return 0; if (error < 0) /* actual error */ return error; /* If the workdir item does not exist, remove it from the index. */ if ((delta->new_file.flags & GIT_DIFF_FLAG_EXISTS) == 0) error = git_index_remove_bypath(data->index, path); else error = git_index_add_bypath(data->index, delta->new_file.path); return error; } static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths, unsigned int flags, git_index_matched_path_cb cb, void *payload) { int error; git_diff *diff; git_pathspec ps; git_repository *repo; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; struct foreach_diff_data data = { index, NULL, flags, cb, payload, }; GIT_ASSERT_ARG(index); GIT_ASSERT_ARG(action == INDEX_ACTION_UPDATE || action == INDEX_ACTION_ADDALL); repo = INDEX_OWNER(index); if (!repo) { return create_index_error(-1, "cannot run update; the index is not backed up by a repository."); } /* * We do the matching ourselves intead of passing the list to * diff because we want to tell the callback which one * matched, which we do not know if we ask diff to filter for us. */ if ((error = git_pathspec__init(&ps, paths)) < 0) return error; opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE; if (action == INDEX_ACTION_ADDALL) { opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS; if (flags == GIT_INDEX_ADD_FORCE) opts.flags |= GIT_DIFF_INCLUDE_IGNORED; } if ((error = git_diff_index_to_workdir(&diff, repo, index, &opts)) < 0) goto cleanup; data.pathspec = &ps; error = git_diff_foreach(diff, apply_each_file, NULL, NULL, NULL, &data); git_diff_free(diff); if (error) /* make sure error is set if callback stopped iteration */ git_error_set_after_callback(error); cleanup: git_pathspec__clear(&ps); return error; } static int index_apply_to_all( git_index *index, int action, const git_strarray *paths, git_index_matched_path_cb cb, void *payload) { int error = 0; size_t i; git_pathspec ps; const char *match; git_buf path = GIT_BUF_INIT; GIT_ASSERT_ARG(index); if ((error = git_pathspec__init(&ps, paths)) < 0) return error; git_vector_sort(&index->entries); for (i = 0; !error && i < index->entries.length; ++i) { git_index_entry *entry = git_vector_get(&index->entries, i); /* check if path actually matches */ if (!git_pathspec__match( &ps.pathspec, entry->path, false, (bool)index->ignore_case, &match, NULL)) continue; /* issue notification callback if requested */ if (cb && (error = cb(entry->path, match, payload)) != 0) { if (error > 0) { /* return > 0 means skip this one */ error = 0; continue; } if (error < 0) /* return < 0 means abort */ break; } /* index manipulation may alter entry, so don't depend on it */ if ((error = git_buf_sets(&path, entry->path)) < 0) break; switch (action) { case INDEX_ACTION_NONE: break; case INDEX_ACTION_UPDATE: error = git_index_add_bypath(index, path.ptr); if (error == GIT_ENOTFOUND) { git_error_clear(); error = git_index_remove_bypath(index, path.ptr); if (!error) /* back up foreach if we removed this */ i--; } break; case INDEX_ACTION_REMOVE: if (!(error = git_index_remove_bypath(index, path.ptr))) i--; /* back up foreach if we removed this */ break; default: git_error_set(GIT_ERROR_INVALID, "unknown index action %d", action); error = -1; break; } } git_buf_dispose(&path); git_pathspec__clear(&ps); return error; } int git_index_remove_all( git_index *index, const git_strarray *pathspec, git_index_matched_path_cb cb, void *payload) { int error = index_apply_to_all( index, INDEX_ACTION_REMOVE, pathspec, cb, payload); if (error) /* make sure error is set if callback stopped iteration */ git_error_set_after_callback(error); return error; } int git_index_update_all( git_index *index, const git_strarray *pathspec, git_index_matched_path_cb cb, void *payload) { int error = index_apply_to_wd_diff(index, INDEX_ACTION_UPDATE, pathspec, 0, cb, payload); if (error) /* make sure error is set if callback stopped iteration */ git_error_set_after_callback(error); return error; } int git_index_snapshot_new(git_vector *snap, git_index *index) { int error; GIT_REFCOUNT_INC(index); git_atomic32_inc(&index->readers); git_vector_sort(&index->entries); error = git_vector_dup(snap, &index->entries, index->entries._cmp); if (error < 0) git_index_snapshot_release(snap, index); return error; } void git_index_snapshot_release(git_vector *snap, git_index *index) { git_vector_free(snap); git_atomic32_dec(&index->readers); git_index_free(index); } int git_index_snapshot_find( size_t *out, git_vector *entries, git_vector_cmp entry_srch, const char *path, size_t path_len, int stage) { return index_find_in_entries(out, entries, entry_srch, path, path_len, stage); } int git_indexwriter_init( git_indexwriter *writer, git_index *index) { int error; GIT_REFCOUNT_INC(index); writer->index = index; if (!index->index_file_path) return create_index_error(-1, "failed to write index: The index is in-memory only"); if ((error = git_filebuf_open( &writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) { if (error == GIT_ELOCKED) git_error_set(GIT_ERROR_INDEX, "the index is locked; this might be due to a concurrent or crashed process"); return error; } writer->should_write = 1; return 0; } int git_indexwriter_init_for_operation( git_indexwriter *writer, git_repository *repo, unsigned int *checkout_strategy) { git_index *index; int error; if ((error = git_repository_index__weakptr(&index, repo)) < 0 || (error = git_indexwriter_init(writer, index)) < 0) return error; writer->should_write = (*checkout_strategy & GIT_CHECKOUT_DONT_WRITE_INDEX) == 0; *checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX; return 0; } int git_indexwriter_commit(git_indexwriter *writer) { int error; git_oid checksum = {{ 0 }}; if (!writer->should_write) return 0; git_vector_sort(&writer->index->entries); git_vector_sort(&writer->index->reuc); if ((error = write_index(&checksum, writer->index, &writer->file)) < 0) { git_indexwriter_cleanup(writer); return error; } if ((error = git_filebuf_commit(&writer->file)) < 0) return error; if ((error = git_futils_filestamp_check( &writer->index->stamp, writer->index->index_file_path)) < 0) { git_error_set(GIT_ERROR_OS, "could not read index timestamp"); return -1; } writer->index->dirty = 0; writer->index->on_disk = 1; git_oid_cpy(&writer->index->checksum, &checksum); git_index_free(writer->index); writer->index = NULL; return 0; } void git_indexwriter_cleanup(git_indexwriter *writer) { git_filebuf_cleanup(&writer->file); git_index_free(writer->index); writer->index = NULL; } /* Deprecated functions */ #ifndef GIT_DEPRECATE_HARD int git_index_add_frombuffer( git_index *index, const git_index_entry *source_entry, const void *buffer, size_t len) { return git_index_add_from_buffer(index, source_entry, buffer, len); } #endif git2r/src/libgit2/src/attrcache.h0000644000175000017500000000300514125111754016453 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_attrcache_h__ #define INCLUDE_attrcache_h__ #include "common.h" #include "attr_file.h" #include "strmap.h" #define GIT_ATTR_CONFIG "core.attributesfile" #define GIT_IGNORE_CONFIG "core.excludesfile" typedef struct { char *cfg_attr_file; /* cached value of core.attributesfile */ char *cfg_excl_file; /* cached value of core.excludesfile */ git_strmap *files; /* hash path to git_attr_cache_entry records */ git_strmap *macros; /* hash name to vector */ git_mutex lock; git_pool pool; } git_attr_cache; extern int git_attr_cache__init(git_repository *repo); /* get file - loading and reload as needed */ extern int git_attr_cache__get( git_attr_file **file, git_repository *repo, git_attr_session *attr_session, git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros); extern bool git_attr_cache__is_cached( git_repository *repo, git_attr_file_source_t source_type, const char *filename); extern int git_attr_cache__alloc_file_entry( git_attr_file_entry **out, git_repository *repo, const char *base, const char *path, git_pool *pool); extern int git_attr_cache__insert_macro( git_repository *repo, git_attr_rule *macro); extern git_attr_rule *git_attr_cache__lookup_macro( git_repository *repo, const char *name); #endif git2r/src/libgit2/src/config_parse.c0000644000175000017500000003113714125111754017156 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "config_parse.h" #include const char *git_config_escapes = "ntb\"\\"; const char *git_config_escaped = "\n\t\b\"\\"; static void set_parse_error(git_config_parser *reader, int col, const char *error_str) { if (col) git_error_set(GIT_ERROR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)", error_str, reader->path, reader->ctx.line_num, col); else git_error_set(GIT_ERROR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ")", error_str, reader->path, reader->ctx.line_num); } GIT_INLINE(int) config_keychar(int c) { return isalnum(c) || c == '-'; } static int strip_comments(char *line, int in_quotes) { int quote_count = in_quotes, backslash_count = 0; char *ptr; for (ptr = line; *ptr; ++ptr) { if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\') quote_count++; if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0 && (backslash_count % 2) == 0) { ptr[0] = '\0'; break; } if (ptr[0] == '\\') backslash_count++; else backslash_count = 0; } /* skip any space at the end */ while (ptr > line && git__isspace(ptr[-1])) { ptr--; } ptr[0] = '\0'; return quote_count; } static int parse_subsection_header(git_config_parser *reader, const char *line, size_t pos, const char *base_name, char **section_name) { int c, rpos; const char *first_quote, *last_quote; const char *line_start = line; git_buf buf = GIT_BUF_INIT; size_t quoted_len, alloc_len, base_name_len = strlen(base_name); /* Skip any additional whitespace before our section name */ while (git__isspace(line[pos])) pos++; /* We should be at the first quotation mark. */ if (line[pos] != '"') { set_parse_error(reader, 0, "missing quotation marks in section header"); goto end_error; } first_quote = &line[pos]; last_quote = strrchr(line, '"'); quoted_len = last_quote - first_quote; if ((last_quote - line) > INT_MAX) { set_parse_error(reader, 0, "invalid section header, line too long"); goto end_error; } if (quoted_len == 0) { set_parse_error(reader, 0, "missing closing quotation mark in section header"); goto end_error; } GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); if (git_buf_grow(&buf, alloc_len) < 0 || git_buf_printf(&buf, "%s.", base_name) < 0) goto end_error; rpos = 0; line = first_quote; c = line[++rpos]; /* * At the end of each iteration, whatever is stored in c will be * added to the string. In case of error, jump to out */ do { switch (c) { case 0: set_parse_error(reader, 0, "unexpected end-of-line in section header"); goto end_error; case '"': goto end_parse; case '\\': c = line[++rpos]; if (c == 0) { set_parse_error(reader, rpos, "unexpected end-of-line in section header"); goto end_error; } default: break; } git_buf_putc(&buf, (char)c); c = line[++rpos]; } while (line + rpos < last_quote); end_parse: if (git_buf_oom(&buf)) goto end_error; if (line[rpos] != '"' || line[rpos + 1] != ']') { set_parse_error(reader, rpos, "unexpected text after closing quotes"); git_buf_dispose(&buf); return -1; } *section_name = git_buf_detach(&buf); return (int)(&line[rpos + 2] - line_start); /* rpos is at the closing quote */ end_error: git_buf_dispose(&buf); return -1; } static int parse_section_header(git_config_parser *reader, char **section_out) { char *name, *name_end; int name_length, c, pos; int result; char *line; size_t line_len; git_parse_advance_ws(&reader->ctx); line = git__strndup(reader->ctx.line, reader->ctx.line_len); if (line == NULL) return -1; /* find the end of the variable's name */ name_end = strrchr(line, ']'); if (name_end == NULL) { git__free(line); set_parse_error(reader, 0, "missing ']' in section header"); return -1; } GIT_ERROR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1); name = git__malloc(line_len); GIT_ERROR_CHECK_ALLOC(name); name_length = 0; pos = 0; /* Make sure we were given a section header */ c = line[pos++]; GIT_ASSERT(c == '['); c = line[pos++]; do { if (git__isspace(c)){ name[name_length] = '\0'; result = parse_subsection_header(reader, line, pos, name, section_out); git__free(line); git__free(name); return result; } if (!config_keychar(c) && c != '.') { set_parse_error(reader, pos, "unexpected character in header"); goto fail_parse; } name[name_length++] = (char)git__tolower(c); } while ((c = line[pos++]) != ']'); if (line[pos - 1] != ']') { set_parse_error(reader, pos, "unexpected end of file"); goto fail_parse; } git__free(line); name[name_length] = 0; *section_out = name; return pos; fail_parse: git__free(line); git__free(name); return -1; } static int skip_bom(git_parse_ctx *parser) { git_buf buf = GIT_BUF_INIT_CONST(parser->content, parser->content_len); git_buf_bom_t bom; int bom_offset = git_buf_detect_bom(&bom, &buf); if (bom == GIT_BUF_BOM_UTF8) git_parse_advance_chars(parser, bom_offset); /* TODO: reference implementation is pretty stupid with BoM */ return 0; } /* (* basic types *) digit = "0".."9" integer = digit { digit } alphabet = "a".."z" + "A" .. "Z" section_char = alphabet | "." | "-" extension_char = (* any character except newline *) any_char = (* any character *) variable_char = "alphabet" | "-" (* actual grammar *) config = { section } section = header { definition } header = "[" section [subsection | subsection_ext] "]" subsection = "." section subsection_ext = "\"" extension "\"" section = section_char { section_char } extension = extension_char { extension_char } definition = variable_name ["=" variable_value] "\n" variable_name = variable_char { variable_char } variable_value = string | boolean | integer string = quoted_string | plain_string quoted_string = "\"" plain_string "\"" plain_string = { any_char } boolean = boolean_true | boolean_false boolean_true = "yes" | "1" | "true" | "on" boolean_false = "no" | "0" | "false" | "off" */ /* '\"' -> '"' etc */ static int unescape_line( char **out, bool *is_multi, const char *ptr, int quote_count) { char *str, *fixed, *esc; size_t ptr_len = strlen(ptr), alloc_len; *is_multi = false; if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) || (str = git__malloc(alloc_len)) == NULL) { return -1; } fixed = str; while (*ptr != '\0') { if (*ptr == '"') { quote_count++; } else if (*ptr != '\\') { *fixed++ = *ptr; } else { /* backslash, check the next char */ ptr++; /* if we're at the end, it's a multiline, so keep the backslash */ if (*ptr == '\0') { *is_multi = true; goto done; } if ((esc = strchr(git_config_escapes, *ptr)) != NULL) { *fixed++ = git_config_escaped[esc - git_config_escapes]; } else { git__free(str); git_error_set(GIT_ERROR_CONFIG, "invalid escape at %s", ptr); return -1; } } ptr++; } done: *fixed = '\0'; *out = str; return 0; } static int parse_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes) { int quote_count; bool multiline = true; while (multiline) { char *line = NULL, *proc_line = NULL; int error; /* Check that the next line exists */ git_parse_advance_line(&reader->ctx); line = git__strndup(reader->ctx.line, reader->ctx.line_len); GIT_ERROR_CHECK_ALLOC(line); /* * We've reached the end of the file, there is no continuation. * (this is not an error). */ if (line[0] == '\0') { error = 0; goto out; } /* If it was just a comment, pretend it didn't exist */ quote_count = strip_comments(line, in_quotes); if (line[0] == '\0') goto next; if ((error = unescape_line(&proc_line, &multiline, line, in_quotes)) < 0) goto out; /* Add this line to the multiline var */ if ((error = git_buf_puts(value, proc_line)) < 0) goto out; next: git__free(line); git__free(proc_line); in_quotes = quote_count; continue; out: git__free(line); git__free(proc_line); return error; } return 0; } GIT_INLINE(bool) is_namechar(char c) { return isalnum(c) || c == '-'; } static int parse_name( char **name, const char **value, git_config_parser *reader, const char *line) { const char *name_end = line, *value_start; *name = NULL; *value = NULL; while (*name_end && is_namechar(*name_end)) name_end++; if (line == name_end) { set_parse_error(reader, 0, "invalid configuration key"); return -1; } value_start = name_end; while (*value_start && git__isspace(*value_start)) value_start++; if (*value_start == '=') { *value = value_start + 1; } else if (*value_start) { set_parse_error(reader, 0, "invalid configuration key"); return -1; } if ((*name = git__strndup(line, name_end - line)) == NULL) return -1; return 0; } static int parse_variable(git_config_parser *reader, char **var_name, char **var_value) { const char *value_start = NULL; char *line = NULL, *name = NULL, *value = NULL; int quote_count, error; bool multiline; *var_name = NULL; *var_value = NULL; git_parse_advance_ws(&reader->ctx); line = git__strndup(reader->ctx.line, reader->ctx.line_len); GIT_ERROR_CHECK_ALLOC(line); quote_count = strip_comments(line, 0); if ((error = parse_name(&name, &value_start, reader, line)) < 0) goto out; /* * Now, let's try to parse the value */ if (value_start != NULL) { while (git__isspace(value_start[0])) value_start++; if ((error = unescape_line(&value, &multiline, value_start, 0)) < 0) goto out; if (multiline) { git_buf multi_value = GIT_BUF_INIT; git_buf_attach(&multi_value, value, 0); value = NULL; if (parse_multiline_variable(reader, &multi_value, quote_count % 2) < 0 || git_buf_oom(&multi_value)) { error = -1; git_buf_dispose(&multi_value); goto out; } value = git_buf_detach(&multi_value); } } *var_name = name; *var_value = value; name = NULL; value = NULL; out: git__free(name); git__free(value); git__free(line); return error; } int git_config_parser_init(git_config_parser *out, const char *path, const char *data, size_t datalen) { out->path = path; return git_parse_ctx_init(&out->ctx, data, datalen); } void git_config_parser_dispose(git_config_parser *parser) { git_parse_ctx_clear(&parser->ctx); } int git_config_parse( git_config_parser *parser, git_config_parser_section_cb on_section, git_config_parser_variable_cb on_variable, git_config_parser_comment_cb on_comment, git_config_parser_eof_cb on_eof, void *payload) { git_parse_ctx *ctx; char *current_section = NULL, *var_name = NULL, *var_value = NULL; int result = 0; ctx = &parser->ctx; skip_bom(ctx); for (; ctx->remain_len > 0; git_parse_advance_line(ctx)) { const char *line_start; size_t line_len; char c; restart: line_start = ctx->line; line_len = ctx->line_len; /* * Get either first non-whitespace character or, if that does * not exist, the first whitespace character. This is required * to preserve whitespaces when writing back the file. */ if (git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE) < 0 && git_parse_peek(&c, ctx, 0) < 0) continue; switch (c) { case '[': /* section header, new section begins */ git__free(current_section); current_section = NULL; result = parse_section_header(parser, ¤t_section); if (result < 0) break; git_parse_advance_chars(ctx, result); if (on_section) result = on_section(parser, current_section, line_start, line_len, payload); /* * After we've parsed the section header we may not be * done with the line. If there's still data in there, * run the next loop with the rest of the current line * instead of moving forward. */ if (!git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE)) goto restart; break; case '\n': /* comment or whitespace-only */ case '\r': case ' ': case '\t': case ';': case '#': if (on_comment) { result = on_comment(parser, line_start, line_len, payload); } break; default: /* assume variable declaration */ if ((result = parse_variable(parser, &var_name, &var_value)) == 0 && on_variable) { result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, payload); git__free(var_name); git__free(var_value); } break; } if (result < 0) goto out; } if (on_eof) result = on_eof(parser, current_section, payload); out: git__free(current_section); return result; } git2r/src/libgit2/src/array.h0000644000175000017500000000601414125111754015636 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_array_h__ #define INCLUDE_array_h__ #include "common.h" /* * Use this to declare a typesafe resizable array of items, a la: * * git_array_t(int) my_ints = GIT_ARRAY_INIT; * ... * int *i = git_array_alloc(my_ints); * GIT_ERROR_CHECK_ALLOC(i); * ... * git_array_clear(my_ints); * * You may also want to do things like: * * typedef git_array_t(my_struct) my_struct_array_t; */ #define git_array_t(type) struct { type *ptr; size_t size, asize; } #define GIT_ARRAY_INIT { NULL, 0, 0 } #define git_array_init(a) \ do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0) #define git_array_init_to_size(a, desired) \ do { (a).size = 0; (a).asize = desired; (a).ptr = git__calloc(desired, sizeof(*(a).ptr)); } while (0) #define git_array_clear(a) \ do { git__free((a).ptr); git_array_init(a); } while (0) #define GIT_ERROR_CHECK_ARRAY(a) GIT_ERROR_CHECK_ALLOC((a).ptr) typedef git_array_t(char) git_array_generic_t; /* use a generic array for growth, return 0 on success */ GIT_INLINE(int) git_array_grow(void *_a, size_t item_size) { volatile git_array_generic_t *a = _a; size_t new_size; char *new_array; if (a->size < 8) { new_size = 8; } else { if (GIT_MULTIPLY_SIZET_OVERFLOW(&new_size, a->size, 3)) goto on_oom; new_size /= 2; } if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL) goto on_oom; a->ptr = new_array; a->asize = new_size; return 0; on_oom: git_array_clear(*a); return -1; } #define git_array_alloc(a) \ (((a).size < (a).asize || git_array_grow(&(a), sizeof(*(a).ptr)) == 0) ? \ &(a).ptr[(a).size++] : (void *)NULL) #define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL) #define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : (void *)NULL) #define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : (void *)NULL) #define git_array_size(a) (a).size #define git_array_valid_index(a, i) ((i) < (a).size) #define git_array_foreach(a, i, element) \ for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++) GIT_INLINE(int) git_array__search( size_t *out, void *array_ptr, size_t item_size, size_t array_len, int (*compare)(const void *, const void *), const void *key) { size_t lim; unsigned char *part, *array = array_ptr, *base = array_ptr; int cmp = -1; for (lim = array_len; lim != 0; lim >>= 1) { part = base + (lim >> 1) * item_size; cmp = (*compare)(key, part); if (cmp == 0) { base = part; break; } if (cmp > 0) { /* key > p; take right partition */ base = part + 1 * item_size; lim--; } /* else take left partition */ } if (out) *out = (base - array) / item_size; return (cmp == 0) ? 0 : GIT_ENOTFOUND; } #define git_array_search(out, a, cmp, key) \ git_array__search(out, (a).ptr, sizeof(*(a).ptr), (a).size, \ (cmp), (key)) #endif git2r/src/libgit2/src/strnlen.h0000644000175000017500000000121314125111754016201 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_strlen_h__ #define INCLUDE_strlen_h__ #if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__) ||\ (defined(_MSC_VER) && _MSC_VER < 1500) # define NO_STRNLEN #endif #ifdef NO_STRNLEN GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) { const char *end = memchr(s, 0, maxlen); return end ? (size_t)(end - s) : maxlen; } #else # define p_strnlen strnlen #endif #endif git2r/src/libgit2/src/oid.c0000644000175000017500000002300514125111754015265 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "oid.h" #include "git2/oid.h" #include "repository.h" #include "threadstate.h" #include #include static char to_hex[] = "0123456789abcdef"; static int oid_error_invalid(const char *msg) { git_error_set(GIT_ERROR_INVALID, "unable to parse OID - %s", msg); return -1; } int git_oid_fromstrn(git_oid *out, const char *str, size_t length) { size_t p; int v; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(str); if (!length) return oid_error_invalid("too short"); if (length > GIT_OID_HEXSZ) return oid_error_invalid("too long"); memset(out->id, 0, GIT_OID_RAWSZ); for (p = 0; p < length; p++) { v = git__fromhex(str[p]); if (v < 0) return oid_error_invalid("contains invalid characters"); out->id[p / 2] |= (unsigned char)(v << (p % 2 ? 0 : 4)); } return 0; } int git_oid_fromstrp(git_oid *out, const char *str) { return git_oid_fromstrn(out, str, strlen(str)); } int git_oid_fromstr(git_oid *out, const char *str) { return git_oid_fromstrn(out, str, GIT_OID_HEXSZ); } GIT_INLINE(char) *fmt_one(char *str, unsigned int val) { *str++ = to_hex[val >> 4]; *str++ = to_hex[val & 0xf]; return str; } int git_oid_nfmt(char *str, size_t n, const git_oid *oid) { size_t i, max_i; if (!oid) { memset(str, 0, n); return 0; } if (n > GIT_OID_HEXSZ) { memset(&str[GIT_OID_HEXSZ], 0, n - GIT_OID_HEXSZ); n = GIT_OID_HEXSZ; } max_i = n / 2; for (i = 0; i < max_i; i++) str = fmt_one(str, oid->id[i]); if (n & 1) *str++ = to_hex[oid->id[i] >> 4]; return 0; } int git_oid_fmt(char *str, const git_oid *oid) { return git_oid_nfmt(str, GIT_OID_HEXSZ, oid); } int git_oid_pathfmt(char *str, const git_oid *oid) { size_t i; str = fmt_one(str, oid->id[0]); *str++ = '/'; for (i = 1; i < sizeof(oid->id); i++) str = fmt_one(str, oid->id[i]); return 0; } char *git_oid_tostr_s(const git_oid *oid) { char *str = GIT_THREADSTATE->oid_fmt; git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid); return str; } char *git_oid_allocfmt(const git_oid *oid) { char *str = git__malloc(GIT_OID_HEXSZ + 1); if (!str) return NULL; git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid); return str; } char *git_oid_tostr(char *out, size_t n, const git_oid *oid) { if (!out || n == 0) return ""; if (n > GIT_OID_HEXSZ + 1) n = GIT_OID_HEXSZ + 1; git_oid_nfmt(out, n - 1, oid); /* allow room for terminating NUL */ out[n - 1] = '\0'; return out; } int git_oid__parse( git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header) { const size_t sha_len = GIT_OID_HEXSZ; const size_t header_len = strlen(header); const char *buffer = *buffer_out; if (buffer + (header_len + sha_len + 1) > buffer_end) return -1; if (memcmp(buffer, header, header_len) != 0) return -1; if (buffer[header_len + sha_len] != '\n') return -1; if (git_oid_fromstr(oid, buffer + header_len) < 0) return -1; *buffer_out = buffer + (header_len + sha_len + 1); return 0; } void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid) { char hex_oid[GIT_OID_HEXSZ]; git_oid_fmt(hex_oid, oid); git_buf_puts(buf, header); git_buf_put(buf, hex_oid, GIT_OID_HEXSZ); git_buf_putc(buf, '\n'); } int git_oid_fromraw(git_oid *out, const unsigned char *raw) { memcpy(out->id, raw, sizeof(out->id)); return 0; } int git_oid_cpy(git_oid *out, const git_oid *src) { memcpy(out->id, src->id, sizeof(out->id)); return 0; } int git_oid_cmp(const git_oid *a, const git_oid *b) { return git_oid__cmp(a, b); } int git_oid_equal(const git_oid *a, const git_oid *b) { return (git_oid__cmp(a, b) == 0); } int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) { const unsigned char *a = oid_a->id; const unsigned char *b = oid_b->id; if (len > GIT_OID_HEXSZ) len = GIT_OID_HEXSZ; while (len > 1) { if (*a != *b) return 1; a++; b++; len -= 2; }; if (len) if ((*a ^ *b) & 0xf0) return 1; return 0; } int git_oid_strcmp(const git_oid *oid_a, const char *str) { const unsigned char *a; unsigned char strval; int hexval; for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) { if ((hexval = git__fromhex(*str++)) < 0) return -1; strval = (unsigned char)(hexval << 4); if (*str) { if ((hexval = git__fromhex(*str++)) < 0) return -1; strval |= hexval; } if (*a != strval) return (*a - strval); } return 0; } int git_oid_streq(const git_oid *oid_a, const char *str) { return git_oid_strcmp(oid_a, str) == 0 ? 0 : -1; } int git_oid_is_zero(const git_oid *oid_a) { const unsigned char *a = oid_a->id; unsigned int i; for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a) if (*a != 0) return 0; return 1; } #ifndef GIT_DEPRECATE_HARD int git_oid_iszero(const git_oid *oid_a) { return git_oid_is_zero(oid_a); } #endif typedef short node_index; typedef union { const char *tail; node_index children[16]; } trie_node; struct git_oid_shorten { trie_node *nodes; size_t node_count, size; int min_length, full; }; static int resize_trie(git_oid_shorten *self, size_t new_size) { self->nodes = git__reallocarray(self->nodes, new_size, sizeof(trie_node)); GIT_ERROR_CHECK_ALLOC(self->nodes); if (new_size > self->size) { memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node)); } self->size = new_size; return 0; } static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid) { trie_node *node, *leaf; node_index idx_leaf; if (os->node_count >= os->size) { if (resize_trie(os, os->size * 2) < 0) return NULL; } idx_leaf = (node_index)os->node_count++; if (os->node_count == SHRT_MAX) { os->full = 1; return NULL; } node = &os->nodes[idx]; node->children[push_at] = -idx_leaf; leaf = &os->nodes[idx_leaf]; leaf->tail = oid; return node; } git_oid_shorten *git_oid_shorten_new(size_t min_length) { git_oid_shorten *os; GIT_ASSERT_ARG_WITH_RETVAL((size_t)((int)min_length) == min_length, NULL); os = git__calloc(1, sizeof(git_oid_shorten)); if (os == NULL) return NULL; if (resize_trie(os, 16) < 0) { git__free(os); return NULL; } os->node_count = 1; os->min_length = (int)min_length; return os; } void git_oid_shorten_free(git_oid_shorten *os) { if (os == NULL) return; git__free(os->nodes); git__free(os); } /* * What wizardry is this? * * This is just a memory-optimized trie: basically a very fancy * 16-ary tree, which is used to store the prefixes of the OID * strings. * * Read more: http://en.wikipedia.org/wiki/Trie * * Magic that happens in this method: * * - Each node in the trie is an union, so it can work both as * a normal node, or as a leaf. * * - Each normal node points to 16 children (one for each possible * character in the oid). This is *not* stored in an array of * pointers, because in a 64-bit arch this would be sucking * 16*sizeof(void*) = 128 bytes of memory per node, which is * insane. What we do is store Node Indexes, and use these indexes * to look up each node in the om->index array. These indexes are * signed shorts, so this limits the amount of unique OIDs that * fit in the structure to about 20000 (assuming a more or less uniform * distribution). * * - All the nodes in om->index array are stored contiguously in * memory, and each of them is 32 bytes, so we fit 2x nodes per * cache line. Convenient for speed. * * - To differentiate the leafs from the normal nodes, we store all * the indexes towards a leaf as a negative index (indexes to normal * nodes are positives). When we find that one of the children for * a node has a negative value, that means it's going to be a leaf. * This reduces the amount of indexes we have by two, but also reduces * the size of each node by 1-4 bytes (the amount we would need to * add a `is_leaf` field): this is good because it allows the nodes * to fit cleanly in cache lines. * * - Once we reach an empty children, instead of continuing to insert * new nodes for each remaining character of the OID, we store a pointer * to the tail in the leaf; if the leaf is reached again, we turn it * into a normal node and use the tail to create a new leaf. * * This is a pretty good balance between performance and memory usage. */ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) { int i; bool is_leaf; node_index idx; if (os->full) { git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full"); return -1; } if (text_oid == NULL) return os->min_length; idx = 0; is_leaf = false; for (i = 0; i < GIT_OID_HEXSZ; ++i) { int c = git__fromhex(text_oid[i]); trie_node *node; if (c == -1) { git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - invalid hex value"); return -1; } node = &os->nodes[idx]; if (is_leaf) { const char *tail; tail = node->tail; node->tail = NULL; node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]); if (node == NULL) { if (os->full) git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full"); return -1; } } if (node->children[c] == 0) { if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) { if (os->full) git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full"); return -1; } break; } idx = node->children[c]; is_leaf = false; if (idx < 0) { node->children[c] = idx = -idx; is_leaf = true; } } if (++i > os->min_length) os->min_length = i; return os->min_length; } git2r/src/libgit2/src/pack-objects.c0000644000175000017500000012221014125111754017055 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "pack-objects.h" #include "zstream.h" #include "delta.h" #include "iterator.h" #include "netops.h" #include "pack.h" #include "thread.h" #include "tree.h" #include "util.h" #include "revwalk.h" #include "commit_list.h" #include "git2/pack.h" #include "git2/commit.h" #include "git2/tag.h" #include "git2/indexer.h" #include "git2/config.h" struct unpacked { git_pobject *object; void *data; struct git_delta_index *index; size_t depth; }; struct tree_walk_context { git_packbuilder *pb; git_buf buf; }; struct pack_write_context { git_indexer *indexer; git_indexer_progress *stats; }; struct walk_object { git_oid id; unsigned int uninteresting:1, seen:1; }; #ifdef GIT_THREADS # define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) git_mutex_##op(&(pb)->mtx) #else # define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) git__noop() #endif #define git_packbuilder__cache_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, lock) #define git_packbuilder__cache_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, unlock) #define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock) #define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock) /* The minimal interval between progress updates (in seconds). */ #define MIN_PROGRESS_UPDATE_INTERVAL 0.5 /* Size of the buffer to feed to zlib */ #define COMPRESS_BUFLEN (1024 * 1024) static unsigned name_hash(const char *name) { unsigned c, hash = 0; if (!name) return 0; /* * This effectively just creates a sortable number from the * last sixteen non-whitespace characters. Last characters * count "most", so things that end in ".c" sort together. */ while ((c = *name++) != 0) { if (git__isspace(c)) continue; hash = (hash >> 2) + (c << 24); } return hash; } static int packbuilder_config(git_packbuilder *pb) { git_config *config; int ret = 0; int64_t val; if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0) return ret; #define config_get(KEY,DST,DFLT) do { \ ret = git_config_get_int64(&val, config, KEY); \ if (!ret) { \ if (!git__is_sizet(val)) { \ git_error_set(GIT_ERROR_CONFIG, \ "configuration value '%s' is too large", KEY); \ ret = -1; \ goto out; \ } \ (DST) = (size_t)val; \ } else if (ret == GIT_ENOTFOUND) { \ (DST) = (DFLT); \ ret = 0; \ } else if (ret < 0) goto out; } while (0) config_get("pack.deltaCacheSize", pb->max_delta_cache_size, GIT_PACK_DELTA_CACHE_SIZE); config_get("pack.deltaCacheLimit", pb->cache_max_small_delta_size, GIT_PACK_DELTA_CACHE_LIMIT); config_get("pack.deltaCacheSize", pb->big_file_threshold, GIT_PACK_BIG_FILE_THRESHOLD); config_get("pack.windowMemory", pb->window_memory_limit, 0); #undef config_get out: git_config_free(config); return ret; } int git_packbuilder_new(git_packbuilder **out, git_repository *repo) { git_packbuilder *pb; *out = NULL; pb = git__calloc(1, sizeof(*pb)); GIT_ERROR_CHECK_ALLOC(pb); if (git_oidmap_new(&pb->object_ix) < 0 || git_oidmap_new(&pb->walk_objects) < 0 || git_pool_init(&pb->object_pool, sizeof(struct walk_object)) < 0) goto on_error; pb->repo = repo; pb->nr_threads = 1; /* do not spawn any thread by default */ if (git_hash_ctx_init(&pb->ctx) < 0 || git_zstream_init(&pb->zstream, GIT_ZSTREAM_DEFLATE) < 0 || git_repository_odb(&pb->odb, repo) < 0 || packbuilder_config(pb) < 0) goto on_error; #ifdef GIT_THREADS if (git_mutex_init(&pb->cache_mutex) || git_mutex_init(&pb->progress_mutex) || git_cond_init(&pb->progress_cond)) { git_error_set(GIT_ERROR_OS, "failed to initialize packbuilder mutex"); goto on_error; } #endif *out = pb; return 0; on_error: git_packbuilder_free(pb); return -1; } unsigned int git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n) { GIT_ASSERT_ARG(pb); #ifdef GIT_THREADS pb->nr_threads = n; #else GIT_UNUSED(n); GIT_ASSERT(pb->nr_threads == 1); #endif return pb->nr_threads; } static int rehash(git_packbuilder *pb) { git_pobject *po; size_t i; git_oidmap_clear(pb->object_ix); for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) { if (git_oidmap_set(pb->object_ix, &po->id, po) < 0) return -1; } return 0; } int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, const char *name) { git_pobject *po; size_t newsize; int ret; GIT_ASSERT_ARG(pb); GIT_ASSERT_ARG(oid); /* If the object already exists in the hash table, then we don't * have any work to do */ if (git_oidmap_exists(pb->object_ix, oid)) return 0; if (pb->nr_objects >= pb->nr_alloc) { GIT_ERROR_CHECK_ALLOC_ADD(&newsize, pb->nr_alloc, 1024); GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newsize, newsize / 2, 3); if (!git__is_uint32(newsize)) { git_error_set(GIT_ERROR_NOMEMORY, "packfile too large to fit in memory."); return -1; } pb->nr_alloc = newsize; pb->object_list = git__reallocarray(pb->object_list, pb->nr_alloc, sizeof(*po)); GIT_ERROR_CHECK_ALLOC(pb->object_list); if (rehash(pb) < 0) return -1; } po = pb->object_list + pb->nr_objects; memset(po, 0x0, sizeof(*po)); if ((ret = git_odb_read_header(&po->size, &po->type, pb->odb, oid)) < 0) return ret; pb->nr_objects++; git_oid_cpy(&po->id, oid); po->hash = name_hash(name); if (git_oidmap_set(pb->object_ix, &po->id, po) < 0) { git_error_set_oom(); return -1; } pb->done = false; if (pb->progress_cb) { double current_time = git__timer(); double elapsed = current_time - pb->last_progress_report_time; if (elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { pb->last_progress_report_time = current_time; ret = pb->progress_cb( GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload); if (ret) return git_error_set_after_callback(ret); } } return 0; } static int get_delta(void **out, git_odb *odb, git_pobject *po) { git_odb_object *src = NULL, *trg = NULL; size_t delta_size; void *delta_buf; int error; *out = NULL; if (git_odb_read(&src, odb, &po->delta->id) < 0 || git_odb_read(&trg, odb, &po->id) < 0) goto on_error; error = git_delta(&delta_buf, &delta_size, git_odb_object_data(src), git_odb_object_size(src), git_odb_object_data(trg), git_odb_object_size(trg), 0); if (error < 0 && error != GIT_EBUFS) goto on_error; if (error == GIT_EBUFS || delta_size != po->delta_size) { git_error_set(GIT_ERROR_INVALID, "delta size changed"); goto on_error; } *out = delta_buf; git_odb_object_free(src); git_odb_object_free(trg); return 0; on_error: git_odb_object_free(src); git_odb_object_free(trg); return -1; } static int write_object( git_packbuilder *pb, git_pobject *po, int (*write_cb)(void *buf, size_t size, void *cb_data), void *cb_data) { git_odb_object *obj = NULL; git_object_t type; unsigned char hdr[10], *zbuf = NULL; void *data = NULL; size_t hdr_len, zbuf_len = COMPRESS_BUFLEN, data_len; int error; /* * If we have a delta base, let's use the delta to save space. * Otherwise load the whole object. 'data' ends up pointing to * whatever data we want to put into the packfile. */ if (po->delta) { if (po->delta_data) data = po->delta_data; else if ((error = get_delta(&data, pb->odb, po)) < 0) goto done; data_len = po->delta_size; type = GIT_OBJECT_REF_DELTA; } else { if ((error = git_odb_read(&obj, pb->odb, &po->id)) < 0) goto done; data = (void *)git_odb_object_data(obj); data_len = git_odb_object_size(obj); type = git_odb_object_type(obj); } /* Write header */ if ((error = git_packfile__object_header(&hdr_len, hdr, data_len, type)) < 0 || (error = write_cb(hdr, hdr_len, cb_data)) < 0 || (error = git_hash_update(&pb->ctx, hdr, hdr_len)) < 0) goto done; if (type == GIT_OBJECT_REF_DELTA) { if ((error = write_cb(po->delta->id.id, GIT_OID_RAWSZ, cb_data)) < 0 || (error = git_hash_update(&pb->ctx, po->delta->id.id, GIT_OID_RAWSZ)) < 0) goto done; } /* Write data */ if (po->z_delta_size) { data_len = po->z_delta_size; if ((error = write_cb(data, data_len, cb_data)) < 0 || (error = git_hash_update(&pb->ctx, data, data_len)) < 0) goto done; } else { zbuf = git__malloc(zbuf_len); GIT_ERROR_CHECK_ALLOC(zbuf); git_zstream_reset(&pb->zstream); if ((error = git_zstream_set_input(&pb->zstream, data, data_len)) < 0) goto done; while (!git_zstream_done(&pb->zstream)) { if ((error = git_zstream_get_output(zbuf, &zbuf_len, &pb->zstream)) < 0 || (error = write_cb(zbuf, zbuf_len, cb_data)) < 0 || (error = git_hash_update(&pb->ctx, zbuf, zbuf_len)) < 0) goto done; zbuf_len = COMPRESS_BUFLEN; /* reuse buffer */ } } /* * If po->delta is true, data is a delta and it is our * responsibility to free it (otherwise it's a git_object's * data). We set po->delta_data to NULL in case we got the * data from there instead of get_delta(). If we didn't, * there's no harm. */ if (po->delta) { git__free(data); po->delta_data = NULL; } pb->nr_written++; done: git__free(zbuf); git_odb_object_free(obj); return error; } enum write_one_status { WRITE_ONE_SKIP = -1, /* already written */ WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */ WRITE_ONE_WRITTEN = 1, /* normal */ WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */ }; static int write_one( enum write_one_status *status, git_packbuilder *pb, git_pobject *po, int (*write_cb)(void *buf, size_t size, void *cb_data), void *cb_data) { int error; if (po->recursing) { *status = WRITE_ONE_RECURSIVE; return 0; } else if (po->written) { *status = WRITE_ONE_SKIP; return 0; } if (po->delta) { po->recursing = 1; if ((error = write_one(status, pb, po->delta, write_cb, cb_data)) < 0) return error; /* we cannot depend on this one */ if (*status == WRITE_ONE_RECURSIVE) po->delta = NULL; } *status = WRITE_ONE_WRITTEN; po->written = 1; po->recursing = 0; return write_object(pb, po, write_cb, cb_data); } GIT_INLINE(void) add_to_write_order(git_pobject **wo, size_t *endp, git_pobject *po) { if (po->filled) return; wo[(*endp)++] = po; po->filled = 1; } static void add_descendants_to_write_order(git_pobject **wo, size_t *endp, git_pobject *po) { int add_to_order = 1; while (po) { if (add_to_order) { git_pobject *s; /* add this node... */ add_to_write_order(wo, endp, po); /* all its siblings... */ for (s = po->delta_sibling; s; s = s->delta_sibling) { add_to_write_order(wo, endp, s); } } /* drop down a level to add left subtree nodes if possible */ if (po->delta_child) { add_to_order = 1; po = po->delta_child; } else { add_to_order = 0; /* our sibling might have some children, it is next */ if (po->delta_sibling) { po = po->delta_sibling; continue; } /* go back to our parent node */ po = po->delta; while (po && !po->delta_sibling) { /* we're on the right side of a subtree, keep * going up until we can go right again */ po = po->delta; } if (!po) { /* done- we hit our original root node */ return; } /* pass it off to sibling at this level */ po = po->delta_sibling; } }; } static void add_family_to_write_order(git_pobject **wo, size_t *endp, git_pobject *po) { git_pobject *root; for (root = po; root->delta; root = root->delta) ; /* nothing */ add_descendants_to_write_order(wo, endp, root); } static int cb_tag_foreach(const char *name, git_oid *oid, void *data) { git_packbuilder *pb = data; git_pobject *po; GIT_UNUSED(name); if ((po = git_oidmap_get(pb->object_ix, oid)) == NULL) return 0; po->tagged = 1; /* TODO: peel objects */ return 0; } static int compute_write_order(git_pobject ***out, git_packbuilder *pb) { size_t i, wo_end, last_untagged; git_pobject **wo; *out = NULL; if (!pb->nr_objects) return 0; if ((wo = git__mallocarray(pb->nr_objects, sizeof(*wo))) == NULL) return -1; for (i = 0; i < pb->nr_objects; i++) { git_pobject *po = pb->object_list + i; po->tagged = 0; po->filled = 0; po->delta_child = NULL; po->delta_sibling = NULL; } /* * Fully connect delta_child/delta_sibling network. * Make sure delta_sibling is sorted in the original * recency order. */ for (i = pb->nr_objects; i > 0;) { git_pobject *po = &pb->object_list[--i]; if (!po->delta) continue; /* Mark me as the first child */ po->delta_sibling = po->delta->delta_child; po->delta->delta_child = po; } /* * Mark objects that are at the tip of tags. */ if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) { git__free(wo); return -1; } /* * Give the objects in the original recency order until * we see a tagged tip. */ for (i = wo_end = 0; i < pb->nr_objects; i++) { git_pobject *po = pb->object_list + i; if (po->tagged) break; add_to_write_order(wo, &wo_end, po); } last_untagged = i; /* * Then fill all the tagged tips. */ for (; i < pb->nr_objects; i++) { git_pobject *po = pb->object_list + i; if (po->tagged) add_to_write_order(wo, &wo_end, po); } /* * And then all remaining commits and tags. */ for (i = last_untagged; i < pb->nr_objects; i++) { git_pobject *po = pb->object_list + i; if (po->type != GIT_OBJECT_COMMIT && po->type != GIT_OBJECT_TAG) continue; add_to_write_order(wo, &wo_end, po); } /* * And then all the trees. */ for (i = last_untagged; i < pb->nr_objects; i++) { git_pobject *po = pb->object_list + i; if (po->type != GIT_OBJECT_TREE) continue; add_to_write_order(wo, &wo_end, po); } /* * Finally all the rest in really tight order */ for (i = last_untagged; i < pb->nr_objects; i++) { git_pobject *po = pb->object_list + i; if (!po->filled) add_family_to_write_order(wo, &wo_end, po); } if (wo_end != pb->nr_objects) { git__free(wo); git_error_set(GIT_ERROR_INVALID, "invalid write order"); return -1; } *out = wo; return 0; } static int write_pack(git_packbuilder *pb, int (*write_cb)(void *buf, size_t size, void *cb_data), void *cb_data) { git_pobject **write_order; git_pobject *po; enum write_one_status status; struct git_pack_header ph; git_oid entry_oid; size_t i = 0; int error; if ((error = compute_write_order(&write_order, pb)) < 0) return error; if (!git__is_uint32(pb->nr_objects)) { git_error_set(GIT_ERROR_INVALID, "too many objects"); error = -1; goto done; } /* Write pack header */ ph.hdr_signature = htonl(PACK_SIGNATURE); ph.hdr_version = htonl(PACK_VERSION); ph.hdr_entries = htonl(pb->nr_objects); if ((error = write_cb(&ph, sizeof(ph), cb_data)) < 0 || (error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0) goto done; pb->nr_remaining = pb->nr_objects; do { pb->nr_written = 0; for ( ; i < pb->nr_objects; ++i) { po = write_order[i]; if ((error = write_one(&status, pb, po, write_cb, cb_data)) < 0) goto done; } pb->nr_remaining -= pb->nr_written; } while (pb->nr_remaining && i < pb->nr_objects); if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0) goto done; error = write_cb(entry_oid.id, GIT_OID_RAWSZ, cb_data); done: /* if callback cancelled writing, we must still free delta_data */ for ( ; i < pb->nr_objects; ++i) { po = write_order[i]; if (po->delta_data) { git__free(po->delta_data); po->delta_data = NULL; } } git__free(write_order); return error; } static int write_pack_buf(void *buf, size_t size, void *data) { git_buf *b = (git_buf *)data; return git_buf_put(b, buf, size); } static int type_size_sort(const void *_a, const void *_b) { const git_pobject *a = (git_pobject *)_a; const git_pobject *b = (git_pobject *)_b; if (a->type > b->type) return -1; if (a->type < b->type) return 1; if (a->hash > b->hash) return -1; if (a->hash < b->hash) return 1; /* * TODO * if (a->preferred_base > b->preferred_base) return -1; if (a->preferred_base < b->preferred_base) return 1; */ if (a->size > b->size) return -1; if (a->size < b->size) return 1; return a < b ? -1 : (a > b); /* newest first */ } static int delta_cacheable( git_packbuilder *pb, size_t src_size, size_t trg_size, size_t delta_size) { size_t new_size; if (git__add_sizet_overflow(&new_size, pb->delta_cache_size, delta_size)) return 0; if (pb->max_delta_cache_size && new_size > pb->max_delta_cache_size) return 0; if (delta_size < pb->cache_max_small_delta_size) return 1; /* cache delta, if objects are large enough compared to delta size */ if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10)) return 1; return 0; } static int try_delta(git_packbuilder *pb, struct unpacked *trg, struct unpacked *src, size_t max_depth, size_t *mem_usage, int *ret) { git_pobject *trg_object = trg->object; git_pobject *src_object = src->object; git_odb_object *obj; size_t trg_size, src_size, delta_size, sizediff, max_size, sz; size_t ref_depth; void *delta_buf; /* Don't bother doing diffs between different types */ if (trg_object->type != src_object->type) { *ret = -1; return 0; } *ret = 0; /* TODO: support reuse-delta */ /* Let's not bust the allowed depth. */ if (src->depth >= max_depth) return 0; /* Now some size filtering heuristics. */ trg_size = trg_object->size; if (!trg_object->delta) { max_size = trg_size/2 - 20; ref_depth = 1; } else { max_size = trg_object->delta_size; ref_depth = trg->depth; } max_size = (uint64_t)max_size * (max_depth - src->depth) / (max_depth - ref_depth + 1); if (max_size == 0) return 0; src_size = src_object->size; sizediff = src_size < trg_size ? trg_size - src_size : 0; if (sizediff >= max_size) return 0; if (trg_size < src_size / 32) return 0; /* Load data if not already done */ if (!trg->data) { if (git_odb_read(&obj, pb->odb, &trg_object->id) < 0) return -1; sz = git_odb_object_size(obj); trg->data = git__malloc(sz); GIT_ERROR_CHECK_ALLOC(trg->data); memcpy(trg->data, git_odb_object_data(obj), sz); git_odb_object_free(obj); if (sz != trg_size) { git_error_set(GIT_ERROR_INVALID, "inconsistent target object length"); return -1; } *mem_usage += sz; } if (!src->data) { size_t obj_sz; if (git_odb_read(&obj, pb->odb, &src_object->id) < 0 || !git__is_ulong(obj_sz = git_odb_object_size(obj))) return -1; sz = obj_sz; src->data = git__malloc(sz); GIT_ERROR_CHECK_ALLOC(src->data); memcpy(src->data, git_odb_object_data(obj), sz); git_odb_object_free(obj); if (sz != src_size) { git_error_set(GIT_ERROR_INVALID, "inconsistent source object length"); return -1; } *mem_usage += sz; } if (!src->index) { if (git_delta_index_init(&src->index, src->data, src_size) < 0) return 0; /* suboptimal pack - out of memory */ *mem_usage += git_delta_index_size(src->index); } if (git_delta_create_from_index(&delta_buf, &delta_size, src->index, trg->data, trg_size, max_size) < 0) return 0; if (trg_object->delta) { /* Prefer only shallower same-sized deltas. */ if (delta_size == trg_object->delta_size && src->depth + 1 >= trg->depth) { git__free(delta_buf); return 0; } } GIT_ASSERT(git_packbuilder__cache_lock(pb) == 0); if (trg_object->delta_data) { git__free(trg_object->delta_data); GIT_ASSERT(pb->delta_cache_size >= trg_object->delta_size); pb->delta_cache_size -= trg_object->delta_size; trg_object->delta_data = NULL; } if (delta_cacheable(pb, src_size, trg_size, delta_size)) { bool overflow = git__add_sizet_overflow( &pb->delta_cache_size, pb->delta_cache_size, delta_size); GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0); if (overflow) { git__free(delta_buf); return -1; } trg_object->delta_data = git__realloc(delta_buf, delta_size); GIT_ERROR_CHECK_ALLOC(trg_object->delta_data); } else { /* create delta when writing the pack */ GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0); git__free(delta_buf); } trg_object->delta = src_object; trg_object->delta_size = delta_size; trg->depth = src->depth + 1; *ret = 1; return 0; } static size_t check_delta_limit(git_pobject *me, size_t n) { git_pobject *child = me->delta_child; size_t m = n; while (child) { size_t c = check_delta_limit(child, n + 1); if (m < c) m = c; child = child->delta_sibling; } return m; } static size_t free_unpacked(struct unpacked *n) { size_t freed_mem = 0; if (n->index) { freed_mem += git_delta_index_size(n->index); git_delta_index_free(n->index); } n->index = NULL; if (n->data) { freed_mem += n->object->size; git__free(n->data); n->data = NULL; } n->object = NULL; n->depth = 0; return freed_mem; } static int report_delta_progress( git_packbuilder *pb, uint32_t count, bool force) { int ret; if (pb->progress_cb) { double current_time = git__timer(); double elapsed = current_time - pb->last_progress_report_time; if (force || elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { pb->last_progress_report_time = current_time; ret = pb->progress_cb( GIT_PACKBUILDER_DELTAFICATION, count, pb->nr_objects, pb->progress_cb_payload); if (ret) return git_error_set_after_callback(ret); } } return 0; } static int find_deltas(git_packbuilder *pb, git_pobject **list, size_t *list_size, size_t window, size_t depth) { git_pobject *po; git_buf zbuf = GIT_BUF_INIT; struct unpacked *array; size_t idx = 0, count = 0; size_t mem_usage = 0; size_t i; int error = -1; array = git__calloc(window, sizeof(struct unpacked)); GIT_ERROR_CHECK_ALLOC(array); for (;;) { struct unpacked *n = array + idx; size_t max_depth, j, best_base = SIZE_MAX; GIT_ASSERT(git_packbuilder__progress_lock(pb) == 0); if (!*list_size) { GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0); break; } pb->nr_deltified += 1; report_delta_progress(pb, pb->nr_deltified, false); po = *list++; (*list_size)--; GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0); mem_usage -= free_unpacked(n); n->object = po; while (pb->window_memory_limit && mem_usage > pb->window_memory_limit && count > 1) { size_t tail = (idx + window - count) % window; mem_usage -= free_unpacked(array + tail); count--; } /* * If the current object is at pack edge, take the depth the * objects that depend on the current object into account * otherwise they would become too deep. */ max_depth = depth; if (po->delta_child) { size_t delta_limit = check_delta_limit(po, 0); if (delta_limit > max_depth) goto next; max_depth -= delta_limit; } j = window; while (--j > 0) { int ret; size_t other_idx = idx + j; struct unpacked *m; if (other_idx >= window) other_idx -= window; m = array + other_idx; if (!m->object) break; if (try_delta(pb, n, m, max_depth, &mem_usage, &ret) < 0) goto on_error; if (ret < 0) break; else if (ret > 0) best_base = other_idx; } /* * If we decided to cache the delta data, then it is best * to compress it right away. First because we have to do * it anyway, and doing it here while we're threaded will * save a lot of time in the non threaded write phase, * as well as allow for caching more deltas within * the same cache size limit. * ... * But only if not writing to stdout, since in that case * the network is most likely throttling writes anyway, * and therefore it is best to go to the write phase ASAP * instead, as we can afford spending more time compressing * between writes at that moment. */ if (po->delta_data) { if (git_zstream_deflatebuf(&zbuf, po->delta_data, po->delta_size) < 0) goto on_error; git__free(po->delta_data); po->delta_data = git__malloc(zbuf.size); GIT_ERROR_CHECK_ALLOC(po->delta_data); memcpy(po->delta_data, zbuf.ptr, zbuf.size); po->z_delta_size = zbuf.size; git_buf_clear(&zbuf); GIT_ASSERT(git_packbuilder__cache_lock(pb) == 0); pb->delta_cache_size -= po->delta_size; pb->delta_cache_size += po->z_delta_size; GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0); } /* * If we made n a delta, and if n is already at max * depth, leaving it in the window is pointless. we * should evict it first. */ if (po->delta && max_depth <= n->depth) continue; /* * Move the best delta base up in the window, after the * currently deltified object, to keep it longer. It will * be the first base object to be attempted next. */ if (po->delta) { struct unpacked swap = array[best_base]; size_t dist = (window + idx - best_base) % window; size_t dst = best_base; while (dist--) { size_t src = (dst + 1) % window; array[dst] = array[src]; dst = src; } array[dst] = swap; } next: idx++; if (count + 1 < window) count++; if (idx >= window) idx = 0; } error = 0; on_error: for (i = 0; i < window; ++i) { git__free(array[i].index); git__free(array[i].data); } git__free(array); git_buf_dispose(&zbuf); return error; } #ifdef GIT_THREADS struct thread_params { git_thread thread; git_packbuilder *pb; git_pobject **list; git_cond cond; git_mutex mutex; size_t list_size; size_t remaining; size_t window; size_t depth; size_t working; size_t data_ready; }; static void *threaded_find_deltas(void *arg) { struct thread_params *me = arg; while (me->remaining) { if (find_deltas(me->pb, me->list, &me->remaining, me->window, me->depth) < 0) { ; /* TODO */ } GIT_ASSERT_WITH_RETVAL(git_packbuilder__progress_lock(me->pb) == 0, NULL); me->working = 0; git_cond_signal(&me->pb->progress_cond); GIT_ASSERT_WITH_RETVAL(git_packbuilder__progress_unlock(me->pb) == 0, NULL); if (git_mutex_lock(&me->mutex)) { git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex"); return NULL; } while (!me->data_ready) git_cond_wait(&me->cond, &me->mutex); /* * We must not set ->data_ready before we wait on the * condition because the main thread may have set it to 1 * before we get here. In order to be sure that new * work is available if we see 1 in ->data_ready, it * was initialized to 0 before this thread was spawned * and we reset it to 0 right away. */ me->data_ready = 0; git_mutex_unlock(&me->mutex); } /* leave ->working 1 so that this doesn't get more work assigned */ return NULL; } static int ll_find_deltas(git_packbuilder *pb, git_pobject **list, size_t list_size, size_t window, size_t depth) { struct thread_params *p; size_t i; int ret, active_threads = 0; if (!pb->nr_threads) pb->nr_threads = git__online_cpus(); if (pb->nr_threads <= 1) { find_deltas(pb, list, &list_size, window, depth); return 0; } p = git__mallocarray(pb->nr_threads, sizeof(*p)); GIT_ERROR_CHECK_ALLOC(p); /* Partition the work among the threads */ for (i = 0; i < pb->nr_threads; ++i) { size_t sub_size = list_size / (pb->nr_threads - i); /* don't use too small segments or no deltas will be found */ if (sub_size < 2*window && i+1 < pb->nr_threads) sub_size = 0; p[i].pb = pb; p[i].window = window; p[i].depth = depth; p[i].working = 1; p[i].data_ready = 0; /* try to split chunks on "path" boundaries */ while (sub_size && sub_size < list_size && list[sub_size]->hash && list[sub_size]->hash == list[sub_size-1]->hash) sub_size++; p[i].list = list; p[i].list_size = sub_size; p[i].remaining = sub_size; list += sub_size; list_size -= sub_size; } /* Start work threads */ for (i = 0; i < pb->nr_threads; ++i) { if (!p[i].list_size) continue; git_mutex_init(&p[i].mutex); git_cond_init(&p[i].cond); ret = git_thread_create(&p[i].thread, threaded_find_deltas, &p[i]); if (ret) { git_error_set(GIT_ERROR_THREAD, "unable to create thread"); return -1; } active_threads++; } /* * Now let's wait for work completion. Each time a thread is done * with its work, we steal half of the remaining work from the * thread with the largest number of unprocessed objects and give * it to that newly idle thread. This ensure good load balancing * until the remaining object list segments are simply too short * to be worth splitting anymore. */ while (active_threads) { struct thread_params *target = NULL; struct thread_params *victim = NULL; size_t sub_size = 0; /* Start by locating a thread that has transitioned its * 'working' flag from 1 -> 0. This indicates that it is * ready to receive more work using our work-stealing * algorithm. */ GIT_ASSERT(git_packbuilder__progress_lock(pb) == 0); for (;;) { for (i = 0; !target && i < pb->nr_threads; i++) if (!p[i].working) target = &p[i]; if (target) break; git_cond_wait(&pb->progress_cond, &pb->progress_mutex); } /* At this point we hold the progress lock and have located * a thread to receive more work. We still need to locate a * thread from which to steal work (the victim). */ for (i = 0; i < pb->nr_threads; i++) if (p[i].remaining > 2*window && (!victim || victim->remaining < p[i].remaining)) victim = &p[i]; if (victim) { sub_size = victim->remaining / 2; list = victim->list + victim->list_size - sub_size; while (sub_size && list[0]->hash && list[0]->hash == list[-1]->hash) { list++; sub_size--; } if (!sub_size) { /* * It is possible for some "paths" to have * so many objects that no hash boundary * might be found. Let's just steal the * exact half in that case. */ sub_size = victim->remaining / 2; list -= sub_size; } target->list = list; victim->list_size -= sub_size; victim->remaining -= sub_size; } target->list_size = sub_size; target->remaining = sub_size; target->working = 1; GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0); if (git_mutex_lock(&target->mutex)) { git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex"); git__free(p); return -1; } target->data_ready = 1; git_cond_signal(&target->cond); git_mutex_unlock(&target->mutex); if (!sub_size) { git_thread_join(&target->thread, NULL); git_cond_free(&target->cond); git_mutex_free(&target->mutex); active_threads--; } } git__free(p); return 0; } #else #define ll_find_deltas(pb, l, ls, w, d) find_deltas(pb, l, &ls, w, d) #endif static int prepare_pack(git_packbuilder *pb) { git_pobject **delta_list; size_t i, n = 0; if (pb->nr_objects == 0 || pb->done) return 0; /* nothing to do */ /* * Although we do not report progress during deltafication, we * at least report that we are in the deltafication stage */ if (pb->progress_cb) pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload); delta_list = git__mallocarray(pb->nr_objects, sizeof(*delta_list)); GIT_ERROR_CHECK_ALLOC(delta_list); for (i = 0; i < pb->nr_objects; ++i) { git_pobject *po = pb->object_list + i; /* Make sure the item is within our size limits */ if (po->size < 50 || po->size > pb->big_file_threshold) continue; delta_list[n++] = po; } if (n > 1) { git__tsort((void **)delta_list, n, type_size_sort); if (ll_find_deltas(pb, delta_list, n, GIT_PACK_WINDOW + 1, GIT_PACK_DEPTH) < 0) { git__free(delta_list); return -1; } } report_delta_progress(pb, pb->nr_objects, true); pb->done = true; git__free(delta_list); return 0; } #define PREPARE_PACK if (prepare_pack(pb) < 0) { return -1; } int git_packbuilder_foreach(git_packbuilder *pb, int (*cb)(void *buf, size_t size, void *payload), void *payload) { PREPARE_PACK; return write_pack(pb, cb, payload); } int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb) { int error; if ((error = git_buf_sanitize(buf)) < 0) return error; PREPARE_PACK; return write_pack(pb, &write_pack_buf, buf); } static int write_cb(void *buf, size_t len, void *payload) { struct pack_write_context *ctx = payload; return git_indexer_append(ctx->indexer, buf, len, ctx->stats); } int git_packbuilder_write( git_packbuilder *pb, const char *path, unsigned int mode, git_indexer_progress_cb progress_cb, void *progress_cb_payload) { int error = -1; git_buf object_path = GIT_BUF_INIT; git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT; git_indexer *indexer = NULL; git_indexer_progress stats; struct pack_write_context ctx; int t; PREPARE_PACK; if (path == NULL) { if ((error = git_repository_item_path(&object_path, pb->repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0) goto cleanup; if ((error = git_buf_joinpath(&object_path, git_buf_cstr(&object_path), "pack")) < 0) goto cleanup; path = git_buf_cstr(&object_path); } opts.progress_cb = progress_cb; opts.progress_cb_payload = progress_cb_payload; if ((error = git_indexer_new(&indexer, path, mode, pb->odb, &opts)) < 0) goto cleanup; if (!git_repository__configmap_lookup(&t, pb->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) git_indexer__set_fsync(indexer, 1); ctx.indexer = indexer; ctx.stats = &stats; if ((error = git_packbuilder_foreach(pb, write_cb, &ctx)) < 0) goto cleanup; if ((error = git_indexer_commit(indexer, &stats)) < 0) goto cleanup; git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer)); cleanup: git_indexer_free(indexer); git_buf_dispose(&object_path); return error; } #undef PREPARE_PACK const git_oid *git_packbuilder_hash(git_packbuilder *pb) { return &pb->pack_oid; } static int cb_tree_walk( const char *root, const git_tree_entry *entry, void *payload) { int error; struct tree_walk_context *ctx = payload; /* A commit inside a tree represents a submodule commit and should be skipped. */ if (git_tree_entry_type(entry) == GIT_OBJECT_COMMIT) return 0; if (!(error = git_buf_sets(&ctx->buf, root)) && !(error = git_buf_puts(&ctx->buf, git_tree_entry_name(entry)))) error = git_packbuilder_insert( ctx->pb, git_tree_entry_id(entry), git_buf_cstr(&ctx->buf)); return error; } int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) { git_commit *commit; if (git_commit_lookup(&commit, pb->repo, oid) < 0 || git_packbuilder_insert(pb, oid, NULL) < 0) return -1; if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0) return -1; git_commit_free(commit); return 0; } int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid) { int error; git_tree *tree = NULL; struct tree_walk_context context = { pb, GIT_BUF_INIT }; if (!(error = git_tree_lookup(&tree, pb->repo, oid)) && !(error = git_packbuilder_insert(pb, oid, NULL))) error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context); git_tree_free(tree); git_buf_dispose(&context.buf); return error; } int git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name) { git_object *obj; int error; GIT_ASSERT_ARG(pb); GIT_ASSERT_ARG(id); if ((error = git_object_lookup(&obj, pb->repo, id, GIT_OBJECT_ANY)) < 0) return error; switch (git_object_type(obj)) { case GIT_OBJECT_BLOB: error = git_packbuilder_insert(pb, id, name); break; case GIT_OBJECT_TREE: error = git_packbuilder_insert_tree(pb, id); break; case GIT_OBJECT_COMMIT: error = git_packbuilder_insert_commit(pb, id); break; case GIT_OBJECT_TAG: if ((error = git_packbuilder_insert(pb, id, name)) < 0) goto cleanup; error = git_packbuilder_insert_recur(pb, git_tag_target_id((git_tag *) obj), NULL); break; default: git_error_set(GIT_ERROR_INVALID, "unknown object type"); error = -1; } cleanup: git_object_free(obj); return error; } size_t git_packbuilder_object_count(git_packbuilder *pb) { return pb->nr_objects; } size_t git_packbuilder_written(git_packbuilder *pb) { return pb->nr_written; } static int lookup_walk_object(struct walk_object **out, git_packbuilder *pb, const git_oid *id) { struct walk_object *obj; obj = git_pool_mallocz(&pb->object_pool, 1); if (!obj) { git_error_set_oom(); return -1; } git_oid_cpy(&obj->id, id); *out = obj; return 0; } static int retrieve_object(struct walk_object **out, git_packbuilder *pb, const git_oid *id) { struct walk_object *obj; int error; if ((obj = git_oidmap_get(pb->walk_objects, id)) == NULL) { if ((error = lookup_walk_object(&obj, pb, id)) < 0) return error; if ((error = git_oidmap_set(pb->walk_objects, &obj->id, obj)) < 0) return error; } *out = obj; return 0; } static int mark_blob_uninteresting(git_packbuilder *pb, const git_oid *id) { int error; struct walk_object *obj; if ((error = retrieve_object(&obj, pb, id)) < 0) return error; obj->uninteresting = 1; return 0; } static int mark_tree_uninteresting(git_packbuilder *pb, const git_oid *id) { struct walk_object *obj; git_tree *tree; int error; size_t i; if ((error = retrieve_object(&obj, pb, id)) < 0) return error; if (obj->uninteresting) return 0; obj->uninteresting = 1; if ((error = git_tree_lookup(&tree, pb->repo, id)) < 0) return error; for (i = 0; i < git_tree_entrycount(tree); i++) { const git_tree_entry *entry = git_tree_entry_byindex(tree, i); const git_oid *entry_id = git_tree_entry_id(entry); switch (git_tree_entry_type(entry)) { case GIT_OBJECT_TREE: if ((error = mark_tree_uninteresting(pb, entry_id)) < 0) goto cleanup; break; case GIT_OBJECT_BLOB: if ((error = mark_blob_uninteresting(pb, entry_id)) < 0) goto cleanup; break; default: /* it's a submodule or something unknown, we don't want it */ ; } } cleanup: git_tree_free(tree); return error; } /* * Mark the edges of the graph uninteresting. Since we start from a * git_revwalk, the commits are already uninteresting, but we need to * mark the trees and blobs. */ static int mark_edges_uninteresting(git_packbuilder *pb, git_commit_list *commits) { int error; git_commit_list *list; git_commit *commit; for (list = commits; list; list = list->next) { if (!list->item->uninteresting) continue; if ((error = git_commit_lookup(&commit, pb->repo, &list->item->oid)) < 0) return error; error = mark_tree_uninteresting(pb, git_commit_tree_id(commit)); git_commit_free(commit); if (error < 0) return error; } return 0; } static int pack_objects_insert_tree(git_packbuilder *pb, git_tree *tree) { size_t i; int error; git_tree *subtree; struct walk_object *obj; const char *name; if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0) return error; if (obj->seen || obj->uninteresting) return 0; obj->seen = 1; if ((error = git_packbuilder_insert(pb, &obj->id, NULL))) return error; for (i = 0; i < git_tree_entrycount(tree); i++) { const git_tree_entry *entry = git_tree_entry_byindex(tree, i); const git_oid *entry_id = git_tree_entry_id(entry); switch (git_tree_entry_type(entry)) { case GIT_OBJECT_TREE: if ((error = git_tree_lookup(&subtree, pb->repo, entry_id)) < 0) return error; error = pack_objects_insert_tree(pb, subtree); git_tree_free(subtree); if (error < 0) return error; break; case GIT_OBJECT_BLOB: if ((error = retrieve_object(&obj, pb, entry_id)) < 0) return error; if (obj->uninteresting) continue; name = git_tree_entry_name(entry); if ((error = git_packbuilder_insert(pb, entry_id, name)) < 0) return error; break; default: /* it's a submodule or something unknown, we don't want it */ ; } } return error; } static int pack_objects_insert_commit(git_packbuilder *pb, struct walk_object *obj) { int error; git_commit *commit = NULL; git_tree *tree = NULL; obj->seen = 1; if ((error = git_packbuilder_insert(pb, &obj->id, NULL)) < 0) return error; if ((error = git_commit_lookup(&commit, pb->repo, &obj->id)) < 0) return error; if ((error = git_tree_lookup(&tree, pb->repo, git_commit_tree_id(commit))) < 0) goto cleanup; if ((error = pack_objects_insert_tree(pb, tree)) < 0) goto cleanup; cleanup: git_commit_free(commit); git_tree_free(tree); return error; } int git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk) { int error; git_oid id; struct walk_object *obj; GIT_ASSERT_ARG(pb); GIT_ASSERT_ARG(walk); if ((error = mark_edges_uninteresting(pb, walk->user_input)) < 0) return error; /* * TODO: git marks the parents of the edges * uninteresting. This may provide a speed advantage, but does * seem to assume the remote does not have a single-commit * history on the other end. */ /* walk down each tree up to the blobs and insert them, stopping when uninteresting */ while ((error = git_revwalk_next(&id, walk)) == 0) { if ((error = retrieve_object(&obj, pb, &id)) < 0) return error; if (obj->seen || obj->uninteresting) continue; if ((error = pack_objects_insert_commit(pb, obj)) < 0) return error; } if (error == GIT_ITEROVER) error = 0; return error; } int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload) { if (!pb) return -1; pb->progress_cb = progress_cb; pb->progress_cb_payload = progress_cb_payload; return 0; } void git_packbuilder_free(git_packbuilder *pb) { if (pb == NULL) return; #ifdef GIT_THREADS git_mutex_free(&pb->cache_mutex); git_mutex_free(&pb->progress_mutex); git_cond_free(&pb->progress_cond); #endif if (pb->odb) git_odb_free(pb->odb); if (pb->object_ix) git_oidmap_free(pb->object_ix); if (pb->object_list) git__free(pb->object_list); git_oidmap_free(pb->walk_objects); git_pool_clear(&pb->object_pool); git_hash_ctx_cleanup(&pb->ctx); git_zstream_free(&pb->zstream); git__free(pb); } git2r/src/libgit2/src/oidmap.h0000644000175000017500000000733314125111754015776 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_oidmap_h__ #define INCLUDE_oidmap_h__ #include "common.h" #include "git2/oid.h" /** A map with `git_oid`s as key. */ typedef struct kh_oid_s git_oidmap; /** * Allocate a new OID map. * * @param out Pointer to the map that shall be allocated. * @return 0 on success, an error code if allocation has failed. */ int git_oidmap_new(git_oidmap **out); /** * Free memory associated with the map. * * Note that this function will _not_ free values added to this * map. * * @param map Pointer to the map that is to be free'd. May be * `NULL`. */ void git_oidmap_free(git_oidmap *map); /** * Clear all entries from the map. * * This function will remove all entries from the associated map. * Memory associated with it will not be released, though. * * @param map Pointer to the map that shall be cleared. May be * `NULL`. */ void git_oidmap_clear(git_oidmap *map); /** * Return the number of elements in the map. * * @parameter map map containing the elements * @return number of elements in the map */ size_t git_oidmap_size(git_oidmap *map); /** * Return value associated with the given key. * * @param map map to search key in * @param key key to search for * @return value associated with the given key or NULL if the key was not found */ void *git_oidmap_get(git_oidmap *map, const git_oid *key); /** * Set the entry for key to value. * * If the map has no corresponding entry for the given key, a new * entry will be created with the given value. If an entry exists * already, its value will be updated to match the given value. * * @param map map to create new entry in * @param key key to set * @param value value to associate the key with; may be NULL * @return zero if the key was successfully set, a negative error * code otherwise */ int git_oidmap_set(git_oidmap *map, const git_oid *key, void *value); /** * Delete an entry from the map. * * Delete the given key and its value from the map. If no such * key exists, this will do nothing. * * @param map map to delete key in * @param key key to delete * @return `0` if the key has been deleted, GIT_ENOTFOUND if no * such key was found, a negative code in case of an * error */ int git_oidmap_delete(git_oidmap *map, const git_oid *key); /** * Check whether a key exists in the given map. * * @param map map to query for the key * @param key key to search for * @return 0 if the key has not been found, 1 otherwise */ int git_oidmap_exists(git_oidmap *map, const git_oid *key); /** * Iterate over entries of the map. * * This functions allows to iterate over all key-value entries of * the map. The current position is stored in the `iter` variable * and should be initialized to `0` before the first call to this * function. * * @param map map to iterate over * @param value pointer to the variable where to store the current * value. May be NULL. * @param iter iterator storing the current position. Initialize * with zero previous to the first call. * @param key pointer to the variable where to store the current * key. May be NULL. * @return `0` if the next entry was correctly retrieved. * GIT_ITEROVER if no entries are left. A negative error * code otherwise. */ int git_oidmap_iterate(void **value, git_oidmap *map, size_t *iter, const git_oid **key); #define git_oidmap_foreach_value(h, vvar, code) { size_t __i = 0; \ while (git_oidmap_iterate((void **) &(vvar), h, &__i, NULL) == 0) { \ code; \ } } #endif git2r/src/libgit2/src/config_entries.c0000644000175000017500000001272514125111754017517 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "config_entries.h" typedef struct config_entry_list { struct config_entry_list *next; struct config_entry_list *last; git_config_entry *entry; } config_entry_list; typedef struct { git_config_entry *entry; bool multivar; } config_entry_map_head; typedef struct config_entries_iterator { git_config_iterator parent; git_config_entries *entries; config_entry_list *head; } config_entries_iterator; struct git_config_entries { git_refcount rc; git_strmap *map; config_entry_list *list; }; int git_config_entries_new(git_config_entries **out) { git_config_entries *entries; int error; entries = git__calloc(1, sizeof(git_config_entries)); GIT_ERROR_CHECK_ALLOC(entries); GIT_REFCOUNT_INC(entries); if ((error = git_strmap_new(&entries->map)) < 0) git__free(entries); else *out = entries; return error; } int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry) { git_config_entry *duplicated; int error; duplicated = git__calloc(1, sizeof(git_config_entry)); GIT_ERROR_CHECK_ALLOC(duplicated); duplicated->name = git__strdup(entry->name); GIT_ERROR_CHECK_ALLOC(duplicated->name); if (entry->value) { duplicated->value = git__strdup(entry->value); GIT_ERROR_CHECK_ALLOC(duplicated->value); } duplicated->level = entry->level; duplicated->include_depth = entry->include_depth; if ((error = git_config_entries_append(entries, duplicated)) < 0) goto out; out: if (error && duplicated) { git__free((char *) duplicated->name); git__free((char *) duplicated->value); git__free(duplicated); } return error; } int git_config_entries_dup(git_config_entries **out, git_config_entries *entries) { git_config_entries *result = NULL; config_entry_list *head; int error; if ((error = git_config_entries_new(&result)) < 0) goto out; for (head = entries->list; head; head = head->next) if ((git_config_entries_dup_entry(result, head->entry)) < 0) goto out; *out = result; result = NULL; out: git_config_entries_free(result); return error; } void git_config_entries_incref(git_config_entries *entries) { GIT_REFCOUNT_INC(entries); } static void config_entries_free(git_config_entries *entries) { config_entry_list *list = NULL, *next; config_entry_map_head *head; git_strmap_foreach_value(entries->map, head, git__free((char *) head->entry->name); git__free(head) ); git_strmap_free(entries->map); list = entries->list; while (list != NULL) { next = list->next; git__free((char *) list->entry->value); git__free(list->entry); git__free(list); list = next; } git__free(entries); } void git_config_entries_free(git_config_entries *entries) { if (entries) GIT_REFCOUNT_DEC(entries, config_entries_free); } int git_config_entries_append(git_config_entries *entries, git_config_entry *entry) { config_entry_list *list_head; config_entry_map_head *map_head; if ((map_head = git_strmap_get(entries->map, entry->name)) != NULL) { map_head->multivar = true; /* * This is a micro-optimization for configuration files * with a lot of same keys. As for multivars the entry's * key will be the same for all entries, we can just free * all except the first entry's name and just re-use it. */ git__free((char *) entry->name); entry->name = map_head->entry->name; } else { map_head = git__calloc(1, sizeof(*map_head)); if ((git_strmap_set(entries->map, entry->name, map_head)) < 0) return -1; } map_head->entry = entry; list_head = git__calloc(1, sizeof(config_entry_list)); GIT_ERROR_CHECK_ALLOC(list_head); list_head->entry = entry; if (entries->list) entries->list->last->next = list_head; else entries->list = list_head; entries->list->last = list_head; return 0; } int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key) { config_entry_map_head *entry; if ((entry = git_strmap_get(entries->map, key)) == NULL) return GIT_ENOTFOUND; *out = entry->entry; return 0; } int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key) { config_entry_map_head *entry; if ((entry = git_strmap_get(entries->map, key)) == NULL) return GIT_ENOTFOUND; if (entry->multivar) { git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar"); return -1; } if (entry->entry->include_depth) { git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included"); return -1; } *out = entry->entry; return 0; } static void config_iterator_free(git_config_iterator *iter) { config_entries_iterator *it = (config_entries_iterator *) iter; git_config_entries_free(it->entries); git__free(it); } static int config_iterator_next( git_config_entry **entry, git_config_iterator *iter) { config_entries_iterator *it = (config_entries_iterator *) iter; if (!it->head) return GIT_ITEROVER; *entry = it->head->entry; it->head = it->head->next; return 0; } int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries) { config_entries_iterator *it; it = git__calloc(1, sizeof(config_entries_iterator)); GIT_ERROR_CHECK_ALLOC(it); it->parent.next = config_iterator_next; it->parent.free = config_iterator_free; it->head = entries->list; it->entries = entries; git_config_entries_incref(entries); *out = &it->parent; return 0; } git2r/src/libgit2/src/reader.h0000644000175000017500000000705214125111754015765 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_reader_h__ #define INCLUDE_reader_h__ #include "common.h" /* Returned when the workdir does not match the index */ #define GIT_READER_MISMATCH 1 typedef struct git_reader git_reader; /* * The `git_reader` structure is a generic interface for reading the * contents of a file by its name, and implementations are provided * for reading out of a tree, the index, and the working directory. * * Note that the reader implementation is meant to have a short * lifecycle and does not increase the refcount of the object that * it's reading. Callers should ensure that they do not use a * reader after disposing the underlying object that it reads. */ struct git_reader { int (*read)(git_buf *out, git_oid *out_oid, git_filemode_t *mode, git_reader *reader, const char *filename); }; /** * Create a `git_reader` that will allow random access to the given * tree. Paths requested via `git_reader_read` will be rooted at this * tree, callers are not expected to recurse through tree lookups. Thus, * you can request to read `/src/foo.c` and the tree provided to this * function will be searched to find another tree named `src`, which * will then be opened to find `foo.c`. * * @param out The reader for the given tree * @param tree The tree object to read * @return 0 on success, or an error code < 0 */ extern int git_reader_for_tree( git_reader **out, git_tree *tree); /** * Create a `git_reader` that will allow random access to the given * index, or the repository's index. * * @param out The reader for the given index * @param repo The repository containing the index * @param index The index to read, or NULL to use the repository's index * @return 0 on success, or an error code < 0 */ extern int git_reader_for_index( git_reader **out, git_repository *repo, git_index *index); /** * Create a `git_reader` that will allow random access to the given * repository's working directory. Note that the contents are read * in repository format, meaning any workdir -> odb filters are * applied. * * If `validate_index` is set to true, reads of files will hash the * on-disk contents and ensure that the resulting object ID matches * the repository's index. This ensures that the working directory * is unmodified from the index contents. * * @param out The reader for the given working directory * @param repo The repository containing the working directory * @param validate_index If true, the working directory contents will * be compared to the index contents during read to ensure that * the working directory is unmodified. * @return 0 on success, or an error code < 0 */ extern int git_reader_for_workdir( git_reader **out, git_repository *repo, bool validate_index); /** * Read the given filename from the reader and populate the given buffer * with the contents and the given oid with the object ID. * * @param out The buffer to populate with the file contents * @param out_id The oid to populate with the object ID * @param reader The reader to read * @param filename The filename to read from the reader */ extern int git_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *reader, const char *filename); /** * Free the given reader and any associated objects. * * @param reader The reader to free */ extern void git_reader_free(git_reader *reader); #endif git2r/src/libgit2/src/blame.c0000644000175000017500000003464114125111754015602 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "blame.h" #include "git2/commit.h" #include "git2/revparse.h" #include "git2/revwalk.h" #include "git2/tree.h" #include "git2/diff.h" #include "git2/blob.h" #include "git2/signature.h" #include "git2/mailmap.h" #include "util.h" #include "repository.h" #include "blame_git.h" static int hunk_byfinalline_search_cmp(const void *key, const void *entry) { git_blame_hunk *hunk = (git_blame_hunk*)entry; size_t lineno = *(size_t*)key; size_t lines_in_hunk = hunk->lines_in_hunk; size_t final_start_line_number = hunk->final_start_line_number; if (lineno < final_start_line_number) return -1; if (lineno >= final_start_line_number + lines_in_hunk) return 1; return 0; } static int paths_cmp(const void *a, const void *b) { return git__strcmp((char*)a, (char*)b); } static int hunk_cmp(const void *_a, const void *_b) { git_blame_hunk *a = (git_blame_hunk*)_a, *b = (git_blame_hunk*)_b; if (a->final_start_line_number > b->final_start_line_number) return 1; else if (a->final_start_line_number < b->final_start_line_number) return -1; else return 0; } static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line) { return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1); } static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line) { return line <= hunk->final_start_line_number; } static git_blame_hunk *new_hunk( size_t start, size_t lines, size_t orig_start, const char *path) { git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk)); if (!hunk) return NULL; hunk->lines_in_hunk = lines; hunk->final_start_line_number = start; hunk->orig_start_line_number = orig_start; hunk->orig_path = path ? git__strdup(path) : NULL; return hunk; } static void free_hunk(git_blame_hunk *hunk) { git__free((void*)hunk->orig_path); git_signature_free(hunk->final_signature); git_signature_free(hunk->orig_signature); git__free(hunk); } static git_blame_hunk *dup_hunk(git_blame_hunk *hunk) { git_blame_hunk *newhunk = new_hunk( hunk->final_start_line_number, hunk->lines_in_hunk, hunk->orig_start_line_number, hunk->orig_path); if (!newhunk) return NULL; git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id); git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id); newhunk->boundary = hunk->boundary; if (git_signature_dup(&newhunk->final_signature, hunk->final_signature) < 0 || git_signature_dup(&newhunk->orig_signature, hunk->orig_signature) < 0) { free_hunk(newhunk); return NULL; } return newhunk; } /* Starting with the hunk that includes start_line, shift all following hunks' * final_start_line by shift_by lines */ static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by) { size_t i; if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) { for (; i < v->length; i++) { git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i]; hunk->final_start_line_number += shift_by; } } } git_blame *git_blame__alloc( git_repository *repo, git_blame_options opts, const char *path) { git_blame *gbr = git__calloc(1, sizeof(git_blame)); if (!gbr) return NULL; gbr->repository = repo; gbr->options = opts; if (git_vector_init(&gbr->hunks, 8, hunk_cmp) < 0 || git_vector_init(&gbr->paths, 8, paths_cmp) < 0 || (gbr->path = git__strdup(path)) == NULL || git_vector_insert(&gbr->paths, git__strdup(path)) < 0) { git_blame_free(gbr); return NULL; } if (opts.flags & GIT_BLAME_USE_MAILMAP && git_mailmap_from_repository(&gbr->mailmap, repo) < 0) { git_blame_free(gbr); return NULL; } return gbr; } void git_blame_free(git_blame *blame) { size_t i; git_blame_hunk *hunk; if (!blame) return; git_vector_foreach(&blame->hunks, i, hunk) free_hunk(hunk); git_vector_free(&blame->hunks); git_vector_free_deep(&blame->paths); git_array_clear(blame->line_index); git_mailmap_free(blame->mailmap); git__free(blame->path); git_blob_free(blame->final_blob); git__free(blame); } uint32_t git_blame_get_hunk_count(git_blame *blame) { GIT_ASSERT_ARG(blame); return (uint32_t)blame->hunks.length; } const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t index) { GIT_ASSERT_ARG_WITH_RETVAL(blame, NULL); return (git_blame_hunk*)git_vector_get(&blame->hunks, index); } const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno) { size_t i, new_lineno = lineno; GIT_ASSERT_ARG_WITH_RETVAL(blame, NULL); if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) { return git_blame_get_hunk_byindex(blame, (uint32_t)i); } return NULL; } static int normalize_options( git_blame_options *out, const git_blame_options *in, git_repository *repo) { git_blame_options dummy = GIT_BLAME_OPTIONS_INIT; if (!in) in = &dummy; memcpy(out, in, sizeof(git_blame_options)); /* No newest_commit => HEAD */ if (git_oid_is_zero(&out->newest_commit)) { if (git_reference_name_to_id(&out->newest_commit, repo, "HEAD") < 0) { return -1; } } /* min_line 0 really means 1 */ if (!out->min_line) out->min_line = 1; /* max_line 0 really means N, but we don't know N yet */ /* Fix up option implications */ if (out->flags & GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES) out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES; if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES) out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES; if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES) out->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE; return 0; } static git_blame_hunk *split_hunk_in_vector( git_vector *vec, git_blame_hunk *hunk, size_t rel_line, bool return_new) { size_t new_line_count; git_blame_hunk *nh; /* Don't split if already at a boundary */ if (rel_line <= 0 || rel_line >= hunk->lines_in_hunk) { return hunk; } new_line_count = hunk->lines_in_hunk - rel_line; nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count, hunk->orig_start_line_number + rel_line, hunk->orig_path); if (!nh) return NULL; git_oid_cpy(&nh->final_commit_id, &hunk->final_commit_id); git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id); /* Adjust hunk that was split */ hunk->lines_in_hunk -= new_line_count; git_vector_insert_sorted(vec, nh, NULL); { git_blame_hunk *ret = return_new ? nh : hunk; return ret; } } /* * Construct a list of char indices for where lines begin * Adapted from core git: * https://github.com/gitster/git/blob/be5c9fb9049ed470e7005f159bb923a5f4de1309/builtin/blame.c#L1760-L1789 */ static int index_blob_lines(git_blame *blame) { const char *buf = blame->final_buf; size_t len = blame->final_buf_size; int num = 0, incomplete = 0, bol = 1; size_t *i; if (len && buf[len-1] != '\n') incomplete++; /* incomplete line at the end */ while (len--) { if (bol) { i = git_array_alloc(blame->line_index); GIT_ERROR_CHECK_ALLOC(i); *i = buf - blame->final_buf; bol = 0; } if (*buf++ == '\n') { num++; bol = 1; } } i = git_array_alloc(blame->line_index); GIT_ERROR_CHECK_ALLOC(i); *i = buf - blame->final_buf; blame->num_lines = num + incomplete; return blame->num_lines; } static git_blame_hunk *hunk_from_entry(git_blame__entry *e, git_blame *blame) { git_blame_hunk *h = new_hunk( e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path); if (!h) return NULL; git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit)); git_oid_cpy(&h->orig_commit_id, git_commit_id(e->suspect->commit)); git_commit_author_with_mailmap( &h->final_signature, e->suspect->commit, blame->mailmap); git_signature_dup(&h->orig_signature, h->final_signature); h->boundary = e->is_boundary ? 1 : 0; return h; } static int load_blob(git_blame *blame) { int error; if (blame->final_blob) return 0; error = git_commit_lookup(&blame->final, blame->repository, &blame->options.newest_commit); if (error < 0) goto cleanup; error = git_object_lookup_bypath((git_object**)&blame->final_blob, (git_object*)blame->final, blame->path, GIT_OBJECT_BLOB); cleanup: return error; } static int blame_internal(git_blame *blame) { int error; git_blame__entry *ent = NULL; git_blame__origin *o; if ((error = load_blob(blame)) < 0 || (error = git_blame__get_origin(&o, blame, blame->final, blame->path)) < 0) goto cleanup; if (git_blob_rawsize(blame->final_blob) > SIZE_MAX) { git_error_set(GIT_ERROR_NOMEMORY, "blob is too large to blame"); error = -1; goto cleanup; } blame->final_buf = git_blob_rawcontent(blame->final_blob); blame->final_buf_size = (size_t)git_blob_rawsize(blame->final_blob); ent = git__calloc(1, sizeof(git_blame__entry)); GIT_ERROR_CHECK_ALLOC(ent); ent->num_lines = index_blob_lines(blame); ent->lno = blame->options.min_line - 1; ent->num_lines = ent->num_lines - blame->options.min_line + 1; if (blame->options.max_line > 0) ent->num_lines = blame->options.max_line - blame->options.min_line + 1; ent->s_lno = ent->lno; ent->suspect = o; blame->ent = ent; error = git_blame__like_git(blame, blame->options.flags); cleanup: for (ent = blame->ent; ent; ) { git_blame__entry *e = ent->next; git_blame_hunk *h = hunk_from_entry(ent, blame); git_vector_insert(&blame->hunks, h); git_blame__free_entry(ent); ent = e; } return error; } /******************************************************************************* * File blaming ******************************************************************************/ int git_blame_file( git_blame **out, git_repository *repo, const char *path, git_blame_options *options) { int error = -1; git_blame_options normOptions = GIT_BLAME_OPTIONS_INIT; git_blame *blame = NULL; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(path); if ((error = normalize_options(&normOptions, options, repo)) < 0) goto on_error; blame = git_blame__alloc(repo, normOptions, path); GIT_ERROR_CHECK_ALLOC(blame); if ((error = load_blob(blame)) < 0) goto on_error; if ((error = blame_internal(blame)) < 0) goto on_error; *out = blame; return 0; on_error: git_blame_free(blame); return error; } /******************************************************************************* * Buffer blaming *******************************************************************************/ static bool hunk_is_bufferblame(git_blame_hunk *hunk) { return hunk && git_oid_is_zero(&hunk->final_commit_id); } static int buffer_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) { git_blame *blame = (git_blame*)payload; uint32_t wedge_line; GIT_UNUSED(delta); wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start; blame->current_diff_line = wedge_line; blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line); if (!blame->current_hunk) { /* Line added at the end of the file */ blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path); GIT_ERROR_CHECK_ALLOC(blame->current_hunk); git_vector_insert(&blame->hunks, blame->current_hunk); } else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){ /* If this hunk doesn't start between existing hunks, split a hunk up so it does */ blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk, wedge_line - blame->current_hunk->orig_start_line_number, true); GIT_ERROR_CHECK_ALLOC(blame->current_hunk); } return 0; } static int ptrs_equal_cmp(const void *a, const void *b) { return ab ? 1 : 0; } static int buffer_line_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { git_blame *blame = (git_blame*)payload; GIT_UNUSED(delta); GIT_UNUSED(hunk); GIT_UNUSED(line); if (line->origin == GIT_DIFF_LINE_ADDITION) { if (hunk_is_bufferblame(blame->current_hunk) && hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) { /* Append to the current buffer-blame hunk */ blame->current_hunk->lines_in_hunk++; shift_hunks_by(&blame->hunks, blame->current_diff_line+1, 1); } else { /* Create a new buffer-blame hunk with this line */ shift_hunks_by(&blame->hunks, blame->current_diff_line, 1); blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path); GIT_ERROR_CHECK_ALLOC(blame->current_hunk); git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL); } blame->current_diff_line++; } if (line->origin == GIT_DIFF_LINE_DELETION) { /* Trim the line from the current hunk; remove it if it's now empty */ size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk+1; if (--(blame->current_hunk->lines_in_hunk) == 0) { size_t i; shift_base--; if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) { git_vector_remove(&blame->hunks, i); free_hunk(blame->current_hunk); blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i); } } shift_hunks_by(&blame->hunks, shift_base, -1); } return 0; } int git_blame_buffer( git_blame **out, git_blame *reference, const char *buffer, size_t buffer_len) { git_blame *blame; git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; size_t i; git_blame_hunk *hunk; diffopts.context_lines = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(reference); GIT_ASSERT_ARG(buffer && buffer_len); blame = git_blame__alloc(reference->repository, reference->options, reference->path); GIT_ERROR_CHECK_ALLOC(blame); /* Duplicate all of the hunk structures in the reference blame */ git_vector_foreach(&reference->hunks, i, hunk) { git_blame_hunk *h = dup_hunk(hunk); GIT_ERROR_CHECK_ALLOC(h); git_vector_insert(&blame->hunks, h); } /* Diff to the reference blob */ git_diff_blob_to_buffer(reference->final_blob, blame->path, buffer, buffer_len, blame->path, &diffopts, NULL, NULL, buffer_hunk_cb, buffer_line_cb, blame); *out = blame; return 0; } int git_blame_options_init(git_blame_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_blame_options, GIT_BLAME_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_blame_init_options(git_blame_options *opts, unsigned int version) { return git_blame_options_init(opts, version); } #endif git2r/src/libgit2/src/streams/0000755000175000017500000000000014145550337016032 5ustar nileshnileshgit2r/src/libgit2/src/streams/openssl_dynamic.h0000644000175000017500000003542314125111754021373 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ /* ==================================================================== * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ /* ==================================================================== * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. * ECC cipher suite support in OpenSSL originally developed by * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ /* ==================================================================== * Copyright 2005 Nokia. All rights reserved. * * The portions of the attached software ("Contribution") is developed by * Nokia Corporation and is licensed pursuant to the OpenSSL open source * license. * * The Contribution, originally written by Mika Kousa and Pasi Eronen of * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites * support (see RFC 4279) to OpenSSL. * * No patent licenses or other rights except those expressly stated in * the OpenSSL open source license shall be deemed granted or received * expressly, by implication, estoppel, or otherwise. * * No assurances are provided by Nokia that the Contribution does not * infringe the patent or other intellectual property rights of any third * party or that the license provides you with all the necessary rights * to make use of the Contribution. * * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ #ifndef INCLUDE_streams_openssl_dynamic_h__ #define INCLUDE_streams_openssl_dynamic_h__ #ifdef GIT_OPENSSL_DYNAMIC # define BIO_CTRL_FLUSH 11 # define BIO_TYPE_SOURCE_SINK 0x0400 # define CRYPTO_LOCK 1 # define GEN_DNS 2 # define GEN_IPADD 7 # define NID_commonName 13 # define NID_subject_alt_name 85 # define SSL_VERIFY_NONE 0x00 # define SSL_CTRL_OPTIONS 32 # define SSL_CTRL_MODE 33 # define SSL_CTRL_SET_TLSEXT_HOSTNAME 55 # define SSL_ERROR_NONE 0 # define SSL_ERROR_SSL 1 # define SSL_ERROR_WANT_READ 2 # define SSL_ERROR_WANT_WRITE 3 # define SSL_ERROR_WANT_X509_LOOKUP 4 # define SSL_ERROR_SYSCALL 5 # define SSL_ERROR_ZERO_RETURN 6 # define SSL_ERROR_WANT_CONNECT 7 # define SSL_ERROR_WANT_ACCEPT 8 # define SSL_OP_NO_COMPRESSION 0x00020000L # define SSL_OP_NO_SSLv2 0x01000000L # define SSL_OP_NO_SSLv3 0x02000000L # define SSL_MODE_AUTO_RETRY 0x00000004L # define TLSEXT_NAMETYPE_host_name 0 # define V_ASN1_UTF8STRING 12 # define X509_V_OK 0 /* Most of the OpenSSL types are mercifully opaque, so we can treat them like `void *` */ typedef struct bio_st BIO; typedef struct bio_method_st BIO_METHOD; typedef void bio_info_cb; typedef void * CRYPTO_EX_DATA; typedef void CRYPTO_THREADID; typedef void GENERAL_NAMES; typedef void SSL; typedef void SSL_CTX; typedef void SSL_METHOD; typedef void X509; typedef void X509_NAME; typedef void X509_NAME_ENTRY; typedef void X509_STORE_CTX; typedef struct { int length; int type; unsigned char *data; long flags; } ASN1_STRING; typedef struct { int type; union { char *ptr; ASN1_STRING *ia5; } d; } GENERAL_NAME; struct bio_st { BIO_METHOD *method; /* bio, mode, argp, argi, argl, ret */ long (*callback) (struct bio_st *, int, const char *, int, long, long); char *cb_arg; /* first argument for the callback */ int init; int shutdown; int flags; /* extra storage */ int retry_reason; int num; void *ptr; struct bio_st *next_bio; /* used by filter BIOs */ struct bio_st *prev_bio; /* used by filter BIOs */ int references; unsigned long num_read; unsigned long num_write; CRYPTO_EX_DATA ex_data; }; struct bio_method_st { int type; const char *name; int (*bwrite) (BIO *, const char *, int); int (*bread) (BIO *, char *, int); int (*bputs) (BIO *, const char *); int (*bgets) (BIO *, char *, int); long (*ctrl) (BIO *, int, long, void *); int (*create) (BIO *); int (*destroy) (BIO *); long (*callback_ctrl) (BIO *, int, bio_info_cb *); }; extern unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x); extern const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x); extern int (*ASN1_STRING_length)(const ASN1_STRING *x); extern int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in); extern int (*ASN1_STRING_type)(const ASN1_STRING *x); extern void *(*BIO_get_data)(BIO *a); extern int (*BIO_get_new_index)(void); extern int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings); extern void (*BIO_meth_free)(BIO_METHOD *biom); extern int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *)); extern int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *)); extern int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *)); extern int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int)); extern int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *)); extern int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int)); extern int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int)); extern BIO_METHOD *(*BIO_meth_new)(int type, const char *name); extern BIO *(*BIO_new)(const BIO_METHOD *type); extern void (*BIO_set_data)(BIO *a, void *ptr); extern void (*BIO_set_init)(BIO *a, int init); extern void (*CRYPTO_free)(void *ptr, const char *file, int line); extern void *(*CRYPTO_malloc)(size_t num, const char *file, int line); extern int (*CRYPTO_num_locks)(void); extern void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line)); extern int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem)); extern int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id)); extern void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val); extern char *(*ERR_error_string)(unsigned long e, char *buf); extern void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len); extern unsigned long (*ERR_get_error)(void); # define OPENSSL_malloc(num) CRYPTO_malloc(num, __FILE__, __LINE__) # define OPENSSL_free(addr) CRYPTO_free(addr, __FILE__, __LINE__) extern int (*SSL_connect)(SSL *ssl); extern long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg); extern void (*SSL_free)(SSL *ssl); extern int (*SSL_get_error)(SSL *ssl, int ret); extern X509 *(*SSL_get_peer_certificate)(const SSL *ssl); extern long (*SSL_get_verify_result)(const SSL *ssl); extern int (*SSL_library_init)(void); extern void (*SSL_load_error_strings)(void); extern SSL *(*SSL_new)(SSL_CTX *ctx); extern int (*SSL_read)(SSL *ssl, const void *buf, int num); extern void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio); extern int (*SSL_shutdown)(SSL *ssl); extern int (*SSL_write)(SSL *ssl, const void *buf, int num); # define SSL_set_tlsext_host_name(s, name) SSL_ctrl((s), SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (char *)(name)); extern long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg); extern void (*SSL_CTX_free)(SSL_CTX *ctx); extern SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method); extern int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); extern int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx); extern long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options); extern void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); extern int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath); # define SSL_CTX_set_mode(ctx, mode) SSL_CTX_ctrl((ctx), SSL_CTRL_MODE, (mode), NULL); extern const SSL_METHOD *(*SSLv23_method)(void); extern const SSL_METHOD *(*TLS_method)(void); extern ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne); extern X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc); extern int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos); extern void (*X509_free)(X509 *a); extern void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx); extern X509_NAME *(*X509_get_subject_name)(const X509 *x); extern int (*i2d_X509)(X509 *a, unsigned char **ppout); extern int (*OPENSSL_sk_num)(const void *sk); extern void *(*OPENSSL_sk_value)(const void *sk, int i); extern void (*OPENSSL_sk_free)(void *sk); extern int (*sk_num)(const void *sk); extern void *(*sk_value)(const void *sk, int i); extern void (*sk_free)(void *sk); extern int sk_GENERAL_NAME_num(const GENERAL_NAME *sk); extern GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i); extern void GENERAL_NAMES_free(GENERAL_NAME *sk); extern int git_openssl_stream_dynamic_init(void); #endif /* GIT_OPENSSL_DYNAMIC */ #endif git2r/src/libgit2/src/streams/registry.h0000644000175000017500000000104714125111754020047 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_streams_registry_h__ #define INCLUDE_streams_registry_h__ #include "common.h" #include "git2/sys/stream.h" /** Configure stream registry. */ int git_stream_registry_global_init(void); /** Lookup a stream registration. */ extern int git_stream_registry_lookup(git_stream_registration *out, git_stream_t type); #endif git2r/src/libgit2/src/streams/openssl.h0000644000175000017500000000163614125111754017666 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_streams_openssl_h__ #define INCLUDE_streams_openssl_h__ #include "common.h" #include "streams/openssl_legacy.h" #include "streams/openssl_dynamic.h" #include "git2/sys/stream.h" extern int git_openssl_stream_global_init(void); #if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC) # include # include # include # include # endif #ifdef GIT_OPENSSL extern int git_openssl__set_cert_location(const char *file, const char *path); extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port); extern int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host); #endif #endif git2r/src/libgit2/src/streams/tls.c0000644000175000017500000000342414125111754016775 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "git2/errors.h" #include "common.h" #include "streams/registry.h" #include "streams/tls.h" #include "streams/mbedtls.h" #include "streams/openssl.h" #include "streams/stransport.h" int git_tls_stream_new(git_stream **out, const char *host, const char *port) { int (*init)(git_stream **, const char *, const char *) = NULL; git_stream_registration custom = {0}; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(host); GIT_ASSERT_ARG(port); if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_TLS)) == 0) { init = custom.init; } else if (error == GIT_ENOTFOUND) { #ifdef GIT_SECURE_TRANSPORT init = git_stransport_stream_new; #elif defined(GIT_OPENSSL) init = git_openssl_stream_new; #elif defined(GIT_MBEDTLS) init = git_mbedtls_stream_new; #endif } else { return error; } if (!init) { git_error_set(GIT_ERROR_SSL, "there is no TLS stream available"); return -1; } return init(out, host, port); } int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host) { int (*wrap)(git_stream **, git_stream *, const char *) = NULL; git_stream_registration custom = {0}; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(in); if (git_stream_registry_lookup(&custom, GIT_STREAM_TLS) == 0) { wrap = custom.wrap; } else { #ifdef GIT_SECURE_TRANSPORT wrap = git_stransport_stream_wrap; #elif defined(GIT_OPENSSL) wrap = git_openssl_stream_wrap; #elif defined(GIT_MBEDTLS) wrap = git_mbedtls_stream_wrap; #endif } if (!wrap) { git_error_set(GIT_ERROR_SSL, "there is no TLS stream available"); return -1; } return wrap(out, in, host); } git2r/src/libgit2/src/streams/socket.h0000644000175000017500000000101414125111754017461 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_streams_socket_h__ #define INCLUDE_streams_socket_h__ #include "common.h" #include "netops.h" typedef struct { git_stream parent; char *host; char *port; GIT_SOCKET s; } git_socket_stream; extern int git_socket_stream_new(git_stream **out, const char *host, const char *port); #endif git2r/src/libgit2/src/streams/openssl_legacy.h0000644000175000017500000000535614125111754021215 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_streams_openssl_legacy_h__ #define INCLUDE_streams_openssl_legacy_h__ #include "streams/openssl_dynamic.h" #if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC) # include # include # include # include # if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) # define GIT_OPENSSL_LEGACY # endif #endif #if defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC) # define OPENSSL_init_ssl OPENSSL_init_ssl__legacy # define BIO_meth_new BIO_meth_new__legacy # define BIO_meth_free BIO_meth_free__legacy # define BIO_meth_set_write BIO_meth_set_write__legacy # define BIO_meth_set_read BIO_meth_set_read__legacy # define BIO_meth_set_puts BIO_meth_set_puts__legacy # define BIO_meth_set_gets BIO_meth_set_gets__legacy # define BIO_meth_set_ctrl BIO_meth_set_ctrl__legacy # define BIO_meth_set_create BIO_meth_set_create__legacy # define BIO_meth_set_destroy BIO_meth_set_destroy__legacy # define BIO_get_new_index BIO_get_new_index__legacy # define BIO_set_data BIO_set_data__legacy # define BIO_set_init BIO_set_init__legacy # define BIO_get_data BIO_get_data__legacy # define ASN1_STRING_get0_data ASN1_STRING_get0_data__legacy #endif #if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC) extern int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings); extern BIO_METHOD *BIO_meth_new__legacy(int type, const char *name); extern void BIO_meth_free__legacy(BIO_METHOD *biom); extern int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int)); extern int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int)); extern int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *)); extern int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int)); extern int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *)); extern int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *)); extern int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *)); extern int BIO_get_new_index__legacy(void); extern void BIO_set_data__legacy(BIO *a, void *ptr); extern void BIO_set_init__legacy(BIO *b, int init); extern void *BIO_get_data__legacy(BIO *a); extern const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x); extern long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op); #endif #endif git2r/src/libgit2/src/streams/mbedtls.h0000644000175000017500000000124614125111754017632 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_steams_mbedtls_h__ #define INCLUDE_steams_mbedtls_h__ #include "common.h" #include "git2/sys/stream.h" extern int git_mbedtls_stream_global_init(void); #ifdef GIT_MBEDTLS extern int git_mbedtls__set_cert_location(const char *file, const char *path); extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port); extern int git_mbedtls_stream_wrap(git_stream **out, git_stream *in, const char *host); #endif #endif git2r/src/libgit2/src/streams/stransport.c0000644000175000017500000002000214125111754020401 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "streams/stransport.h" #ifdef GIT_SECURE_TRANSPORT #include #include #include #include "git2/transport.h" #include "streams/socket.h" static int stransport_error(OSStatus ret) { CFStringRef message; if (ret == noErr || ret == errSSLClosedGraceful) { git_error_clear(); return 0; } #if !TARGET_OS_IPHONE message = SecCopyErrorMessageString(ret, NULL); GIT_ERROR_CHECK_ALLOC(message); git_error_set(GIT_ERROR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8)); CFRelease(message); #else git_error_set(GIT_ERROR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret); GIT_UNUSED(message); #endif return -1; } typedef struct { git_stream parent; git_stream *io; int owned; SSLContextRef ctx; CFDataRef der_data; git_cert_x509 cert_info; } stransport_stream; static int stransport_connect(git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; int error; SecTrustRef trust = NULL; SecTrustResultType sec_res; OSStatus ret; if (st->owned && (error = git_stream_connect(st->io)) < 0) return error; ret = SSLHandshake(st->ctx); if (ret != errSSLServerAuthCompleted) { git_error_set(GIT_ERROR_SSL, "unexpected return value from ssl handshake %d", (int)ret); return -1; } if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) goto on_error; if (!trust) return GIT_ECERTIFICATE; if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr) goto on_error; CFRelease(trust); if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) { git_error_set(GIT_ERROR_SSL, "internal security trust error"); return -1; } if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure || sec_res == kSecTrustResultFatalTrustFailure) { git_error_set(GIT_ERROR_SSL, "untrusted connection error"); return GIT_ECERTIFICATE; } return 0; on_error: if (trust) CFRelease(trust); return stransport_error(ret); } static int stransport_certificate(git_cert **out, git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; SecTrustRef trust = NULL; SecCertificateRef sec_cert; OSStatus ret; if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) return stransport_error(ret); sec_cert = SecTrustGetCertificateAtIndex(trust, 0); st->der_data = SecCertificateCopyData(sec_cert); CFRelease(trust); if (st->der_data == NULL) { git_error_set(GIT_ERROR_SSL, "retrieved invalid certificate data"); return -1; } st->cert_info.parent.cert_type = GIT_CERT_X509; st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data); st->cert_info.len = CFDataGetLength(st->der_data); *out = (git_cert *)&st->cert_info; return 0; } static int stransport_set_proxy( git_stream *stream, const git_proxy_options *proxy_opts) { stransport_stream *st = (stransport_stream *) stream; return git_stream_set_proxy(st->io, proxy_opts); } /* * Contrary to typical network IO callbacks, Secure Transport write callback is * expected to write *all* passed data, not just as much as it can, and any * other case would be considered a failure. * * This behavior is actually not specified in the Apple documentation, but is * required for things to work correctly (and incidentally, that's also how * Apple implements it in its projects at opensource.apple.com). * * Libgit2 streams happen to already have this very behavior so this is just * passthrough. */ static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len) { git_stream *io = (git_stream *) conn; if (git_stream__write_full(io, data, *len, 0) < 0) return -36; /* "ioErr" from MacErrors.h which is not available on iOS */ return noErr; } static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags) { stransport_stream *st = (stransport_stream *) stream; size_t data_len, processed; OSStatus ret; GIT_UNUSED(flags); data_len = min(len, SSIZE_MAX); if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr) return stransport_error(ret); GIT_ASSERT(processed < SSIZE_MAX); return (ssize_t)processed; } /* * Contrary to typical network IO callbacks, Secure Transport read callback is * expected to read *exactly* the requested number of bytes, not just as much * as it can, and any other case would be considered a failure. * * This behavior is actually not specified in the Apple documentation, but is * required for things to work correctly (and incidentally, that's also how * Apple implements it in its projects at opensource.apple.com). */ static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len) { git_stream *io = (git_stream *) conn; OSStatus error = noErr; size_t off = 0; ssize_t ret; do { ret = git_stream_read(io, data + off, *len - off); if (ret < 0) { error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */ break; } if (ret == 0) { error = errSSLClosedGraceful; break; } off += ret; } while (off < *len); *len = off; return error; } static ssize_t stransport_read(git_stream *stream, void *data, size_t len) { stransport_stream *st = (stransport_stream *) stream; size_t processed; OSStatus ret; if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr) return stransport_error(ret); return processed; } static int stransport_close(git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; OSStatus ret; ret = SSLClose(st->ctx); if (ret != noErr && ret != errSSLClosedGraceful) return stransport_error(ret); return st->owned ? git_stream_close(st->io) : 0; } static void stransport_free(git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; if (st->owned) git_stream_free(st->io); CFRelease(st->ctx); if (st->der_data) CFRelease(st->der_data); git__free(st); } static int stransport_wrap( git_stream **out, git_stream *in, const char *host, int owned) { stransport_stream *st; OSStatus ret; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(in); GIT_ASSERT_ARG(host); st = git__calloc(1, sizeof(stransport_stream)); GIT_ERROR_CHECK_ALLOC(st); st->io = in; st->owned = owned; st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); if (!st->ctx) { git_error_set(GIT_ERROR_NET, "failed to create SSL context"); git__free(st); return -1; } if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr || (ret = SSLSetConnection(st->ctx, st->io)) != noErr || (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr || (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr || (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr || (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) { CFRelease(st->ctx); git__free(st); return stransport_error(ret); } st->parent.version = GIT_STREAM_VERSION; st->parent.encrypted = 1; st->parent.proxy_support = git_stream_supports_proxy(st->io); st->parent.connect = stransport_connect; st->parent.certificate = stransport_certificate; st->parent.set_proxy = stransport_set_proxy; st->parent.read = stransport_read; st->parent.write = stransport_write; st->parent.close = stransport_close; st->parent.free = stransport_free; *out = (git_stream *) st; return 0; } int git_stransport_stream_wrap( git_stream **out, git_stream *in, const char *host) { return stransport_wrap(out, in, host, 0); } int git_stransport_stream_new(git_stream **out, const char *host, const char *port) { git_stream *stream = NULL; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(host); error = git_socket_stream_new(&stream, host, port); if (!error) error = stransport_wrap(out, stream, host, 1); if (error < 0 && stream) { git_stream_close(stream); git_stream_free(stream); } return error; } #endif git2r/src/libgit2/src/streams/openssl_dynamic.c0000644000175000017500000003327614125111754021372 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "streams/openssl.h" #include "streams/openssl_dynamic.h" #if defined(GIT_OPENSSL) && defined(GIT_OPENSSL_DYNAMIC) #include "runtime.h" #include unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x); const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x); int (*ASN1_STRING_length)(const ASN1_STRING *x); int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in); int (*ASN1_STRING_type)(const ASN1_STRING *x); void *(*BIO_get_data)(BIO *a); int (*BIO_get_new_index)(void); int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings); void (*BIO_meth_free)(BIO_METHOD *biom); int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *)); int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *)); int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *)); int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int)); int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *)); int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int)); int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int)); BIO_METHOD *(*BIO_meth_new)(int type, const char *name); BIO *(*BIO_new)(const BIO_METHOD *type); void (*BIO_set_data)(BIO *a, void *ptr); void (*BIO_set_init)(BIO *a, int init); void (*CRYPTO_free)(void *ptr, const char *file, int line); void *(*CRYPTO_malloc)(size_t num, const char *file, int line); int (*CRYPTO_num_locks)(void); void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line)); int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem)); int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id)); void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val); char *(*ERR_error_string)(unsigned long e, char *buf); void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len); unsigned long (*ERR_get_error)(void); int (*SSL_connect)(SSL *ssl); long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg); void (*SSL_free)(SSL *ssl); int (*SSL_get_error)(SSL *ssl, int ret); X509 *(*SSL_get_peer_certificate)(const SSL *ssl); long (*SSL_get_verify_result)(const SSL *ssl); int (*SSL_library_init)(void); void (*SSL_load_error_strings)(void); SSL *(*SSL_new)(SSL_CTX *ctx); int (*SSL_read)(SSL *ssl, const void *buf, int num); void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio); int (*SSL_shutdown)(SSL *ssl); int (*SSL_write)(SSL *ssl, const void *buf, int num); long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg); void (*SSL_CTX_free)(SSL_CTX *ctx); SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method); int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx); long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options); void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath); const SSL_METHOD *(*SSLv23_method)(void); const SSL_METHOD *(*TLS_method)(void); ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne); X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc); int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos); void (*X509_free)(X509 *a); void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx); X509_NAME *(*X509_get_subject_name)(const X509 *x); int (*i2d_X509)(X509 *a, unsigned char **ppout); int (*OPENSSL_sk_num)(const void *sk); void *(*OPENSSL_sk_value)(const void *sk, int i); void (*OPENSSL_sk_free)(void *sk); int (*sk_num)(const void *sk); void *(*sk_value)(const void *sk, int i); void (*sk_free)(void *sk); void *openssl_handle; GIT_INLINE(void *) openssl_sym(int *err, const char *name, bool required) { void *symbol; /* if we've seen an err, noop to retain it */ if (*err) return NULL; if ((symbol = dlsym(openssl_handle, name)) == NULL && required) { const char *msg = dlerror(); git_error_set(GIT_ERROR_SSL, "could not load ssl function '%s': %s", name, msg ? msg : "unknown error"); *err = -1; } return symbol; } static void dynamic_shutdown(void) { dlclose(openssl_handle); openssl_handle = NULL; } int git_openssl_stream_dynamic_init(void) { int err = 0; if ((openssl_handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL && (openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL && (openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL && (openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL && (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) { git_error_set(GIT_ERROR_SSL, "could not load ssl libraries"); return -1; } ASN1_STRING_data = (unsigned char *(*)(ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_data", false); ASN1_STRING_get0_data = (const unsigned char *(*)(const ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_get0_data", false); ASN1_STRING_length = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_length", true); ASN1_STRING_to_UTF8 = (int (*)(unsigned char **, const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_to_UTF8", true); ASN1_STRING_type = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_type", true); BIO_get_data = (void *(*)(BIO *))openssl_sym(&err, "BIO_get_data", false); BIO_get_new_index = (int (*)(void))openssl_sym(&err, "BIO_get_new_index", false); BIO_meth_free = (void (*)(BIO_METHOD *))openssl_sym(&err, "BIO_meth_free", false); BIO_meth_new = (BIO_METHOD *(*)(int, const char *))openssl_sym(&err, "BIO_meth_new", false); BIO_meth_set_create = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_create", false); BIO_meth_set_ctrl = (int (*)(BIO_METHOD *, long (*)(BIO *, int, long, void *)))openssl_sym(&err, "BIO_meth_set_ctrl", false); BIO_meth_set_destroy = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_destroy", false); BIO_meth_set_gets = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_gets", false); BIO_meth_set_puts = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *)))openssl_sym(&err, "BIO_meth_set_puts", false); BIO_meth_set_read = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_read", false); BIO_meth_set_write = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *, int)))openssl_sym(&err, "BIO_meth_set_write", false); BIO_new = (BIO *(*)(const BIO_METHOD *))openssl_sym(&err, "BIO_new", true); BIO_set_data = (void (*)(BIO *a, void *))openssl_sym(&err, "BIO_set_data", false); BIO_set_init = (void (*)(BIO *a, int))openssl_sym(&err, "BIO_set_init", false); CRYPTO_free = (void (*)(void *, const char *, int))openssl_sym(&err, "CRYPTO_free", true); CRYPTO_malloc = (void *(*)(size_t, const char *, int))openssl_sym(&err, "CRYPTO_malloc", true); CRYPTO_num_locks = (int (*)(void))openssl_sym(&err, "CRYPTO_num_locks", false); CRYPTO_set_locking_callback = (void (*)(void (*)(int, int, const char *, int)))openssl_sym(&err, "CRYPTO_set_locking_callback", false); CRYPTO_set_mem_functions = (int (*)(void *(*)(size_t), void *(*)(void *, size_t), void (*f)(void *)))openssl_sym(&err, "CRYPTO_set_mem_functions", true); CRYPTO_THREADID_set_callback = (int (*)(void (*)(CRYPTO_THREADID *)))openssl_sym(&err, "CRYPTO_THREADID_set_callback", false); CRYPTO_THREADID_set_numeric = (void (*)(CRYPTO_THREADID *, unsigned long))openssl_sym(&err, "CRYPTO_THREADID_set_numeric", false); ERR_error_string = (char *(*)(unsigned long, char *))openssl_sym(&err, "ERR_error_string", true); ERR_error_string_n = (void (*)(unsigned long, char *, size_t))openssl_sym(&err, "ERR_error_string_n", true); ERR_get_error = (unsigned long (*)(void))openssl_sym(&err, "ERR_get_error", true); OPENSSL_init_ssl = (int (*)(uint64_t opts, const void *settings))openssl_sym(&err, "OPENSSL_init_ssl", false); OPENSSL_sk_num = (int (*)(const void *))openssl_sym(&err, "OPENSSL_sk_num", false); OPENSSL_sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "OPENSSL_sk_value", false); OPENSSL_sk_free = (void (*)(void *))openssl_sym(&err, "OPENSSL_sk_free", false); sk_num = (int (*)(const void *))openssl_sym(&err, "sk_num", false); sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "sk_value", false); sk_free = (void (*)(void *))openssl_sym(&err, "sk_free", false); SSL_connect = (int (*)(SSL *))openssl_sym(&err, "SSL_connect", true); SSL_ctrl = (long (*)(SSL *, int, long, void *))openssl_sym(&err, "SSL_ctrl", true); SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", true); SSL_library_init = (int (*)(void))openssl_sym(&err, "SSL_library_init", false); SSL_free = (void (*)(SSL *))openssl_sym(&err, "SSL_free", true); SSL_get_error = (int (*)(SSL *, int))openssl_sym(&err, "SSL_get_error", true); SSL_get_verify_result = (long (*)(const SSL *ssl))openssl_sym(&err, "SSL_get_verify_result", true); SSL_load_error_strings = (void (*)(void))openssl_sym(&err, "SSL_load_error_strings", false); SSL_new = (SSL *(*)(SSL_CTX *))openssl_sym(&err, "SSL_new", true); SSL_read = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_read", true); SSL_set_bio = (void (*)(SSL *, BIO *, BIO *))openssl_sym(&err, "SSL_set_bio", true); SSL_shutdown = (int (*)(SSL *ssl))openssl_sym(&err, "SSL_shutdown", true); SSL_write = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_write", true); SSL_CTX_ctrl = (long (*)(SSL_CTX *, int, long, void *))openssl_sym(&err, "SSL_CTX_ctrl", true); SSL_CTX_free = (void (*)(SSL_CTX *))openssl_sym(&err, "SSL_CTX_free", true); SSL_CTX_new = (SSL_CTX *(*)(const SSL_METHOD *))openssl_sym(&err, "SSL_CTX_new", true); SSL_CTX_set_cipher_list = (int (*)(SSL_CTX *, const char *))openssl_sym(&err, "SSL_CTX_set_cipher_list", true); SSL_CTX_set_default_verify_paths = (int (*)(SSL_CTX *ctx))openssl_sym(&err, "SSL_CTX_set_default_verify_paths", true); SSL_CTX_set_options = (long (*)(SSL_CTX *, long))openssl_sym(&err, "SSL_CTX_set_options", false); SSL_CTX_set_verify = (void (*)(SSL_CTX *, int, int (*)(int, X509_STORE_CTX *)))openssl_sym(&err, "SSL_CTX_set_verify", true); SSL_CTX_load_verify_locations = (int (*)(SSL_CTX *, const char *, const char *))openssl_sym(&err, "SSL_CTX_load_verify_locations", true); SSLv23_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "SSLv23_method", false); TLS_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "TLS_method", false); X509_NAME_ENTRY_get_data = (ASN1_STRING *(*)(const X509_NAME_ENTRY *))openssl_sym(&err, "X509_NAME_ENTRY_get_data", true); X509_NAME_get_entry = (X509_NAME_ENTRY *(*)(X509_NAME *, int))openssl_sym(&err, "X509_NAME_get_entry", true); X509_NAME_get_index_by_NID = (int (*)(X509_NAME *, int, int))openssl_sym(&err, "X509_NAME_get_index_by_NID", true); X509_free = (void (*)(X509 *))openssl_sym(&err, "X509_free", true); X509_get_ext_d2i = (void *(*)(const X509 *x, int nid, int *crit, int *idx))openssl_sym(&err, "X509_get_ext_d2i", true); X509_get_subject_name = (X509_NAME *(*)(const X509 *))openssl_sym(&err, "X509_get_subject_name", true); i2d_X509 = (int (*)(X509 *a, unsigned char **ppout))openssl_sym(&err, "i2d_X509", true); if (err) goto on_error; /* Add legacy functionality */ if (!OPENSSL_init_ssl) { OPENSSL_init_ssl = OPENSSL_init_ssl__legacy; if (!SSL_library_init || !SSL_load_error_strings || !CRYPTO_num_locks || !CRYPTO_set_locking_callback || !CRYPTO_THREADID_set_callback || !CRYPTO_THREADID_set_numeric) { git_error_set(GIT_ERROR_SSL, "could not load legacy openssl initialization functions"); goto on_error; } } if (!SSL_CTX_set_options) SSL_CTX_set_options = SSL_CTX_set_options__legacy; if (TLS_method) SSLv23_method = TLS_method; if (!BIO_meth_new) { BIO_meth_new = BIO_meth_new__legacy; BIO_meth_new = BIO_meth_new__legacy; BIO_meth_free = BIO_meth_free__legacy; BIO_meth_set_write = BIO_meth_set_write__legacy; BIO_meth_set_read = BIO_meth_set_read__legacy; BIO_meth_set_puts = BIO_meth_set_puts__legacy; BIO_meth_set_gets = BIO_meth_set_gets__legacy; BIO_meth_set_ctrl = BIO_meth_set_ctrl__legacy; BIO_meth_set_create = BIO_meth_set_create__legacy; BIO_meth_set_destroy = BIO_meth_set_destroy__legacy; BIO_get_new_index = BIO_get_new_index__legacy; BIO_set_data = BIO_set_data__legacy; BIO_set_init = BIO_set_init__legacy; BIO_get_data = BIO_get_data__legacy; } if (!ASN1_STRING_get0_data) { if (!ASN1_STRING_data) { git_error_set(GIT_ERROR_SSL, "could not load legacy openssl string function"); goto on_error; } ASN1_STRING_get0_data = ASN1_STRING_get0_data__legacy; } if ((!OPENSSL_sk_num && !sk_num) || (!OPENSSL_sk_value && !sk_value) || (!OPENSSL_sk_free && !sk_free)) { git_error_set(GIT_ERROR_SSL, "could not load legacy openssl stack functions"); goto on_error; } if (git_runtime_shutdown_register(dynamic_shutdown) != 0) goto on_error; return 0; on_error: dlclose(openssl_handle); return -1; } int sk_GENERAL_NAME_num(const GENERAL_NAME *sk) { if (OPENSSL_sk_num) return OPENSSL_sk_num(sk); else if (sk_num) return sk_num(sk); GIT_ASSERT_WITH_RETVAL(false, 0); return 0; } GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i) { if (OPENSSL_sk_value) return OPENSSL_sk_value(sk, i); else if (sk_value) return sk_value(sk, i); GIT_ASSERT_WITH_RETVAL(false, NULL); return NULL; } void GENERAL_NAMES_free(GENERAL_NAME *sk) { if (OPENSSL_sk_free) OPENSSL_sk_free(sk); else if (sk_free) sk_free(sk); } #endif /* GIT_OPENSSL && GIT_OPENSSL_DYNAMIC */ git2r/src/libgit2/src/streams/registry.c0000644000175000017500000000546514125111754020052 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "streams/registry.h" #include "runtime.h" #include "streams/tls.h" #include "streams/mbedtls.h" #include "streams/openssl.h" #include "streams/stransport.h" struct stream_registry { git_rwlock lock; git_stream_registration callbacks; git_stream_registration tls_callbacks; }; static struct stream_registry stream_registry; static void shutdown_stream_registry(void) { git_rwlock_free(&stream_registry.lock); } int git_stream_registry_global_init(void) { if (git_rwlock_init(&stream_registry.lock) < 0) return -1; return git_runtime_shutdown_register(shutdown_stream_registry); } GIT_INLINE(void) stream_registration_cpy( git_stream_registration *target, git_stream_registration *src) { if (src) memcpy(target, src, sizeof(git_stream_registration)); else memset(target, 0, sizeof(git_stream_registration)); } int git_stream_registry_lookup(git_stream_registration *out, git_stream_t type) { git_stream_registration *target; int error = GIT_ENOTFOUND; GIT_ASSERT_ARG(out); switch(type) { case GIT_STREAM_STANDARD: target = &stream_registry.callbacks; break; case GIT_STREAM_TLS: target = &stream_registry.tls_callbacks; break; default: git_error_set(GIT_ERROR_INVALID, "invalid stream type"); return -1; } if (git_rwlock_rdlock(&stream_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock stream registry"); return -1; } if (target->init) { stream_registration_cpy(out, target); error = 0; } git_rwlock_rdunlock(&stream_registry.lock); return error; } int git_stream_register(git_stream_t type, git_stream_registration *registration) { GIT_ASSERT(!registration || registration->init); GIT_ERROR_CHECK_VERSION(registration, GIT_STREAM_VERSION, "stream_registration"); if (git_rwlock_wrlock(&stream_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock stream registry"); return -1; } if ((type & GIT_STREAM_STANDARD) == GIT_STREAM_STANDARD) stream_registration_cpy(&stream_registry.callbacks, registration); if ((type & GIT_STREAM_TLS) == GIT_STREAM_TLS) stream_registration_cpy(&stream_registry.tls_callbacks, registration); git_rwlock_wrunlock(&stream_registry.lock); return 0; } #ifndef GIT_DEPRECATE_HARD int git_stream_register_tls( int GIT_CALLBACK(ctor)(git_stream **out, const char *host, const char *port)) { git_stream_registration registration = {0}; if (ctor) { registration.version = GIT_STREAM_VERSION; registration.init = ctor; registration.wrap = NULL; return git_stream_register(GIT_STREAM_TLS, ®istration); } else { return git_stream_register(GIT_STREAM_TLS, NULL); } } #endif git2r/src/libgit2/src/streams/stransport.h0000644000175000017500000000107514125111754020417 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_streams_stransport_h__ #define INCLUDE_streams_stransport_h__ #include "common.h" #include "git2/sys/stream.h" #ifdef GIT_SECURE_TRANSPORT extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port); extern int git_stransport_stream_wrap(git_stream **out, git_stream *in, const char *host); #endif #endif git2r/src/libgit2/src/streams/socket.c0000644000175000017500000001172014125111754017461 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "streams/socket.h" #include "posix.h" #include "netops.h" #include "registry.h" #include "stream.h" #ifndef _WIN32 # include # include # include # include # include # include # include #else # include # include # ifdef _MSC_VER # pragma comment(lib, "ws2_32") # endif #endif #ifdef GIT_WIN32 static void net_set_error(const char *str) { int error = WSAGetLastError(); char * win32_error = git_win32_get_error_message(error); if (win32_error) { git_error_set(GIT_ERROR_NET, "%s: %s", str, win32_error); git__free(win32_error); } else { git_error_set(GIT_ERROR_NET, "%s", str); } } #else static void net_set_error(const char *str) { git_error_set(GIT_ERROR_NET, "%s: %s", str, strerror(errno)); } #endif static int close_socket(GIT_SOCKET s) { if (s == INVALID_SOCKET) return 0; #ifdef GIT_WIN32 if (SOCKET_ERROR == closesocket(s)) return -1; if (0 != WSACleanup()) { git_error_set(GIT_ERROR_OS, "winsock cleanup failed"); return -1; } return 0; #else return close(s); #endif } static int socket_connect(git_stream *stream) { struct addrinfo *info = NULL, *p; struct addrinfo hints; git_socket_stream *st = (git_socket_stream *) stream; GIT_SOCKET s = INVALID_SOCKET; int ret; #ifdef GIT_WIN32 /* on win32, the WSA context needs to be initialized * before any socket calls can be performed */ WSADATA wsd; if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { git_error_set(GIT_ERROR_OS, "winsock init failed"); return -1; } if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { WSACleanup(); git_error_set(GIT_ERROR_OS, "winsock init failed"); return -1; } #endif memset(&hints, 0x0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) { git_error_set(GIT_ERROR_NET, "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); return -1; } for (p = info; p != NULL; p = p->ai_next) { s = socket(p->ai_family, p->ai_socktype | SOCK_CLOEXEC, p->ai_protocol); if (s == INVALID_SOCKET) continue; if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) break; /* If we can't connect, try the next one */ close_socket(s); s = INVALID_SOCKET; } /* Oops, we couldn't connect to any address */ if (s == INVALID_SOCKET && p == NULL) { git_error_set(GIT_ERROR_OS, "failed to connect to %s", st->host); p_freeaddrinfo(info); return -1; } st->s = s; p_freeaddrinfo(info); return 0; } static ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags) { git_socket_stream *st = (git_socket_stream *) stream; ssize_t written; errno = 0; if ((written = p_send(st->s, data, len, flags)) < 0) { net_set_error("error sending data"); return -1; } return written; } static ssize_t socket_read(git_stream *stream, void *data, size_t len) { ssize_t ret; git_socket_stream *st = (git_socket_stream *) stream; if ((ret = p_recv(st->s, data, len, 0)) < 0) net_set_error("error receiving socket data"); return ret; } static int socket_close(git_stream *stream) { git_socket_stream *st = (git_socket_stream *) stream; int error; error = close_socket(st->s); st->s = INVALID_SOCKET; return error; } static void socket_free(git_stream *stream) { git_socket_stream *st = (git_socket_stream *) stream; git__free(st->host); git__free(st->port); git__free(st); } static int default_socket_stream_new( git_stream **out, const char *host, const char *port) { git_socket_stream *st; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(host); GIT_ASSERT_ARG(port); st = git__calloc(1, sizeof(git_socket_stream)); GIT_ERROR_CHECK_ALLOC(st); st->host = git__strdup(host); GIT_ERROR_CHECK_ALLOC(st->host); if (port) { st->port = git__strdup(port); GIT_ERROR_CHECK_ALLOC(st->port); } st->parent.version = GIT_STREAM_VERSION; st->parent.connect = socket_connect; st->parent.write = socket_write; st->parent.read = socket_read; st->parent.close = socket_close; st->parent.free = socket_free; st->s = INVALID_SOCKET; *out = (git_stream *) st; return 0; } int git_socket_stream_new( git_stream **out, const char *host, const char *port) { int (*init)(git_stream **, const char *, const char *) = NULL; git_stream_registration custom = {0}; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(host); GIT_ASSERT_ARG(port); if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_STANDARD)) == 0) init = custom.init; else if (error == GIT_ENOTFOUND) init = default_socket_stream_new; else return error; if (!init) { git_error_set(GIT_ERROR_NET, "there is no socket stream available"); return -1; } return init(out, host, port); } git2r/src/libgit2/src/streams/mbedtls.c0000644000175000017500000003121514125111754017624 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "streams/mbedtls.h" #ifdef GIT_MBEDTLS #include #include "runtime.h" #include "stream.h" #include "streams/socket.h" #include "netops.h" #include "git2/transport.h" #include "util.h" #ifndef GIT_DEFAULT_CERT_LOCATION #define GIT_DEFAULT_CERT_LOCATION NULL #endif /* Work around C90-conformance issues */ #if defined(_MSC_VER) # define inline __inline #elif defined(__GNUC__) # define inline __inline__ #else # define inline #endif #include #include #include #include #include #undef inline #define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA" #define GIT_SSL_DEFAULT_CIPHERS_COUNT 30 static mbedtls_ssl_config *git__ssl_conf; static int ciphers_list[GIT_SSL_DEFAULT_CIPHERS_COUNT]; static mbedtls_entropy_context *mbedtls_entropy; /** * This function aims to clean-up the SSL context which * we allocated. */ static void shutdown_ssl(void) { if (git__ssl_conf) { mbedtls_x509_crt_free(git__ssl_conf->ca_chain); git__free(git__ssl_conf->ca_chain); mbedtls_ctr_drbg_free(git__ssl_conf->p_rng); git__free(git__ssl_conf->p_rng); mbedtls_ssl_config_free(git__ssl_conf); git__free(git__ssl_conf); git__ssl_conf = NULL; } if (mbedtls_entropy) { mbedtls_entropy_free(mbedtls_entropy); git__free(mbedtls_entropy); mbedtls_entropy = NULL; } } int git_mbedtls_stream_global_init(void) { int loaded = 0; char *crtpath = GIT_DEFAULT_CERT_LOCATION; struct stat statbuf; mbedtls_ctr_drbg_context *ctr_drbg = NULL; size_t ciphers_known = 0; char *cipher_name = NULL; char *cipher_string = NULL; char *cipher_string_tmp = NULL; git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config)); GIT_ERROR_CHECK_ALLOC(git__ssl_conf); mbedtls_ssl_config_init(git__ssl_conf); if (mbedtls_ssl_config_defaults(git__ssl_conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS"); goto cleanup; } /* configure TLSv1 */ mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); /* verify_server_cert is responsible for making the check. * OPTIONAL because REQUIRED drops the certificate as soon as the check * is made, so we can never see the certificate and override it. */ mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL); /* set the list of allowed ciphersuites */ ciphers_known = 0; cipher_string = cipher_string_tmp = git__strdup(GIT_SSL_DEFAULT_CIPHERS); GIT_ERROR_CHECK_ALLOC(cipher_string); while ((cipher_name = git__strtok(&cipher_string_tmp, ":")) != NULL) { int cipherid = mbedtls_ssl_get_ciphersuite_id(cipher_name); if (cipherid == 0) continue; if (ciphers_known >= ARRAY_SIZE(ciphers_list)) { git_error_set(GIT_ERROR_SSL, "out of cipher list space"); goto cleanup; } ciphers_list[ciphers_known++] = cipherid; } git__free(cipher_string); if (!ciphers_known) { git_error_set(GIT_ERROR_SSL, "no cipher could be enabled"); goto cleanup; } mbedtls_ssl_conf_ciphersuites(git__ssl_conf, ciphers_list); /* Seeding the random number generator */ mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context)); GIT_ERROR_CHECK_ALLOC(mbedtls_entropy); mbedtls_entropy_init(mbedtls_entropy); ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context)); GIT_ERROR_CHECK_ALLOC(ctr_drbg); mbedtls_ctr_drbg_init(ctr_drbg); if (mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, mbedtls_entropy, NULL, 0) != 0) { git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS entropy pool"); goto cleanup; } mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg); /* load default certificates */ if (crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) loaded = (git_mbedtls__set_cert_location(crtpath, NULL) == 0); if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) loaded = (git_mbedtls__set_cert_location(NULL, crtpath) == 0); return git_runtime_shutdown_register(shutdown_ssl); cleanup: mbedtls_ctr_drbg_free(ctr_drbg); git__free(ctr_drbg); mbedtls_ssl_config_free(git__ssl_conf); git__free(git__ssl_conf); git__ssl_conf = NULL; return -1; } static int bio_read(void *b, unsigned char *buf, size_t len) { git_stream *io = (git_stream *) b; return (int) git_stream_read(io, buf, min(len, INT_MAX)); } static int bio_write(void *b, const unsigned char *buf, size_t len) { git_stream *io = (git_stream *) b; return (int) git_stream_write(io, (const char *)buf, min(len, INT_MAX), 0); } static int ssl_set_error(mbedtls_ssl_context *ssl, int error) { char errbuf[512]; int ret = -1; GIT_ASSERT(error != MBEDTLS_ERR_SSL_WANT_READ); GIT_ASSERT(error != MBEDTLS_ERR_SSL_WANT_WRITE); if (error != 0) mbedtls_strerror( error, errbuf, 512 ); switch(error) { case 0: git_error_set(GIT_ERROR_SSL, "SSL error: unknown error"); break; case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: git_error_set(GIT_ERROR_SSL, "SSL error: %#04x [%x] - %s", error, ssl->session_negotiate->verify_result, errbuf); ret = GIT_ECERTIFICATE; break; default: git_error_set(GIT_ERROR_SSL, "SSL error: %#04x - %s", error, errbuf); } return ret; } static int ssl_teardown(mbedtls_ssl_context *ssl) { int ret = 0; ret = mbedtls_ssl_close_notify(ssl); if (ret < 0) ret = ssl_set_error(ssl, ret); mbedtls_ssl_free(ssl); return ret; } static int verify_server_cert(mbedtls_ssl_context *ssl) { int ret = -1; if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) { char vrfy_buf[512]; int len = mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", ret); if (len >= 1) vrfy_buf[len - 1] = '\0'; /* Remove trailing \n */ git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid: %#04x - %s", ret, vrfy_buf); return GIT_ECERTIFICATE; } return 0; } typedef struct { git_stream parent; git_stream *io; int owned; bool connected; char *host; mbedtls_ssl_context *ssl; git_cert_x509 cert_info; } mbedtls_stream; static int mbedtls_connect(git_stream *stream) { int ret; mbedtls_stream *st = (mbedtls_stream *) stream; if (st->owned && (ret = git_stream_connect(st->io)) < 0) return ret; st->connected = true; mbedtls_ssl_set_hostname(st->ssl, st->host); mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL); if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0) return ssl_set_error(st->ssl, ret); return verify_server_cert(st->ssl); } static int mbedtls_certificate(git_cert **out, git_stream *stream) { unsigned char *encoded_cert; mbedtls_stream *st = (mbedtls_stream *) stream; const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(st->ssl); if (!cert) { git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate"); return -1; } /* Retrieve the length of the certificate first */ if (cert->raw.len == 0) { git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information"); return -1; } encoded_cert = git__malloc(cert->raw.len); GIT_ERROR_CHECK_ALLOC(encoded_cert); memcpy(encoded_cert, cert->raw.p, cert->raw.len); st->cert_info.parent.cert_type = GIT_CERT_X509; st->cert_info.data = encoded_cert; st->cert_info.len = cert->raw.len; *out = &st->cert_info.parent; return 0; } static int mbedtls_set_proxy(git_stream *stream, const git_proxy_options *proxy_options) { mbedtls_stream *st = (mbedtls_stream *) stream; return git_stream_set_proxy(st->io, proxy_options); } static ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t len, int flags) { mbedtls_stream *st = (mbedtls_stream *) stream; int written; GIT_UNUSED(flags); /* * `mbedtls_ssl_write` can only represent INT_MAX bytes * written via its return value. We thus need to clamp * the maximum number of bytes written. */ len = min(len, INT_MAX); if ((written = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0) return ssl_set_error(st->ssl, written); return written; } static ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len) { mbedtls_stream *st = (mbedtls_stream *) stream; int ret; if ((ret = mbedtls_ssl_read(st->ssl, (unsigned char *)data, len)) <= 0) ssl_set_error(st->ssl, ret); return ret; } static int mbedtls_stream_close(git_stream *stream) { mbedtls_stream *st = (mbedtls_stream *) stream; int ret = 0; if (st->connected && (ret = ssl_teardown(st->ssl)) != 0) return -1; st->connected = false; return st->owned ? git_stream_close(st->io) : 0; } static void mbedtls_stream_free(git_stream *stream) { mbedtls_stream *st = (mbedtls_stream *) stream; if (st->owned) git_stream_free(st->io); git__free(st->host); git__free(st->cert_info.data); mbedtls_ssl_free(st->ssl); git__free(st->ssl); git__free(st); } static int mbedtls_stream_wrap( git_stream **out, git_stream *in, const char *host, int owned) { mbedtls_stream *st; int error; st = git__calloc(1, sizeof(mbedtls_stream)); GIT_ERROR_CHECK_ALLOC(st); st->io = in; st->owned = owned; st->ssl = git__malloc(sizeof(mbedtls_ssl_context)); GIT_ERROR_CHECK_ALLOC(st->ssl); mbedtls_ssl_init(st->ssl); if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) { git_error_set(GIT_ERROR_SSL, "failed to create ssl object"); error = -1; goto out_err; } st->host = git__strdup(host); GIT_ERROR_CHECK_ALLOC(st->host); st->parent.version = GIT_STREAM_VERSION; st->parent.encrypted = 1; st->parent.proxy_support = git_stream_supports_proxy(st->io); st->parent.connect = mbedtls_connect; st->parent.certificate = mbedtls_certificate; st->parent.set_proxy = mbedtls_set_proxy; st->parent.read = mbedtls_stream_read; st->parent.write = mbedtls_stream_write; st->parent.close = mbedtls_stream_close; st->parent.free = mbedtls_stream_free; *out = (git_stream *) st; return 0; out_err: mbedtls_ssl_free(st->ssl); git_stream_close(st->io); git_stream_free(st->io); git__free(st); return error; } int git_mbedtls_stream_wrap( git_stream **out, git_stream *in, const char *host) { return mbedtls_stream_wrap(out, in, host, 0); } int git_mbedtls_stream_new( git_stream **out, const char *host, const char *port) { git_stream *stream; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(host); GIT_ASSERT_ARG(port); if ((error = git_socket_stream_new(&stream, host, port)) < 0) return error; if ((error = mbedtls_stream_wrap(out, stream, host, 1)) < 0) { git_stream_close(stream); git_stream_free(stream); } return error; } int git_mbedtls__set_cert_location(const char *file, const char *path) { int ret = 0; char errbuf[512]; mbedtls_x509_crt *cacert; GIT_ASSERT_ARG(file || path); cacert = git__malloc(sizeof(mbedtls_x509_crt)); GIT_ERROR_CHECK_ALLOC(cacert); mbedtls_x509_crt_init(cacert); if (file) ret = mbedtls_x509_crt_parse_file(cacert, file); if (ret >= 0 && path) ret = mbedtls_x509_crt_parse_path(cacert, path); /* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */ if (ret < 0) { mbedtls_x509_crt_free(cacert); git__free(cacert); mbedtls_strerror( ret, errbuf, 512 ); git_error_set(GIT_ERROR_SSL, "failed to load CA certificates: %#04x - %s", ret, errbuf); return -1; } mbedtls_x509_crt_free(git__ssl_conf->ca_chain); git__free(git__ssl_conf->ca_chain); mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL); return 0; } #else #include "stream.h" int git_mbedtls_stream_global_init(void) { return 0; } #endif git2r/src/libgit2/src/streams/openssl_legacy.c0000644000175000017500000001041214125111754021175 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "streams/openssl.h" #include "streams/openssl_legacy.h" #include "runtime.h" #include "git2/sys/openssl.h" #if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC) # include # include # include # include #endif #if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC) /* * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it * which do not exist in previous versions. We define these inline functions so * we can program against the interface instead of littering the implementation * with ifdefs. We do the same for OPENSSL_init_ssl. */ int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings) { GIT_UNUSED(opts); GIT_UNUSED(settings); SSL_load_error_strings(); SSL_library_init(); return 0; } BIO_METHOD *BIO_meth_new__legacy(int type, const char *name) { BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD)); if (!meth) { return NULL; } meth->type = type; meth->name = name; return meth; } void BIO_meth_free__legacy(BIO_METHOD *biom) { git__free(biom); } int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int)) { biom->bwrite = write; return 1; } int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int)) { biom->bread = read; return 1; } int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *)) { biom->bputs = puts; return 1; } int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int)) { biom->bgets = gets; return 1; } int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *)) { biom->ctrl = ctrl; return 1; } int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *)) { biom->create = create; return 1; } int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *)) { biom->destroy = destroy; return 1; } int BIO_get_new_index__legacy(void) { /* This exists as of 1.1 so before we'd just have 0 */ return 0; } void BIO_set_init__legacy(BIO *b, int init) { b->init = init; } void BIO_set_data__legacy(BIO *a, void *ptr) { a->ptr = ptr; } void *BIO_get_data__legacy(BIO *a) { return a->ptr; } const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x) { return ASN1_STRING_data((ASN1_STRING *)x); } long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op) { return SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, op, NULL); } # if defined(GIT_THREADS) static git_mutex *openssl_locks; static void openssl_locking_function(int mode, int n, const char *file, int line) { int lock; GIT_UNUSED(file); GIT_UNUSED(line); lock = mode & CRYPTO_LOCK; if (lock) (void)git_mutex_lock(&openssl_locks[n]); else git_mutex_unlock(&openssl_locks[n]); } static void shutdown_ssl_locking(void) { int num_locks, i; num_locks = CRYPTO_num_locks(); CRYPTO_set_locking_callback(NULL); for (i = 0; i < num_locks; ++i) git_mutex_free(&openssl_locks[i]); git__free(openssl_locks); } static void threadid_cb(CRYPTO_THREADID *threadid) { GIT_UNUSED(threadid); CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid()); } int git_openssl_set_locking(void) { int num_locks, i; #ifndef GIT_THREADS git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads"); return -1; #endif #ifdef GIT_OPENSSL_DYNAMIC /* * This function is required on legacy versions of OpenSSL; when building * with dynamically-loaded OpenSSL, we detect whether we loaded it or not. */ if (!CRYPTO_set_locking_callback) return 0; #endif CRYPTO_THREADID_set_callback(threadid_cb); num_locks = CRYPTO_num_locks(); openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); GIT_ERROR_CHECK_ALLOC(openssl_locks); for (i = 0; i < num_locks; i++) { if (git_mutex_init(&openssl_locks[i]) != 0) { git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks"); return -1; } } CRYPTO_set_locking_callback(openssl_locking_function); return git_runtime_shutdown_register(shutdown_ssl_locking); } #endif /* GIT_THREADS */ #endif /* GIT_OPENSSL_LEGACY || GIT_OPENSSL_DYNAMIC */ git2r/src/libgit2/src/streams/tls.h0000644000175000017500000000201214125111754016772 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_streams_tls_h__ #define INCLUDE_streams_tls_h__ #include "common.h" #include "git2/sys/stream.h" /** * Create a TLS stream with the most appropriate backend available for * the current platform, whether that's SecureTransport on macOS, * OpenSSL or mbedTLS on other Unixes, or something else entirely. */ extern int git_tls_stream_new(git_stream **out, const char *host, const char *port); /** * Create a TLS stream on top of an existing insecure stream, using * the most appropriate backend available for the current platform. * * This allows us to create a CONNECT stream on top of a proxy; * using SecureTransport on macOS, OpenSSL or mbedTLS on other * Unixes, or something else entirely. */ extern int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host); #endif git2r/src/libgit2/src/streams/openssl.c0000644000175000017500000004117114125111754017657 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "streams/openssl.h" #include "streams/openssl_legacy.h" #include "streams/openssl_dynamic.h" #ifdef GIT_OPENSSL #include #include "common.h" #include "runtime.h" #include "settings.h" #include "posix.h" #include "stream.h" #include "streams/socket.h" #include "netops.h" #include "git2/transport.h" #include "git2/sys/openssl.h" #ifndef GIT_WIN32 # include # include # include #endif #ifndef GIT_OPENSSL_DYNAMIC # include # include # include # include #endif SSL_CTX *git__ssl_ctx; #define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA" static BIO_METHOD *git_stream_bio_method; static int init_bio_method(void); /** * This function aims to clean-up the SSL context which * we allocated. */ static void shutdown_ssl(void) { if (git_stream_bio_method) { BIO_meth_free(git_stream_bio_method); git_stream_bio_method = NULL; } if (git__ssl_ctx) { SSL_CTX_free(git__ssl_ctx); git__ssl_ctx = NULL; } } #ifdef VALGRIND # if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC) static void *git_openssl_malloc(size_t bytes, const char *file, int line) { GIT_UNUSED(file); GIT_UNUSED(line); return git__calloc(1, bytes); } static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line) { GIT_UNUSED(file); GIT_UNUSED(line); return git__realloc(mem, size); } static void git_openssl_free(void *mem, const char *file, int line) { GIT_UNUSED(file); GIT_UNUSED(line); git__free(mem); } # else /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */ static void *git_openssl_malloc(size_t bytes) { return git__calloc(1, bytes); } static void *git_openssl_realloc(void *mem, size_t size) { return git__realloc(mem, size); } static void git_openssl_free(void *mem) { git__free(mem); } # endif /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */ #endif /* VALGRIND */ static int openssl_init(void) { long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; const char *ciphers = git_libgit2__ssl_ciphers(); #ifdef VALGRIND static bool allocators_initialized = false; #endif /* Older OpenSSL and MacOS OpenSSL doesn't have this */ #ifdef SSL_OP_NO_COMPRESSION ssl_opts |= SSL_OP_NO_COMPRESSION; #endif #ifdef VALGRIND /* * Swap in our own allocator functions that initialize * allocated memory to avoid spurious valgrind warnings. * Don't error on failure; many builds of OpenSSL do not * allow you to set these functions. */ if (!allocators_initialized) { CRYPTO_set_mem_functions(git_openssl_malloc, git_openssl_realloc, git_openssl_free); allocators_initialized = true; } #endif OPENSSL_init_ssl(0, NULL); /* * Load SSLv{2,3} and TLSv1 so that we can talk with servers * which use the SSL hellos, which are often used for * compatibility. We then disable SSL so we only allow OpenSSL * to speak TLSv1 to perform the encryption itself. */ if (!(git__ssl_ctx = SSL_CTX_new(SSLv23_method()))) goto error; SSL_CTX_set_options(git__ssl_ctx, ssl_opts); SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) goto error; if (!ciphers) ciphers = GIT_SSL_DEFAULT_CIPHERS; if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) goto error; if (init_bio_method() < 0) goto error; return git_runtime_shutdown_register(shutdown_ssl); error: git_error_set(GIT_ERROR_NET, "could not initialize openssl: %s", ERR_error_string(ERR_get_error(), NULL)); SSL_CTX_free(git__ssl_ctx); git__ssl_ctx = NULL; return -1; } /* * When we use dynamic loading, we defer OpenSSL initialization until * it's first used. `openssl_ensure_initialized` will do the work * under a mutex. */ git_mutex openssl_mutex; bool openssl_initialized; int git_openssl_stream_global_init(void) { #ifndef GIT_OPENSSL_DYNAMIC return openssl_init(); #else if (git_mutex_init(&openssl_mutex) != 0) return -1; return 0; #endif } static int openssl_ensure_initialized(void) { #ifdef GIT_OPENSSL_DYNAMIC int error = 0; if (git_mutex_lock(&openssl_mutex) != 0) return -1; if (!openssl_initialized) { if ((error = git_openssl_stream_dynamic_init()) == 0) error = openssl_init(); openssl_initialized = true; } error |= git_mutex_unlock(&openssl_mutex); return error; #else return 0; #endif } #if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC) int git_openssl_set_locking(void) { # ifdef GIT_THREADS return 0; # else git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads"); return -1; # endif } #endif static int bio_create(BIO *b) { BIO_set_init(b, 1); BIO_set_data(b, NULL); return 1; } static int bio_destroy(BIO *b) { if (!b) return 0; BIO_set_data(b, NULL); return 1; } static int bio_read(BIO *b, char *buf, int len) { git_stream *io = (git_stream *) BIO_get_data(b); return (int) git_stream_read(io, buf, len); } static int bio_write(BIO *b, const char *buf, int len) { git_stream *io = (git_stream *) BIO_get_data(b); return (int) git_stream_write(io, buf, len, 0); } static long bio_ctrl(BIO *b, int cmd, long num, void *ptr) { GIT_UNUSED(b); GIT_UNUSED(num); GIT_UNUSED(ptr); if (cmd == BIO_CTRL_FLUSH) return 1; return 0; } static int bio_gets(BIO *b, char *buf, int len) { GIT_UNUSED(b); GIT_UNUSED(buf); GIT_UNUSED(len); return -1; } static int bio_puts(BIO *b, const char *str) { return bio_write(b, str, strlen(str)); } static int init_bio_method(void) { /* Set up the BIO_METHOD we use for wrapping our own stream implementations */ git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream"); GIT_ERROR_CHECK_ALLOC(git_stream_bio_method); BIO_meth_set_write(git_stream_bio_method, bio_write); BIO_meth_set_read(git_stream_bio_method, bio_read); BIO_meth_set_puts(git_stream_bio_method, bio_puts); BIO_meth_set_gets(git_stream_bio_method, bio_gets); BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl); BIO_meth_set_create(git_stream_bio_method, bio_create); BIO_meth_set_destroy(git_stream_bio_method, bio_destroy); return 0; } static int ssl_set_error(SSL *ssl, int error) { int err; unsigned long e; err = SSL_get_error(ssl, error); GIT_ASSERT(err != SSL_ERROR_WANT_READ); GIT_ASSERT(err != SSL_ERROR_WANT_WRITE); switch (err) { case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: git_error_set(GIT_ERROR_SSL, "SSL error: connection failure"); break; case SSL_ERROR_WANT_X509_LOOKUP: git_error_set(GIT_ERROR_SSL, "SSL error: x509 error"); break; case SSL_ERROR_SYSCALL: e = ERR_get_error(); if (e > 0) { char errmsg[256]; ERR_error_string_n(e, errmsg, sizeof(errmsg)); git_error_set(GIT_ERROR_NET, "SSL error: %s", errmsg); break; } else if (error < 0) { git_error_set(GIT_ERROR_OS, "SSL error: syscall failure"); break; } git_error_set(GIT_ERROR_SSL, "SSL error: received early EOF"); return GIT_EEOF; break; case SSL_ERROR_SSL: { char errmsg[256]; e = ERR_get_error(); ERR_error_string_n(e, errmsg, sizeof(errmsg)); git_error_set(GIT_ERROR_SSL, "SSL error: %s", errmsg); break; } case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: default: git_error_set(GIT_ERROR_SSL, "SSL error: unknown error"); break; } return -1; } static int ssl_teardown(SSL *ssl) { int ret; ret = SSL_shutdown(ssl); if (ret < 0) ret = ssl_set_error(ssl, ret); else ret = 0; return ret; } static int check_host_name(const char *name, const char *host) { if (!strcasecmp(name, host)) return 0; if (gitno__match_host(name, host) < 0) return -1; return 0; } static int verify_server_cert(SSL *ssl, const char *host) { X509 *cert = NULL; X509_NAME *peer_name; ASN1_STRING *str; unsigned char *peer_cn = NULL; int matched = -1, type = GEN_DNS; GENERAL_NAMES *alts; struct in6_addr addr6; struct in_addr addr4; void *addr = NULL; int i = -1, j, error = 0; if (SSL_get_verify_result(ssl) != X509_V_OK) { git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid"); return GIT_ECERTIFICATE; } /* Try to parse the host as an IP address to see if it is */ if (p_inet_pton(AF_INET, host, &addr4)) { type = GEN_IPADD; addr = &addr4; } else { if (p_inet_pton(AF_INET6, host, &addr6)) { type = GEN_IPADD; addr = &addr6; } } cert = SSL_get_peer_certificate(ssl); if (!cert) { error = -1; git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate"); goto cleanup; } /* Check the alternative names */ alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (alts) { int num; num = sk_GENERAL_NAME_num(alts); for (i = 0; i < num && matched != 1; i++) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i); const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5); size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5); /* Skip any names of a type we're not looking for */ if (gn->type != type) continue; if (type == GEN_DNS) { /* If it contains embedded NULs, don't even try */ if (memchr(name, '\0', namelen)) continue; if (check_host_name(name, host) < 0) matched = 0; else matched = 1; } else if (type == GEN_IPADD) { /* Here name isn't so much a name but a binary representation of the IP */ matched = addr && !!memcmp(name, addr, namelen); } } } GENERAL_NAMES_free(alts); if (matched == 0) goto cert_fail_name; if (matched == 1) { goto cleanup; } /* If no alternative names are available, check the common name */ peer_name = X509_get_subject_name(cert); if (peer_name == NULL) goto on_error; if (peer_name) { /* Get the index of the last CN entry */ while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0) i = j; } if (i < 0) goto on_error; str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i)); if (str == NULL) goto on_error; /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */ if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) { int size = ASN1_STRING_length(str); if (size > 0) { peer_cn = OPENSSL_malloc(size + 1); GIT_ERROR_CHECK_ALLOC(peer_cn); memcpy(peer_cn, ASN1_STRING_get0_data(str), size); peer_cn[size] = '\0'; } else { goto cert_fail_name; } } else { int size = ASN1_STRING_to_UTF8(&peer_cn, str); GIT_ERROR_CHECK_ALLOC(peer_cn); if (memchr(peer_cn, '\0', size)) goto cert_fail_name; } if (check_host_name((char *)peer_cn, host) < 0) goto cert_fail_name; goto cleanup; cert_fail_name: error = GIT_ECERTIFICATE; git_error_set(GIT_ERROR_SSL, "hostname does not match certificate"); goto cleanup; on_error: error = ssl_set_error(ssl, 0); goto cleanup; cleanup: X509_free(cert); OPENSSL_free(peer_cn); return error; } typedef struct { git_stream parent; git_stream *io; int owned; bool connected; char *host; SSL *ssl; git_cert_x509 cert_info; } openssl_stream; static int openssl_connect(git_stream *stream) { int ret; BIO *bio; openssl_stream *st = (openssl_stream *) stream; if (st->owned && (ret = git_stream_connect(st->io)) < 0) return ret; bio = BIO_new(git_stream_bio_method); GIT_ERROR_CHECK_ALLOC(bio); BIO_set_data(bio, st->io); SSL_set_bio(st->ssl, bio, bio); /* specify the host in case SNI is needed */ #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME SSL_set_tlsext_host_name(st->ssl, st->host); #endif if ((ret = SSL_connect(st->ssl)) <= 0) return ssl_set_error(st->ssl, ret); st->connected = true; return verify_server_cert(st->ssl, st->host); } static int openssl_certificate(git_cert **out, git_stream *stream) { openssl_stream *st = (openssl_stream *) stream; X509 *cert = SSL_get_peer_certificate(st->ssl); unsigned char *guard, *encoded_cert = NULL; int error, len; /* Retrieve the length of the certificate first */ len = i2d_X509(cert, NULL); if (len < 0) { git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information"); error = -1; goto out; } encoded_cert = git__malloc(len); GIT_ERROR_CHECK_ALLOC(encoded_cert); /* i2d_X509 makes 'guard' point to just after the data */ guard = encoded_cert; len = i2d_X509(cert, &guard); if (len < 0) { git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information"); error = -1; goto out; } st->cert_info.parent.cert_type = GIT_CERT_X509; st->cert_info.data = encoded_cert; st->cert_info.len = len; encoded_cert = NULL; *out = &st->cert_info.parent; error = 0; out: git__free(encoded_cert); X509_free(cert); return error; } static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts) { openssl_stream *st = (openssl_stream *) stream; return git_stream_set_proxy(st->io, proxy_opts); } static ssize_t openssl_write(git_stream *stream, const char *data, size_t data_len, int flags) { openssl_stream *st = (openssl_stream *) stream; int ret, len = min(data_len, INT_MAX); GIT_UNUSED(flags); if ((ret = SSL_write(st->ssl, data, len)) <= 0) return ssl_set_error(st->ssl, ret); return ret; } static ssize_t openssl_read(git_stream *stream, void *data, size_t len) { openssl_stream *st = (openssl_stream *) stream; int ret; if ((ret = SSL_read(st->ssl, data, len)) <= 0) return ssl_set_error(st->ssl, ret); return ret; } static int openssl_close(git_stream *stream) { openssl_stream *st = (openssl_stream *) stream; int ret; if (st->connected && (ret = ssl_teardown(st->ssl)) < 0) return -1; st->connected = false; return st->owned ? git_stream_close(st->io) : 0; } static void openssl_free(git_stream *stream) { openssl_stream *st = (openssl_stream *) stream; if (st->owned) git_stream_free(st->io); SSL_free(st->ssl); git__free(st->host); git__free(st->cert_info.data); git__free(st); } static int openssl_stream_wrap( git_stream **out, git_stream *in, const char *host, int owned) { openssl_stream *st; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(in); GIT_ASSERT_ARG(host); st = git__calloc(1, sizeof(openssl_stream)); GIT_ERROR_CHECK_ALLOC(st); st->io = in; st->owned = owned; st->ssl = SSL_new(git__ssl_ctx); if (st->ssl == NULL) { git_error_set(GIT_ERROR_SSL, "failed to create ssl object"); git__free(st); return -1; } st->host = git__strdup(host); GIT_ERROR_CHECK_ALLOC(st->host); st->parent.version = GIT_STREAM_VERSION; st->parent.encrypted = 1; st->parent.proxy_support = git_stream_supports_proxy(st->io); st->parent.connect = openssl_connect; st->parent.certificate = openssl_certificate; st->parent.set_proxy = openssl_set_proxy; st->parent.read = openssl_read; st->parent.write = openssl_write; st->parent.close = openssl_close; st->parent.free = openssl_free; *out = (git_stream *) st; return 0; } int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host) { if (openssl_ensure_initialized() < 0) return -1; return openssl_stream_wrap(out, in, host, 0); } int git_openssl_stream_new(git_stream **out, const char *host, const char *port) { git_stream *stream = NULL; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(host); GIT_ASSERT_ARG(port); if (openssl_ensure_initialized() < 0) return -1; if ((error = git_socket_stream_new(&stream, host, port)) < 0) return error; if ((error = openssl_stream_wrap(out, stream, host, 1)) < 0) { git_stream_close(stream); git_stream_free(stream); } return error; } int git_openssl__set_cert_location(const char *file, const char *path) { if (openssl_ensure_initialized() < 0) return -1; if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) { char errmsg[256]; ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); git_error_set(GIT_ERROR_SSL, "OpenSSL error: failed to load certificates: %s", errmsg); return -1; } return 0; } #else #include "stream.h" #include "git2/sys/openssl.h" int git_openssl_stream_global_init(void) { return 0; } int git_openssl_set_locking(void) { git_error_set(GIT_ERROR_SSL, "libgit2 was not built with OpenSSL support"); return -1; } #endif git2r/src/libgit2/src/object.c0000644000175000017500000003237214125111754015767 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "object.h" #include "git2/object.h" #include "repository.h" #include "commit.h" #include "hash.h" #include "tree.h" #include "blob.h" #include "oid.h" #include "tag.h" bool git_object__strict_input_validation = true; extern int git_odb_hash(git_oid *out, const void *data, size_t len, git_object_t type); size_t git_object__size(git_object_t type); typedef struct { const char *str; /* type name string */ size_t size; /* size in bytes of the object structure */ int (*parse)(void *self, git_odb_object *obj); int (*parse_raw)(void *self, const char *data, size_t size); void (*free)(void *self); } git_object_def; static git_object_def git_objects_table[] = { /* 0 = GIT_OBJECT__EXT1 */ { "", 0, NULL, NULL, NULL }, /* 1 = GIT_OBJECT_COMMIT */ { "commit", sizeof(git_commit), git_commit__parse, git_commit__parse_raw, git_commit__free }, /* 2 = GIT_OBJECT_TREE */ { "tree", sizeof(git_tree), git_tree__parse, git_tree__parse_raw, git_tree__free }, /* 3 = GIT_OBJECT_BLOB */ { "blob", sizeof(git_blob), git_blob__parse, git_blob__parse_raw, git_blob__free }, /* 4 = GIT_OBJECT_TAG */ { "tag", sizeof(git_tag), git_tag__parse, git_tag__parse_raw, git_tag__free }, /* 5 = GIT_OBJECT__EXT2 */ { "", 0, NULL, NULL, NULL }, /* 6 = GIT_OBJECT_OFS_DELTA */ { "OFS_DELTA", 0, NULL, NULL, NULL }, /* 7 = GIT_OBJECT_REF_DELTA */ { "REF_DELTA", 0, NULL, NULL, NULL }, }; int git_object__from_raw( git_object **object_out, const char *data, size_t size, git_object_t type) { git_object_def *def; git_object *object; size_t object_size; int error; GIT_ASSERT_ARG(object_out); *object_out = NULL; /* Validate type match */ if (type != GIT_OBJECT_BLOB && type != GIT_OBJECT_TREE && type != GIT_OBJECT_COMMIT && type != GIT_OBJECT_TAG) { git_error_set(GIT_ERROR_INVALID, "the requested type is invalid"); return GIT_ENOTFOUND; } if ((object_size = git_object__size(type)) == 0) { git_error_set(GIT_ERROR_INVALID, "the requested type is invalid"); return GIT_ENOTFOUND; } /* Allocate and initialize base object */ object = git__calloc(1, object_size); GIT_ERROR_CHECK_ALLOC(object); object->cached.flags = GIT_CACHE_STORE_PARSED; object->cached.type = type; if ((error = git_odb_hash(&object->cached.oid, data, size, type)) < 0) return error; /* Parse raw object data */ def = &git_objects_table[type]; GIT_ASSERT(def->free && def->parse_raw); if ((error = def->parse_raw(object, data, size)) < 0) { def->free(object); return error; } git_cached_obj_incref(object); *object_out = object; return 0; } int git_object__from_odb_object( git_object **object_out, git_repository *repo, git_odb_object *odb_obj, git_object_t type) { int error; size_t object_size; git_object_def *def; git_object *object = NULL; GIT_ASSERT_ARG(object_out); *object_out = NULL; /* Validate type match */ if (type != GIT_OBJECT_ANY && type != odb_obj->cached.type) { git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB"); return GIT_ENOTFOUND; } if ((object_size = git_object__size(odb_obj->cached.type)) == 0) { git_error_set(GIT_ERROR_INVALID, "the requested type is invalid"); return GIT_ENOTFOUND; } /* Allocate and initialize base object */ object = git__calloc(1, object_size); GIT_ERROR_CHECK_ALLOC(object); git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); object->cached.type = odb_obj->cached.type; object->cached.size = odb_obj->cached.size; object->repo = repo; /* Parse raw object data */ def = &git_objects_table[odb_obj->cached.type]; GIT_ASSERT(def->free && def->parse); if ((error = def->parse(object, odb_obj)) < 0) def->free(object); else *object_out = git_cache_store_parsed(&repo->objects, object); return error; } void git_object__free(void *obj) { git_object_t type = ((git_object *)obj)->cached.type; if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) || !git_objects_table[type].free) git__free(obj); else git_objects_table[type].free(obj); } int git_object_lookup_prefix( git_object **object_out, git_repository *repo, const git_oid *id, size_t len, git_object_t type) { git_object *object = NULL; git_odb *odb = NULL; git_odb_object *odb_obj = NULL; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(object_out); GIT_ASSERT_ARG(id); if (len < GIT_OID_MINPREFIXLEN) { git_error_set(GIT_ERROR_OBJECT, "ambiguous lookup - OID prefix is too short"); return GIT_EAMBIGUOUS; } error = git_repository_odb__weakptr(&odb, repo); if (error < 0) return error; if (len > GIT_OID_HEXSZ) len = GIT_OID_HEXSZ; if (len == GIT_OID_HEXSZ) { git_cached_obj *cached = NULL; /* We want to match the full id : we can first look up in the cache, * since there is no need to check for non ambiguousity */ cached = git_cache_get_any(&repo->objects, id); if (cached != NULL) { if (cached->flags == GIT_CACHE_STORE_PARSED) { object = (git_object *)cached; if (type != GIT_OBJECT_ANY && type != object->cached.type) { git_object_free(object); git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB"); return GIT_ENOTFOUND; } *object_out = object; return 0; } else if (cached->flags == GIT_CACHE_STORE_RAW) { odb_obj = (git_odb_object *)cached; } else { GIT_ASSERT(!"Wrong caching type in the global object cache"); } } else { /* Object was not found in the cache, let's explore the backends. * We could just use git_odb_read_unique_short_oid, * it is the same cost for packed and loose object backends, * but it may be much more costly for sqlite and hiredis. */ error = git_odb_read(&odb_obj, odb, id); } } else { git_oid short_oid = {{ 0 }}; git_oid__cpy_prefix(&short_oid, id, len); /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have * 2 options : * - We always search in the cache first. If we find that short oid is * ambiguous, we can stop. But in all the other cases, we must then * explore all the backends (to find an object if there was match, * or to check that oid is not ambiguous if we have found 1 match in * the cache) * - We never explore the cache, go right to exploring the backends * We chose the latter : we explore directly the backends. */ error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len); } if (error < 0) return error; error = git_object__from_odb_object(object_out, repo, odb_obj, type); git_odb_object_free(odb_obj); return error; } int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_object_t type) { return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type); } void git_object_free(git_object *object) { if (object == NULL) return; git_cached_obj_decref(object); } const git_oid *git_object_id(const git_object *obj) { GIT_ASSERT_ARG_WITH_RETVAL(obj, NULL); return &obj->cached.oid; } git_object_t git_object_type(const git_object *obj) { GIT_ASSERT_ARG_WITH_RETVAL(obj, GIT_OBJECT_INVALID); return obj->cached.type; } git_repository *git_object_owner(const git_object *obj) { GIT_ASSERT_ARG_WITH_RETVAL(obj, NULL); return obj->repo; } const char *git_object_type2string(git_object_t type) { if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) return ""; return git_objects_table[type].str; } git_object_t git_object_string2type(const char *str) { if (!str) return GIT_OBJECT_INVALID; return git_object_stringn2type(str, strlen(str)); } git_object_t git_object_stringn2type(const char *str, size_t len) { size_t i; if (!str || !len || !*str) return GIT_OBJECT_INVALID; for (i = 0; i < ARRAY_SIZE(git_objects_table); i++) if (*git_objects_table[i].str && !git__prefixncmp(str, len, git_objects_table[i].str)) return (git_object_t)i; return GIT_OBJECT_INVALID; } int git_object_typeisloose(git_object_t type) { if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) return 0; return (git_objects_table[type].size > 0) ? 1 : 0; } size_t git_object__size(git_object_t type) { if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) return 0; return git_objects_table[type].size; } static int dereference_object(git_object **dereferenced, git_object *obj) { git_object_t type = git_object_type(obj); switch (type) { case GIT_OBJECT_COMMIT: return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj); case GIT_OBJECT_TAG: return git_tag_target(dereferenced, (git_tag*)obj); case GIT_OBJECT_BLOB: case GIT_OBJECT_TREE: return GIT_EPEEL; default: return GIT_EINVALIDSPEC; } } static int peel_error(int error, const git_oid *oid, git_object_t type) { const char *type_name; char hex_oid[GIT_OID_HEXSZ + 1]; type_name = git_object_type2string(type); git_oid_fmt(hex_oid, oid); hex_oid[GIT_OID_HEXSZ] = '\0'; git_error_set(GIT_ERROR_OBJECT, "the git_object of id '%s' can not be " "successfully peeled into a %s (git_object_t=%i).", hex_oid, type_name, type); return error; } static int check_type_combination(git_object_t type, git_object_t target) { if (type == target) return 0; switch (type) { case GIT_OBJECT_BLOB: case GIT_OBJECT_TREE: /* a blob or tree can never be peeled to anything but themselves */ return GIT_EINVALIDSPEC; break; case GIT_OBJECT_COMMIT: /* a commit can only be peeled to a tree */ if (target != GIT_OBJECT_TREE && target != GIT_OBJECT_ANY) return GIT_EINVALIDSPEC; break; case GIT_OBJECT_TAG: /* a tag may point to anything, so we let anything through */ break; default: return GIT_EINVALIDSPEC; } return 0; } int git_object_peel( git_object **peeled, const git_object *object, git_object_t target_type) { git_object *source, *deref = NULL; int error; GIT_ASSERT_ARG(object); GIT_ASSERT_ARG(peeled); GIT_ASSERT_ARG(target_type == GIT_OBJECT_TAG || target_type == GIT_OBJECT_COMMIT || target_type == GIT_OBJECT_TREE || target_type == GIT_OBJECT_BLOB || target_type == GIT_OBJECT_ANY); if ((error = check_type_combination(git_object_type(object), target_type)) < 0) return peel_error(error, git_object_id(object), target_type); if (git_object_type(object) == target_type) return git_object_dup(peeled, (git_object *)object); source = (git_object *)object; while (!(error = dereference_object(&deref, source))) { if (source != object) git_object_free(source); if (git_object_type(deref) == target_type) { *peeled = deref; return 0; } if (target_type == GIT_OBJECT_ANY && git_object_type(deref) != git_object_type(object)) { *peeled = deref; return 0; } source = deref; deref = NULL; } if (source != object) git_object_free(source); git_object_free(deref); if (error) error = peel_error(error, git_object_id(object), target_type); return error; } int git_object_dup(git_object **dest, git_object *source) { git_cached_obj_incref(source); *dest = source; return 0; } int git_object_lookup_bypath( git_object **out, const git_object *treeish, const char *path, git_object_t type) { int error = -1; git_tree *tree = NULL; git_tree_entry *entry = NULL; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(treeish); GIT_ASSERT_ARG(path); if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJECT_TREE)) < 0 || (error = git_tree_entry_bypath(&entry, tree, path)) < 0) { goto cleanup; } if (type != GIT_OBJECT_ANY && git_tree_entry_type(entry) != type) { git_error_set(GIT_ERROR_OBJECT, "object at path '%s' is not of the asked-for type %d", path, type); error = GIT_EINVALIDSPEC; goto cleanup; } error = git_tree_entry_to_object(out, git_object_owner(treeish), entry); cleanup: git_tree_entry_free(entry); git_tree_free(tree); return error; } int git_object_short_id(git_buf *out, const git_object *obj) { git_repository *repo; int len = GIT_ABBREV_DEFAULT, error; git_oid id = {{0}}; git_odb *odb; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(obj); if ((error = git_buf_sanitize(out)) < 0) return error; repo = git_object_owner(obj); if ((error = git_repository__configmap_lookup(&len, repo, GIT_CONFIGMAP_ABBREV)) < 0) return error; if ((error = git_repository_odb(&odb, repo)) < 0) return error; while (len < GIT_OID_HEXSZ) { /* set up short oid */ memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2); if (len & 1) id.id[len / 2] &= 0xf0; error = git_odb_exists_prefix(NULL, odb, &id, len); if (error != GIT_EAMBIGUOUS) break; git_error_clear(); len++; } if (!error && !(error = git_buf_grow(out, len + 1))) { git_oid_tostr(out->ptr, len + 1, &id); out->size = len; } git_odb_free(odb); return error; } bool git_object__is_valid( git_repository *repo, const git_oid *id, git_object_t expected_type) { git_odb *odb; git_object_t actual_type; size_t len; int error; if (!git_object__strict_input_validation) return true; if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || (error = git_odb_read_header(&len, &actual_type, odb, id)) < 0) return false; if (expected_type != GIT_OBJECT_ANY && expected_type != actual_type) { git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB"); return false; } return true; } git2r/src/libgit2/src/iterator.h0000644000175000017500000002227714125111754016362 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_iterator_h__ #define INCLUDE_iterator_h__ #include "common.h" #include "git2/index.h" #include "vector.h" #include "buffer.h" #include "ignore.h" typedef struct git_iterator git_iterator; typedef enum { GIT_ITERATOR_EMPTY = 0, GIT_ITERATOR_TREE = 1, GIT_ITERATOR_INDEX = 2, GIT_ITERATOR_WORKDIR = 3, GIT_ITERATOR_FS = 4, } git_iterator_t; typedef enum { /** ignore case for entry sort order */ GIT_ITERATOR_IGNORE_CASE = (1u << 0), /** force case sensitivity for entry sort order */ GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1), /** return tree items in addition to blob items */ GIT_ITERATOR_INCLUDE_TREES = (1u << 2), /** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */ GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3), /** convert precomposed unicode to decomposed unicode */ GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4), /** never convert precomposed unicode to decomposed unicode */ GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE = (1u << 5), /** include conflicts */ GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 6), /** descend into symlinked directories */ GIT_ITERATOR_DESCEND_SYMLINKS = (1u << 7), /** hash files in workdir or filesystem iterators */ GIT_ITERATOR_INCLUDE_HASH = (1u << 8), } git_iterator_flag_t; typedef enum { GIT_ITERATOR_STATUS_NORMAL = 0, GIT_ITERATOR_STATUS_IGNORED = 1, GIT_ITERATOR_STATUS_EMPTY = 2, GIT_ITERATOR_STATUS_FILTERED = 3 } git_iterator_status_t; typedef struct { const char *start; const char *end; /* paths to include in the iterator (literal). if set, any paths not * listed here will be excluded from iteration. */ git_strarray pathlist; /* flags, from above */ unsigned int flags; } git_iterator_options; #define GIT_ITERATOR_OPTIONS_INIT {0} typedef struct { int (*current)(const git_index_entry **, git_iterator *); int (*advance)(const git_index_entry **, git_iterator *); int (*advance_into)(const git_index_entry **, git_iterator *); int (*advance_over)( const git_index_entry **, git_iterator_status_t *, git_iterator *); int (*reset)(git_iterator *); void (*free)(git_iterator *); } git_iterator_callbacks; struct git_iterator { git_iterator_t type; git_iterator_callbacks *cb; git_repository *repo; git_index *index; char *start; size_t start_len; char *end; size_t end_len; bool started; bool ended; git_vector pathlist; size_t pathlist_walk_idx; int (*strcomp)(const char *a, const char *b); int (*strncomp)(const char *a, const char *b, size_t n); int (*prefixcomp)(const char *str, const char *prefix); int (*entry_srch)(const void *key, const void *array_member); size_t stat_calls; unsigned int flags; }; extern int git_iterator_for_nothing( git_iterator **out, git_iterator_options *options); /* tree iterators will match the ignore_case value from the index of the * repository, unless you override with a non-zero flag value */ extern int git_iterator_for_tree( git_iterator **out, git_tree *tree, git_iterator_options *options); /* index iterators will take the ignore_case value from the index; the * ignore_case flags are not used */ extern int git_iterator_for_index( git_iterator **out, git_repository *repo, git_index *index, git_iterator_options *options); extern int git_iterator_for_workdir_ext( git_iterator **out, git_repository *repo, const char *repo_workdir, git_index *index, git_tree *tree, git_iterator_options *options); /* workdir iterators will match the ignore_case value from the index of the * repository, unless you override with a non-zero flag value */ GIT_INLINE(int) git_iterator_for_workdir( git_iterator **out, git_repository *repo, git_index *index, git_tree *tree, git_iterator_options *options) { return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, options); } /* for filesystem iterators, you have to explicitly pass in the ignore_case * behavior that you desire */ extern int git_iterator_for_filesystem( git_iterator **out, const char *root, git_iterator_options *options); extern void git_iterator_free(git_iterator *iter); /* Return a git_index_entry structure for the current value the iterator * is looking at or NULL if the iterator is at the end. * * The entry may noy be fully populated. Tree iterators will only have a * value mode, OID, and path. Workdir iterators will not have an OID (but * you can use `git_iterator_current_oid()` to calculate it on demand). * * You do not need to free the entry. It is still "owned" by the iterator. * Once you call `git_iterator_advance()` then the old entry is no longer * guaranteed to be valid - it may be freed or just overwritten in place. */ GIT_INLINE(int) git_iterator_current( const git_index_entry **entry, git_iterator *iter) { return iter->cb->current(entry, iter); } /** * Advance to the next item for the iterator. * * If GIT_ITERATOR_INCLUDE_TREES is set, this may be a tree item. If * GIT_ITERATOR_DONT_AUTOEXPAND is set, calling this again when on a tree * item will skip over all the items under that tree. */ GIT_INLINE(int) git_iterator_advance( const git_index_entry **entry, git_iterator *iter) { return iter->cb->advance(entry, iter); } /** * Iterate into a tree item (when GIT_ITERATOR_DONT_AUTOEXPAND is set). * * git_iterator_advance() steps through all items being iterated over * (either with or without trees, depending on GIT_ITERATOR_INCLUDE_TREES), * but if GIT_ITERATOR_DONT_AUTOEXPAND is set, it will skip to the next * sibling of a tree instead of going to the first child of the tree. In * that case, use this function to advance to the first child of the tree. * * If the current item is not a tree, this is a no-op. * * For filesystem and working directory iterators, a tree (i.e. directory) * can be empty. In that case, this function returns GIT_ENOTFOUND and * does not advance. That can't happen for tree and index iterators. */ GIT_INLINE(int) git_iterator_advance_into( const git_index_entry **entry, git_iterator *iter) { return iter->cb->advance_into(entry, iter); } /* Advance over a directory and check if it contains no files or just * ignored files. * * In a tree or the index, all directories will contain files, but in the * working directory it is possible to have an empty directory tree or a * tree that only contains ignored files. Many Git operations treat these * cases specially. This advances over a directory (presumably an * untracked directory) but checks during the scan if there are any files * and any non-ignored files. */ GIT_INLINE(int) git_iterator_advance_over( const git_index_entry **entry, git_iterator_status_t *status, git_iterator *iter) { return iter->cb->advance_over(entry, status, iter); } /** * Go back to the start of the iteration. */ GIT_INLINE(int) git_iterator_reset(git_iterator *iter) { return iter->cb->reset(iter); } /** * Go back to the start of the iteration after updating the `start` and * `end` pathname boundaries of the iteration. */ extern int git_iterator_reset_range( git_iterator *iter, const char *start, const char *end); GIT_INLINE(git_iterator_t) git_iterator_type(git_iterator *iter) { return iter->type; } GIT_INLINE(git_repository *) git_iterator_owner(git_iterator *iter) { return iter->repo; } GIT_INLINE(git_index *) git_iterator_index(git_iterator *iter) { return iter->index; } GIT_INLINE(git_iterator_flag_t) git_iterator_flags(git_iterator *iter) { return iter->flags; } GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter) { return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0); } extern int git_iterator_set_ignore_case( git_iterator *iter, bool ignore_case); extern int git_iterator_current_tree_entry( const git_tree_entry **entry_out, git_iterator *iter); extern int git_iterator_current_parent_tree( const git_tree **tree_out, git_iterator *iter, size_t depth); extern bool git_iterator_current_is_ignored(git_iterator *iter); extern bool git_iterator_current_tree_is_ignored(git_iterator *iter); /** * Get full path of the current item from a workdir iterator. This will * return NULL for a non-workdir iterator. The git_buf is still owned by * the iterator; this is exposed just for efficiency. */ extern int git_iterator_current_workdir_path( git_buf **path, git_iterator *iter); /** * Retrieve the index stored in the iterator. * * Only implemented for the workdir and index iterators. */ extern git_index *git_iterator_index(git_iterator *iter); typedef int (*git_iterator_foreach_cb)( const git_index_entry *entry, void *data); /** * Walk the given iterator and invoke the callback for each path * contained in the iterator. */ extern int git_iterator_foreach( git_iterator *iterator, git_iterator_foreach_cb cb, void *data); typedef int (*git_iterator_walk_cb)( const git_index_entry **entries, void *data); /** * Walk the given iterators in lock-step. The given callback will be * called for each unique path, with the index entry in each iterator * (or NULL if the given iterator does not contain that path). */ extern int git_iterator_walk( git_iterator **iterators, size_t cnt, git_iterator_walk_cb cb, void *data); #endif git2r/src/libgit2/src/config_mem.c0000644000175000017500000001307514125111754016623 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "config.h" #include "config_backend.h" #include "config_parse.h" #include "config_entries.h" typedef struct { git_config_backend parent; git_config_entries *entries; git_buf cfg; } config_memory_backend; typedef struct { git_config_entries *entries; git_config_level_t level; } config_memory_parse_data; static int config_error_readonly(void) { git_error_set(GIT_ERROR_CONFIG, "this backend is read-only"); return -1; } static int read_variable_cb( git_config_parser *reader, const char *current_section, const char *var_name, const char *var_value, const char *line, size_t line_len, void *payload) { config_memory_parse_data *parse_data = (config_memory_parse_data *) payload; git_buf buf = GIT_BUF_INIT; git_config_entry *entry; const char *c; int result; GIT_UNUSED(reader); GIT_UNUSED(line); GIT_UNUSED(line_len); if (current_section) { /* TODO: Once warnings land, we should likely warn * here. Git appears to warn in most cases if it sees * un-namespaced config options. */ git_buf_puts(&buf, current_section); git_buf_putc(&buf, '.'); } for (c = var_name; *c; c++) git_buf_putc(&buf, git__tolower(*c)); if (git_buf_oom(&buf)) return -1; entry = git__calloc(1, sizeof(git_config_entry)); GIT_ERROR_CHECK_ALLOC(entry); entry->name = git_buf_detach(&buf); entry->value = var_value ? git__strdup(var_value) : NULL; entry->level = parse_data->level; entry->include_depth = 0; if ((result = git_config_entries_append(parse_data->entries, entry)) < 0) return result; return result; } static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo) { config_memory_backend *memory_backend = (config_memory_backend *) backend; git_config_parser parser = GIT_PARSE_CTX_INIT; config_memory_parse_data parse_data; int error; GIT_UNUSED(repo); if ((error = git_config_parser_init(&parser, "in-memory", memory_backend->cfg.ptr, memory_backend->cfg.size)) < 0) goto out; parse_data.entries = memory_backend->entries; parse_data.level = level; if ((error = git_config_parse(&parser, NULL, read_variable_cb, NULL, NULL, &parse_data)) < 0) goto out; out: git_config_parser_dispose(&parser); return error; } static int config_memory_get(git_config_backend *backend, const char *key, git_config_entry **out) { config_memory_backend *memory_backend = (config_memory_backend *) backend; return git_config_entries_get(out, memory_backend->entries, key); } static int config_memory_iterator( git_config_iterator **iter, git_config_backend *backend) { config_memory_backend *memory_backend = (config_memory_backend *) backend; git_config_entries *entries; int error; if ((error = git_config_entries_dup(&entries, memory_backend->entries)) < 0) goto out; if ((error = git_config_entries_iterator_new(iter, entries)) < 0) goto out; out: /* Let iterator delete duplicated entries when it's done */ git_config_entries_free(entries); return error; } static int config_memory_set(git_config_backend *backend, const char *name, const char *value) { GIT_UNUSED(backend); GIT_UNUSED(name); GIT_UNUSED(value); return config_error_readonly(); } static int config_memory_set_multivar( git_config_backend *backend, const char *name, const char *regexp, const char *value) { GIT_UNUSED(backend); GIT_UNUSED(name); GIT_UNUSED(regexp); GIT_UNUSED(value); return config_error_readonly(); } static int config_memory_delete(git_config_backend *backend, const char *name) { GIT_UNUSED(backend); GIT_UNUSED(name); return config_error_readonly(); } static int config_memory_delete_multivar(git_config_backend *backend, const char *name, const char *regexp) { GIT_UNUSED(backend); GIT_UNUSED(name); GIT_UNUSED(regexp); return config_error_readonly(); } static int config_memory_lock(git_config_backend *backend) { GIT_UNUSED(backend); return config_error_readonly(); } static int config_memory_unlock(git_config_backend *backend, int success) { GIT_UNUSED(backend); GIT_UNUSED(success); return config_error_readonly(); } static void config_memory_free(git_config_backend *_backend) { config_memory_backend *backend = (config_memory_backend *)_backend; if (backend == NULL) return; git_config_entries_free(backend->entries); git_buf_dispose(&backend->cfg); git__free(backend); } int git_config_backend_from_string(git_config_backend **out, const char *cfg, size_t len) { config_memory_backend *backend; backend = git__calloc(1, sizeof(config_memory_backend)); GIT_ERROR_CHECK_ALLOC(backend); if (git_config_entries_new(&backend->entries) < 0) { git__free(backend); return -1; } if (git_buf_set(&backend->cfg, cfg, len) < 0) { git_config_entries_free(backend->entries); git__free(backend); return -1; } backend->parent.version = GIT_CONFIG_BACKEND_VERSION; backend->parent.readonly = 1; backend->parent.open = config_memory_open; backend->parent.get = config_memory_get; backend->parent.set = config_memory_set; backend->parent.set_multivar = config_memory_set_multivar; backend->parent.del = config_memory_delete; backend->parent.del_multivar = config_memory_delete_multivar; backend->parent.iterator = config_memory_iterator; backend->parent.lock = config_memory_lock; backend->parent.unlock = config_memory_unlock; backend->parent.snapshot = git_config_backend_snapshot; backend->parent.free = config_memory_free; *out = (git_config_backend *)backend; return 0; } git2r/src/libgit2/src/message.h0000644000175000017500000000066614125111754016153 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_message_h__ #define INCLUDE_message_h__ #include "common.h" #include "git2/message.h" #include "buffer.h" int git_message__prettify(git_buf *message_out, const char *message, int strip_comments); #endif git2r/src/libgit2/src/errors.c0000644000175000017500000001063514125111754016033 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "threadstate.h" #include "posix.h" #include "buffer.h" #include "libgit2.h" /******************************************** * New error handling ********************************************/ static git_error g_git_oom_error = { "Out of memory", GIT_ERROR_NOMEMORY }; static git_error g_git_uninitialized_error = { "libgit2 has not been initialized; you must call git_libgit2_init", GIT_ERROR_INVALID }; static void set_error_from_buffer(int error_class) { git_error *error = &GIT_THREADSTATE->error_t; git_buf *buf = &GIT_THREADSTATE->error_buf; error->message = buf->ptr; error->klass = error_class; GIT_THREADSTATE->last_error = error; } static void set_error(int error_class, char *string) { git_buf *buf = &GIT_THREADSTATE->error_buf; git_buf_clear(buf); if (string) { git_buf_puts(buf, string); git__free(string); } set_error_from_buffer(error_class); } void git_error_set_oom(void) { GIT_THREADSTATE->last_error = &g_git_oom_error; } void git_error_set(int error_class, const char *fmt, ...) { va_list ap; va_start(ap, fmt); git_error_vset(error_class, fmt, ap); va_end(ap); } void git_error_vset(int error_class, const char *fmt, va_list ap) { #ifdef GIT_WIN32 DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0; #endif int error_code = (error_class == GIT_ERROR_OS) ? errno : 0; git_buf *buf = &GIT_THREADSTATE->error_buf; git_buf_clear(buf); if (fmt) { git_buf_vprintf(buf, fmt, ap); if (error_class == GIT_ERROR_OS) git_buf_PUTS(buf, ": "); } if (error_class == GIT_ERROR_OS) { #ifdef GIT_WIN32 char * win32_error = git_win32_get_error_message(win32_error_code); if (win32_error) { git_buf_puts(buf, win32_error); git__free(win32_error); SetLastError(0); } else #endif if (error_code) git_buf_puts(buf, strerror(error_code)); if (error_code) errno = 0; } if (!git_buf_oom(buf)) set_error_from_buffer(error_class); } int git_error_set_str(int error_class, const char *string) { git_buf *buf = &GIT_THREADSTATE->error_buf; GIT_ASSERT_ARG(string); git_buf_clear(buf); git_buf_puts(buf, string); if (git_buf_oom(buf)) return -1; set_error_from_buffer(error_class); return 0; } void git_error_clear(void) { if (GIT_THREADSTATE->last_error != NULL) { set_error(0, NULL); GIT_THREADSTATE->last_error = NULL; } errno = 0; #ifdef GIT_WIN32 SetLastError(0); #endif } const git_error *git_error_last(void) { /* If the library is not initialized, return a static error. */ if (!git_libgit2_init_count()) return &g_git_uninitialized_error; return GIT_THREADSTATE->last_error; } int git_error_state_capture(git_error_state *state, int error_code) { git_error *error = GIT_THREADSTATE->last_error; git_buf *error_buf = &GIT_THREADSTATE->error_buf; memset(state, 0, sizeof(git_error_state)); if (!error_code) return 0; state->error_code = error_code; state->oom = (error == &g_git_oom_error); if (error) { state->error_msg.klass = error->klass; if (state->oom) state->error_msg.message = g_git_oom_error.message; else state->error_msg.message = git_buf_detach(error_buf); } git_error_clear(); return error_code; } int git_error_state_restore(git_error_state *state) { int ret = 0; git_error_clear(); if (state && state->error_msg.message) { if (state->oom) git_error_set_oom(); else set_error(state->error_msg.klass, state->error_msg.message); ret = state->error_code; memset(state, 0, sizeof(git_error_state)); } return ret; } void git_error_state_free(git_error_state *state) { if (!state) return; if (!state->oom) git__free(state->error_msg.message); memset(state, 0, sizeof(git_error_state)); } int git_error_system_last(void) { #ifdef GIT_WIN32 return GetLastError(); #else return errno; #endif } void git_error_system_set(int code) { #ifdef GIT_WIN32 SetLastError(code); #else errno = code; #endif } /* Deprecated error values and functions */ #ifndef GIT_DEPRECATE_HARD const git_error *giterr_last(void) { return git_error_last(); } void giterr_clear(void) { git_error_clear(); } void giterr_set_str(int error_class, const char *string) { git_error_set_str(error_class, string); } void giterr_set_oom(void) { git_error_set_oom(); } #endif git2r/src/libgit2/src/tree-cache.c0000644000175000017500000001454514125111754016523 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "tree-cache.h" #include "pool.h" #include "tree.h" static git_tree_cache *find_child( const git_tree_cache *tree, const char *path, const char *end) { size_t i, dirlen = end ? (size_t)(end - path) : strlen(path); for (i = 0; i < tree->children_count; ++i) { git_tree_cache *child = tree->children[i]; if (child->namelen == dirlen && !memcmp(path, child->name, dirlen)) return child; } return NULL; } void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path) { const char *ptr = path, *end; if (tree == NULL) return; tree->entry_count = -1; while (ptr != NULL) { end = strchr(ptr, '/'); if (end == NULL) /* End of path */ break; tree = find_child(tree, ptr, end); if (tree == NULL) /* We don't have that tree */ return; tree->entry_count = -1; ptr = end + 1; } } const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path) { const char *ptr = path, *end; if (tree == NULL) { return NULL; } while (1) { end = strchr(ptr, '/'); tree = find_child(tree, ptr, end); if (tree == NULL) /* Can't find it */ return NULL; if (end == NULL || *end + 1 == '\0') return tree; ptr = end + 1; } } static int read_tree_internal(git_tree_cache **out, const char **buffer_in, const char *buffer_end, git_pool *pool) { git_tree_cache *tree = NULL; const char *name_start, *buffer; int count; buffer = name_start = *buffer_in; if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) goto corrupted; if (++buffer >= buffer_end) goto corrupted; if (git_tree_cache_new(&tree, name_start, pool) < 0) return -1; /* Blank-terminated ASCII decimal number of entries in this tree */ if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0) goto corrupted; tree->entry_count = count; if (*buffer != ' ' || ++buffer >= buffer_end) goto corrupted; /* Number of children of the tree, newline-terminated */ if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0 || count < 0) goto corrupted; tree->children_count = count; if (*buffer != '\n' || ++buffer > buffer_end) goto corrupted; /* The SHA1 is only there if it's not invalidated */ if (tree->entry_count >= 0) { /* 160-bit SHA-1 for this tree and it's children */ if (buffer + GIT_OID_RAWSZ > buffer_end) goto corrupted; git_oid_fromraw(&tree->oid, (const unsigned char *)buffer); buffer += GIT_OID_RAWSZ; } /* Parse children: */ if (tree->children_count > 0) { size_t i, bufsize; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bufsize, tree->children_count, sizeof(git_tree_cache*)); tree->children = git_pool_malloc(pool, bufsize); GIT_ERROR_CHECK_ALLOC(tree->children); memset(tree->children, 0x0, bufsize); for (i = 0; i < tree->children_count; ++i) { if (read_tree_internal(&tree->children[i], &buffer, buffer_end, pool) < 0) goto corrupted; } } *buffer_in = buffer; *out = tree; return 0; corrupted: git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index"); return -1; } int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool) { const char *buffer_end = buffer + buffer_size; if (read_tree_internal(tree, &buffer, buffer_end, pool) < 0) return -1; if (buffer < buffer_end) { git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index (unexpected trailing data)"); return -1; } return 0; } static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool) { git_repository *repo; size_t i, j, nentries, ntrees, alloc_size; int error; repo = git_tree_owner(tree); git_oid_cpy(&cache->oid, git_tree_id(tree)); nentries = git_tree_entrycount(tree); /* * We make sure we know how many trees we need to allocate for * so we don't have to realloc and change the pointers for the * parents. */ ntrees = 0; for (i = 0; i < nentries; i++) { const git_tree_entry *entry; entry = git_tree_entry_byindex(tree, i); if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE) ntrees++; } GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_size, ntrees, sizeof(git_tree_cache *)); cache->children_count = ntrees; cache->children = git_pool_mallocz(pool, alloc_size); GIT_ERROR_CHECK_ALLOC(cache->children); j = 0; for (i = 0; i < nentries; i++) { const git_tree_entry *entry; git_tree *subtree; entry = git_tree_entry_byindex(tree, i); if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE) { cache->entry_count++; continue; } if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), pool)) < 0) return error; if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0) return error; error = read_tree_recursive(cache->children[j], subtree, pool); git_tree_free(subtree); cache->entry_count += cache->children[j]->entry_count; j++; if (error < 0) return error; } return 0; } int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool) { int error; git_tree_cache *cache; if ((error = git_tree_cache_new(&cache, "", pool)) < 0) return error; if ((error = read_tree_recursive(cache, tree, pool)) < 0) return error; *out = cache; return 0; } int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool) { size_t name_len, alloc_size; git_tree_cache *tree; name_len = strlen(name); GIT_ERROR_CHECK_ALLOC_ADD3(&alloc_size, sizeof(git_tree_cache), name_len, 1); tree = git_pool_malloc(pool, alloc_size); GIT_ERROR_CHECK_ALLOC(tree); memset(tree, 0x0, sizeof(git_tree_cache)); /* NUL-terminated tree name */ tree->namelen = name_len; memcpy(tree->name, name, name_len); tree->name[name_len] = '\0'; *out = tree; return 0; } static void write_tree(git_buf *out, git_tree_cache *tree) { size_t i; git_buf_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count); if (tree->entry_count != -1) git_buf_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ); for (i = 0; i < tree->children_count; i++) write_tree(out, tree->children[i]); } int git_tree_cache_write(git_buf *out, git_tree_cache *tree) { write_tree(out, tree); return git_buf_oom(out) ? -1 : 0; } git2r/src/libgit2/src/assert_safe.h0000644000175000017500000000370514125111754017023 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_assert_safe_h__ #define INCLUDE_assert_safe_h__ /* * In a debug build, we'll assert(3) for aide in debugging. In release * builds, we will provide macros that will set an error message that * indicate a failure and return. Note that memory leaks can occur in * a release-mode assertion failure -- it is impractical to provide * safe clean up routines in these very extreme failures, but care * should be taken to not leak very large objects. */ #if (defined(_DEBUG) || defined(GIT_ASSERT_HARD)) && GIT_ASSERT_HARD != 0 # include # define GIT_ASSERT(expr) assert(expr) # define GIT_ASSERT_ARG(expr) assert(expr) # define GIT_ASSERT_WITH_RETVAL(expr, fail) assert(expr) # define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) assert(expr) #else /** Internal consistency check to stop the function. */ # define GIT_ASSERT(expr) GIT_ASSERT_WITH_RETVAL(expr, -1) /** * Assert that a consumer-provided argument is valid, setting an * actionable error message and returning -1 if it is not. */ # define GIT_ASSERT_ARG(expr) GIT_ASSERT_ARG_WITH_RETVAL(expr, -1) /** Internal consistency check to return the `fail` param on failure. */ # define GIT_ASSERT_WITH_RETVAL(expr, fail) \ GIT_ASSERT__WITH_RETVAL(expr, GIT_ERROR_INTERNAL, "unrecoverable internal error", fail) /** * Assert that a consumer-provided argument is valid, setting an * actionable error message and returning the `fail` param if not. */ # define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) \ GIT_ASSERT__WITH_RETVAL(expr, GIT_ERROR_INVALID, "invalid argument", fail) # define GIT_ASSERT__WITH_RETVAL(expr, code, msg, fail) do { \ if (!(expr)) { \ git_error_set(code, "%s: '%s'", msg, #expr); \ return fail; \ } \ } while(0) #endif /* GIT_ASSERT_HARD */ #endif git2r/src/libgit2/src/commit_list.c0000644000175000017500000001170614125111754017042 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "commit_list.h" #include "revwalk.h" #include "pool.h" #include "odb.h" #include "commit.h" int git_commit_list_generation_cmp(const void *a, const void *b) { uint32_t generation_a = ((git_commit_list_node *) a)->generation; uint32_t generation_b = ((git_commit_list_node *) b)->generation; if (!generation_a || !generation_b) { /* Fall back to comparing by timestamps if at least one commit lacks a generation. */ return git_commit_list_time_cmp(a, b); } if (generation_a < generation_b) return 1; if (generation_a > generation_b) return -1; return 0; } int git_commit_list_time_cmp(const void *a, const void *b) { int64_t time_a = ((git_commit_list_node *) a)->time; int64_t time_b = ((git_commit_list_node *) b)->time; if (time_a < time_b) return 1; if (time_a > time_b) return -1; return 0; } git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p) { git_commit_list *new_list = git__malloc(sizeof(git_commit_list)); if (new_list != NULL) { new_list->item = item; new_list->next = *list_p; } *list_p = new_list; return new_list; } git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p) { git_commit_list **pp = list_p; git_commit_list *p; while ((p = *pp) != NULL) { if (git_commit_list_time_cmp(p->item, item) > 0) break; pp = &p->next; } return git_commit_list_insert(item, pp); } git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk) { return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1); } static git_commit_list_node **alloc_parents( git_revwalk *walk, git_commit_list_node *commit, size_t n_parents) { size_t bytes; if (n_parents <= PARENTS_PER_COMMIT) return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node)); if (git__multiply_sizet_overflow(&bytes, n_parents, sizeof(git_commit_list_node *))) return NULL; return (git_commit_list_node **)git_pool_malloc(&walk->commit_pool, bytes); } void git_commit_list_free(git_commit_list **list_p) { git_commit_list *list = *list_p; if (list == NULL) return; while (list) { git_commit_list *temp = list; list = temp->next; git__free(temp); } *list_p = NULL; } git_commit_list_node *git_commit_list_pop(git_commit_list **stack) { git_commit_list *top = *stack; git_commit_list_node *item = top ? top->item : NULL; if (top) { *stack = top->next; git__free(top); } return item; } static int commit_quick_parse( git_revwalk *walk, git_commit_list_node *node, git_odb_object *obj) { git_oid *parent_oid; git_commit *commit; int error; size_t i; commit = git__calloc(1, sizeof(*commit)); GIT_ERROR_CHECK_ALLOC(commit); commit->object.repo = walk->repo; if ((error = git_commit__parse_ext(commit, obj, GIT_COMMIT_PARSE_QUICK)) < 0) { git__free(commit); return error; } if (!git__is_uint16(git_array_size(commit->parent_ids))) { git__free(commit); git_error_set(GIT_ERROR_INVALID, "commit has more than 2^16 parents"); return -1; } node->generation = 0; node->time = commit->committer->when.time; node->out_degree = (uint16_t) git_array_size(commit->parent_ids); node->parents = alloc_parents(walk, node, node->out_degree); GIT_ERROR_CHECK_ALLOC(node->parents); git_array_foreach(commit->parent_ids, i, parent_oid) { node->parents[i] = git_revwalk__commit_lookup(walk, parent_oid); } git_commit__free(commit); node->parsed = 1; return 0; } int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit) { git_odb_object *obj; git_commit_graph_file *cgraph_file = NULL; int error; if (commit->parsed) return 0; /* Let's try to use the commit graph first. */ git_odb__get_commit_graph_file(&cgraph_file, walk->odb); if (cgraph_file) { git_commit_graph_entry e; error = git_commit_graph_entry_find(&e, cgraph_file, &commit->oid, GIT_OID_RAWSZ); if (error == 0 && git__is_uint16(e.parent_count)) { size_t i; commit->generation = (uint32_t)e.generation; commit->time = e.commit_time; commit->out_degree = (uint16_t)e.parent_count; commit->parents = alloc_parents(walk, commit, commit->out_degree); GIT_ERROR_CHECK_ALLOC(commit->parents); for (i = 0; i < commit->out_degree; ++i) { git_commit_graph_entry parent; error = git_commit_graph_entry_parent(&parent, cgraph_file, &e, i); if (error < 0) return error; commit->parents[i] = git_revwalk__commit_lookup(walk, &parent.sha1); } commit->parsed = 1; return 0; } } if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0) return error; if (obj->cached.type != GIT_OBJECT_COMMIT) { git_error_set(GIT_ERROR_INVALID, "object is no commit object"); error = -1; } else error = commit_quick_parse(walk, commit, obj); git_odb_object_free(obj); return error; } git2r/src/libgit2/src/transaction.h0000644000175000017500000000057514125111754017053 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_transaction_h__ #define INCLUDE_transaction_h__ #include "common.h" int git_transaction_config_new(git_transaction **out, git_config *cfg); #endif git2r/src/libgit2/src/util.h0000644000175000017500000002532014125111754015476 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_util_h__ #define INCLUDE_util_h__ #include "common.h" #ifndef GIT_WIN32 # include #endif #include "git2/buffer.h" #include "buffer.h" #include "common.h" #include "strnlen.h" #include "thread.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define bitsizeof(x) (CHAR_BIT * sizeof(x)) #define MSB(x, bits) ((x) & (~UINT64_C(0) << (bitsizeof(x) - (bits)))) #ifndef min # define min(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef max # define max(a,b) ((a) > (b) ? (a) : (b)) #endif #if defined(__GNUC__) # define GIT_CONTAINER_OF(ptr, type, member) \ __builtin_choose_expr( \ __builtin_offsetof(type, member) == 0 && \ __builtin_types_compatible_p(__typeof__(&((type *) 0)->member), __typeof__(ptr)), \ ((type *) (ptr)), \ (void)0) #else # define GIT_CONTAINER_OF(ptr, type, member) (type *)(ptr) #endif #define GIT_DATE_RFC2822_SZ 32 /** * Return the length of a constant string. * We are aware that `strlen` performs the same task and is usually * optimized away by the compiler, whilst being safer because it returns * valid values when passed a pointer instead of a constant string; however * this macro will transparently work with wide-char and single-char strings. */ #define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1) #define STRCMP_CASESELECT(IGNORE_CASE, STR1, STR2) \ ((IGNORE_CASE) ? strcasecmp((STR1), (STR2)) : strcmp((STR1), (STR2))) #define CASESELECT(IGNORE_CASE, ICASE, CASE) \ ((IGNORE_CASE) ? (ICASE) : (CASE)) extern int git__prefixcmp(const char *str, const char *prefix); extern int git__prefixcmp_icase(const char *str, const char *prefix); extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix); extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix); extern int git__suffixcmp(const char *str, const char *suffix); GIT_INLINE(int) git__signum(int val) { return ((val > 0) - (val < 0)); } extern int git__strntol32(int32_t *n, const char *buff, size_t buff_len, const char **end_buf, int base); extern int git__strntol64(int64_t *n, const char *buff, size_t buff_len, const char **end_buf, int base); extern void git__hexdump(const char *buffer, size_t n); extern uint32_t git__hash(const void *key, int len, uint32_t seed); /* 32-bit cross-platform rotl */ #ifdef _MSC_VER /* use built-in method in MSVC */ # define git__rotl(v, s) (uint32_t)_rotl(v, s) #else /* use bitops in GCC; with o2 this gets optimized to a rotl instruction */ # define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s)))) #endif extern char *git__strtok(char **end, const char *sep); extern char *git__strsep(char **end, const char *sep); extern void git__strntolower(char *str, size_t len); extern void git__strtolower(char *str); #ifdef GIT_WIN32 GIT_INLINE(int) git__tolower(int c) { return (c >= 'A' && c <= 'Z') ? (c + 32) : c; } #else # define git__tolower(a) tolower(a) #endif extern size_t git__linenlen(const char *buffer, size_t buffer_len); GIT_INLINE(const char *) git__next_line(const char *s) { while (*s && *s != '\n') s++; while (*s == '\n' || *s == '\r') s++; return s; } GIT_INLINE(const void *) git__memrchr(const void *s, int c, size_t n) { const unsigned char *cp; if (n != 0) { cp = (unsigned char *)s + n; do { if (*(--cp) == (unsigned char)c) return cp; } while (--n != 0); } return NULL; } extern const void * git__memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); typedef int (*git__tsort_cmp)(const void *a, const void *b); extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp); typedef int (*git__sort_r_cmp)(const void *a, const void *b, void *payload); extern void git__tsort_r( void **dst, size_t size, git__sort_r_cmp cmp, void *payload); extern void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload); /** * @param position If non-NULL, this will be set to the position where the * element is or would be inserted if not found. * @return 0 if found; GIT_ENOTFOUND if not found */ extern int git__bsearch( void **array, size_t array_len, const void *key, int (*compare)(const void *key, const void *element), size_t *position); extern int git__bsearch_r( void **array, size_t array_len, const void *key, int (*compare_r)(const void *key, const void *element, void *payload), void *payload, size_t *position); #define git__strcmp strcmp #define git__strncmp strncmp extern int git__strcmp_cb(const void *a, const void *b); extern int git__strcasecmp_cb(const void *a, const void *b); extern int git__strcasecmp(const char *a, const char *b); extern int git__strncasecmp(const char *a, const char *b, size_t sz); extern int git__strcasesort_cmp(const char *a, const char *b); /* * Compare some NUL-terminated `a` to a possibly non-NUL terminated * `b` of length `b_len`; like `strncmp` but ensuring that * `strlen(a) == b_len` as well. */ GIT_INLINE(int) git__strlcmp(const char *a, const char *b, size_t b_len) { int cmp = strncmp(a, b, b_len); return cmp ? cmp : (int)a[b_len]; } typedef struct { git_atomic32 refcount; void *owner; } git_refcount; typedef void (*git_refcount_freeptr)(void *r); #define GIT_REFCOUNT_INC(r) { \ git_atomic32_inc(&(r)->rc.refcount); \ } #define GIT_REFCOUNT_DEC(_r, do_free) { \ git_refcount *r = &(_r)->rc; \ int val = git_atomic32_dec(&r->refcount); \ if (val <= 0 && r->owner == NULL) { do_free(_r); } \ } #define GIT_REFCOUNT_OWN(r, o) { \ (void)git_atomic_swap((r)->rc.owner, o); \ } #define GIT_REFCOUNT_OWNER(r) git_atomic_load((r)->rc.owner) #define GIT_REFCOUNT_VAL(r) git_atomic32_get((r)->rc.refcount) static signed char from_hex[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 30 */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 40 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* f0 */ }; GIT_INLINE(int) git__fromhex(char h) { return from_hex[(unsigned char) h]; } GIT_INLINE(int) git__ishex(const char *str) { unsigned i; for (i=0; str[i] != '\0'; i++) if (git__fromhex(str[i]) < 0) return 0; return 1; } GIT_INLINE(size_t) git__size_t_bitmask(size_t v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; return v; } GIT_INLINE(size_t) git__size_t_powerof2(size_t v) { return git__size_t_bitmask(v) + 1; } GIT_INLINE(bool) git__isupper(int c) { return (c >= 'A' && c <= 'Z'); } GIT_INLINE(bool) git__isalpha(int c) { return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); } GIT_INLINE(bool) git__isdigit(int c) { return (c >= '0' && c <= '9'); } GIT_INLINE(bool) git__isspace(int c) { return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v'); } GIT_INLINE(bool) git__isspace_nonlf(int c) { return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v'); } GIT_INLINE(bool) git__iswildcard(int c) { return (c == '*' || c == '?' || c == '['); } GIT_INLINE(bool) git__isxdigit(int c) { return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } /* * Parse a string value as a boolean, just like Core Git does. * * Valid values for true are: 'true', 'yes', 'on' * Valid values for false are: 'false', 'no', 'off' */ extern int git__parse_bool(int *out, const char *value); /* * Parse a string into a value as a git_time_t. * * Sample valid input: * - "yesterday" * - "July 17, 2003" * - "2003-7-17 08:23" */ extern int git__date_parse(git_time_t *out, const char *date); /* * Format a git_time as a RFC2822 string * * @param out buffer to store formatted date; a '\\0' terminator will automatically be added. * @param len size of the buffer; should be atleast `GIT_DATE_RFC2822_SZ` in size; * @param date the date to be formatted * @return 0 if successful; -1 on error */ extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date); /* * Unescapes a string in-place. * * Edge cases behavior: * - "jackie\" -> "jacky\" * - "chan\\" -> "chan\" */ extern size_t git__unescape(char *str); /* * Safely zero-out memory, making sure that the compiler * doesn't optimize away the operation. */ GIT_INLINE(void) git__memzero(void *data, size_t size) { #ifdef _MSC_VER SecureZeroMemory((PVOID)data, size); #else volatile uint8_t *scan = (volatile uint8_t *)data; while (size--) *scan++ = 0x0; #endif } #ifdef GIT_WIN32 GIT_INLINE(double) git__timer(void) { /* GetTickCount64 returns the number of milliseconds that have * elapsed since the system was started. */ return (double) GetTickCount64() / (double) 1000; } #elif __APPLE__ #include GIT_INLINE(double) git__timer(void) { uint64_t time = mach_absolute_time(); static double scaling_factor = 0; if (scaling_factor == 0) { mach_timebase_info_data_t info; (void)mach_timebase_info(&info); scaling_factor = (double)info.numer / (double)info.denom; } return (double)time * scaling_factor / 1.0E9; } #elif defined(__amigaos4__) #include GIT_INLINE(double) git__timer(void) { struct TimeVal tv; ITimer->GetUpTime(&tv); return (double)tv.Seconds + (double)tv.Microseconds / 1.0E6; } #else #include GIT_INLINE(double) git__timer(void) { struct timeval tv; #ifdef CLOCK_MONOTONIC struct timespec tp; if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) return (double) tp.tv_sec + (double) tp.tv_nsec / 1.0E9; #endif /* Fall back to using gettimeofday */ gettimeofday(&tv, NULL); return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6; } #endif extern int git__getenv(git_buf *out, const char *name); extern int git__online_cpus(void); GIT_INLINE(int) git__noop(void) { return 0; } #include "alloc.h" #endif git2r/src/libgit2/src/status.h0000644000175000017500000000074214125111754016045 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_status_h__ #define INCLUDE_status_h__ #include "common.h" #include "diff.h" #include "git2/status.h" #include "git2/diff.h" struct git_status_list { git_status_options opts; git_diff *head2idx; git_diff *idx2wd; git_vector paired; }; #endif git2r/src/libgit2/src/allocators/0000755000175000017500000000000014145550337016517 5ustar nileshnileshgit2r/src/libgit2/src/allocators/win32_leakcheck.h0000644000175000017500000000064614125111754021624 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_allocators_win32_leakcheck_h #define INCLUDE_allocators_win32_leakcheck_h #include "common.h" #include "alloc.h" int git_win32_leakcheck_init_allocator(git_allocator *allocator); #endif git2r/src/libgit2/src/allocators/stdalloc.c0000644000175000017500000000552414125111754020470 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "stdalloc.h" static void *stdalloc__malloc(size_t len, const char *file, int line) { void *ptr; GIT_UNUSED(file); GIT_UNUSED(line); #ifdef GIT_DEBUG_STRICT_ALLOC if (!len) return NULL; #endif ptr = malloc(len); if (!ptr) git_error_set_oom(); return ptr; } static void *stdalloc__calloc(size_t nelem, size_t elsize, const char *file, int line) { void *ptr; GIT_UNUSED(file); GIT_UNUSED(line); #ifdef GIT_DEBUG_STRICT_ALLOC if (!elsize || !nelem) return NULL; #endif ptr = calloc(nelem, elsize); if (!ptr) git_error_set_oom(); return ptr; } static char *stdalloc__strdup(const char *str, const char *file, int line) { char *ptr; GIT_UNUSED(file); GIT_UNUSED(line); ptr = strdup(str); if (!ptr) git_error_set_oom(); return ptr; } static char *stdalloc__strndup(const char *str, size_t n, const char *file, int line) { size_t length = 0, alloclength; char *ptr; length = p_strnlen(str, n); if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) || !(ptr = stdalloc__malloc(alloclength, file, line))) return NULL; if (length) memcpy(ptr, str, length); ptr[length] = '\0'; return ptr; } static char *stdalloc__substrdup(const char *start, size_t n, const char *file, int line) { char *ptr; size_t alloclen; if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) || !(ptr = stdalloc__malloc(alloclen, file, line))) return NULL; memcpy(ptr, start, n); ptr[n] = '\0'; return ptr; } static void *stdalloc__realloc(void *ptr, size_t size, const char *file, int line) { void *new_ptr; GIT_UNUSED(file); GIT_UNUSED(line); #ifdef GIT_DEBUG_STRICT_ALLOC if (!size) return NULL; #endif new_ptr = realloc(ptr, size); if (!new_ptr) git_error_set_oom(); return new_ptr; } static void *stdalloc__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line) { size_t newsize; if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize)) return NULL; return stdalloc__realloc(ptr, newsize, file, line); } static void *stdalloc__mallocarray(size_t nelem, size_t elsize, const char *file, int line) { return stdalloc__reallocarray(NULL, nelem, elsize, file, line); } static void stdalloc__free(void *ptr) { free(ptr); } int git_stdalloc_init_allocator(git_allocator *allocator) { allocator->gmalloc = stdalloc__malloc; allocator->gcalloc = stdalloc__calloc; allocator->gstrdup = stdalloc__strdup; allocator->gstrndup = stdalloc__strndup; allocator->gsubstrdup = stdalloc__substrdup; allocator->grealloc = stdalloc__realloc; allocator->greallocarray = stdalloc__reallocarray; allocator->gmallocarray = stdalloc__mallocarray; allocator->gfree = stdalloc__free; return 0; } git2r/src/libgit2/src/allocators/failalloc.c0000644000175000017500000000325414125111754020607 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "failalloc.h" void *git_failalloc_malloc(size_t len, const char *file, int line) { GIT_UNUSED(len); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line) { GIT_UNUSED(nelem); GIT_UNUSED(elsize); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } char *git_failalloc_strdup(const char *str, const char *file, int line) { GIT_UNUSED(str); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line) { GIT_UNUSED(str); GIT_UNUSED(n); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line) { GIT_UNUSED(start); GIT_UNUSED(n); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line) { GIT_UNUSED(ptr); GIT_UNUSED(size); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line) { GIT_UNUSED(ptr); GIT_UNUSED(nelem); GIT_UNUSED(elsize); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line) { GIT_UNUSED(nelem); GIT_UNUSED(elsize); GIT_UNUSED(file); GIT_UNUSED(line); return NULL; } void git_failalloc_free(void *ptr) { GIT_UNUSED(ptr); } git2r/src/libgit2/src/allocators/win32_leakcheck.c0000644000175000017500000000572114125111754021616 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "win32_leakcheck.h" #if defined(GIT_WIN32_LEAKCHECK) #include "win32/w32_leakcheck.h" static void *leakcheck_malloc(size_t len, const char *file, int line) { void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line); if (!ptr) git_error_set_oom(); return ptr; } static void *leakcheck_calloc(size_t nelem, size_t elsize, const char *file, int line) { void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line); if (!ptr) git_error_set_oom(); return ptr; } static char *leakcheck_strdup(const char *str, const char *file, int line) { char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line); if (!ptr) git_error_set_oom(); return ptr; } static char *leakcheck_strndup(const char *str, size_t n, const char *file, int line) { size_t length = 0, alloclength; char *ptr; length = p_strnlen(str, n); if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) || !(ptr = leakcheck_malloc(alloclength, file, line))) return NULL; if (length) memcpy(ptr, str, length); ptr[length] = '\0'; return ptr; } static char *leakcheck_substrdup(const char *start, size_t n, const char *file, int line) { char *ptr; size_t alloclen; if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) || !(ptr = leakcheck_malloc(alloclen, file, line))) return NULL; memcpy(ptr, start, n); ptr[n] = '\0'; return ptr; } static void *leakcheck_realloc(void *ptr, size_t size, const char *file, int line) { void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line); if (!new_ptr) git_error_set_oom(); return new_ptr; } static void *leakcheck_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line) { size_t newsize; if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize)) return NULL; return leakcheck_realloc(ptr, newsize, file, line); } static void *leakcheck_mallocarray(size_t nelem, size_t elsize, const char *file, int line) { return leakcheck_reallocarray(NULL, nelem, elsize, file, line); } static void leakcheck_free(void *ptr) { free(ptr); } int git_win32_leakcheck_init_allocator(git_allocator *allocator) { allocator->gmalloc = leakcheck_malloc; allocator->gcalloc = leakcheck_calloc; allocator->gstrdup = leakcheck_strdup; allocator->gstrndup = leakcheck_strndup; allocator->gsubstrdup = leakcheck_substrdup; allocator->grealloc = leakcheck_realloc; allocator->greallocarray = leakcheck_reallocarray; allocator->gmallocarray = leakcheck_mallocarray; allocator->gfree = leakcheck_free; return 0; } #else int git_win32_leakcheck_init_allocator(git_allocator *allocator) { GIT_UNUSED(allocator); git_error_set(GIT_EINVALID, "leakcheck memory allocator not available"); return -1; } #endif git2r/src/libgit2/src/allocators/failalloc.h0000644000175000017500000000211214125111754020604 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_allocators_failalloc_h__ #define INCLUDE_allocators_failalloc_h__ #include "common.h" extern void *git_failalloc_malloc(size_t len, const char *file, int line); extern void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line); extern char *git_failalloc_strdup(const char *str, const char *file, int line); extern char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line); extern char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line); extern void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line); extern void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line); extern void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line); extern void git_failalloc_free(void *ptr); #endif git2r/src/libgit2/src/allocators/stdalloc.h0000644000175000017500000000062514125111754020472 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_allocators_stdalloc_h__ #define INCLUDE_allocators_stdalloc_h__ #include "common.h" #include "alloc.h" int git_stdalloc_init_allocator(git_allocator *allocator); #endif git2r/src/libgit2/src/diff_xdiff.c0000644000175000017500000001663414125111754016614 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "diff_xdiff.h" #include "git2/errors.h" #include "diff.h" #include "diff_driver.h" #include "patch_generate.h" static int git_xdiff_scan_int(const char **str, int *value) { const char *scan = *str; int v = 0, digits = 0; /* find next digit */ for (scan = *str; *scan && !git__isdigit(*scan); scan++); /* parse next number */ for (; git__isdigit(*scan); scan++, digits++) v = (v * 10) + (*scan - '0'); *str = scan; *value = v; return (digits > 0) ? 0 : -1; } static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header) { /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ if (*header != '@') goto fail; if (git_xdiff_scan_int(&header, &hunk->old_start) < 0) goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0) goto fail; } else hunk->old_lines = 1; if (git_xdiff_scan_int(&header, &hunk->new_start) < 0) goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0) goto fail; } else hunk->new_lines = 1; if (hunk->old_start < 0 || hunk->new_start < 0) goto fail; return 0; fail: git_error_set(GIT_ERROR_INVALID, "malformed hunk header from xdiff"); return -1; } typedef struct { git_xdiff_output *xo; git_patch_generated *patch; git_diff_hunk hunk; int old_lineno, new_lineno; mmfile_t xd_old_data, xd_new_data; } git_xdiff_info; static int diff_update_lines( git_xdiff_info *info, git_diff_line *line, const char *content, size_t content_len) { const char *scan = content, *scan_end = content + content_len; for (line->num_lines = 0; scan < scan_end; ++scan) if (*scan == '\n') ++line->num_lines; line->content = content; line->content_len = content_len; /* expect " "/"-"/"+", then data */ switch (line->origin) { case GIT_DIFF_LINE_ADDITION: case GIT_DIFF_LINE_DEL_EOFNL: line->old_lineno = -1; line->new_lineno = info->new_lineno; info->new_lineno += (int)line->num_lines; break; case GIT_DIFF_LINE_DELETION: case GIT_DIFF_LINE_ADD_EOFNL: line->old_lineno = info->old_lineno; line->new_lineno = -1; info->old_lineno += (int)line->num_lines; break; case GIT_DIFF_LINE_CONTEXT: case GIT_DIFF_LINE_CONTEXT_EOFNL: line->old_lineno = info->old_lineno; line->new_lineno = info->new_lineno; info->old_lineno += (int)line->num_lines; info->new_lineno += (int)line->num_lines; break; default: git_error_set(GIT_ERROR_INVALID, "unknown diff line origin %02x", (unsigned int)line->origin); return -1; } return 0; } static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) { git_xdiff_info *info = priv; git_patch_generated *patch = info->patch; const git_diff_delta *delta = patch->base.delta; git_patch_generated_output *output = &info->xo->output; git_diff_line line; size_t buffer_len; if (len == 1) { output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr); if (output->error < 0) return output->error; info->hunk.header_len = bufs[0].size; if (info->hunk.header_len >= sizeof(info->hunk.header)) info->hunk.header_len = sizeof(info->hunk.header) - 1; /* Sanitize the hunk header in case there is invalid Unicode */ buffer_len = git_utf8_valid_buf_length(bufs[0].ptr, info->hunk.header_len); /* Sanitizing the hunk header may delete the newline, so add it back again if there is room */ if (buffer_len < info->hunk.header_len) { bufs[0].ptr[buffer_len] = '\n'; buffer_len += 1; info->hunk.header_len = buffer_len; } memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len); info->hunk.header[info->hunk.header_len] = '\0'; if (output->hunk_cb != NULL && (output->error = output->hunk_cb( delta, &info->hunk, output->payload))) return output->error; info->old_lineno = info->hunk.old_start; info->new_lineno = info->hunk.new_start; } if (len == 2 || len == 3) { /* expect " "/"-"/"+", then data */ line.origin = (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION : (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION : GIT_DIFF_LINE_CONTEXT; if (line.origin == GIT_DIFF_LINE_ADDITION) line.content_offset = bufs[1].ptr - info->xd_new_data.ptr; else if (line.origin == GIT_DIFF_LINE_DELETION) line.content_offset = bufs[1].ptr - info->xd_old_data.ptr; else line.content_offset = -1; output->error = diff_update_lines( info, &line, bufs[1].ptr, bufs[1].size); if (!output->error && output->data_cb != NULL) output->error = output->data_cb( delta, &info->hunk, &line, output->payload); } if (len == 3 && !output->error) { /* If we have a '+' and a third buf, then we have added a line * without a newline and the old code had one, so DEL_EOFNL. * If we have a '-' and a third buf, then we have removed a line * with out a newline but added a blank line, so ADD_EOFNL. */ line.origin = (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL : (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL : GIT_DIFF_LINE_CONTEXT_EOFNL; line.content_offset = -1; output->error = diff_update_lines( info, &line, bufs[2].ptr, bufs[2].size); if (!output->error && output->data_cb != NULL) output->error = output->data_cb( delta, &info->hunk, &line, output->payload); } return output->error; } static int git_xdiff(git_patch_generated_output *output, git_patch_generated *patch) { git_xdiff_output *xo = (git_xdiff_output *)output; git_xdiff_info info; git_diff_find_context_payload findctxt; memset(&info, 0, sizeof(info)); info.patch = patch; info.xo = xo; xo->callback.priv = &info; git_diff_find_context_init( &xo->config.find_func, &findctxt, git_patch_generated_driver(patch)); xo->config.find_func_priv = &findctxt; if (xo->config.find_func != NULL) xo->config.flags |= XDL_EMIT_FUNCNAMES; else xo->config.flags &= ~XDL_EMIT_FUNCNAMES; /* TODO: check ofile.opts_flags to see if driver-specific per-file * updates are needed to xo->params.flags */ git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch); git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch); if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE || info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) { git_error_set(GIT_ERROR_INVALID, "files too large for diff"); return -1; } xdl_diff(&info.xd_old_data, &info.xd_new_data, &xo->params, &xo->config, &xo->callback); git_diff_find_context_clear(&findctxt); return xo->output.error; } void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) { uint32_t flags = opts ? opts->flags : 0; xo->output.diff_cb = git_xdiff; xo->config.ctxlen = opts ? opts->context_lines : 3; xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0; if (flags & GIT_DIFF_IGNORE_WHITESPACE) xo->params.flags |= XDF_WHITESPACE_FLAGS; if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE; if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL) xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; if (flags & GIT_DIFF_INDENT_HEURISTIC) xo->params.flags |= XDF_INDENT_HEURISTIC; if (flags & GIT_DIFF_PATIENCE) xo->params.flags |= XDF_PATIENCE_DIFF; if (flags & GIT_DIFF_MINIMAL) xo->params.flags |= XDF_NEED_MINIMAL; if (flags & GIT_DIFF_IGNORE_BLANK_LINES) xo->params.flags |= XDF_IGNORE_BLANK_LINES; xo->callback.outf = git_xdiff_cb; } git2r/src/libgit2/src/diff_driver.h0000644000175000017500000000305414125111754017004 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_diff_driver_h__ #define INCLUDE_diff_driver_h__ #include "common.h" #include "attr_file.h" #include "buffer.h" typedef struct git_diff_driver_registry git_diff_driver_registry; git_diff_driver_registry *git_diff_driver_registry_new(void); void git_diff_driver_registry_free(git_diff_driver_registry *); typedef struct git_diff_driver git_diff_driver; int git_diff_driver_lookup(git_diff_driver **, git_repository *, git_attr_session *attrsession, const char *); void git_diff_driver_free(git_diff_driver *); /* diff option flags to force off and on for this driver */ void git_diff_driver_update_options(uint32_t *option_flags, git_diff_driver *); /* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */ int git_diff_driver_content_is_binary( git_diff_driver *, const char *content, size_t content_len); typedef long (*git_diff_find_context_fn)( const char *, long, char *, long, void *); typedef int (*git_diff_find_context_line)( git_diff_driver *, git_buf *); typedef struct { git_diff_driver *driver; git_diff_find_context_line match_line; git_buf line; } git_diff_find_context_payload; void git_diff_find_context_init( git_diff_find_context_fn *findfn_out, git_diff_find_context_payload *payload_out, git_diff_driver *driver); void git_diff_find_context_clear(git_diff_find_context_payload *); #endif git2r/src/libgit2/src/pool.h0000644000175000017500000000764214125111754015501 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_pool_h__ #define INCLUDE_pool_h__ #include "common.h" #include "vector.h" typedef struct git_pool_page git_pool_page; #ifndef GIT_DEBUG_POOL /** * Chunked allocator. * * A `git_pool` can be used when you want to cheaply allocate * multiple items of the same type and are willing to free them * all together with a single call. The two most common cases * are a set of fixed size items (such as lots of OIDs) or a * bunch of strings. * * Internally, a `git_pool` allocates pages of memory and then * deals out blocks from the trailing unused portion of each page. * The pages guarantee that the number of actual allocations done * will be much smaller than the number of items needed. * * For examples of how to set up a `git_pool` see `git_pool_init`. */ typedef struct { git_pool_page *pages; /* allocated pages */ size_t item_size; /* size of single alloc unit in bytes */ size_t page_size; /* size of page in bytes */ } git_pool; #define GIT_POOL_INIT { NULL, 0, 0 } #else /** * Debug chunked allocator. * * Acts just like `git_pool` but instead of actually pooling allocations it * passes them through to `git__malloc`. This makes it possible to easily debug * systems that use `git_pool` using valgrind. * * In order to track allocations during the lifetime of the pool we use a * `git_vector`. When the pool is deallocated everything in the vector is * freed. * * `API is exactly the same as the standard `git_pool` with one exception. * Since we aren't allocating pages to hand out in chunks we can't easily * implement `git_pool__open_pages`. */ typedef struct { git_vector allocations; size_t item_size; size_t page_size; } git_pool; #define GIT_POOL_INIT { GIT_VECTOR_INIT, 0, 0 } #endif /** * Initialize a pool. * * To allocation strings, use like this: * * git_pool_init(&string_pool, 1); * my_string = git_pool_strdup(&string_pool, your_string); * * To allocate items of fixed size, use like this: * * git_pool_init(&pool, sizeof(item)); * my_item = git_pool_malloc(&pool, 1); * * Of course, you can use this in other ways, but those are the * two most common patterns. */ extern int git_pool_init(git_pool *pool, size_t item_size); /** * Free all items in pool */ extern void git_pool_clear(git_pool *pool); /** * Swap two pools with one another */ extern void git_pool_swap(git_pool *a, git_pool *b); /** * Allocate space for one or more items from a pool. */ extern void *git_pool_malloc(git_pool *pool, size_t items); extern void *git_pool_mallocz(git_pool *pool, size_t items); /** * Allocate space and duplicate string data into it. * * This is allowed only for pools with item_size == sizeof(char) */ extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n); /** * Allocate space and duplicate a string into it. * * This is allowed only for pools with item_size == sizeof(char) */ extern char *git_pool_strdup(git_pool *pool, const char *str); /** * Allocate space and duplicate a string into it, NULL is no error. * * This is allowed only for pools with item_size == sizeof(char) */ extern char *git_pool_strdup_safe(git_pool *pool, const char *str); /** * Allocate space for the concatenation of two strings. * * This is allowed only for pools with item_size == sizeof(char) */ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b); /* * Misc utilities */ #ifndef GIT_DEBUG_POOL extern uint32_t git_pool__open_pages(git_pool *pool); #endif extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr); /** * This function is being called by our global setup routines to * initialize the system pool size. * * @return 0 on success, <0 on failure */ extern int git_pool_global_init(void); #endif git2r/src/libgit2/src/diff_xdiff.h0000644000175000017500000000175514125111754016617 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_diff_xdiff_h__ #define INCLUDE_diff_xdiff_h__ #include "common.h" #include "diff.h" #include "xdiff/xdiff.h" #include "patch_generate.h" /* xdiff cannot cope with large files. these files should not be passed to * xdiff. callers should treat these large files as binary. */ #define GIT_XDIFF_MAX_SIZE (INT64_C(1024) * 1024 * 1023) /* A git_xdiff_output is a git_patch_generate_output with extra fields * necessary to use libxdiff. Calling git_xdiff_init() will set the diff_cb * field of the output to use xdiff to generate the diffs. */ typedef struct { git_patch_generated_output output; xdemitconf_t config; xpparam_t params; xdemitcb_t callback; } git_xdiff_output; void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts); #endif git2r/src/libgit2/src/parse.c0000644000175000017500000000514614125111754015632 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "parse.h" int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len) { if (content && content_len) { ctx->content = content; ctx->content_len = content_len; } else { ctx->content = ""; ctx->content_len = 0; } ctx->remain = ctx->content; ctx->remain_len = ctx->content_len; ctx->line = ctx->remain; ctx->line_len = git__linenlen(ctx->line, ctx->remain_len); ctx->line_num = 1; return 0; } void git_parse_ctx_clear(git_parse_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); ctx->content = ""; } void git_parse_advance_line(git_parse_ctx *ctx) { ctx->line += ctx->line_len; ctx->remain_len -= ctx->line_len; ctx->line_len = git__linenlen(ctx->line, ctx->remain_len); ctx->line_num++; } void git_parse_advance_chars(git_parse_ctx *ctx, size_t char_cnt) { ctx->line += char_cnt; ctx->remain_len -= char_cnt; ctx->line_len -= char_cnt; } int git_parse_advance_expected( git_parse_ctx *ctx, const char *expected, size_t expected_len) { if (ctx->line_len < expected_len) return -1; if (memcmp(ctx->line, expected, expected_len) != 0) return -1; git_parse_advance_chars(ctx, expected_len); return 0; } int git_parse_advance_ws(git_parse_ctx *ctx) { int ret = -1; while (ctx->line_len > 0 && ctx->line[0] != '\n' && git__isspace(ctx->line[0])) { ctx->line++; ctx->line_len--; ctx->remain_len--; ret = 0; } return ret; } int git_parse_advance_nl(git_parse_ctx *ctx) { if (ctx->line_len != 1 || ctx->line[0] != '\n') return -1; git_parse_advance_line(ctx); return 0; } int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base) { const char *end; int ret; if (ctx->line_len < 1 || !git__isdigit(ctx->line[0])) return -1; if ((ret = git__strntol64(out, ctx->line, ctx->line_len, &end, base)) < 0) return -1; git_parse_advance_chars(ctx, (end - ctx->line)); return 0; } int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx) { if (ctx->line_len < GIT_OID_HEXSZ) return -1; if ((git_oid_fromstrn(out, ctx->line, GIT_OID_HEXSZ)) < 0) return -1; git_parse_advance_chars(ctx, GIT_OID_HEXSZ); return 0; } int git_parse_peek(char *out, git_parse_ctx *ctx, int flags) { size_t remain = ctx->line_len; const char *ptr = ctx->line; while (remain) { char c = *ptr; if ((flags & GIT_PARSE_PEEK_SKIP_WHITESPACE) && git__isspace(c)) { remain--; ptr++; continue; } *out = c; return 0; } return -1; } git2r/src/libgit2/src/bitvec.h0000644000175000017500000000334014125111754015773 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_bitvec_h__ #define INCLUDE_bitvec_h__ #include "common.h" /* * This is a silly little fixed length bit vector type that will store * vectors of 64 bits or less directly in the structure and allocate * memory for vectors longer than 64 bits. You can use the two versions * transparently through the API and avoid heap allocation completely when * using a short bit vector as a result. */ typedef struct { size_t length; union { uint64_t *words; uint64_t bits; } u; } git_bitvec; GIT_INLINE(int) git_bitvec_init(git_bitvec *bv, size_t capacity) { memset(bv, 0x0, sizeof(*bv)); if (capacity >= 64) { bv->length = (capacity / 64) + 1; bv->u.words = git__calloc(bv->length, sizeof(uint64_t)); if (!bv->u.words) return -1; } return 0; } #define GIT_BITVEC_MASK(BIT) ((uint64_t)1 << (BIT % 64)) #define GIT_BITVEC_WORD(BV, BIT) (BV->length ? &BV->u.words[BIT / 64] : &BV->u.bits) GIT_INLINE(void) git_bitvec_set(git_bitvec *bv, size_t bit, bool on) { uint64_t *word = GIT_BITVEC_WORD(bv, bit); uint64_t mask = GIT_BITVEC_MASK(bit); if (on) *word |= mask; else *word &= ~mask; } GIT_INLINE(bool) git_bitvec_get(git_bitvec *bv, size_t bit) { uint64_t *word = GIT_BITVEC_WORD(bv, bit); return (*word & GIT_BITVEC_MASK(bit)) != 0; } GIT_INLINE(void) git_bitvec_clear(git_bitvec *bv) { if (!bv->length) bv->u.bits = 0; else memset(bv->u.words, 0x0, bv->length * sizeof(uint64_t)); } GIT_INLINE(void) git_bitvec_free(git_bitvec *bv) { if (bv->length) git__free(bv->u.words); } #endif git2r/src/libgit2/src/unix/0000755000175000017500000000000014145550337015337 5ustar nileshnileshgit2r/src/libgit2/src/unix/map.c0000644000175000017500000000274314125111754016260 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/common.h" #if !defined(GIT_WIN32) && !defined(NO_MMAP) #include "map.h" #include #include #include int git__page_size(size_t *page_size) { long sc_page_size = sysconf(_SC_PAGE_SIZE); if (sc_page_size < 0) { git_error_set(GIT_ERROR_OS, "can't determine system page size"); return -1; } *page_size = (size_t) sc_page_size; return 0; } int git__mmap_alignment(size_t *alignment) { return git__page_size(alignment); } int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset) { int mprot = PROT_READ; int mflag = 0; GIT_MMAP_VALIDATE(out, len, prot, flags); out->data = NULL; out->len = 0; if (prot & GIT_PROT_WRITE) mprot |= PROT_WRITE; if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED) mflag = MAP_SHARED; else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE) mflag = MAP_PRIVATE; else mflag = MAP_SHARED; out->data = mmap(NULL, len, mprot, mflag, fd, offset); if (!out->data || out->data == MAP_FAILED) { git_error_set(GIT_ERROR_OS, "failed to mmap. Could not write data"); return -1; } out->len = len; return 0; } int p_munmap(git_map *map) { GIT_ASSERT_ARG(map); munmap(map->data, map->len); map->data = NULL; map->len = 0; return 0; } #endif git2r/src/libgit2/src/unix/realpath.c0000644000175000017500000000125114125111754017274 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/common.h" #ifndef GIT_WIN32 #include #include #include #include char *p_realpath(const char *pathname, char *resolved) { char *ret; if ((ret = realpath(pathname, resolved)) == NULL) return NULL; #ifdef __OpenBSD__ /* The OpenBSD realpath function behaves differently, * figure out if the file exists */ if (access(ret, F_OK) < 0) ret = NULL; #endif return ret; } #endif git2r/src/libgit2/src/unix/posix.h0000644000175000017500000000560314125111754016650 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_unix_posix_h__ #define INCLUDE_unix_posix_h__ #ifndef LIBGIT2_NO_FEATURES_H # include "git2/sys/features.h" #endif #include #include #include #include #include typedef int GIT_SOCKET; #define INVALID_SOCKET -1 #define p_lseek(f,n,w) lseek(f, n, w) #define p_fstat(f,b) fstat(f, b) #define p_lstat(p,b) lstat(p,b) #define p_stat(p,b) stat(p, b) #if defined(GIT_USE_STAT_MTIMESPEC) # define st_atime_nsec st_atimespec.tv_nsec # define st_mtime_nsec st_mtimespec.tv_nsec # define st_ctime_nsec st_ctimespec.tv_nsec #elif defined(GIT_USE_STAT_MTIM) # define st_atime_nsec st_atim.tv_nsec # define st_mtime_nsec st_mtim.tv_nsec # define st_ctime_nsec st_ctim.tv_nsec #elif !defined(GIT_USE_STAT_MTIME_NSEC) && defined(GIT_USE_NSEC) # error GIT_USE_NSEC defined but unknown struct stat nanosecond type #endif #define p_utimes(f, t) utimes(f, t) #define p_readlink(a, b, c) readlink(a, b, c) #define p_symlink(o,n) symlink(o, n) #define p_link(o,n) link(o, n) #define p_unlink(p) unlink(p) #define p_mkdir(p,m) mkdir(p, m) extern char *p_realpath(const char *, char *); GIT_INLINE(int) p_fsync(int fd) { p_fsync__cnt++; return fsync(fd); } #define p_recv(s,b,l,f) recv(s,b,l,f) #define p_send(s,b,l,f) send(s,b,l,f) #define p_inet_pton(a, b, c) inet_pton(a, b, c) #define p_strcasecmp(s1, s2) strcasecmp(s1, s2) #define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c) #define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a) #define p_snprintf snprintf #define p_mkstemp(p) mkstemp(p) #define p_chdir(p) chdir(p) #define p_rmdir(p) rmdir(p) #define p_access(p,m) access(p,m) #define p_ftruncate(fd, sz) ftruncate(fd, sz) /* * Pre-Android 5 did not implement a virtual filesystem atop FAT * partitions for Unix permissions, which causes chmod to fail. However, * Unix permissions have no effect on Android anyway as file permissions * are not actually managed this way, so treating it as a no-op across * all Android is safe. */ #ifdef __ANDROID__ # define p_chmod(p,m) 0 #else # define p_chmod(p,m) chmod(p, m) #endif /* see win32/posix.h for explanation about why this exists */ #define p_lstat_posixly(p,b) lstat(p,b) #define p_localtime_r(c, r) localtime_r(c, r) #define p_gmtime_r(c, r) gmtime_r(c, r) #define p_timeval timeval #ifdef GIT_USE_FUTIMENS GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2]) { struct timespec s[2]; s[0].tv_sec = t[0].tv_sec; s[0].tv_nsec = t[0].tv_usec * 1000; s[1].tv_sec = t[1].tv_sec; s[1].tv_nsec = t[1].tv_usec * 1000; return futimens(f, s); } #else # define p_futimes futimes #endif #define p_pread(f, d, s, o) pread(f, d, s, o) #define p_pwrite(f, d, s, o) pwrite(f, d, s, o) #endif git2r/src/libgit2/src/unix/pthread.h0000644000175000017500000000416414125111754017136 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_unix_pthread_h__ #define INCLUDE_unix_pthread_h__ typedef struct { pthread_t thread; } git_thread; GIT_INLINE(int) git_threads_global_init(void) { return 0; } #define git_thread_create(git_thread_ptr, start_routine, arg) \ pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg) #define git_thread_join(git_thread_ptr, status) \ pthread_join((git_thread_ptr)->thread, status) #define git_thread_currentid() ((size_t)(pthread_self())) #define git_thread_exit(retval) pthread_exit(retval) /* Git Mutex */ #define git_mutex pthread_mutex_t #define git_mutex_init(a) pthread_mutex_init(a, NULL) #define git_mutex_lock(a) pthread_mutex_lock(a) #define git_mutex_unlock(a) pthread_mutex_unlock(a) #define git_mutex_free(a) pthread_mutex_destroy(a) /* Git condition vars */ #define git_cond pthread_cond_t #define git_cond_init(c) pthread_cond_init(c, NULL) #define git_cond_free(c) pthread_cond_destroy(c) #define git_cond_wait(c, l) pthread_cond_wait(c, l) #define git_cond_signal(c) pthread_cond_signal(c) #define git_cond_broadcast(c) pthread_cond_broadcast(c) /* Pthread (-ish) rwlock * * This differs from normal pthreads rwlocks in two ways: * 1. Separate APIs for releasing read locks and write locks (as * opposed to the pure POSIX API which only has one unlock fn) * 2. You should not use recursive read locks (i.e. grabbing a read * lock in a thread that already holds a read lock) because the * Windows implementation doesn't support it */ #define git_rwlock pthread_rwlock_t #define git_rwlock_init(a) pthread_rwlock_init(a, NULL) #define git_rwlock_rdlock(a) pthread_rwlock_rdlock(a) #define git_rwlock_rdunlock(a) pthread_rwlock_unlock(a) #define git_rwlock_wrlock(a) pthread_rwlock_wrlock(a) #define git_rwlock_wrunlock(a) pthread_rwlock_unlock(a) #define git_rwlock_free(a) pthread_rwlock_destroy(a) #define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER #endif git2r/src/libgit2/src/thread.h0000644000175000017500000003040714125111754015772 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_thread_h__ #define INCLUDE_thread_h__ #if defined(GIT_THREADS) #if defined(__clang__) # if (__clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 1)) # error Atomic primitives do not exist on this version of clang; configure libgit2 with -DTHREADSAFE=OFF # else # define GIT_BUILTIN_ATOMIC # endif #elif defined(__GNUC__) # if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)) # error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF # elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) # define GIT_BUILTIN_ATOMIC # else # define GIT_BUILTIN_SYNC # endif #endif #endif /* GIT_THREADS */ /* Common operations even if threading has been disabled */ typedef struct { #if defined(GIT_WIN32) volatile long val; #else volatile int val; #endif } git_atomic32; #ifdef GIT_ARCH_64 typedef struct { #if defined(GIT_WIN32) volatile __int64 val; #else volatile int64_t val; #endif } git_atomic64; typedef git_atomic64 git_atomic_ssize; #define git_atomic_ssize_set git_atomic64_set #define git_atomic_ssize_add git_atomic64_add #define git_atomic_ssize_get git_atomic64_get #else typedef git_atomic32 git_atomic_ssize; #define git_atomic_ssize_set git_atomic32_set #define git_atomic_ssize_add git_atomic32_add #define git_atomic_ssize_get git_atomic32_get #endif #ifdef GIT_THREADS #ifdef GIT_WIN32 # include "win32/thread.h" #else # include "unix/pthread.h" #endif /* * Atomically sets the contents of *a to be val. */ GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val) { #if defined(GIT_WIN32) InterlockedExchange(&a->val, (LONG)val); #elif defined(GIT_BUILTIN_ATOMIC) __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) __sync_lock_test_and_set(&a->val, val); #else # error "Unsupported architecture for atomic operations" #endif } /* * Atomically increments the contents of *a by 1, and stores the result back into *a. * @return the result of the operation. */ GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a) { #if defined(GIT_WIN32) return InterlockedIncrement(&a->val); #elif defined(GIT_BUILTIN_ATOMIC) return __atomic_add_fetch(&a->val, 1, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) return __sync_add_and_fetch(&a->val, 1); #else # error "Unsupported architecture for atomic operations" #endif } /* * Atomically adds the contents of *a and addend, and stores the result back into *a. * @return the result of the operation. */ GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend) { #if defined(GIT_WIN32) return InterlockedAdd(&a->val, addend); #elif defined(GIT_BUILTIN_ATOMIC) return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) return __sync_add_and_fetch(&a->val, addend); #else # error "Unsupported architecture for atomic operations" #endif } /* * Atomically decrements the contents of *a by 1, and stores the result back into *a. * @return the result of the operation. */ GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a) { #if defined(GIT_WIN32) return InterlockedDecrement(&a->val); #elif defined(GIT_BUILTIN_ATOMIC) return __atomic_sub_fetch(&a->val, 1, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) return __sync_sub_and_fetch(&a->val, 1); #else # error "Unsupported architecture for atomic operations" #endif } /* * Atomically gets the contents of *a. * @return the contents of *a. */ GIT_INLINE(int) git_atomic32_get(git_atomic32 *a) { #if defined(GIT_WIN32) return (int)InterlockedCompareExchange(&a->val, 0, 0); #elif defined(GIT_BUILTIN_ATOMIC) return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) return __sync_val_compare_and_swap(&a->val, 0, 0); #else # error "Unsupported architecture for atomic operations" #endif } GIT_INLINE(void *) git_atomic__compare_and_swap( void * volatile *ptr, void *oldval, void *newval) { #if defined(GIT_WIN32) return InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval); #elif defined(GIT_BUILTIN_ATOMIC) void *foundval = oldval; __atomic_compare_exchange(ptr, &foundval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return foundval; #elif defined(GIT_BUILTIN_SYNC) return __sync_val_compare_and_swap(ptr, oldval, newval); #else # error "Unsupported architecture for atomic operations" #endif } GIT_INLINE(volatile void *) git_atomic__swap( void * volatile *ptr, void *newval) { #if defined(GIT_WIN32) return InterlockedExchangePointer(ptr, newval); #elif defined(GIT_BUILTIN_ATOMIC) void * volatile foundval = NULL; __atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST); return foundval; #elif defined(GIT_BUILTIN_SYNC) return (volatile void *)__sync_lock_test_and_set(ptr, newval); #else # error "Unsupported architecture for atomic operations" #endif } GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr) { #if defined(GIT_WIN32) void *newval = NULL, *oldval = NULL; return (volatile void *)InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval); #elif defined(GIT_BUILTIN_ATOMIC) return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) return (volatile void *)__sync_val_compare_and_swap(ptr, 0, 0); #else # error "Unsupported architecture for atomic operations" #endif } #ifdef GIT_ARCH_64 /* * Atomically adds the contents of *a and addend, and stores the result back into *a. * @return the result of the operation. */ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) { #if defined(GIT_WIN32) return InterlockedAdd64(&a->val, addend); #elif defined(GIT_BUILTIN_ATOMIC) return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) return __sync_add_and_fetch(&a->val, addend); #else # error "Unsupported architecture for atomic operations" #endif } /* * Atomically sets the contents of *a to be val. */ GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val) { #if defined(GIT_WIN32) InterlockedExchange64(&a->val, val); #elif defined(GIT_BUILTIN_ATOMIC) __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) __sync_lock_test_and_set(&a->val, val); #else # error "Unsupported architecture for atomic operations" #endif } /* * Atomically gets the contents of *a. * @return the contents of *a. */ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) { #if defined(GIT_WIN32) return (int64_t)InterlockedCompareExchange64(&a->val, 0, 0); #elif defined(GIT_BUILTIN_ATOMIC) return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST); #elif defined(GIT_BUILTIN_SYNC) return __sync_val_compare_and_swap(&a->val, 0, 0); #else # error "Unsupported architecture for atomic operations" #endif } #endif #else #define git_threads_global_init git__noop #define git_thread unsigned int #define git_thread_create(thread, start_routine, arg) git__noop() #define git_thread_join(id, status) git__noop() /* Pthreads Mutex */ #define git_mutex unsigned int #define git_mutex_init(a) git__noop() #define git_mutex_init(a) git__noop() #define git_mutex_lock(a) git__noop() #define git_mutex_unlock(a) git__noop() #define git_mutex_free(a) git__noop() /* Pthreads condition vars */ #define git_cond unsigned int #define git_cond_init(c) git__noop() #define git_cond_free(c) git__noop() #define git_cond_wait(c, l) git__noop() #define git_cond_signal(c) git__noop() #define git_cond_broadcast(c) git__noop() /* Pthreads rwlock */ #define git_rwlock unsigned int #define git_rwlock_init(a) git__noop() #define git_rwlock_rdlock(a) git__noop() #define git_rwlock_rdunlock(a) git__noop() #define git_rwlock_wrlock(a) git__noop() #define git_rwlock_wrunlock(a) git__noop() #define git_rwlock_free(a) git__noop() #define GIT_RWLOCK_STATIC_INIT 0 GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val) { a->val = val; } GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a) { return ++a->val; } GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend) { a->val += addend; return a->val; } GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a) { return --a->val; } GIT_INLINE(int) git_atomic32_get(git_atomic32 *a) { return (int)a->val; } GIT_INLINE(void *) git_atomic__compare_and_swap( void * volatile *ptr, void *oldval, void *newval) { void *foundval = *ptr; if (foundval == oldval) *ptr = newval; return foundval; } GIT_INLINE(volatile void *) git_atomic__swap( void * volatile *ptr, void *newval) { volatile void *old = *ptr; *ptr = newval; return old; } GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr) { return *ptr; } #ifdef GIT_ARCH_64 GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) { a->val += addend; return a->val; } GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val) { a->val = val; } GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) { return (int64_t)a->val; } #endif #endif /* * Atomically replace the contents of *ptr (if they are equal to oldval) with * newval. ptr must point to a pointer or a value that is the same size as a * pointer. This is semantically compatible with: * * #define git_atomic_compare_and_swap(ptr, oldval, newval) \ * ({ \ * void *foundval = *ptr; \ * if (foundval == oldval) \ * *ptr = newval; \ * foundval; \ * }) * * @return the original contents of *ptr. */ #define git_atomic_compare_and_swap(ptr, oldval, newval) \ git_atomic__compare_and_swap((void * volatile *)ptr, oldval, newval) /* * Atomically replace the contents of v with newval. v must be the same size as * a pointer. This is semantically compatible with: * * #define git_atomic_swap(v, newval) \ * ({ \ * volatile void *old = v; \ * v = newval; \ * old; \ * }) * * @return the original contents of v. */ #define git_atomic_swap(v, newval) \ (void *)git_atomic__swap((void * volatile *)&(v), newval) /* * Atomically reads the contents of v. v must be the same size as a pointer. * This is semantically compatible with: * * #define git_atomic_load(v) v * * @return the contents of v. */ #define git_atomic_load(v) \ (void *)git_atomic__load((void * volatile *)&(v)) #if defined(GIT_THREADS) # if defined(GIT_WIN32) # define GIT_MEMORY_BARRIER MemoryBarrier() # elif defined(GIT_BUILTIN_ATOMIC) # define GIT_MEMORY_BARRIER __atomic_thread_fence(__ATOMIC_SEQ_CST) # elif defined(GIT_BUILTIN_SYNC) # define GIT_MEMORY_BARRIER __sync_synchronize() # endif #else # define GIT_MEMORY_BARRIER /* noop */ #endif /* Thread-local data */ #if !defined(GIT_THREADS) # define git_tlsdata_key int #elif defined(GIT_WIN32) # define git_tlsdata_key DWORD #elif defined(_POSIX_THREADS) # define git_tlsdata_key pthread_key_t #else # error unknown threading model #endif /** * Create a thread-local data key. The destroy function will be * called upon thread exit. On some platforms, it may be called * when all threads have deleted their keys. * * Note that the tlsdata functions do not set an error message on * failure; this is because the error handling in libgit2 is itself * handled by thread-local data storage. * * @param key the tlsdata key * @param destroy_fn function pointer called upon thread exit * @return 0 on success, non-zero on failure */ int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *)); /** * Set a the thread-local value for the given key. * * @param key the tlsdata key to store data on * @param value the pointer to store * @return 0 on success, non-zero on failure */ int git_tlsdata_set(git_tlsdata_key key, void *value); /** * Get the thread-local value for the given key. * * @param key the tlsdata key to retrieve the value of * @return the pointer stored with git_tlsdata_set */ void *git_tlsdata_get(git_tlsdata_key key); /** * Delete the given thread-local key. * * @param key the tlsdata key to dispose * @return 0 on success, non-zero on failure */ int git_tlsdata_dispose(git_tlsdata_key key); #endif git2r/src/libgit2/src/clone.c0000644000175000017500000003736514125111754015630 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "clone.h" #include "git2/clone.h" #include "git2/remote.h" #include "git2/revparse.h" #include "git2/branch.h" #include "git2/config.h" #include "git2/checkout.h" #include "git2/commit.h" #include "git2/tree.h" #include "remote.h" #include "futils.h" #include "refs.h" #include "path.h" #include "repository.h" #include "odb.h" static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link); static int create_branch( git_reference **branch, git_repository *repo, const git_oid *target, const char *name, const char *log_message) { git_commit *head_obj = NULL; git_reference *branch_ref = NULL; git_buf refname = GIT_BUF_INIT; int error; /* Find the target commit */ if ((error = git_commit_lookup(&head_obj, repo, target)) < 0) return error; /* Create the new branch */ if ((error = git_buf_printf(&refname, GIT_REFS_HEADS_DIR "%s", name)) < 0) return error; error = git_reference_create(&branch_ref, repo, git_buf_cstr(&refname), target, 0, log_message); git_buf_dispose(&refname); git_commit_free(head_obj); if (!error) *branch = branch_ref; else git_reference_free(branch_ref); return error; } static int setup_tracking_config( git_repository *repo, const char *branch_name, const char *remote_name, const char *merge_target) { git_config *cfg; git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT; int error = -1; if (git_repository_config__weakptr(&cfg, repo) < 0) return -1; if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0) goto cleanup; if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0) goto cleanup; if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0) goto cleanup; if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0) goto cleanup; error = 0; cleanup: git_buf_dispose(&remote_key); git_buf_dispose(&merge_key); return error; } static int create_tracking_branch( git_reference **branch, git_repository *repo, const git_oid *target, const char *branch_name, const char *log_message) { int error; if ((error = create_branch(branch, repo, target, branch_name, log_message)) < 0) return error; return setup_tracking_config( repo, branch_name, GIT_REMOTE_ORIGIN, git_reference_name(*branch)); } static int update_head_to_new_branch( git_repository *repo, const git_oid *target, const char *name, const char *reflog_message) { git_reference *tracking_branch = NULL; int error; if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR)) name += strlen(GIT_REFS_HEADS_DIR); error = create_tracking_branch(&tracking_branch, repo, target, name, reflog_message); if (!error) error = git_repository_set_head( repo, git_reference_name(tracking_branch)); git_reference_free(tracking_branch); /* if it already existed, then the user's refspec created it for us, ignore it' */ if (error == GIT_EEXISTS) error = 0; return error; } static int update_head_to_default(git_repository *repo) { git_buf initialbranch = GIT_BUF_INIT; const char *branch_name; int error = 0; if ((error = git_repository_initialbranch(&initialbranch, repo)) < 0) goto done; if (git__prefixcmp(initialbranch.ptr, GIT_REFS_HEADS_DIR) != 0) { git_error_set(GIT_ERROR_INVALID, "invalid initial branch '%s'", initialbranch.ptr); error = -1; goto done; } branch_name = initialbranch.ptr + strlen(GIT_REFS_HEADS_DIR); error = setup_tracking_config(repo, branch_name, GIT_REMOTE_ORIGIN, initialbranch.ptr); done: git_buf_dispose(&initialbranch); return error; } static int update_remote_head( git_repository *repo, git_remote *remote, git_buf *target, const char *reflog_message) { git_refspec *refspec; git_reference *remote_head = NULL; git_buf remote_head_name = GIT_BUF_INIT; git_buf remote_branch_name = GIT_BUF_INIT; int error; /* Determine the remote tracking ref name from the local branch */ refspec = git_remote__matching_refspec(remote, git_buf_cstr(target)); if (refspec == NULL) { git_error_set(GIT_ERROR_NET, "the remote's default branch does not fit the refspec configuration"); error = GIT_EINVALIDSPEC; goto cleanup; } if ((error = git_refspec_transform( &remote_branch_name, refspec, git_buf_cstr(target))) < 0) goto cleanup; if ((error = git_buf_printf(&remote_head_name, "%s%s/%s", GIT_REFS_REMOTES_DIR, git_remote_name(remote), GIT_HEAD_FILE)) < 0) goto cleanup; error = git_reference_symbolic_create( &remote_head, repo, git_buf_cstr(&remote_head_name), git_buf_cstr(&remote_branch_name), true, reflog_message); cleanup: git_reference_free(remote_head); git_buf_dispose(&remote_branch_name); git_buf_dispose(&remote_head_name); return error; } static int update_head_to_remote( git_repository *repo, git_remote *remote, const char *reflog_message) { int error = 0; size_t refs_len; const git_remote_head *remote_head, **refs; const git_oid *remote_head_id; git_buf branch = GIT_BUF_INIT; if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) return error; /* We cloned an empty repository or one with an unborn HEAD */ if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE)) return update_head_to_default(repo); /* We know we have HEAD, let's see where it points */ remote_head = refs[0]; GIT_ASSERT(remote_head); remote_head_id = &remote_head->oid; error = git_remote_default_branch(&branch, remote); if (error == GIT_ENOTFOUND) { error = git_repository_set_head_detached( repo, remote_head_id); goto cleanup; } if ((error = update_remote_head(repo, remote, &branch, reflog_message)) < 0) goto cleanup; error = update_head_to_new_branch( repo, remote_head_id, git_buf_cstr(&branch), reflog_message); cleanup: git_buf_dispose(&branch); return error; } static int update_head_to_branch( git_repository *repo, git_remote *remote, const char *branch, const char *reflog_message) { int retcode; git_buf remote_branch_name = GIT_BUF_INIT; git_reference *remote_ref = NULL; git_buf default_branch = GIT_BUF_INIT; GIT_ASSERT_ARG(remote); GIT_ASSERT_ARG(branch); if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s", git_remote_name(remote), branch)) < 0 ) goto cleanup; if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0) goto cleanup; if ((retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch, reflog_message)) < 0) goto cleanup; if ((retcode = git_remote_default_branch(&default_branch, remote)) < 0) goto cleanup; if (!git_remote__matching_refspec(remote, git_buf_cstr(&default_branch))) goto cleanup; retcode = update_remote_head(repo, remote, &default_branch, reflog_message); cleanup: git_reference_free(remote_ref); git_buf_dispose(&remote_branch_name); git_buf_dispose(&default_branch); return retcode; } static int default_repository_create(git_repository **out, const char *path, int bare, void *payload) { GIT_UNUSED(payload); return git_repository_init(out, path, bare); } static int default_remote_create( git_remote **out, git_repository *repo, const char *name, const char *url, void *payload) { GIT_UNUSED(payload); return git_remote_create(out, repo, name, url); } /* * submodules? */ static int create_and_configure_origin( git_remote **out, git_repository *repo, const char *url, const git_clone_options *options) { int error; git_remote *origin = NULL; char buf[GIT_PATH_MAX]; git_remote_create_cb remote_create = options->remote_cb; void *payload = options->remote_cb_payload; /* If the path exists and is a dir, the url should be the absolute path */ if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) { if (p_realpath(url, buf) == NULL) return -1; url = buf; } if (!remote_create) { remote_create = default_remote_create; payload = NULL; } if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0) goto on_error; *out = origin; return 0; on_error: git_remote_free(origin); return error; } static bool should_checkout( git_repository *repo, bool is_bare, const git_checkout_options *opts) { if (is_bare) return false; if (!opts) return false; if (opts->checkout_strategy == GIT_CHECKOUT_NONE) return false; return !git_repository_head_unborn(repo); } static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const char *reflog_message) { int error; if (branch) error = update_head_to_branch(repo, remote, branch, reflog_message); /* Point HEAD to the same ref as the remote's head */ else error = update_head_to_remote(repo, remote, reflog_message); if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts)) error = git_checkout_head(repo, co_opts); return error; } static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch) { int error; git_buf reflog_message = GIT_BUF_INIT; git_fetch_options fetch_opts; git_remote *remote; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(_remote); if (!git_repository_is_empty(repo)) { git_error_set(GIT_ERROR_INVALID, "the repository is not empty"); return -1; } if ((error = git_remote_dup(&remote, _remote)) < 0) return error; memcpy(&fetch_opts, opts, sizeof(git_fetch_options)); fetch_opts.update_fetchhead = 0; fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_buf_cstr(&reflog_message))) != 0) goto cleanup; error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message)); cleanup: git_remote_free(remote); git_buf_dispose(&reflog_message); return error; } int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local) { git_buf fromurl = GIT_BUF_INIT; const char *path = url_or_path; bool is_url, is_local; if (local == GIT_CLONE_NO_LOCAL) return 0; if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) { if (git_path_fromurl(&fromurl, url_or_path) < 0) { is_local = -1; goto done; } path = fromurl.ptr; } is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) && git_path_isdir(path); done: git_buf_dispose(&fromurl); return is_local; } static int git__clone( git_repository **out, const char *url, const char *local_path, const git_clone_options *_options, int use_existing) { int error = 0; git_repository *repo = NULL; git_remote *origin; git_clone_options options = GIT_CLONE_OPTIONS_INIT; uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES; git_repository_create_cb repository_cb; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(url); GIT_ASSERT_ARG(local_path); if (_options) memcpy(&options, _options, sizeof(git_clone_options)); GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options"); /* Only clone to a new directory or an empty directory */ if (git_path_exists(local_path) && !use_existing && !git_path_is_empty_dir(local_path)) { git_error_set(GIT_ERROR_INVALID, "'%s' exists and is not an empty directory", local_path); return GIT_EEXISTS; } /* Only remove the root directory on failure if we create it */ if (git_path_exists(local_path)) rmdir_flags |= GIT_RMDIR_SKIP_ROOT; if (options.repository_cb) repository_cb = options.repository_cb; else repository_cb = default_repository_create; if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0) return error; if (!(error = create_and_configure_origin(&origin, repo, url, &options))) { int clone_local = git_clone__should_clone_local(url, options.local); int link = options.local != GIT_CLONE_LOCAL_NO_LINKS; if (clone_local == 1) error = clone_local_into( repo, origin, &options.fetch_opts, &options.checkout_opts, options.checkout_branch, link); else if (clone_local == 0) error = clone_into( repo, origin, &options.fetch_opts, &options.checkout_opts, options.checkout_branch); else error = -1; git_remote_free(origin); } if (error != 0) { git_error_state last_error = {0}; git_error_state_capture(&last_error, error); git_repository_free(repo); repo = NULL; (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); git_error_state_restore(&last_error); } *out = repo; return error; } int git_clone( git_repository **out, const char *url, const char *local_path, const git_clone_options *_options) { return git__clone(out, url, local_path, _options, 0); } int git_clone__submodule( git_repository **out, const char *url, const char *local_path, const git_clone_options *_options) { return git__clone(out, url, local_path, _options, 1); } int git_clone_options_init(git_clone_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_clone_init_options(git_clone_options *opts, unsigned int version) { return git_clone_options_init(opts, version); } #endif static bool can_link(const char *src, const char *dst, int link) { #ifdef GIT_WIN32 GIT_UNUSED(src); GIT_UNUSED(dst); GIT_UNUSED(link); return false; #else struct stat st_src, st_dst; if (!link) return false; if (p_stat(src, &st_src) < 0) return false; if (p_stat(dst, &st_dst) < 0) return false; return st_src.st_dev == st_dst.st_dev; #endif } static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link) { int error, flags; git_repository *src; git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT; git_buf reflog_message = GIT_BUF_INIT; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(remote); if (!git_repository_is_empty(repo)) { git_error_set(GIT_ERROR_INVALID, "the repository is not empty"); return -1; } /* * Let's figure out what path we should use for the source * repo, if it's not rooted, the path should be relative to * the repository's worktree/gitdir. */ if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0) return error; /* Copy .git/objects/ from the source to the target */ if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) { git_buf_dispose(&src_path); return error; } if (git_repository_item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0 || git_repository_item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) { error = -1; goto cleanup; } flags = 0; if (can_link(git_repository_path(src), git_repository_path(repo), link)) flags |= GIT_CPDIR_LINK_FILES; error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb), flags, GIT_OBJECT_DIR_MODE); /* * can_link() doesn't catch all variations, so if we hit an * error and did want to link, let's try again without trying * to link. */ if (error < 0 && link) { flags &= ~GIT_CPDIR_LINK_FILES; error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb), flags, GIT_OBJECT_DIR_MODE); } if (error < 0) goto cleanup; git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); if ((error = git_remote_fetch(remote, NULL, fetch_opts, git_buf_cstr(&reflog_message))) != 0) goto cleanup; error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message)); cleanup: git_buf_dispose(&reflog_message); git_buf_dispose(&src_path); git_buf_dispose(&src_odb); git_buf_dispose(&dst_odb); git_repository_free(src); return error; } git2r/src/libgit2/src/repository.c0000644000175000017500000023431714125111754016743 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "repository.h" #include #include "git2/object.h" #include "git2/sys/repository.h" #include "common.h" #include "commit.h" #include "tag.h" #include "blob.h" #include "futils.h" #include "sysdir.h" #include "filebuf.h" #include "index.h" #include "config.h" #include "refs.h" #include "filter.h" #include "odb.h" #include "refdb.h" #include "remote.h" #include "merge.h" #include "diff_driver.h" #include "annotated_commit.h" #include "submodule.h" #include "worktree.h" #include "strmap.h" #ifdef GIT_WIN32 # include "win32/w32_util.h" #endif bool git_repository__fsync_gitdir = false; static const struct { git_repository_item_t parent; git_repository_item_t fallback; const char *name; bool directory; } items[] = { { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, NULL, true }, { GIT_REPOSITORY_ITEM_WORKDIR, GIT_REPOSITORY_ITEM__LAST, NULL, true }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM__LAST, NULL, true }, { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, "index", false }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "objects", true }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "refs", true }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "packed-refs", false }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "remotes", true }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "config", false }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "info", true }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "hooks", true }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "logs", true }, { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, "modules", true }, { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "worktrees", true } }; static int check_repositoryformatversion(int *version, git_config *config); static int check_extensions(git_config *config, int version); #define GIT_COMMONDIR_FILE "commondir" #define GIT_GITDIR_FILE "gitdir" #define GIT_FILE_CONTENT_PREFIX "gitdir:" #define GIT_BRANCH_DEFAULT "master" #define GIT_REPO_VERSION 0 #define GIT_REPO_MAX_VERSION 1 git_buf git_repository__reserved_names_win32[] = { { DOT_GIT, 0, CONST_STRLEN(DOT_GIT) }, { GIT_DIR_SHORTNAME, 0, CONST_STRLEN(GIT_DIR_SHORTNAME) } }; size_t git_repository__reserved_names_win32_len = 2; git_buf git_repository__reserved_names_posix[] = { { DOT_GIT, 0, CONST_STRLEN(DOT_GIT) }, }; size_t git_repository__reserved_names_posix_len = 1; static void set_odb(git_repository *repo, git_odb *odb) { if (odb) { GIT_REFCOUNT_OWN(odb, repo); GIT_REFCOUNT_INC(odb); } if ((odb = git_atomic_swap(repo->_odb, odb)) != NULL) { GIT_REFCOUNT_OWN(odb, NULL); git_odb_free(odb); } } static void set_refdb(git_repository *repo, git_refdb *refdb) { if (refdb) { GIT_REFCOUNT_OWN(refdb, repo); GIT_REFCOUNT_INC(refdb); } if ((refdb = git_atomic_swap(repo->_refdb, refdb)) != NULL) { GIT_REFCOUNT_OWN(refdb, NULL); git_refdb_free(refdb); } } static void set_config(git_repository *repo, git_config *config) { if (config) { GIT_REFCOUNT_OWN(config, repo); GIT_REFCOUNT_INC(config); } if ((config = git_atomic_swap(repo->_config, config)) != NULL) { GIT_REFCOUNT_OWN(config, NULL); git_config_free(config); } git_repository__configmap_lookup_cache_clear(repo); } static void set_index(git_repository *repo, git_index *index) { if (index) { GIT_REFCOUNT_OWN(index, repo); GIT_REFCOUNT_INC(index); } if ((index = git_atomic_swap(repo->_index, index)) != NULL) { GIT_REFCOUNT_OWN(index, NULL); git_index_free(index); } } int git_repository__cleanup(git_repository *repo) { GIT_ASSERT_ARG(repo); git_repository_submodule_cache_clear(repo); git_cache_clear(&repo->objects); git_attr_cache_flush(repo); set_config(repo, NULL); set_index(repo, NULL); set_odb(repo, NULL); set_refdb(repo, NULL); return 0; } void git_repository_free(git_repository *repo) { size_t i; if (repo == NULL) return; git_repository__cleanup(repo); git_cache_dispose(&repo->objects); git_diff_driver_registry_free(repo->diff_drivers); repo->diff_drivers = NULL; for (i = 0; i < repo->reserved_names.size; i++) git_buf_dispose(git_array_get(repo->reserved_names, i)); git_array_clear(repo->reserved_names); git__free(repo->gitlink); git__free(repo->gitdir); git__free(repo->commondir); git__free(repo->workdir); git__free(repo->namespace); git__free(repo->ident_name); git__free(repo->ident_email); git__memzero(repo, sizeof(*repo)); git__free(repo); } /* Check if we have a separate commondir (e.g. we have a worktree) */ static int lookup_commondir(bool *separate, git_buf *commondir, git_buf *repository_path) { git_buf common_link = GIT_BUF_INIT; int error; /* * If there's no commondir file, the repository path is the * common path, but it needs a trailing slash. */ if (!git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) { if ((error = git_buf_set(commondir, repository_path->ptr, repository_path->size)) == 0) error = git_path_to_dir(commondir); *separate = false; goto done; } *separate = true; if ((error = git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE)) < 0 || (error = git_futils_readbuffer(&common_link, common_link.ptr)) < 0) goto done; git_buf_rtrim(&common_link); if (git_path_is_relative(common_link.ptr)) { if ((error = git_buf_joinpath(commondir, repository_path->ptr, common_link.ptr)) < 0) goto done; } else { git_buf_swap(commondir, &common_link); } git_buf_dispose(&common_link); /* Make sure the commondir path always has a trailing slash */ error = git_path_prettify_dir(commondir, commondir->ptr, NULL); done: return error; } GIT_INLINE(int) validate_repo_path(git_buf *path) { /* * The longest static path in a repository (or commondir) is the * packed refs file. (Loose refs may be longer since they * include the reference name, but will be validated when the * path is constructed.) */ static size_t suffix_len = CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_HEXSZ; return git_path_validate_filesystem_with_suffix( path->ptr, path->size, suffix_len); } /* * Git repository open methods * * Open a repository object from its path */ static int is_valid_repository_path(bool *out, git_buf *repository_path, git_buf *common_path) { bool separate_commondir = false; int error; *out = false; if ((error = lookup_commondir(&separate_commondir, common_path, repository_path)) < 0) return error; /* Ensure HEAD file exists */ if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false) return 0; /* Check files in common dir */ if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false) return 0; if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false) return 0; /* Ensure the repo (and commondir) are valid paths */ if ((error = validate_repo_path(common_path)) < 0 || (separate_commondir && (error = validate_repo_path(repository_path)) < 0)) return error; *out = true; return 0; } static git_repository *repository_alloc(void) { git_repository *repo = git__calloc(1, sizeof(git_repository)); if (repo == NULL || git_cache_init(&repo->objects) < 0) goto on_error; git_array_init_to_size(repo->reserved_names, 4); if (!repo->reserved_names.ptr) goto on_error; /* set all the entries in the configmap cache to `unset` */ git_repository__configmap_lookup_cache_clear(repo); return repo; on_error: if (repo) git_cache_dispose(&repo->objects); git__free(repo); return NULL; } int git_repository_new(git_repository **out) { git_repository *repo; *out = repo = repository_alloc(); GIT_ERROR_CHECK_ALLOC(repo); repo->is_bare = 1; repo->is_worktree = 0; return 0; } static int load_config_data(git_repository *repo, const git_config *config) { int is_bare; int err = git_config_get_bool(&is_bare, config, "core.bare"); if (err < 0 && err != GIT_ENOTFOUND) return err; /* Try to figure out if it's bare, default to non-bare if it's not set */ if (err != GIT_ENOTFOUND) repo->is_bare = is_bare && !repo->is_worktree; else repo->is_bare = 0; return 0; } static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path) { int error; git_config_entry *ce; git_buf worktree = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT; if (repo->is_bare) return 0; if ((error = git_config__lookup_entry( &ce, config, "core.worktree", false)) < 0) return error; if (repo->is_worktree) { char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE); if (!gitlink) { error = -1; goto cleanup; } git_buf_attach(&worktree, gitlink, 0); if ((git_path_dirname_r(&worktree, worktree.ptr)) < 0 || git_path_to_dir(&worktree) < 0) { error = -1; goto cleanup; } repo->workdir = git_buf_detach(&worktree); } else if (ce && ce->value) { if ((error = git_path_prettify_dir( &worktree, ce->value, repo->gitdir)) < 0) goto cleanup; repo->workdir = git_buf_detach(&worktree); } else if (parent_path && git_path_isdir(parent_path->ptr)) repo->workdir = git_buf_detach(parent_path); else { if (git_path_dirname_r(&worktree, repo->gitdir) < 0 || git_path_to_dir(&worktree) < 0) { error = -1; goto cleanup; } repo->workdir = git_buf_detach(&worktree); } GIT_ERROR_CHECK_ALLOC(repo->workdir); cleanup: git_buf_dispose(&path); git_config_entry_free(ce); return error; } /* * This function returns furthest offset into path where a ceiling dir * is found, so we can stop processing the path at that point. * * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on * the stack could remove directories name limits, but at the cost of doing * repeated malloc/frees inside the loop below, so let's not do it now. */ static size_t find_ceiling_dir_offset( const char *path, const char *ceiling_directories) { char buf[GIT_PATH_MAX + 1]; char buf2[GIT_PATH_MAX + 1]; const char *ceil, *sep; size_t len, max_len = 0, min_len; GIT_ASSERT_ARG(path); min_len = (size_t)(git_path_root(path) + 1); if (ceiling_directories == NULL || min_len == 0) return min_len; for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) { for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++); len = sep - ceil; if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1) continue; strncpy(buf, ceil, len); buf[len] = '\0'; if (p_realpath(buf, buf2) == NULL) continue; len = strlen(buf2); if (len > 0 && buf2[len-1] == '/') buf[--len] = '\0'; if (!strncmp(path, buf2, len) && (path[len] == '/' || !path[len]) && len > max_len) { max_len = len; } } return (max_len <= min_len ? min_len : max_len); } /* * Read the contents of `file_path` and set `path_out` to the repo dir that * it points to. Before calling, set `path_out` to the base directory that * should be used if the contents of `file_path` are a relative path. */ static int read_gitfile(git_buf *path_out, const char *file_path) { int error = 0; git_buf file = GIT_BUF_INIT; size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX); GIT_ASSERT_ARG(path_out); GIT_ASSERT_ARG(file_path); if (git_futils_readbuffer(&file, file_path) < 0) return -1; git_buf_rtrim(&file); /* apparently on Windows, some people use backslashes in paths */ git_path_mkposix(file.ptr); if (git_buf_len(&file) <= prefix_len || memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { git_error_set(GIT_ERROR_REPOSITORY, "the `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = git_buf_cstr(&file) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; error = git_path_prettify_dir( path_out, gitlink, git_buf_cstr(path_out)); } git_buf_dispose(&file); return error; } static int find_repo( git_buf *gitdir_path, git_buf *workdir_path, git_buf *gitlink_path, git_buf *commondir_path, const char *start_path, uint32_t flags, const char *ceiling_dirs) { git_buf path = GIT_BUF_INIT; git_buf repo_link = GIT_BUF_INIT; git_buf common_link = GIT_BUF_INIT; struct stat st; dev_t initial_device = 0; int min_iterations; bool in_dot_git, is_valid; size_t ceiling_offset = 0; int error; git_buf_clear(gitdir_path); error = git_path_prettify(&path, start_path, NULL); if (error < 0) return error; /* in_dot_git toggles each loop: * /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a * With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT, we * assume we started with /a/b/c.git and don't append .git the first * time through. * min_iterations indicates the number of iterations left before going * further counts as a search. */ if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) { in_dot_git = true; min_iterations = 1; } else { in_dot_git = false; min_iterations = 2; } for (;;) { if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) { if (!in_dot_git) { if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) goto out; } in_dot_git = !in_dot_git; } if (p_stat(path.ptr, &st) == 0) { /* check that we have not crossed device boundaries */ if (initial_device == 0) initial_device = st.st_dev; else if (st.st_dev != initial_device && !(flags & GIT_REPOSITORY_OPEN_CROSS_FS)) break; if (S_ISDIR(st.st_mode)) { if ((error = is_valid_repository_path(&is_valid, &path, &common_link)) < 0) goto out; if (is_valid) { if ((error = git_path_to_dir(&path)) < 0 || (error = git_buf_set(gitdir_path, path.ptr, path.size)) < 0) goto out; if (gitlink_path) if ((error = git_buf_attach(gitlink_path, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0) goto out; if (commondir_path) git_buf_swap(&common_link, commondir_path); break; } } else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) { if ((error = read_gitfile(&repo_link, path.ptr)) < 0 || (error = is_valid_repository_path(&is_valid, &repo_link, &common_link)) < 0) goto out; if (is_valid) { git_buf_swap(gitdir_path, &repo_link); if (gitlink_path) if ((error = git_buf_put(gitlink_path, path.ptr, path.size)) < 0) goto out; if (commondir_path) git_buf_swap(&common_link, commondir_path); } break; } } /* Move up one directory. If we're in_dot_git, we'll search the * parent itself next. If we're !in_dot_git, we'll search .git * in the parent directory next (added at the top of the loop). */ if ((error = git_path_dirname_r(&path, path.ptr)) < 0) goto out; /* Once we've checked the directory (and .git if applicable), * find the ceiling for a search. */ if (min_iterations && (--min_iterations == 0)) ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); /* Check if we should stop searching here. */ if (min_iterations == 0 && (path.ptr[ceiling_offset] == 0 || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH))) break; } if (workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { if (!git_buf_len(gitdir_path)) git_buf_clear(workdir_path); else if ((error = git_path_dirname_r(workdir_path, path.ptr)) < 0 || (error = git_path_to_dir(workdir_path)) < 0) goto out; } /* If we didn't find the repository, and we don't have any other error * to report, report that. */ if (!git_buf_len(gitdir_path)) { git_error_set(GIT_ERROR_REPOSITORY, "could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; goto out; } out: git_buf_dispose(&path); git_buf_dispose(&repo_link); git_buf_dispose(&common_link); return error; } int git_repository_open_bare( git_repository **repo_ptr, const char *bare_path) { git_buf path = GIT_BUF_INIT, common_path = GIT_BUF_INIT; git_repository *repo = NULL; bool is_valid; int error; if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0 || (error = is_valid_repository_path(&is_valid, &path, &common_path)) < 0) return error; if (!is_valid) { git_buf_dispose(&path); git_buf_dispose(&common_path); git_error_set(GIT_ERROR_REPOSITORY, "path is not a repository: %s", bare_path); return GIT_ENOTFOUND; } repo = repository_alloc(); GIT_ERROR_CHECK_ALLOC(repo); repo->gitdir = git_buf_detach(&path); GIT_ERROR_CHECK_ALLOC(repo->gitdir); repo->commondir = git_buf_detach(&common_path); GIT_ERROR_CHECK_ALLOC(repo->commondir); /* of course we're bare! */ repo->is_bare = 1; repo->is_worktree = 0; repo->workdir = NULL; *repo_ptr = repo; return 0; } static int _git_repository_open_ext_from_env( git_repository **out, const char *start_path) { git_repository *repo = NULL; git_index *index = NULL; git_odb *odb = NULL; git_buf dir_buf = GIT_BUF_INIT; git_buf ceiling_dirs_buf = GIT_BUF_INIT; git_buf across_fs_buf = GIT_BUF_INIT; git_buf index_file_buf = GIT_BUF_INIT; git_buf namespace_buf = GIT_BUF_INIT; git_buf object_dir_buf = GIT_BUF_INIT; git_buf alts_buf = GIT_BUF_INIT; git_buf work_tree_buf = GIT_BUF_INIT; git_buf common_dir_buf = GIT_BUF_INIT; const char *ceiling_dirs = NULL; unsigned flags = 0; int error; if (!start_path) { error = git__getenv(&dir_buf, "GIT_DIR"); if (error == GIT_ENOTFOUND) { git_error_clear(); start_path = "."; } else if (error < 0) goto error; else { start_path = git_buf_cstr(&dir_buf); flags |= GIT_REPOSITORY_OPEN_NO_SEARCH; flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT; } } error = git__getenv(&ceiling_dirs_buf, "GIT_CEILING_DIRECTORIES"); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) goto error; else ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); error = git__getenv(&across_fs_buf, "GIT_DISCOVERY_ACROSS_FILESYSTEM"); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) goto error; else { int across_fs = 0; error = git_config_parse_bool(&across_fs, git_buf_cstr(&across_fs_buf)); if (error < 0) goto error; if (across_fs) flags |= GIT_REPOSITORY_OPEN_CROSS_FS; } error = git__getenv(&index_file_buf, "GIT_INDEX_FILE"); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) goto error; else { error = git_index_open(&index, git_buf_cstr(&index_file_buf)); if (error < 0) goto error; } error = git__getenv(&namespace_buf, "GIT_NAMESPACE"); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) goto error; error = git__getenv(&object_dir_buf, "GIT_OBJECT_DIRECTORY"); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) goto error; else { error = git_odb_open(&odb, git_buf_cstr(&object_dir_buf)); if (error < 0) goto error; } error = git__getenv(&work_tree_buf, "GIT_WORK_TREE"); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) goto error; else { git_error_set(GIT_ERROR_INVALID, "GIT_WORK_TREE unimplemented"); error = GIT_ERROR; goto error; } error = git__getenv(&work_tree_buf, "GIT_COMMON_DIR"); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error < 0) goto error; else { git_error_set(GIT_ERROR_INVALID, "GIT_COMMON_DIR unimplemented"); error = GIT_ERROR; goto error; } error = git_repository_open_ext(&repo, start_path, flags, ceiling_dirs); if (error < 0) goto error; if (odb) git_repository_set_odb(repo, odb); error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES"); if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } else if (error < 0) goto error; else { const char *end; char *alt, *sep; if (!odb) { error = git_repository_odb(&odb, repo); if (error < 0) goto error; } end = git_buf_cstr(&alts_buf) + git_buf_len(&alts_buf); for (sep = alt = alts_buf.ptr; sep != end; alt = sep+1) { for (sep = alt; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++) ; if (*sep) *sep = '\0'; error = git_odb_add_disk_alternate(odb, alt); if (error < 0) goto error; } } if (git_buf_len(&namespace_buf)) { error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf)); if (error < 0) goto error; } git_repository_set_index(repo, index); if (out) { *out = repo; goto success; } error: git_repository_free(repo); success: git_odb_free(odb); git_index_free(index); git_buf_dispose(&common_dir_buf); git_buf_dispose(&work_tree_buf); git_buf_dispose(&alts_buf); git_buf_dispose(&object_dir_buf); git_buf_dispose(&namespace_buf); git_buf_dispose(&index_file_buf); git_buf_dispose(&across_fs_buf); git_buf_dispose(&ceiling_dirs_buf); git_buf_dispose(&dir_buf); return error; } static int repo_is_worktree(unsigned *out, const git_repository *repo) { git_buf gitdir_link = GIT_BUF_INIT; int error; /* Worktrees cannot have the same commondir and gitdir */ if (repo->commondir && repo->gitdir && !strcmp(repo->commondir, repo->gitdir)) { *out = 0; return 0; } if ((error = git_buf_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0) return -1; /* A 'gitdir' file inside a git directory is currently * only used when the repository is a working tree. */ *out = !!git_path_exists(gitdir_link.ptr); git_buf_dispose(&gitdir_link); return error; } int git_repository_open_ext( git_repository **repo_ptr, const char *start_path, unsigned int flags, const char *ceiling_dirs) { int error; unsigned is_worktree; git_buf gitdir = GIT_BUF_INIT, workdir = GIT_BUF_INIT, gitlink = GIT_BUF_INIT, commondir = GIT_BUF_INIT; git_repository *repo = NULL; git_config *config = NULL; int version = 0; if (flags & GIT_REPOSITORY_OPEN_FROM_ENV) return _git_repository_open_ext_from_env(repo_ptr, start_path); if (repo_ptr) *repo_ptr = NULL; error = find_repo( &gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs); if (error < 0 || !repo_ptr) goto cleanup; repo = repository_alloc(); GIT_ERROR_CHECK_ALLOC(repo); repo->gitdir = git_buf_detach(&gitdir); GIT_ERROR_CHECK_ALLOC(repo->gitdir); if (gitlink.size) { repo->gitlink = git_buf_detach(&gitlink); GIT_ERROR_CHECK_ALLOC(repo->gitlink); } if (commondir.size) { repo->commondir = git_buf_detach(&commondir); GIT_ERROR_CHECK_ALLOC(repo->commondir); } if ((error = repo_is_worktree(&is_worktree, repo)) < 0) goto cleanup; repo->is_worktree = is_worktree; /* * We'd like to have the config, but git doesn't particularly * care if it's not there, so we need to deal with that. */ error = git_repository_config_snapshot(&config, repo); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; if (config && (error = check_repositoryformatversion(&version, config)) < 0) goto cleanup; if ((error = check_extensions(config, version)) < 0) goto cleanup; if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) repo->is_bare = 1; else { if (config && ((error = load_config_data(repo, config)) < 0 || (error = load_workdir(repo, config, &workdir)) < 0)) goto cleanup; } cleanup: git_buf_dispose(&gitdir); git_buf_dispose(&workdir); git_buf_dispose(&gitlink); git_buf_dispose(&commondir); git_config_free(config); if (error < 0) git_repository_free(repo); else if (repo_ptr) *repo_ptr = repo; return error; } int git_repository_open(git_repository **repo_out, const char *path) { return git_repository_open_ext( repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL); } int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *wt) { git_buf path = GIT_BUF_INIT; git_repository *repo = NULL; size_t len; int err; GIT_ASSERT_ARG(repo_out); GIT_ASSERT_ARG(wt); *repo_out = NULL; len = strlen(wt->gitlink_path); if (len <= 4 || strcasecmp(wt->gitlink_path + len - 4, ".git")) { err = -1; goto out; } if ((err = git_buf_set(&path, wt->gitlink_path, len - 4)) < 0) goto out; if ((err = git_repository_open(&repo, path.ptr)) < 0) goto out; *repo_out = repo; out: git_buf_dispose(&path); return err; } int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb) { git_repository *repo; repo = repository_alloc(); GIT_ERROR_CHECK_ALLOC(repo); git_repository_set_odb(repo, odb); *repo_out = repo; return 0; } int git_repository_discover( git_buf *out, const char *start_path, int across_fs, const char *ceiling_dirs) { uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0; int error; GIT_ASSERT_ARG(start_path); if ((error = git_buf_sanitize(out)) < 0) return error; return find_repo(out, NULL, NULL, NULL, start_path, flags, ceiling_dirs); } static int load_config( git_config **out, git_repository *repo, const char *global_config_path, const char *xdg_config_path, const char *system_config_path, const char *programdata_path) { int error; git_buf config_path = GIT_BUF_INIT; git_config *cfg = NULL; GIT_ASSERT_ARG(out); if ((error = git_config_new(&cfg)) < 0) return error; if (repo) { if ((error = git_repository_item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG)) == 0) error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, repo, 0); if (error && error != GIT_ENOTFOUND) goto on_error; git_buf_dispose(&config_path); } if (global_config_path != NULL && (error = git_config_add_file_ondisk( cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, repo, 0)) < 0 && error != GIT_ENOTFOUND) goto on_error; if (xdg_config_path != NULL && (error = git_config_add_file_ondisk( cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, repo, 0)) < 0 && error != GIT_ENOTFOUND) goto on_error; if (system_config_path != NULL && (error = git_config_add_file_ondisk( cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, repo, 0)) < 0 && error != GIT_ENOTFOUND) goto on_error; if (programdata_path != NULL && (error = git_config_add_file_ondisk( cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, repo, 0)) < 0 && error != GIT_ENOTFOUND) goto on_error; git_error_clear(); /* clear any lingering ENOTFOUND errors */ *out = cfg; return 0; on_error: git_buf_dispose(&config_path); git_config_free(cfg); *out = NULL; return error; } static const char *path_unless_empty(git_buf *buf) { return git_buf_len(buf) > 0 ? git_buf_cstr(buf) : NULL; } int git_repository_config__weakptr(git_config **out, git_repository *repo) { int error = 0; if (repo->_config == NULL) { git_buf global_buf = GIT_BUF_INIT; git_buf xdg_buf = GIT_BUF_INIT; git_buf system_buf = GIT_BUF_INIT; git_buf programdata_buf = GIT_BUF_INIT; git_config *config; git_config_find_global(&global_buf); git_config_find_xdg(&xdg_buf); git_config_find_system(&system_buf); git_config_find_programdata(&programdata_buf); /* If there is no global file, open a backend for it anyway */ if (git_buf_len(&global_buf) == 0) git_config__global_location(&global_buf); error = load_config( &config, repo, path_unless_empty(&global_buf), path_unless_empty(&xdg_buf), path_unless_empty(&system_buf), path_unless_empty(&programdata_buf)); if (!error) { GIT_REFCOUNT_OWN(config, repo); if (git_atomic_compare_and_swap(&repo->_config, NULL, config) != NULL) { GIT_REFCOUNT_OWN(config, NULL); git_config_free(config); } } git_buf_dispose(&global_buf); git_buf_dispose(&xdg_buf); git_buf_dispose(&system_buf); git_buf_dispose(&programdata_buf); } *out = repo->_config; return error; } int git_repository_config(git_config **out, git_repository *repo) { if (git_repository_config__weakptr(out, repo) < 0) return -1; GIT_REFCOUNT_INC(*out); return 0; } int git_repository_config_snapshot(git_config **out, git_repository *repo) { int error; git_config *weak; if ((error = git_repository_config__weakptr(&weak, repo)) < 0) return error; return git_config_snapshot(out, weak); } int git_repository_set_config(git_repository *repo, git_config *config) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(config); set_config(repo, config); return 0; } int git_repository_odb__weakptr(git_odb **out, git_repository *repo) { int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(out); *out = git_atomic_load(repo->_odb); if (*out == NULL) { git_buf odb_path = GIT_BUF_INIT; git_odb *odb; if ((error = git_repository_item_path(&odb_path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0 || (error = git_odb_new(&odb)) < 0) return error; GIT_REFCOUNT_OWN(odb, repo); if ((error = git_odb__set_caps(odb, GIT_ODB_CAP_FROM_OWNER)) < 0 || (error = git_odb__add_default_backends(odb, odb_path.ptr, 0, 0)) < 0) { git_odb_free(odb); return error; } if (git_atomic_compare_and_swap(&repo->_odb, NULL, odb) != NULL) { GIT_REFCOUNT_OWN(odb, NULL); git_odb_free(odb); } git_buf_dispose(&odb_path); *out = git_atomic_load(repo->_odb); } return error; } int git_repository_odb(git_odb **out, git_repository *repo) { if (git_repository_odb__weakptr(out, repo) < 0) return -1; GIT_REFCOUNT_INC(*out); return 0; } int git_repository_set_odb(git_repository *repo, git_odb *odb) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(odb); set_odb(repo, odb); return 0; } int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) { int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); if (repo->_refdb == NULL) { git_refdb *refdb; error = git_refdb_open(&refdb, repo); if (!error) { GIT_REFCOUNT_OWN(refdb, repo); if (git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb) != NULL) { GIT_REFCOUNT_OWN(refdb, NULL); git_refdb_free(refdb); } } } *out = repo->_refdb; return error; } int git_repository_refdb(git_refdb **out, git_repository *repo) { if (git_repository_refdb__weakptr(out, repo) < 0) return -1; GIT_REFCOUNT_INC(*out); return 0; } int git_repository_set_refdb(git_repository *repo, git_refdb *refdb) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(refdb); set_refdb(repo, refdb); return 0; } int git_repository_index__weakptr(git_index **out, git_repository *repo) { int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); if (repo->_index == NULL) { git_buf index_path = GIT_BUF_INIT; git_index *index; if ((error = git_buf_joinpath(&index_path, repo->gitdir, GIT_INDEX_FILE)) < 0) return error; error = git_index_open(&index, index_path.ptr); if (!error) { GIT_REFCOUNT_OWN(index, repo); if (git_atomic_compare_and_swap(&repo->_index, NULL, index) != NULL) { GIT_REFCOUNT_OWN(index, NULL); git_index_free(index); } error = git_index_set_caps(repo->_index, GIT_INDEX_CAPABILITY_FROM_OWNER); } git_buf_dispose(&index_path); } *out = repo->_index; return error; } int git_repository_index(git_index **out, git_repository *repo) { if (git_repository_index__weakptr(out, repo) < 0) return -1; GIT_REFCOUNT_INC(*out); return 0; } int git_repository_set_index(git_repository *repo, git_index *index) { GIT_ASSERT_ARG(repo); set_index(repo, index); return 0; } int git_repository_set_namespace(git_repository *repo, const char *namespace) { git__free(repo->namespace); if (namespace == NULL) { repo->namespace = NULL; return 0; } return (repo->namespace = git__strdup(namespace)) ? 0 : -1; } const char *git_repository_get_namespace(git_repository *repo) { return repo->namespace; } #ifdef GIT_WIN32 static int reserved_names_add8dot3(git_repository *repo, const char *path) { char *name = git_win32_path_8dot3_name(path); const char *def = GIT_DIR_SHORTNAME; const char *def_dot_git = DOT_GIT; size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME); size_t def_dot_git_len = CONST_STRLEN(DOT_GIT); git_buf *buf; if (!name) return 0; name_len = strlen(name); if ((name_len == def_len && memcmp(name, def, def_len) == 0) || (name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) { git__free(name); return 0; } if ((buf = git_array_alloc(repo->reserved_names)) == NULL) return -1; git_buf_attach(buf, name, name_len); return true; } bool git_repository__reserved_names( git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs) { GIT_UNUSED(include_ntfs); if (repo->reserved_names.size == 0) { git_buf *buf; size_t i; /* Add the static defaults */ for (i = 0; i < git_repository__reserved_names_win32_len; i++) { if ((buf = git_array_alloc(repo->reserved_names)) == NULL) goto on_error; buf->ptr = git_repository__reserved_names_win32[i].ptr; buf->size = git_repository__reserved_names_win32[i].size; } /* Try to add any repo-specific reserved names - the gitlink file * within a submodule or the repository (if the repository directory * is beneath the workdir). These are typically `.git`, but should * be protected in case they are not. Note, repo and workdir paths * are always prettified to end in `/`, so a prefixcmp is safe. */ if (!repo->is_bare) { int (*prefixcmp)(const char *, const char *); int error, ignorecase; error = git_repository__configmap_lookup( &ignorecase, repo, GIT_CONFIGMAP_IGNORECASE); prefixcmp = (error || ignorecase) ? git__prefixcmp_icase : git__prefixcmp; if (repo->gitlink && reserved_names_add8dot3(repo, repo->gitlink) < 0) goto on_error; if (repo->gitdir && prefixcmp(repo->gitdir, repo->workdir) == 0 && reserved_names_add8dot3(repo, repo->gitdir) < 0) goto on_error; } } *out = repo->reserved_names.ptr; *outlen = repo->reserved_names.size; return true; /* Always give good defaults, even on OOM */ on_error: *out = git_repository__reserved_names_win32; *outlen = git_repository__reserved_names_win32_len; return false; } #else bool git_repository__reserved_names( git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs) { GIT_UNUSED(repo); if (include_ntfs) { *out = git_repository__reserved_names_win32; *outlen = git_repository__reserved_names_win32_len; } else { *out = git_repository__reserved_names_posix; *outlen = git_repository__reserved_names_posix_len; } return true; } #endif static int check_repositoryformatversion(int *version, git_config *config) { int error; error = git_config_get_int32(version, config, "core.repositoryformatversion"); /* git ignores this if the config variable isn't there */ if (error == GIT_ENOTFOUND) return 0; if (error < 0) return -1; if (GIT_REPO_MAX_VERSION < *version) { git_error_set(GIT_ERROR_REPOSITORY, "unsupported repository version %d; only versions up to %d are supported", *version, GIT_REPO_MAX_VERSION); return -1; } return 0; } static const char *builtin_extensions[] = { "noop" }; static git_vector user_extensions = GIT_VECTOR_INIT; static int check_valid_extension(const git_config_entry *entry, void *payload) { git_buf cfg = GIT_BUF_INIT; bool reject; const char *extension; size_t i; int error = 0; GIT_UNUSED(payload); git_vector_foreach (&user_extensions, i, extension) { git_buf_clear(&cfg); /* * Users can specify that they don't want to support an * extension with a '!' prefix. */ if ((reject = (extension[0] == '!')) == true) extension = &extension[1]; if ((error = git_buf_printf(&cfg, "extensions.%s", extension)) < 0) goto done; if (strcmp(entry->name, cfg.ptr) == 0) { if (reject) goto fail; goto done; } } for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) { extension = builtin_extensions[i]; if ((error = git_buf_printf(&cfg, "extensions.%s", extension)) < 0) goto done; if (strcmp(entry->name, cfg.ptr) == 0) goto done; } fail: git_error_set(GIT_ERROR_REPOSITORY, "unsupported extension name %s", entry->name); error = -1; done: git_buf_dispose(&cfg); return error; } static int check_extensions(git_config *config, int version) { if (version < 1) return 0; return git_config_foreach_match(config, "^extensions\\.", check_valid_extension, NULL); } int git_repository__extensions(char ***out, size_t *out_len) { git_vector extensions; const char *builtin, *user; char *extension; size_t i, j; if (git_vector_init(&extensions, 8, NULL) < 0) return -1; for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) { bool match = false; builtin = builtin_extensions[i]; git_vector_foreach (&user_extensions, j, user) { if (user[0] == '!' && strcmp(builtin, &user[1]) == 0) { match = true; break; } } if (match) continue; if ((extension = git__strdup(builtin)) == NULL || git_vector_insert(&extensions, extension) < 0) return -1; } git_vector_foreach (&user_extensions, i, user) { if (user[0] == '!') continue; if ((extension = git__strdup(user)) == NULL || git_vector_insert(&extensions, extension) < 0) return -1; } *out = (char **)git_vector_detach(out_len, NULL, &extensions); return 0; } int git_repository__set_extensions(const char **extensions, size_t len) { char *extension; size_t i; git_repository__free_extensions(); for (i = 0; i < len; i++) { if ((extension = git__strdup(extensions[i])) == NULL || git_vector_insert(&user_extensions, extension) < 0) return -1; } return 0; } void git_repository__free_extensions(void) { git_vector_free_deep(&user_extensions); } int git_repository_create_head(const char *git_dir, const char *ref_name) { git_buf ref_path = GIT_BUF_INIT; git_filebuf ref = GIT_FILEBUF_INIT; const char *fmt; int error; if ((error = git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE)) < 0 || (error = git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE)) < 0) goto out; if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0) fmt = "ref: %s\n"; else fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n"; if ((error = git_filebuf_printf(&ref, fmt, ref_name)) < 0 || (error = git_filebuf_commit(&ref)) < 0) goto out; out: git_buf_dispose(&ref_path); git_filebuf_cleanup(&ref); return error; } static bool is_chmod_supported(const char *file_path) { struct stat st1, st2; if (p_stat(file_path, &st1) < 0) return false; if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0) return false; if (p_stat(file_path, &st2) < 0) return false; return (st1.st_mode != st2.st_mode); } static bool is_filesystem_case_insensitive(const char *gitdir_path) { git_buf path = GIT_BUF_INIT; int is_insensitive = -1; if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg")) is_insensitive = git_path_exists(git_buf_cstr(&path)); git_buf_dispose(&path); return is_insensitive; } static bool are_symlinks_supported(const char *wd_path) { git_config *config = NULL; git_buf global_buf = GIT_BUF_INIT; git_buf xdg_buf = GIT_BUF_INIT; git_buf system_buf = GIT_BUF_INIT; git_buf programdata_buf = GIT_BUF_INIT; int symlinks = 0; /* * To emulate Git for Windows, symlinks on Windows must be explicitly * opted-in. We examine the system configuration for a core.symlinks * set to true. If found, we then examine the filesystem to see if * symlinks are _actually_ supported by the current user. If that is * _not_ set, then we do not test or enable symlink support. */ #ifdef GIT_WIN32 git_config_find_global(&global_buf); git_config_find_xdg(&xdg_buf); git_config_find_system(&system_buf); git_config_find_programdata(&programdata_buf); if (load_config(&config, NULL, path_unless_empty(&global_buf), path_unless_empty(&xdg_buf), path_unless_empty(&system_buf), path_unless_empty(&programdata_buf)) < 0) goto done; if (git_config_get_bool(&symlinks, config, "core.symlinks") < 0 || !symlinks) goto done; #endif if (!(symlinks = git_path_supports_symlinks(wd_path))) goto done; done: git_buf_dispose(&global_buf); git_buf_dispose(&xdg_buf); git_buf_dispose(&system_buf); git_buf_dispose(&programdata_buf); git_config_free(config); return symlinks != 0; } static int create_empty_file(const char *path, mode_t mode) { int fd; if ((fd = p_creat(path, mode)) < 0) { git_error_set(GIT_ERROR_OS, "error while creating '%s'", path); return -1; } if (p_close(fd) < 0) { git_error_set(GIT_ERROR_OS, "error while closing '%s'", path); return -1; } return 0; } static int repo_local_config( git_config **out, git_buf *config_dir, git_repository *repo, const char *repo_dir) { int error = 0; git_config *parent; const char *cfg_path; if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0) return -1; cfg_path = git_buf_cstr(config_dir); /* make LOCAL config if missing */ if (!git_path_isfile(cfg_path) && (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0) return error; /* if no repo, just open that file directly */ if (!repo) return git_config_open_ondisk(out, cfg_path); /* otherwise, open parent config and get that level */ if ((error = git_repository_config__weakptr(&parent, repo)) < 0) return error; if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) { git_error_clear(); if (!(error = git_config_add_file_ondisk( parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, repo, false))) error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL); } git_config_free(parent); return error; } static int repo_init_fs_configs( git_config *cfg, const char *cfg_path, const char *repo_dir, const char *work_dir, bool update_ignorecase) { int error = 0; if (!work_dir) work_dir = repo_dir; if ((error = git_config_set_bool( cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0) return error; if (!are_symlinks_supported(work_dir)) { if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0) return error; } else if (git_config_delete_entry(cfg, "core.symlinks") < 0) git_error_clear(); if (update_ignorecase) { if (is_filesystem_case_insensitive(repo_dir)) { if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0) return error; } else if (git_config_delete_entry(cfg, "core.ignorecase") < 0) git_error_clear(); } #ifdef GIT_USE_ICONV if ((error = git_config_set_bool( cfg, "core.precomposeunicode", git_path_does_fs_decompose_unicode(work_dir))) < 0) return error; /* on non-iconv platforms, don't even set core.precomposeunicode */ #endif return 0; } static int repo_init_config( const char *repo_dir, const char *work_dir, uint32_t flags, uint32_t mode) { int error = 0; git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT; git_config *config = NULL; bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0); bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0); int version = 0; if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0) goto cleanup; if (is_reinit && (error = check_repositoryformatversion(&version, config)) < 0) goto cleanup; if ((error = check_extensions(config, version)) < 0) goto cleanup; #define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \ goto cleanup; } while (0) SET_REPO_CONFIG(bool, "core.bare", is_bare); SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION); if ((error = repo_init_fs_configs( config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0) goto cleanup; if (!is_bare) { SET_REPO_CONFIG(bool, "core.logallrefupdates", true); if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) { if ((error = git_buf_sets(&worktree_path, work_dir)) < 0) goto cleanup; if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK)) if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0) goto cleanup; SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr); } else if (is_reinit) { if (git_config_delete_entry(config, "core.worktree") < 0) git_error_clear(); } } if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) { SET_REPO_CONFIG(int32, "core.sharedrepository", 1); SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true); } else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) { SET_REPO_CONFIG(int32, "core.sharedrepository", 2); SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true); } cleanup: git_buf_dispose(&cfg_path); git_buf_dispose(&worktree_path); git_config_free(config); return error; } static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p) { git_repository *smrepo = NULL; GIT_UNUSED(n); GIT_UNUSED(p); if (git_submodule_open(&smrepo, sm) < 0 || git_repository_reinit_filesystem(smrepo, true) < 0) git_error_clear(); git_repository_free(smrepo); return 0; } int git_repository_reinit_filesystem(git_repository *repo, int recurse) { int error = 0; git_buf path = GIT_BUF_INIT; git_config *config = NULL; const char *repo_dir = git_repository_path(repo); if (!(error = repo_local_config(&config, &path, repo, repo_dir))) error = repo_init_fs_configs( config, path.ptr, repo_dir, git_repository_workdir(repo), true); git_config_free(config); git_buf_dispose(&path); git_repository__configmap_lookup_cache_clear(repo); if (!repo->is_bare && recurse) (void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL); return error; } static int repo_write_template( const char *git_dir, bool allow_overwrite, const char *file, mode_t mode, bool hidden, const char *content) { git_buf path = GIT_BUF_INIT; int fd, error = 0, flags; if (git_buf_joinpath(&path, git_dir, file) < 0) return -1; if (allow_overwrite) flags = O_WRONLY | O_CREAT | O_TRUNC; else flags = O_WRONLY | O_CREAT | O_EXCL; fd = p_open(git_buf_cstr(&path), flags, mode); if (fd >= 0) { error = p_write(fd, content, strlen(content)); p_close(fd); } else if (errno != EEXIST) error = fd; #ifdef GIT_WIN32 if (!error && hidden) { if (git_win32__set_hidden(path.ptr, true) < 0) error = -1; } #else GIT_UNUSED(hidden); #endif git_buf_dispose(&path); if (error) git_error_set(GIT_ERROR_OS, "failed to initialize repository with template '%s'", file); return error; } static int repo_write_gitlink( const char *in_dir, const char *to_repo, bool use_relative_path) { int error; git_buf buf = GIT_BUF_INIT; git_buf path_to_repo = GIT_BUF_INIT; struct stat st; git_path_dirname_r(&buf, to_repo); git_path_to_dir(&buf); if (git_buf_oom(&buf)) return -1; /* don't write gitlink to natural workdir */ if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 && strcmp(in_dir, buf.ptr) == 0) { error = GIT_PASSTHROUGH; goto cleanup; } if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0) goto cleanup; if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) { git_error_set(GIT_ERROR_REPOSITORY, "cannot overwrite gitlink file into path '%s'", in_dir); error = GIT_EEXISTS; goto cleanup; } git_buf_clear(&buf); error = git_buf_sets(&path_to_repo, to_repo); if (!error && use_relative_path) error = git_path_make_relative(&path_to_repo, in_dir); if (!error) error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr); if (!error) error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr); cleanup: git_buf_dispose(&buf); git_buf_dispose(&path_to_repo); return error; } static mode_t pick_dir_mode(git_repository_init_options *opts) { if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK) return 0777; if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) return (0775 | S_ISGID); if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) return (0777 | S_ISGID); return opts->mode; } #include "repo_template.h" static int repo_init_structure( const char *repo_dir, const char *work_dir, git_repository_init_options *opts) { int error = 0; repo_template_item *tpl; bool external_tpl = ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0); mode_t dmode = pick_dir_mode(opts); bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK; /* Hide the ".git" directory */ #ifdef GIT_WIN32 if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) { if (git_win32__set_hidden(repo_dir, true) < 0) { git_error_set(GIT_ERROR_OS, "failed to mark Git repository folder as hidden"); return -1; } } #endif /* Create the .git gitlink if appropriate */ if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 && (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0) { if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0) return -1; } /* Copy external template if requested */ if (external_tpl) { git_config *cfg = NULL; const char *tdir = NULL; bool default_template = false; git_buf template_buf = GIT_BUF_INIT; if (opts->template_path) tdir = opts->template_path; else if ((error = git_config_open_default(&cfg)) >= 0) { if (!git_config_get_path(&template_buf, cfg, "init.templatedir")) tdir = template_buf.ptr; git_error_clear(); } if (!tdir) { if (!(error = git_sysdir_find_template_dir(&template_buf))) tdir = template_buf.ptr; default_template = true; } /* * If tdir was the empty string, treat it like tdir was a path to an * empty directory (so, don't do any copying). This is the behavior * that git(1) exhibits, although it doesn't seem to be officially * documented. */ if (tdir && git__strcmp(tdir, "") != 0) { uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE | GIT_CPDIR_COPY_DOTFILES; if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK) cpflags |= GIT_CPDIR_CHMOD_DIRS; error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode); } git_buf_dispose(&template_buf); git_config_free(cfg); if (error < 0) { if (!default_template) return error; /* if template was default, ignore error and use internal */ git_error_clear(); external_tpl = false; error = 0; } } /* Copy internal template * - always ensure existence of dirs * - only create files if no external template was specified */ for (tpl = repo_template; !error && tpl->path; ++tpl) { if (!tpl->content) { uint32_t mkdir_flags = GIT_MKDIR_PATH; if (chmod) mkdir_flags |= GIT_MKDIR_CHMOD; error = git_futils_mkdir_relative( tpl->path, repo_dir, dmode, mkdir_flags, NULL); } else if (!external_tpl) { const char *content = tpl->content; if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0) content = opts->description; error = repo_write_template( repo_dir, false, tpl->path, tpl->mode, false, content); } } return error; } static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2) { /* When making parent directories during repository initialization * don't try to set gid or grant world write access */ return git_futils_mkdir( buf->ptr, mode & ~(S_ISGID | 0002), GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR | (skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST)); } static int repo_init_directories( git_buf *repo_path, git_buf *wd_path, const char *given_repo, git_repository_init_options *opts) { int error = 0; bool is_bare, add_dotgit, has_dotgit, natural_wd; mode_t dirmode; /* There are three possible rules for what we are allowed to create: * - MKPATH means anything we need * - MKDIR means just the .git directory and its parent and the workdir * - Neither means only the .git directory can be created * * There are 5 "segments" of path that we might need to deal with: * 1. The .git directory * 2. The parent of the .git directory * 3. Everything above the parent of the .git directory * 4. The working directory (often the same as #2) * 5. Everything above the working directory (often the same as #3) * * For all directories created, we start with the init_mode value for * permissions and then strip off bits in some cases: * * For MKPATH, we create #3 (and #5) paths without S_ISGID or S_IWOTH * For MKPATH and MKDIR, we create #2 (and #4) without S_ISGID * For all rules, we create #1 using the untouched init_mode */ /* set up repo path */ is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0); add_dotgit = (opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 && !is_bare && git__suffixcmp(given_repo, "/" DOT_GIT) != 0 && git__suffixcmp(given_repo, "/" GIT_DIR) != 0; if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0) return -1; has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0); if (has_dotgit) opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT; /* set up workdir path */ if (!is_bare) { if (opts->workdir_path) { if (git_path_join_unrooted( wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0) return -1; } else if (has_dotgit) { if (git_path_dirname_r(wd_path, repo_path->ptr) < 0) return -1; } else { git_error_set(GIT_ERROR_REPOSITORY, "cannot pick working directory" " for non-bare repository that isn't a '.git' directory"); return -1; } if (git_path_to_dir(wd_path) < 0) return -1; } else { git_buf_clear(wd_path); } natural_wd = has_dotgit && wd_path->size > 0 && wd_path->size + strlen(GIT_DIR) == repo_path->size && memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0; if (natural_wd) opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD; /* create directories as needed / requested */ dirmode = pick_dir_mode(opts); if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) { /* create path #5 */ if (wd_path->size > 0 && (error = mkdir_parent(wd_path, dirmode, false)) < 0) return error; /* create path #3 (if not the same as #5) */ if (!natural_wd && (error = mkdir_parent(repo_path, dirmode, has_dotgit)) < 0) return error; } if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 || (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) { /* create path #4 */ if (wd_path->size > 0 && (error = git_futils_mkdir( wd_path->ptr, dirmode & ~S_ISGID, GIT_MKDIR_VERIFY_DIR)) < 0) return error; /* create path #2 (if not the same as #4) */ if (!natural_wd && (error = git_futils_mkdir( repo_path->ptr, dirmode & ~S_ISGID, GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0) return error; } if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 || (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 || has_dotgit) { /* create path #1 */ error = git_futils_mkdir(repo_path->ptr, dirmode, GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0)); } /* prettify both directories now that they are created */ if (!error) { error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL); if (!error && wd_path->size > 0) error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL); } return error; } static int repo_init_head(const char *repo_dir, const char *given) { git_config *cfg = NULL; git_buf head_path = GIT_BUF_INIT, cfg_branch = GIT_BUF_INIT; const char *initial_head = NULL; int error; if ((error = git_buf_joinpath(&head_path, repo_dir, GIT_HEAD_FILE)) < 0) goto out; /* * A template may have set a HEAD; use that unless it's been * overridden by the caller's given initial head setting. */ if (git_path_exists(head_path.ptr) && !given) goto out; if (given) { initial_head = given; } else if ((error = git_config_open_default(&cfg)) >= 0 && (error = git_config_get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0 && *cfg_branch.ptr) { initial_head = cfg_branch.ptr; } if (!initial_head) initial_head = GIT_BRANCH_DEFAULT; error = git_repository_create_head(repo_dir, initial_head); out: git_config_free(cfg); git_buf_dispose(&head_path); git_buf_dispose(&cfg_branch); return error; } static int repo_init_create_origin(git_repository *repo, const char *url) { int error; git_remote *remote; if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) { git_remote_free(remote); } return error; } int git_repository_init( git_repository **repo_out, const char *path, unsigned is_bare) { git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */ if (is_bare) opts.flags |= GIT_REPOSITORY_INIT_BARE; return git_repository_init_ext(repo_out, path, &opts); } int git_repository_init_ext( git_repository **out, const char *given_repo, git_repository_init_options *opts) { git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT, common_path = GIT_BUF_INIT; const char *wd; bool is_valid; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(given_repo); GIT_ASSERT_ARG(opts); GIT_ERROR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options"); if ((error = repo_init_directories(&repo_path, &wd_path, given_repo, opts)) < 0) goto out; wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path); if ((error = is_valid_repository_path(&is_valid, &repo_path, &common_path)) < 0) goto out; if (is_valid) { if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) { git_error_set(GIT_ERROR_REPOSITORY, "attempt to reinitialize '%s'", given_repo); error = GIT_EEXISTS; goto out; } opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT; if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0) goto out; /* TODO: reinitialize the templates */ } else { if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 || (error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0 || (error = repo_init_head(repo_path.ptr, opts->initial_head)) < 0) goto out; } if ((error = git_repository_open(out, repo_path.ptr)) < 0) goto out; if (opts->origin_url && (error = repo_init_create_origin(*out, opts->origin_url)) < 0) goto out; out: git_buf_dispose(&common_path); git_buf_dispose(&repo_path); git_buf_dispose(&wd_path); return error; } int git_repository_head_detached(git_repository *repo) { git_reference *ref; git_odb *odb = NULL; int exists; if (git_repository_odb__weakptr(&odb, repo) < 0) return -1; if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0) return -1; if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { git_reference_free(ref); return 0; } exists = git_odb_exists(odb, git_reference_target(ref)); git_reference_free(ref); return exists; } int git_repository_head_detached_for_worktree(git_repository *repo, const char *name) { git_reference *ref = NULL; int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0) goto out; error = (git_reference_type(ref) != GIT_REFERENCE_SYMBOLIC); out: git_reference_free(ref); return error; } int git_repository_head(git_reference **head_out, git_repository *repo) { git_reference *head; int error; GIT_ASSERT_ARG(head_out); if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0) return error; if (git_reference_type(head) == GIT_REFERENCE_DIRECT) { *head_out = head; return 0; } error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1); git_reference_free(head); return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error; } int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name) { git_repository *worktree_repo = NULL; git_worktree *worktree = NULL; git_reference *head = NULL; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); *out = NULL; if ((error = git_worktree_lookup(&worktree, repo, name)) < 0 || (error = git_repository_open_from_worktree(&worktree_repo, worktree)) < 0 || (error = git_reference_lookup(&head, worktree_repo, GIT_HEAD_FILE)) < 0) goto out; if (git_reference_type(head) != GIT_REFERENCE_DIRECT) { if ((error = git_reference_lookup_resolved(out, worktree_repo, git_reference_symbolic_target(head), -1)) < 0) goto out; } else { *out = head; head = NULL; } out: git_reference_free(head); git_worktree_free(worktree); git_repository_free(worktree_repo); return error; } int git_repository_foreach_worktree(git_repository *repo, git_repository_foreach_worktree_cb cb, void *payload) { git_strarray worktrees = {0}; git_repository *worktree_repo = NULL; git_worktree *worktree = NULL; int error; size_t i; /* apply operation to repository supplied when commondir is empty, implying there's * no linked worktrees to iterate, which can occur when using custom odb/refdb */ if (!repo->commondir) return cb(repo, payload); if ((error = git_repository_open(&worktree_repo, repo->commondir)) < 0 || (error = cb(worktree_repo, payload) != 0)) goto out; git_repository_free(worktree_repo); worktree_repo = NULL; if ((error = git_worktree_list(&worktrees, repo)) < 0) goto out; for (i = 0; i < worktrees.count; i++) { git_repository_free(worktree_repo); worktree_repo = NULL; git_worktree_free(worktree); worktree = NULL; if ((error = git_worktree_lookup(&worktree, repo, worktrees.strings[i]) < 0) || (error = git_repository_open_from_worktree(&worktree_repo, worktree)) < 0) { if (error != GIT_ENOTFOUND) goto out; error = 0; continue; } if ((error = cb(worktree_repo, payload)) != 0) goto out; } out: git_strarray_dispose(&worktrees); git_repository_free(worktree_repo); git_worktree_free(worktree); return error; } int git_repository_head_unborn(git_repository *repo) { git_reference *ref = NULL; int error; error = git_repository_head(&ref, repo); git_reference_free(ref); if (error == GIT_EUNBORNBRANCH) { git_error_clear(); return 1; } if (error < 0) return -1; return 0; } static int repo_contains_no_reference(git_repository *repo) { git_reference_iterator *iter; const char *refname; int error; if ((error = git_reference_iterator_new(&iter, repo)) < 0) return error; error = git_reference_next_name(&refname, iter); git_reference_iterator_free(iter); if (error == GIT_ITEROVER) return 1; return error; } int git_repository_initialbranch(git_buf *out, git_repository *repo) { git_config *config; git_config_entry *entry = NULL; const char *branch; int valid, error; if ((error = git_repository_config__weakptr(&config, repo)) < 0) return error; if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0 && *entry->value) { branch = entry->value; } else if (!error || error == GIT_ENOTFOUND) { branch = GIT_BRANCH_DEFAULT; } else { goto done; } if ((error = git_buf_puts(out, GIT_REFS_HEADS_DIR)) < 0 || (error = git_buf_puts(out, branch)) < 0 || (error = git_reference_name_is_valid(&valid, out->ptr)) < 0) goto done; if (!valid) { git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid branch name"); error = -1; } done: git_config_entry_free(entry); return error; } int git_repository_is_empty(git_repository *repo) { git_reference *head = NULL; git_buf initialbranch = GIT_BUF_INIT; int result = 0; if ((result = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0 || (result = git_repository_initialbranch(&initialbranch, repo)) < 0) goto done; result = (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC && strcmp(git_reference_symbolic_target(head), initialbranch.ptr) == 0 && repo_contains_no_reference(repo)); done: git_reference_free(head); git_buf_dispose(&initialbranch); return result; } static const char *resolved_parent_path(const git_repository *repo, git_repository_item_t item, git_repository_item_t fallback) { const char *parent; switch (item) { case GIT_REPOSITORY_ITEM_GITDIR: parent = git_repository_path(repo); break; case GIT_REPOSITORY_ITEM_WORKDIR: parent = git_repository_workdir(repo); break; case GIT_REPOSITORY_ITEM_COMMONDIR: parent = git_repository_commondir(repo); break; default: git_error_set(GIT_ERROR_INVALID, "invalid item directory"); return NULL; } if (!parent && fallback != GIT_REPOSITORY_ITEM__LAST) return resolved_parent_path(repo, fallback, GIT_REPOSITORY_ITEM__LAST); return parent; } int git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item) { const char *parent = resolved_parent_path(repo, items[item].parent, items[item].fallback); if (parent == NULL) { git_error_set(GIT_ERROR_INVALID, "path cannot exist in repository"); return GIT_ENOTFOUND; } if (git_buf_sets(out, parent) < 0) return -1; if (items[item].name) { if (git_buf_joinpath(out, parent, items[item].name) < 0) return -1; } if (items[item].directory) { if (git_path_to_dir(out) < 0) return -1; } return 0; } const char *git_repository_path(const git_repository *repo) { GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL); return repo->gitdir; } const char *git_repository_workdir(const git_repository *repo) { GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL); if (repo->is_bare) return NULL; return repo->workdir; } int git_repository_workdir_path( git_buf *out, git_repository *repo, const char *path) { int error; if (!repo->workdir) { git_error_set(GIT_ERROR_REPOSITORY, "repository has no working directory"); return GIT_EBAREREPO; } if (!(error = git_buf_joinpath(out, repo->workdir, path))) error = git_path_validate_workdir_buf(repo, out); return error; } const char *git_repository_commondir(const git_repository *repo) { GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL); return repo->commondir; } int git_repository_set_workdir( git_repository *repo, const char *workdir, int update_gitlink) { int error = 0; git_buf path = GIT_BUF_INIT; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(workdir); if (git_path_prettify_dir(&path, workdir, NULL) < 0) return -1; if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) return 0; if (update_gitlink) { git_config *config; if (git_repository_config__weakptr(&config, repo) < 0) return -1; error = repo_write_gitlink(path.ptr, git_repository_path(repo), false); /* passthrough error means gitlink is unnecessary */ if (error == GIT_PASSTHROUGH) error = git_config_delete_entry(config, "core.worktree"); else if (!error) error = git_config_set_string(config, "core.worktree", path.ptr); if (!error) error = git_config_set_bool(config, "core.bare", false); } if (!error) { char *old_workdir = repo->workdir; repo->workdir = git_buf_detach(&path); repo->is_bare = 0; git__free(old_workdir); } return error; } int git_repository_is_bare(const git_repository *repo) { GIT_ASSERT_ARG(repo); return repo->is_bare; } int git_repository_is_worktree(const git_repository *repo) { GIT_ASSERT_ARG(repo); return repo->is_worktree; } int git_repository_set_bare(git_repository *repo) { int error; git_config *config; GIT_ASSERT_ARG(repo); if (repo->is_bare) return 0; if ((error = git_repository_config__weakptr(&config, repo)) < 0) return error; if ((error = git_config_set_bool(config, "core.bare", true)) < 0) return error; if ((error = git_config__update_entry(config, "core.worktree", NULL, true, true)) < 0) return error; git__free(repo->workdir); repo->workdir = NULL; repo->is_bare = 1; return 0; } int git_repository_head_tree(git_tree **tree, git_repository *repo) { git_reference *head; git_object *obj; int error; if ((error = git_repository_head(&head, repo)) < 0) return error; if ((error = git_reference_peel(&obj, head, GIT_OBJECT_TREE)) < 0) goto cleanup; *tree = (git_tree *)obj; cleanup: git_reference_free(head); return error; } int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; char orig_head_str[GIT_OID_HEXSZ]; int error = 0; git_oid_fmt(orig_head_str, orig_head); if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) == 0 && (error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0) error = git_filebuf_commit(&file); if (error < 0) git_filebuf_cleanup(&file); git_buf_dispose(&file_path); return error; } int git_repository_message(git_buf *out, git_repository *repo) { git_buf path = GIT_BUF_INIT; struct stat st; int error; if ((error = git_buf_sanitize(out)) < 0) return error; if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0) return -1; if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) { if (errno == ENOENT) error = GIT_ENOTFOUND; git_error_set(GIT_ERROR_OS, "could not access message file"); } else { error = git_futils_readbuffer(out, git_buf_cstr(&path)); } git_buf_dispose(&path); return error; } int git_repository_message_remove(git_repository *repo) { git_buf path = GIT_BUF_INIT; int error; if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0) return -1; error = p_unlink(git_buf_cstr(&path)); git_buf_dispose(&path); return error; } int git_repository_hashfile( git_oid *out, git_repository *repo, const char *path, git_object_t type, const char *as_path) { int error; git_filter_list *fl = NULL; git_file fd = -1; uint64_t len; git_buf full_path = GIT_BUF_INIT; const char *workdir = git_repository_workdir(repo); /* as_path can be NULL */ GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(path); GIT_ASSERT_ARG(repo); if ((error = git_path_join_unrooted(&full_path, path, workdir, NULL)) < 0 || (error = git_path_validate_workdir_buf(repo, &full_path)) < 0) return error; /* * NULL as_path means that we should derive it from the * given path. */ if (!as_path) { if (workdir && !git__prefixcmp(full_path.ptr, workdir)) as_path = full_path.ptr + strlen(workdir); else as_path = ""; } /* passing empty string for "as_path" indicated --no-filters */ if (strlen(as_path) > 0) { error = git_filter_list_load( &fl, repo, NULL, as_path, GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT); if (error < 0) return error; } /* at this point, error is a count of the number of loaded filters */ fd = git_futils_open_ro(full_path.ptr); if (fd < 0) { error = fd; goto cleanup; } if ((error = git_futils_filesize(&len, fd)) < 0) goto cleanup; if (!git__is_sizet(len)) { git_error_set(GIT_ERROR_OS, "file size overflow for 32-bit systems"); error = -1; goto cleanup; } error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl); cleanup: if (fd >= 0) p_close(fd); git_filter_list_free(fl); git_buf_dispose(&full_path); return error; } static int checkout_message(git_buf *out, git_reference *old, const char *new) { git_buf_puts(out, "checkout: moving from "); if (git_reference_type(old) == GIT_REFERENCE_SYMBOLIC) git_buf_puts(out, git_reference__shorthand(git_reference_symbolic_target(old))); else git_buf_puts(out, git_oid_tostr_s(git_reference_target(old))); git_buf_puts(out, " to "); if (git_reference__is_branch(new) || git_reference__is_tag(new) || git_reference__is_remote(new)) git_buf_puts(out, git_reference__shorthand(new)); else git_buf_puts(out, new); if (git_buf_oom(out)) return -1; return 0; } static int detach(git_repository *repo, const git_oid *id, const char *new) { int error; git_buf log_message = GIT_BUF_INIT; git_object *object = NULL, *peeled = NULL; git_reference *new_head = NULL, *current = NULL; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(id); if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0) return error; if ((error = git_object_lookup(&object, repo, id, GIT_OBJECT_ANY)) < 0) goto cleanup; if ((error = git_object_peel(&peeled, object, GIT_OBJECT_COMMIT)) < 0) goto cleanup; if (new == NULL) new = git_oid_tostr_s(git_object_id(peeled)); if ((error = checkout_message(&log_message, current, new)) < 0) goto cleanup; error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message)); cleanup: git_buf_dispose(&log_message); git_object_free(object); git_object_free(peeled); git_reference_free(current); git_reference_free(new_head); return error; } int git_repository_set_head( git_repository *repo, const char *refname) { git_reference *ref = NULL, *current = NULL, *new_head = NULL; git_buf log_message = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(refname); if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0) return error; if ((error = checkout_message(&log_message, current, refname)) < 0) goto cleanup; error = git_reference_lookup(&ref, repo, refname); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; if (ref && current->type == GIT_REFERENCE_SYMBOLIC && git__strcmp(current->target.symbolic, ref->name) && git_reference_is_branch(ref) && git_branch_is_checked_out(ref)) { git_error_set(GIT_ERROR_REPOSITORY, "cannot set HEAD to reference '%s' as it is the current HEAD " "of a linked repository.", git_reference_name(ref)); error = -1; goto cleanup; } if (!error) { if (git_reference_is_branch(ref)) { error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), true, git_buf_cstr(&log_message)); } else { error = detach(repo, git_reference_target(ref), git_reference_is_tag(ref) || git_reference_is_remote(ref) ? refname : NULL); } } else if (git_reference__is_branch(refname)) { error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, true, git_buf_cstr(&log_message)); } cleanup: git_buf_dispose(&log_message); git_reference_free(current); git_reference_free(ref); git_reference_free(new_head); return error; } int git_repository_set_head_detached( git_repository *repo, const git_oid *commitish) { return detach(repo, commitish, NULL); } int git_repository_set_head_detached_from_annotated( git_repository *repo, const git_annotated_commit *commitish) { GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(commitish); return detach(repo, git_annotated_commit_id(commitish), commitish->description); } int git_repository_detach_head(git_repository *repo) { git_reference *old_head = NULL, *new_head = NULL, *current = NULL; git_object *object = NULL; git_buf log_message = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(repo); if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0) return error; if ((error = git_repository_head(&old_head, repo)) < 0) goto cleanup; if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJECT_COMMIT)) < 0) goto cleanup; if ((error = checkout_message(&log_message, current, git_oid_tostr_s(git_object_id(object)))) < 0) goto cleanup; error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), 1, git_buf_cstr(&log_message)); cleanup: git_buf_dispose(&log_message); git_object_free(object); git_reference_free(old_head); git_reference_free(new_head); git_reference_free(current); return error; } /** * Loosely ported from git.git * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289 */ int git_repository_state(git_repository *repo) { git_buf repo_path = GIT_BUF_INIT; int state = GIT_REPOSITORY_STATE_NONE; GIT_ASSERT_ARG(repo); if (git_buf_puts(&repo_path, repo->gitdir) < 0) return -1; if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE)) state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE; else if (git_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR)) state = GIT_REPOSITORY_STATE_REBASE_MERGE; else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE)) state = GIT_REPOSITORY_STATE_REBASE; else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE)) state = GIT_REPOSITORY_STATE_APPLY_MAILBOX; else if (git_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR)) state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE; else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE)) state = GIT_REPOSITORY_STATE_MERGE; else if (git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) { state = GIT_REPOSITORY_STATE_REVERT; if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { state = GIT_REPOSITORY_STATE_REVERT_SEQUENCE; } } else if (git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) { state = GIT_REPOSITORY_STATE_CHERRYPICK; if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { state = GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE; } } else if (git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE)) state = GIT_REPOSITORY_STATE_BISECT; git_buf_dispose(&repo_path); return state; } int git_repository__cleanup_files( git_repository *repo, const char *files[], size_t files_len) { git_buf buf = GIT_BUF_INIT; size_t i; int error; for (error = 0, i = 0; !error && i < files_len; ++i) { const char *path; if (git_buf_joinpath(&buf, repo->gitdir, files[i]) < 0) return -1; path = git_buf_cstr(&buf); if (git_path_isfile(path)) { error = p_unlink(path); } else if (git_path_isdir(path)) { error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); } git_buf_clear(&buf); } git_buf_dispose(&buf); return error; } static const char *state_files[] = { GIT_MERGE_HEAD_FILE, GIT_MERGE_MODE_FILE, GIT_MERGE_MSG_FILE, GIT_REVERT_HEAD_FILE, GIT_CHERRYPICK_HEAD_FILE, GIT_BISECT_LOG_FILE, GIT_REBASE_MERGE_DIR, GIT_REBASE_APPLY_DIR, GIT_SEQUENCER_DIR, }; int git_repository_state_cleanup(git_repository *repo) { GIT_ASSERT_ARG(repo); return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); } int git_repository_is_shallow(git_repository *repo) { git_buf path = GIT_BUF_INIT; struct stat st; int error; if ((error = git_buf_joinpath(&path, repo->gitdir, "shallow")) < 0) return error; error = git_path_lstat(path.ptr, &st); git_buf_dispose(&path); if (error == GIT_ENOTFOUND) { git_error_clear(); return 0; } if (error < 0) return error; return st.st_size == 0 ? 0 : 1; } int git_repository_init_options_init( git_repository_init_options *opts, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( opts, version, git_repository_init_options, GIT_REPOSITORY_INIT_OPTIONS_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_repository_init_init_options( git_repository_init_options *opts, unsigned int version) { return git_repository_init_options_init(opts, version); } #endif int git_repository_ident(const char **name, const char **email, const git_repository *repo) { *name = repo->ident_name; *email = repo->ident_email; return 0; } int git_repository_set_ident(git_repository *repo, const char *name, const char *email) { char *tmp_name = NULL, *tmp_email = NULL; if (name) { tmp_name = git__strdup(name); GIT_ERROR_CHECK_ALLOC(tmp_name); } if (email) { tmp_email = git__strdup(email); GIT_ERROR_CHECK_ALLOC(tmp_email); } tmp_name = git_atomic_swap(repo->ident_name, tmp_name); tmp_email = git_atomic_swap(repo->ident_email, tmp_email); git__free(tmp_name); git__free(tmp_email); return 0; } int git_repository_submodule_cache_all(git_repository *repo) { GIT_ASSERT_ARG(repo); return git_submodule_cache_init(&repo->submodule_cache, repo); } int git_repository_submodule_cache_clear(git_repository *repo) { int error = 0; GIT_ASSERT_ARG(repo); error = git_submodule_cache_free(repo->submodule_cache); repo->submodule_cache = NULL; return error; } git2r/src/libgit2/src/pool.c0000644000175000017500000001242714125111754015471 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "pool.h" #include "posix.h" #ifndef GIT_WIN32 #include #endif struct git_pool_page { git_pool_page *next; size_t size; size_t avail; GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8); }; static void *pool_alloc_page(git_pool *pool, size_t size); #ifndef GIT_DEBUG_POOL static size_t system_page_size = 0; int git_pool_global_init(void) { if (git__page_size(&system_page_size) < 0) system_page_size = 4096; /* allow space for malloc overhead */ system_page_size -= (2 * sizeof(void *)) + sizeof(git_pool_page); return 0; } int git_pool_init(git_pool *pool, size_t item_size) { GIT_ASSERT_ARG(pool); GIT_ASSERT_ARG(item_size >= 1); memset(pool, 0, sizeof(git_pool)); pool->item_size = item_size; pool->page_size = system_page_size; return 0; } void git_pool_clear(git_pool *pool) { git_pool_page *scan, *next; for (scan = pool->pages; scan != NULL; scan = next) { next = scan->next; git__free(scan); } pool->pages = NULL; } static void *pool_alloc_page(git_pool *pool, size_t size) { git_pool_page *page; const size_t new_page_size = (size <= pool->page_size) ? pool->page_size : size; size_t alloc_size; if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) || !(page = git__malloc(alloc_size))) return NULL; page->size = new_page_size; page->avail = new_page_size - size; page->next = pool->pages; pool->pages = page; return page->data; } static void *pool_alloc(git_pool *pool, size_t size) { git_pool_page *page = pool->pages; void *ptr = NULL; if (!page || page->avail < size) return pool_alloc_page(pool, size); ptr = &page->data[page->size - page->avail]; page->avail -= size; return ptr; } uint32_t git_pool__open_pages(git_pool *pool) { uint32_t ct = 0; git_pool_page *scan; for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; return ct; } bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) { git_pool_page *scan; for (scan = pool->pages; scan != NULL; scan = scan->next) if ((void *)scan->data <= ptr && (void *)(((char *)scan->data) + scan->size) > ptr) return true; return false; } #else int git_pool_global_init(void) { return 0; } static int git_pool__ptr_cmp(const void * a, const void * b) { if(a > b) { return 1; } if(a < b) { return -1; } else { return 0; } } int git_pool_init(git_pool *pool, size_t item_size) { GIT_ASSERT_ARG(pool); GIT_ASSERT_ARG(item_size >= 1); memset(pool, 0, sizeof(git_pool)); pool->item_size = item_size; pool->page_size = git_pool__system_page_size(); git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp); return 0; } void git_pool_clear(git_pool *pool) { git_vector_free_deep(&pool->allocations); } static void *pool_alloc(git_pool *pool, size_t size) { void *ptr = NULL; if((ptr = git__malloc(size)) == NULL) { return NULL; } git_vector_insert_sorted(&pool->allocations, ptr, NULL); return ptr; } bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) { size_t pos; return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND; } #endif void git_pool_swap(git_pool *a, git_pool *b) { git_pool temp; if (a == b) return; memcpy(&temp, a, sizeof(temp)); memcpy(a, b, sizeof(temp)); memcpy(b, &temp, sizeof(temp)); } static size_t alloc_size(git_pool *pool, size_t count) { const size_t align = sizeof(void *) - 1; if (pool->item_size > 1) { const size_t item_size = (pool->item_size + align) & ~align; return item_size * count; } return (count + align) & ~align; } void *git_pool_malloc(git_pool *pool, size_t items) { return pool_alloc(pool, alloc_size(pool, items)); } void *git_pool_mallocz(git_pool *pool, size_t items) { const size_t size = alloc_size(pool, items); void *ptr = pool_alloc(pool, size); if (ptr) memset(ptr, 0x0, size); return ptr; } char *git_pool_strndup(git_pool *pool, const char *str, size_t n) { char *ptr = NULL; GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL); GIT_ASSERT_ARG_WITH_RETVAL(str, NULL); GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL); if (n == SIZE_MAX) return NULL; if ((ptr = git_pool_malloc(pool, (n + 1))) != NULL) { memcpy(ptr, str, n); ptr[n] = '\0'; } return ptr; } char *git_pool_strdup(git_pool *pool, const char *str) { GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL); GIT_ASSERT_ARG_WITH_RETVAL(str, NULL); GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL); return git_pool_strndup(pool, str, strlen(str)); } char *git_pool_strdup_safe(git_pool *pool, const char *str) { return str ? git_pool_strdup(pool, str) : NULL; } char *git_pool_strcat(git_pool *pool, const char *a, const char *b) { void *ptr; size_t len_a, len_b, total; GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL); GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL); len_a = a ? strlen(a) : 0; len_b = b ? strlen(b) : 0; if (GIT_ADD_SIZET_OVERFLOW(&total, len_a, len_b) || GIT_ADD_SIZET_OVERFLOW(&total, total, 1)) return NULL; if ((ptr = git_pool_malloc(pool, total)) != NULL) { if (len_a) memcpy(ptr, a, len_a); if (len_b) memcpy(((char *)ptr) + len_a, b, len_b); *(((char *)ptr) + len_a + len_b) = '\0'; } return ptr; } git2r/src/libgit2/src/refs.h0000644000175000017500000001110214125111754015451 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_refs_h__ #define INCLUDE_refs_h__ #include "common.h" #include "git2/oid.h" #include "git2/refs.h" #include "git2/refdb.h" #include "strmap.h" #include "buffer.h" #include "oid.h" extern bool git_reference__enable_symbolic_ref_target_validation; #define GIT_REFS_DIR "refs/" #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" #define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/" #define GIT_REFS_NOTES_DIR GIT_REFS_DIR "notes/" #define GIT_REFS_DIR_MODE 0777 #define GIT_REFS_FILE_MODE 0666 #define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF" #define GIT_SYMREF "ref: " #define GIT_PACKEDREFS_FILE "packed-refs" #define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled sorted " #define GIT_PACKEDREFS_FILE_MODE 0666 #define GIT_HEAD_FILE "HEAD" #define GIT_ORIG_HEAD_FILE "ORIG_HEAD" #define GIT_FETCH_HEAD_FILE "FETCH_HEAD" #define GIT_MERGE_HEAD_FILE "MERGE_HEAD" #define GIT_REVERT_HEAD_FILE "REVERT_HEAD" #define GIT_CHERRYPICK_HEAD_FILE "CHERRY_PICK_HEAD" #define GIT_BISECT_LOG_FILE "BISECT_LOG" #define GIT_REBASE_MERGE_DIR "rebase-merge/" #define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive" #define GIT_REBASE_APPLY_DIR "rebase-apply/" #define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing" #define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying" #define GIT_SEQUENCER_DIR "sequencer/" #define GIT_SEQUENCER_HEAD_FILE GIT_SEQUENCER_DIR "head" #define GIT_SEQUENCER_OPTIONS_FILE GIT_SEQUENCER_DIR "options" #define GIT_SEQUENCER_TODO_FILE GIT_SEQUENCER_DIR "todo" #define GIT_STASH_FILE "stash" #define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE #define GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE (1u << 16) #define GIT_REFERENCE_FORMAT__VALIDATION_DISABLE (1u << 15) #define GIT_REFNAME_MAX 1024 typedef char git_refname_t[GIT_REFNAME_MAX]; struct git_reference { git_refdb *db; git_reference_t type; union { git_oid oid; char *symbolic; } target; git_oid peel; char name[GIT_FLEX_ARRAY]; }; /** * Reallocate the reference with a new name * * Note that this is a dangerous operation, as on success, all existing * pointers to the old reference will now be dangling. Only call this on objects * you control, possibly using `git_reference_dup`. */ git_reference *git_reference__realloc(git_reference **ptr_to_ref, const char *name); int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags); int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *sig, const char *log_message); int git_reference__name_is_valid(int *valid, const char *name, unsigned int flags); int git_reference__is_branch(const char *ref_name); int git_reference__is_remote(const char *ref_name); int git_reference__is_tag(const char *ref_name); int git_reference__is_note(const char *ref_name); const char *git_reference__shorthand(const char *name); /** * Lookup a reference by name and try to resolve to an OID. * * You can control how many dereferences this will attempt to resolve the * reference with the `max_deref` parameter, or pass -1 to use a sane * default. If you pass 0 for `max_deref`, this will not attempt to resolve * the reference. For any value of `max_deref` other than 0, not * successfully resolving the reference will be reported as an error. * The generated reference must be freed by the user. * * @param reference_out Pointer to the looked-up reference * @param repo The repository to look up the reference * @param name The long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...) * @param max_deref Maximum number of dereferences to make of symbolic refs, 0 means simple lookup, < 0 means use default reasonable value * @return 0 on success or < 0 on error; not being able to resolve the reference is an error unless 0 was passed for max_deref */ int git_reference_lookup_resolved( git_reference **reference_out, git_repository *repo, const char *name, int max_deref); int git_reference__log_signature(git_signature **out, git_repository *repo); /** Update a reference after a commit. */ int git_reference__update_for_commit( git_repository *repo, git_reference *ref, const char *ref_name, const git_oid *id, const char *operation); int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo); #endif git2r/src/libgit2/src/util.c0000644000175000017500000004111114125111754015465 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "util.h" #include "common.h" #ifdef GIT_WIN32 # include "win32/utf-conv.h" # include "win32/w32_buffer.h" # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include # ifdef HAVE_QSORT_S # include # endif #endif #ifdef _MSC_VER # include #endif #if defined(hpux) || defined(__hpux) || defined(_hpux) # include #endif int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base) { const char *p; int64_t n, nn, v; int c, ovfl, neg, ndig; p = nptr; neg = 0; n = 0; ndig = 0; ovfl = 0; /* * White space */ while (nptr_len && git__isspace(*p)) p++, nptr_len--; if (!nptr_len) goto Return; /* * Sign */ if (*p == '-' || *p == '+') { if (*p == '-') neg = 1; p++; nptr_len--; } if (!nptr_len) goto Return; /* * Automatically detect the base if none was given to us. * Right now, we assume that a number starting with '0x' * is hexadecimal and a number starting with '0' is * octal. */ if (base == 0) { if (*p != '0') base = 10; else if (nptr_len > 2 && (p[1] == 'x' || p[1] == 'X')) base = 16; else base = 8; } if (base < 0 || 36 < base) goto Return; /* * Skip prefix of '0x'-prefixed hexadecimal numbers. There is no * need to do the same for '0'-prefixed octal numbers as a * leading '0' does not have any impact. Also, if we skip a * leading '0' in such a string, then we may end up with no * digits left and produce an error later on which isn't one. */ if (base == 16 && nptr_len > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { p += 2; nptr_len -= 2; } /* * Non-empty sequence of digits */ for (; nptr_len > 0; p++,ndig++,nptr_len--) { c = *p; v = base; if ('0'<=c && c<='9') v = c - '0'; else if ('a'<=c && c<='z') v = c - 'a' + 10; else if ('A'<=c && c<='Z') v = c - 'A' + 10; if (v >= base) break; v = neg ? -v : v; if (git__multiply_int64_overflow(&nn, n, base) || git__add_int64_overflow(&n, nn, v)) { ovfl = 1; /* Keep on iterating until the end of this number */ continue; } } Return: if (ndig == 0) { git_error_set(GIT_ERROR_INVALID, "failed to convert string to long: not a number"); return -1; } if (endptr) *endptr = p; if (ovfl) { git_error_set(GIT_ERROR_INVALID, "failed to convert string to long: overflow error"); return -1; } *result = n; return 0; } int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base) { const char *tmp_endptr; int32_t tmp_int; int64_t tmp_long; int error; if ((error = git__strntol64(&tmp_long, nptr, nptr_len, &tmp_endptr, base)) < 0) return error; tmp_int = tmp_long & 0xFFFFFFFF; if (tmp_int != tmp_long) { int len = (int)(tmp_endptr - nptr); git_error_set(GIT_ERROR_INVALID, "failed to convert: '%.*s' is too large", len, nptr); return -1; } *result = tmp_int; if (endptr) *endptr = tmp_endptr; return error; } int git__strcasecmp(const char *a, const char *b) { while (*a && *b && git__tolower(*a) == git__tolower(*b)) ++a, ++b; return ((unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b)); } int git__strcasesort_cmp(const char *a, const char *b) { int cmp = 0; while (*a && *b) { if (*a != *b) { if (git__tolower(*a) != git__tolower(*b)) break; /* use case in sort order even if not in equivalence */ if (!cmp) cmp = (int)(*(const uint8_t *)a) - (int)(*(const uint8_t *)b); } ++a, ++b; } if (*a || *b) return (unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b); return cmp; } int git__strncasecmp(const char *a, const char *b, size_t sz) { int al, bl; do { al = (unsigned char)git__tolower(*a); bl = (unsigned char)git__tolower(*b); ++a, ++b; } while (--sz && al && al == bl); return al - bl; } void git__strntolower(char *str, size_t len) { size_t i; for (i = 0; i < len; ++i) { str[i] = (char)git__tolower(str[i]); } } void git__strtolower(char *str) { git__strntolower(str, strlen(str)); } GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase) { int s, p; while (str_n--) { s = (unsigned char)*str++; p = (unsigned char)*prefix++; if (icase) { s = git__tolower(s); p = git__tolower(p); } if (!p) return 0; if (s != p) return s - p; } return (0 - *prefix); } int git__prefixcmp(const char *str, const char *prefix) { unsigned char s, p; while (1) { p = *prefix++; s = *str++; if (!p) return 0; if (s != p) return s - p; } } int git__prefixncmp(const char *str, size_t str_n, const char *prefix) { return prefixcmp(str, str_n, prefix, false); } int git__prefixcmp_icase(const char *str, const char *prefix) { return prefixcmp(str, SIZE_MAX, prefix, true); } int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix) { return prefixcmp(str, str_n, prefix, true); } int git__suffixcmp(const char *str, const char *suffix) { size_t a = strlen(str); size_t b = strlen(suffix); if (a < b) return -1; return strcmp(str + (a - b), suffix); } char *git__strtok(char **end, const char *sep) { char *ptr = *end; while (*ptr && strchr(sep, *ptr)) ++ptr; if (*ptr) { char *start = ptr; *end = start + 1; while (**end && !strchr(sep, **end)) ++*end; if (**end) { **end = '\0'; ++*end; } return start; } return NULL; } /* Similar to strtok, but does not collapse repeated tokens. */ char *git__strsep(char **end, const char *sep) { char *start = *end, *ptr = *end; while (*ptr && !strchr(sep, *ptr)) ++ptr; if (*ptr) { *end = ptr + 1; *ptr = '\0'; return start; } return NULL; } size_t git__linenlen(const char *buffer, size_t buffer_len) { char *nl = memchr(buffer, '\n', buffer_len); return nl ? (size_t)(nl - buffer) + 1 : buffer_len; } /* * Adapted Not So Naive algorithm from http://www-igm.univ-mlv.fr/~lecroq/string/ */ const void * git__memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { const char *h, *n; size_t j, k, l; if (needlelen > haystacklen || !haystacklen || !needlelen) return NULL; h = (const char *) haystack, n = (const char *) needle; if (needlelen == 1) return memchr(haystack, *n, haystacklen); if (n[0] == n[1]) { k = 2; l = 1; } else { k = 1; l = 2; } j = 0; while (j <= haystacklen - needlelen) { if (n[1] != h[j + 1]) { j += k; } else { if (memcmp(n + 2, h + j + 2, needlelen - 2) == 0 && n[0] == h[j]) return h + j; j += l; } } return NULL; } void git__hexdump(const char *buffer, size_t len) { static const size_t LINE_WIDTH = 16; size_t line_count, last_line, i, j; const char *line; line_count = (len / LINE_WIDTH); last_line = (len % LINE_WIDTH); for (i = 0; i < line_count; ++i) { printf("%08" PRIxZ " ", (i * LINE_WIDTH)); line = buffer + (i * LINE_WIDTH); for (j = 0; j < LINE_WIDTH; ++j, ++line) { printf("%02x ", (unsigned char)*line & 0xFF); if (j == (LINE_WIDTH / 2)) printf(" "); } printf(" |"); line = buffer + (i * LINE_WIDTH); for (j = 0; j < LINE_WIDTH; ++j, ++line) printf("%c", (*line >= 32 && *line <= 126) ? *line : '.'); printf("|\n"); } if (last_line > 0) { printf("%08" PRIxZ " ", (line_count * LINE_WIDTH)); line = buffer + (line_count * LINE_WIDTH); for (j = 0; j < last_line; ++j, ++line) { printf("%02x ", (unsigned char)*line & 0xFF); if (j == (LINE_WIDTH / 2)) printf(" "); } if (j < (LINE_WIDTH / 2)) printf(" "); for (j = 0; j < (LINE_WIDTH - last_line); ++j) printf(" "); printf(" |"); line = buffer + (line_count * LINE_WIDTH); for (j = 0; j < last_line; ++j, ++line) printf("%c", (*line >= 32 && *line <= 126) ? *line : '.'); printf("|\n"); } printf("\n"); } #ifdef GIT_LEGACY_HASH uint32_t git__hash(const void *key, int len, unsigned int seed) { const uint32_t m = 0x5bd1e995; const int r = 24; uint32_t h = seed ^ len; const unsigned char *data = (const unsigned char *)key; while(len >= 4) { uint32_t k = *(uint32_t *)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; h ^= h >> 13; h *= m; h ^= h >> 15; return h; } #else /* Cross-platform version of Murmurhash3 http://code.google.com/p/smhasher/wiki/MurmurHash3 by Austin Appleby (aappleby@gmail.com) This code is on the public domain. */ uint32_t git__hash(const void *key, int len, uint32_t seed) { #define MURMUR_BLOCK() {\ k1 *= c1; \ k1 = git__rotl(k1,11);\ k1 *= c2;\ h1 ^= k1;\ h1 = h1*3 + 0x52dce729;\ c1 = c1*5 + 0x7b7d159c;\ c2 = c2*5 + 0x6bce6396;\ } const uint8_t *data = (const uint8_t*)key; const int nblocks = len / 4; const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4); const uint8_t *tail = (const uint8_t *)(data + nblocks * 4); uint32_t h1 = 0x971e137b ^ seed; uint32_t k1; uint32_t c1 = 0x95543787; uint32_t c2 = 0x2ad7eb25; int i; for (i = -nblocks; i; i++) { k1 = blocks[i]; MURMUR_BLOCK(); } k1 = 0; switch(len & 3) { case 3: k1 ^= tail[2] << 16; /* fall through */ case 2: k1 ^= tail[1] << 8; /* fall through */ case 1: k1 ^= tail[0]; MURMUR_BLOCK(); } h1 ^= len; h1 ^= h1 >> 16; h1 *= 0x85ebca6b; h1 ^= h1 >> 13; h1 *= 0xc2b2ae35; h1 ^= h1 >> 16; return h1; } #endif /** * A modified `bsearch` from the BSD glibc. * * Copyright (c) 1990 Regents of the University of California. * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. [rescinded 22 July 1999] * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ int git__bsearch( void **array, size_t array_len, const void *key, int (*compare)(const void *, const void *), size_t *position) { size_t lim; int cmp = -1; void **part, **base = array; for (lim = array_len; lim != 0; lim >>= 1) { part = base + (lim >> 1); cmp = (*compare)(key, *part); if (cmp == 0) { base = part; break; } if (cmp > 0) { /* key > p; take right partition */ base = part + 1; lim--; } /* else take left partition */ } if (position) *position = (base - array); return (cmp == 0) ? 0 : GIT_ENOTFOUND; } int git__bsearch_r( void **array, size_t array_len, const void *key, int (*compare_r)(const void *, const void *, void *), void *payload, size_t *position) { size_t lim; int cmp = -1; void **part, **base = array; for (lim = array_len; lim != 0; lim >>= 1) { part = base + (lim >> 1); cmp = (*compare_r)(key, *part, payload); if (cmp == 0) { base = part; break; } if (cmp > 0) { /* key > p; take right partition */ base = part + 1; lim--; } /* else take left partition */ } if (position) *position = (base - array); return (cmp == 0) ? 0 : GIT_ENOTFOUND; } /** * A strcmp wrapper * * We don't want direct pointers to the CRT on Windows, we may * get stdcall conflicts. */ int git__strcmp_cb(const void *a, const void *b) { return strcmp((const char *)a, (const char *)b); } int git__strcasecmp_cb(const void *a, const void *b) { return strcasecmp((const char *)a, (const char *)b); } int git__parse_bool(int *out, const char *value) { /* A missing value means true */ if (value == NULL || !strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) { *out = 1; return 0; } if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off") || value[0] == '\0') { *out = 0; return 0; } return -1; } size_t git__unescape(char *str) { char *scan, *pos = str; if (!str) return 0; for (scan = str; *scan; pos++, scan++) { if (*scan == '\\' && *(scan + 1) != '\0') scan++; /* skip '\' but include next char */ if (pos != scan) *pos = *scan; } if (pos != scan) { *pos = '\0'; } return (pos - str); } #if defined(HAVE_QSORT_S) || defined(HAVE_QSORT_R_BSD) typedef struct { git__sort_r_cmp cmp; void *payload; } git__qsort_r_glue; static int GIT_LIBGIT2_CALL git__qsort_r_glue_cmp( void *payload, const void *a, const void *b) { git__qsort_r_glue *glue = payload; return glue->cmp(a, b, glue->payload); } #endif #if !defined(HAVE_QSORT_R_BSD) && \ !defined(HAVE_QSORT_R_GNU) && \ !defined(HAVE_QSORT_S) static void swap(uint8_t *a, uint8_t *b, size_t elsize) { char tmp[256]; while (elsize) { size_t n = elsize < sizeof(tmp) ? elsize : sizeof(tmp); memcpy(tmp, a + elsize - n, n); memcpy(a + elsize - n, b + elsize - n, n); memcpy(b + elsize - n, tmp, n); elsize -= n; } } static void insertsort( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { uint8_t *base = els; uint8_t *end = base + nel * elsize; uint8_t *i, *j; for (i = base + elsize; i < end; i += elsize) for (j = i; j > base && cmp(j, j - elsize, payload) < 0; j -= elsize) swap(j, j - elsize, elsize); } #endif void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { #if defined(HAVE_QSORT_R_BSD) git__qsort_r_glue glue = { cmp, payload }; qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp); #elif defined(HAVE_QSORT_R_GNU) qsort_r(els, nel, elsize, cmp, payload); #elif defined(HAVE_QSORT_S) git__qsort_r_glue glue = { cmp, payload }; qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue); #else insertsort(els, nel, elsize, cmp, payload); #endif } #ifdef GIT_WIN32 int git__getenv(git_buf *out, const char *name) { wchar_t *wide_name = NULL, *wide_value = NULL; DWORD value_len; int error = -1; git_buf_clear(out); if (git__utf8_to_16_alloc(&wide_name, name) < 0) return -1; if ((value_len = GetEnvironmentVariableW(wide_name, NULL, 0)) > 0) { wide_value = git__malloc(value_len * sizeof(wchar_t)); GIT_ERROR_CHECK_ALLOC(wide_value); value_len = GetEnvironmentVariableW(wide_name, wide_value, value_len); } if (value_len) error = git_buf_put_w(out, wide_value, value_len); else if (GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_ENVVAR_NOT_FOUND) error = GIT_ENOTFOUND; else git_error_set(GIT_ERROR_OS, "could not read environment variable '%s'", name); git__free(wide_name); git__free(wide_value); return error; } #else int git__getenv(git_buf *out, const char *name) { const char *val = getenv(name); git_buf_clear(out); if (!val) return GIT_ENOTFOUND; return git_buf_puts(out, val); } #endif /* * By doing this in two steps we can at least get * the function to be somewhat coherent, even * with this disgusting nest of #ifdefs. */ #ifndef _SC_NPROCESSORS_ONLN # ifdef _SC_NPROC_ONLN # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN # elif defined _SC_CRAY_NCPU # define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU # endif #endif int git__online_cpus(void) { #ifdef _SC_NPROCESSORS_ONLN long ncpus; #endif #ifdef _WIN32 SYSTEM_INFO info; GetSystemInfo(&info); if ((int)info.dwNumberOfProcessors > 0) return (int)info.dwNumberOfProcessors; #elif defined(hpux) || defined(__hpux) || defined(_hpux) struct pst_dynamic psd; if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0)) return (int)psd.psd_proc_cnt; #endif #ifdef _SC_NPROCESSORS_ONLN if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0) return (int)ncpus; #endif return 1; } git2r/src/libgit2/src/futils.h0000644000175000017500000003044114125111754016027 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_futils_h__ #define INCLUDE_futils_h__ #include "common.h" #include "map.h" #include "posix.h" #include "path.h" #include "pool.h" #include "strmap.h" #include "oid.h" /** * Filebuffer methods * * Read whole files into an in-memory buffer for processing */ extern int git_futils_readbuffer(git_buf *obj, const char *path); extern int git_futils_readbuffer_updated( git_buf *obj, const char *path, git_oid *checksum, int *updated); extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len); /* Additional constants for `git_futils_writebuffer`'s `open_flags`. We * support these internally and they will be removed before the `open` call. */ #ifndef O_FSYNC # define O_FSYNC (1 << 31) #endif extern int git_futils_writebuffer( const git_buf *buf, const char *path, int open_flags, mode_t mode); /** * File utils * * These are custom filesystem-related helper methods. They are * rather high level, and wrap the underlying POSIX methods * * All these methods return 0 on success, * or an error code on failure and an error message is set. */ /** * Create and open a file, while also * creating all the folders in its path */ extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode); /** * Create and open a process-locked file */ extern int git_futils_creat_locked(const char *path, const mode_t mode); /** * Create and open a process-locked file, while * also creating all the folders in its path */ extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode); /** * Create a path recursively. */ extern int git_futils_mkdir_r(const char *path, const mode_t mode); /** * Flags to pass to `git_futils_mkdir`. * * * GIT_MKDIR_EXCL is "exclusive" - i.e. generate an error if dir exists. * * GIT_MKDIR_PATH says to make all components in the path. * * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation * * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path * * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path * * GIT_MKDIR_SKIP_LAST2 says to leave off the last 2 elements of the path * * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST * * GIT_MKDIR_REMOVE_FILES says to remove files and recreate dirs * * GIT_MKDIR_REMOVE_SYMLINKS says to remove symlinks and recreate dirs * * Note that the chmod options will be executed even if the directory already * exists, unless GIT_MKDIR_EXCL is given. */ typedef enum { GIT_MKDIR_EXCL = 1, GIT_MKDIR_PATH = 2, GIT_MKDIR_CHMOD = 4, GIT_MKDIR_CHMOD_PATH = 8, GIT_MKDIR_SKIP_LAST = 16, GIT_MKDIR_SKIP_LAST2 = 32, GIT_MKDIR_VERIFY_DIR = 64, GIT_MKDIR_REMOVE_FILES = 128, GIT_MKDIR_REMOVE_SYMLINKS = 256, } git_futils_mkdir_flags; struct git_futils_mkdir_perfdata { size_t stat_calls; size_t mkdir_calls; size_t chmod_calls; }; struct git_futils_mkdir_options { git_strmap *dir_map; git_pool *pool; struct git_futils_mkdir_perfdata perfdata; }; /** * Create a directory or entire path. * * This makes a directory (and the entire path leading up to it if requested), * and optionally chmods the directory immediately after (or each part of the * path if requested). * * @param path The path to create, relative to base. * @param base Root for relative path. These directories will never be made. * @param mode The mode to use for created directories. * @param flags Combination of the mkdir flags above. * @param opts Extended options, or null. * @return 0 on success, else error code */ extern int git_futils_mkdir_relative(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts); /** * Create a directory or entire path. Similar to `git_futils_mkdir_relative` * without performance data. */ extern int git_futils_mkdir(const char *path, mode_t mode, uint32_t flags); /** * Create all the folders required to contain * the full path of a file */ extern int git_futils_mkpath2file(const char *path, const mode_t mode); /** * Flags to pass to `git_futils_rmdir_r`. * * * GIT_RMDIR_EMPTY_HIERARCHY - the default; remove hierarchy of empty * dirs and generate error if any files are found. * * GIT_RMDIR_REMOVE_FILES - attempt to remove files in the hierarchy. * * GIT_RMDIR_SKIP_NONEMPTY - skip non-empty directories with no error. * * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base * if removing this item leaves them empty * * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR * * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself */ typedef enum { GIT_RMDIR_EMPTY_HIERARCHY = 0, GIT_RMDIR_REMOVE_FILES = (1 << 0), GIT_RMDIR_SKIP_NONEMPTY = (1 << 1), GIT_RMDIR_EMPTY_PARENTS = (1 << 2), GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3), GIT_RMDIR_SKIP_ROOT = (1 << 4), } git_futils_rmdir_flags; /** * Remove path and any files and directories beneath it. * * @param path Path to the top level directory to process. * @param base Root for relative path. * @param flags Combination of git_futils_rmdir_flags values * @return 0 on success; -1 on error. */ extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags); /** * Create and open a temporary file with a `_git2_` suffix. * Writes the filename into path_out. * @return On success, an open file descriptor, else an error code < 0. */ extern int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode); /** * Move a file on the filesystem, create the * destination path if it doesn't exist */ extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode); /** * Copy a file * * The filemode will be used for the newly created file. */ extern int git_futils_cp( const char *from, const char *to, mode_t filemode); /** * Set the files atime and mtime to the given time, or the current time * if `ts` is NULL. */ extern int git_futils_touch(const char *path, time_t *when); /** * Flags that can be passed to `git_futils_cp_r`. * * - GIT_CPDIR_CREATE_EMPTY_DIRS: create directories even if there are no * files under them (otherwise directories will only be created lazily * when a file inside them is copied). * - GIT_CPDIR_COPY_SYMLINKS: copy symlinks, otherwise they are ignored. * - GIT_CPDIR_COPY_DOTFILES: copy files with leading '.', otherwise ignored. * - GIT_CPDIR_OVERWRITE: overwrite pre-existing files with source content, * otherwise they are silently skipped. * - GIT_CPDIR_CHMOD_DIRS: explicitly chmod directories to `dirmode` * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the * source file to the target; with this flag, always use 0666 (or 0777 if * source has exec bits set) for target. * - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files */ typedef enum { GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0), GIT_CPDIR_COPY_SYMLINKS = (1u << 1), GIT_CPDIR_COPY_DOTFILES = (1u << 2), GIT_CPDIR_OVERWRITE = (1u << 3), GIT_CPDIR_CHMOD_DIRS = (1u << 4), GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5), GIT_CPDIR_LINK_FILES = (1u << 6), } git_futils_cpdir_flags; /** * Copy a directory tree. * * This copies directories and files from one root to another. You can * pass a combinationof GIT_CPDIR flags as defined above. * * If you pass the CHMOD flag, then the dirmode will be applied to all * directories that are created during the copy, overiding the natural * permissions. If you do not pass the CHMOD flag, then the dirmode * will actually be copied from the source files and the `dirmode` arg * will be ignored. */ extern int git_futils_cp_r( const char *from, const char *to, uint32_t flags, mode_t dirmode); /** * Open a file readonly and set error if needed. */ extern int git_futils_open_ro(const char *path); /** * Truncate a file, creating it if it doesn't exist. */ extern int git_futils_truncate(const char *path, int mode); /** * Get the filesize in bytes of a file */ extern int git_futils_filesize(uint64_t *out, git_file fd); #define GIT_PERMS_IS_EXEC(MODE) (((MODE) & 0100) != 0) #define GIT_PERMS_CANONICAL(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0755 : 0644) #define GIT_PERMS_FOR_WRITE(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0777 : 0666) #define GIT_MODE_PERMS_MASK 0777 #define GIT_MODE_TYPE_MASK 0170000 #define GIT_MODE_TYPE(MODE) ((MODE) & GIT_MODE_TYPE_MASK) #define GIT_MODE_ISBLOB(MODE) (GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) /** * Convert a mode_t from the OS to a legal git mode_t value. */ extern mode_t git_futils_canonical_mode(mode_t raw_mode); /** * Read-only map all or part of a file into memory. * When possible this function should favor a virtual memory * style mapping over some form of malloc()+read(), as the * data access will be random and is not likely to touch the * majority of the region requested. * * @param out buffer to populate with the mapping information. * @param fd open descriptor to configure the mapping from. * @param begin first byte to map, this should be page aligned. * @param len number of bytes to map. * @return * - 0 on success; * - -1 on error. */ extern int git_futils_mmap_ro( git_map *out, git_file fd, off64_t begin, size_t len); /** * Read-only map an entire file. * * @param out buffer to populate with the mapping information. * @param path path to file to be opened. * @return * - 0 on success; * - GIT_ENOTFOUND if not found; * - -1 on an unspecified OS related error. */ extern int git_futils_mmap_ro_file( git_map *out, const char *path); /** * Release the memory associated with a previous memory mapping. * @param map the mapping description previously configured. */ extern void git_futils_mmap_free(git_map *map); /** * Create a "fake" symlink (text file containing the target path). * * @param target original symlink target * @param path symlink file to be created * @return 0 on success, -1 on error */ extern int git_futils_fake_symlink(const char *target, const char *path); /** * A file stamp represents a snapshot of information about a file that can * be used to test if the file changes. This portable implementation is * based on stat data about that file, but it is possible that OS specific * versions could be implemented in the future. */ typedef struct { struct timespec mtime; uint64_t size; unsigned int ino; } git_futils_filestamp; /** * Compare stat information for file with reference info. * * This function updates the file stamp to current data for the given path * and returns 0 if the file is up-to-date relative to the prior setting, * 1 if the file has been changed, or GIT_ENOTFOUND if the file doesn't * exist. This will not call git_error_set, so you must set the error if you * plan to return an error. * * @param stamp File stamp to be checked * @param path Path to stat and check if changed * @return 0 if up-to-date, 1 if out-of-date, GIT_ENOTFOUND if cannot stat */ extern int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path); /** * Set or reset file stamp data * * This writes the target file stamp. If the source is NULL, this will set * the target stamp to values that will definitely be out of date. If the * source is not NULL, this copies the source values to the target. * * @param tgt File stamp to write to * @param src File stamp to copy from or NULL to clear the target */ extern void git_futils_filestamp_set( git_futils_filestamp *tgt, const git_futils_filestamp *src); /** * Set file stamp data from stat structure */ extern void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st); /** * `fsync` the parent directory of the given path, if `fsync` is * supported for directories on this platform. * * @param path Path of the directory to sync. * @return 0 on success, -1 on error */ extern int git_futils_fsync_dir(const char *path); /** * `fsync` the parent directory of the given path, if `fsync` is * supported for directories on this platform. * * @param path Path of the file whose parent directory should be synced. * @return 0 on success, -1 on error */ extern int git_futils_fsync_parent(const char *path); #endif git2r/src/libgit2/src/patch_parse.h0000644000175000017500000000232214125111754017007 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_patch_parse_h__ #define INCLUDE_patch_parse_h__ #include "common.h" #include "parse.h" #include "patch.h" typedef struct { git_refcount rc; git_patch_options opts; git_parse_ctx parse_ctx; } git_patch_parse_ctx; extern git_patch_parse_ctx *git_patch_parse_ctx_init( const char *content, size_t content_len, const git_patch_options *opts); extern void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx); /** * Create a patch for a single file from the contents of a patch buffer. * * @param out The patch to be created * @param contents The contents of a patch file * @param contents_len The length of the patch file * @param opts The git_patch_options * @return 0 on success, <0 on failure. */ extern int git_patch_from_buffer( git_patch **out, const char *contents, size_t contents_len, const git_patch_options *opts); extern int git_patch_parse( git_patch **out, git_patch_parse_ctx *ctx); extern int git_patch_parsed_from_diff(git_patch **, git_diff *, size_t); #endif git2r/src/libgit2/src/threadstate.h0000644000175000017500000000111414125111754017024 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_threadstate_h__ #define INCLUDE_threadstate_h__ #include "common.h" typedef struct { git_error *last_error; git_error error_t; git_buf error_buf; char oid_fmt[GIT_OID_HEXSZ+1]; } git_threadstate; extern int git_threadstate_global_init(void); extern git_threadstate *git_threadstate_get(void); #define GIT_THREADSTATE (git_threadstate_get()) #endif git2r/src/libgit2/src/refs.c0000644000175000017500000007555014125111754015465 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "refs.h" #include "hash.h" #include "repository.h" #include "futils.h" #include "filebuf.h" #include "pack.h" #include "reflog.h" #include "refdb.h" #include #include #include #include #include #include #include #include #include bool git_reference__enable_symbolic_ref_target_validation = true; enum { GIT_PACKREF_HAS_PEEL = 1, GIT_PACKREF_WAS_LOOSE = 2 }; static git_reference *alloc_ref(const char *name) { git_reference *ref = NULL; size_t namelen = strlen(name), reflen; if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) && !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) && (ref = git__calloc(1, reflen)) != NULL) memcpy(ref->name, name, namelen + 1); return ref; } git_reference *git_reference__alloc_symbolic( const char *name, const char *target) { git_reference *ref; GIT_ASSERT_ARG_WITH_RETVAL(name, NULL); GIT_ASSERT_ARG_WITH_RETVAL(target, NULL); ref = alloc_ref(name); if (!ref) return NULL; ref->type = GIT_REFERENCE_SYMBOLIC; if ((ref->target.symbolic = git__strdup(target)) == NULL) { git__free(ref); return NULL; } return ref; } git_reference *git_reference__alloc( const char *name, const git_oid *oid, const git_oid *peel) { git_reference *ref; GIT_ASSERT_ARG_WITH_RETVAL(name, NULL); GIT_ASSERT_ARG_WITH_RETVAL(oid, NULL); ref = alloc_ref(name); if (!ref) return NULL; ref->type = GIT_REFERENCE_DIRECT; git_oid_cpy(&ref->target.oid, oid); if (peel != NULL) git_oid_cpy(&ref->peel, peel); return ref; } git_reference *git_reference__realloc( git_reference **ptr_to_ref, const char *name) { size_t namelen, reflen; git_reference *rewrite = NULL; GIT_ASSERT_ARG_WITH_RETVAL(ptr_to_ref, NULL); GIT_ASSERT_ARG_WITH_RETVAL(name, NULL); namelen = strlen(name); if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) && !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) && (rewrite = git__realloc(*ptr_to_ref, reflen)) != NULL) memcpy(rewrite->name, name, namelen + 1); *ptr_to_ref = NULL; return rewrite; } int git_reference_dup(git_reference **dest, git_reference *source) { if (source->type == GIT_REFERENCE_SYMBOLIC) *dest = git_reference__alloc_symbolic(source->name, source->target.symbolic); else *dest = git_reference__alloc(source->name, &source->target.oid, &source->peel); GIT_ERROR_CHECK_ALLOC(*dest); (*dest)->db = source->db; GIT_REFCOUNT_INC((*dest)->db); return 0; } void git_reference_free(git_reference *reference) { if (reference == NULL) return; if (reference->type == GIT_REFERENCE_SYMBOLIC) git__free(reference->target.symbolic); if (reference->db) GIT_REFCOUNT_DEC(reference->db, git_refdb__free); git__free(reference); } int git_reference_delete(git_reference *ref) { const git_oid *old_id = NULL; const char *old_target = NULL; if (!strcmp(ref->name, "HEAD")) { git_error_set(GIT_ERROR_REFERENCE, "cannot delete HEAD"); return GIT_ERROR; } if (ref->type == GIT_REFERENCE_DIRECT) old_id = &ref->target.oid; else old_target = ref->target.symbolic; return git_refdb_delete(ref->db, ref->name, old_id, old_target); } int git_reference_remove(git_repository *repo, const char *name) { git_refdb *db; int error; if ((error = git_repository_refdb__weakptr(&db, repo)) < 0) return error; return git_refdb_delete(db, name, NULL, NULL); } int git_reference_lookup(git_reference **ref_out, git_repository *repo, const char *name) { return git_reference_lookup_resolved(ref_out, repo, name, 0); } int git_reference_name_to_id( git_oid *out, git_repository *repo, const char *name) { int error; git_reference *ref; if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0) return error; git_oid_cpy(out, git_reference_target(ref)); git_reference_free(ref); return 0; } static int reference_normalize_for_repo( git_refname_t out, git_repository *repo, const char *name, bool validate) { int precompose; unsigned int flags = GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL; if (!git_repository__configmap_lookup(&precompose, repo, GIT_CONFIGMAP_PRECOMPOSE) && precompose) flags |= GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE; if (!validate) flags |= GIT_REFERENCE_FORMAT__VALIDATION_DISABLE; return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags); } int git_reference_lookup_resolved( git_reference **ref_out, git_repository *repo, const char *name, int max_nesting) { git_refname_t normalized; git_refdb *refdb; int error = 0; GIT_ASSERT_ARG(ref_out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if ((error = reference_normalize_for_repo(normalized, repo, name, true)) < 0 || (error = git_repository_refdb__weakptr(&refdb, repo)) < 0 || (error = git_refdb_resolve(ref_out, refdb, normalized, max_nesting)) < 0) return error; /* * The resolved reference may be a symbolic reference in case its * target doesn't exist. If the user asked us to resolve (e.g. * `max_nesting != 0`), then we need to return an error in case we got * a symbolic reference back. */ if (max_nesting && git_reference_type(*ref_out) == GIT_REFERENCE_SYMBOLIC) { git_reference_free(*ref_out); *ref_out = NULL; return GIT_ENOTFOUND; } return 0; } int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname) { int error = 0, i, valid; bool fallbackmode = true, foundvalid = false; git_reference *ref; git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT; static const char *formatters[] = { "%s", GIT_REFS_DIR "%s", GIT_REFS_TAGS_DIR "%s", GIT_REFS_HEADS_DIR "%s", GIT_REFS_REMOTES_DIR "%s", GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE, NULL }; if (*refname) git_buf_puts(&name, refname); else { git_buf_puts(&name, GIT_HEAD_FILE); fallbackmode = false; } for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) { git_buf_clear(&refnamebuf); if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0 || (error = git_reference_name_is_valid(&valid, git_buf_cstr(&refnamebuf))) < 0) goto cleanup; if (!valid) { error = GIT_EINVALIDSPEC; continue; } foundvalid = true; error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1); if (!error) { *out = ref; error = 0; goto cleanup; } if (error != GIT_ENOTFOUND) goto cleanup; } cleanup: if (error && !foundvalid) { /* never found a valid reference name */ git_error_set(GIT_ERROR_REFERENCE, "could not use '%s' as valid reference name", git_buf_cstr(&name)); } if (error == GIT_ENOTFOUND) git_error_set(GIT_ERROR_REFERENCE, "no reference found for shorthand '%s'", refname); git_buf_dispose(&name); git_buf_dispose(&refnamebuf); return error; } /** * Getters */ git_reference_t git_reference_type(const git_reference *ref) { GIT_ASSERT_ARG(ref); return ref->type; } const char *git_reference_name(const git_reference *ref) { GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); return ref->name; } git_repository *git_reference_owner(const git_reference *ref) { GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); return ref->db->repo; } const git_oid *git_reference_target(const git_reference *ref) { GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); if (ref->type != GIT_REFERENCE_DIRECT) return NULL; return &ref->target.oid; } const git_oid *git_reference_target_peel(const git_reference *ref) { GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); if (ref->type != GIT_REFERENCE_DIRECT || git_oid_is_zero(&ref->peel)) return NULL; return &ref->peel; } const char *git_reference_symbolic_target(const git_reference *ref) { GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); if (ref->type != GIT_REFERENCE_SYMBOLIC) return NULL; return ref->target.symbolic; } static int reference__create( git_reference **ref_out, git_repository *repo, const char *name, const git_oid *oid, const char *symbolic, int force, const git_signature *signature, const char *log_message, const git_oid *old_id, const char *old_target) { git_refname_t normalized; git_refdb *refdb; git_reference *ref = NULL; int error = 0; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(symbolic || signature); if (ref_out) *ref_out = NULL; error = reference_normalize_for_repo(normalized, repo, name, true); if (error < 0) return error; error = git_repository_refdb__weakptr(&refdb, repo); if (error < 0) return error; if (oid != NULL) { GIT_ASSERT(symbolic == NULL); if (!git_object__is_valid(repo, oid, GIT_OBJECT_ANY)) { git_error_set(GIT_ERROR_REFERENCE, "target OID for the reference doesn't exist on the repository"); return -1; } ref = git_reference__alloc(normalized, oid, NULL); } else { git_refname_t normalized_target; error = reference_normalize_for_repo(normalized_target, repo, symbolic, git_reference__enable_symbolic_ref_target_validation); if (error < 0) return error; ref = git_reference__alloc_symbolic(normalized, normalized_target); } GIT_ERROR_CHECK_ALLOC(ref); if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) { git_reference_free(ref); return error; } if (ref_out == NULL) git_reference_free(ref); else *ref_out = ref; return 0; } static int refs_configured_ident(git_signature **out, const git_repository *repo) { if (repo->ident_name && repo->ident_email) return git_signature_now(out, repo->ident_name, repo->ident_email); /* if not configured let us fall-through to the next method */ return -1; } int git_reference__log_signature(git_signature **out, git_repository *repo) { int error; git_signature *who; if(((error = refs_configured_ident(&who, repo)) < 0) && ((error = git_signature_default(&who, repo)) < 0) && ((error = git_signature_now(&who, "unknown", "unknown")) < 0)) return error; *out = who; return 0; } int git_reference_create_matching( git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force, const git_oid *old_id, const char *log_message) { int error; git_signature *who = NULL; GIT_ASSERT_ARG(id); if ((error = git_reference__log_signature(&who, repo)) < 0) return error; error = reference__create( ref_out, repo, name, id, NULL, force, who, log_message, old_id, NULL); git_signature_free(who); return error; } int git_reference_create( git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force, const char *log_message) { return git_reference_create_matching(ref_out, repo, name, id, force, NULL, log_message); } int git_reference_symbolic_create_matching( git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force, const char *old_target, const char *log_message) { int error; git_signature *who = NULL; GIT_ASSERT_ARG(target); if ((error = git_reference__log_signature(&who, repo)) < 0) return error; error = reference__create( ref_out, repo, name, NULL, target, force, who, log_message, NULL, old_target); git_signature_free(who); return error; } int git_reference_symbolic_create( git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force, const char *log_message) { return git_reference_symbolic_create_matching(ref_out, repo, name, target, force, NULL, log_message); } static int ensure_is_an_updatable_direct_reference(git_reference *ref) { if (ref->type == GIT_REFERENCE_DIRECT) return 0; git_error_set(GIT_ERROR_REFERENCE, "cannot set OID on symbolic reference"); return -1; } int git_reference_set_target( git_reference **out, git_reference *ref, const git_oid *id, const char *log_message) { int error; git_repository *repo; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(ref); GIT_ASSERT_ARG(id); repo = ref->db->repo; if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0) return error; return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, log_message); } static int ensure_is_an_updatable_symbolic_reference(git_reference *ref) { if (ref->type == GIT_REFERENCE_SYMBOLIC) return 0; git_error_set(GIT_ERROR_REFERENCE, "cannot set symbolic target on a direct reference"); return -1; } int git_reference_symbolic_set_target( git_reference **out, git_reference *ref, const char *target, const char *log_message) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(ref); GIT_ASSERT_ARG(target); if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0) return error; return git_reference_symbolic_create_matching( out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message); } typedef struct { const char *old_name; git_refname_t new_name; } refs_update_head_payload; static int refs_update_head(git_repository *worktree, void *_payload) { refs_update_head_payload *payload = (refs_update_head_payload *)_payload; git_reference *head = NULL, *updated = NULL; int error; if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0) goto out; if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC || git__strcmp(git_reference_symbolic_target(head), payload->old_name) != 0) goto out; /* Update HEAD if it was pointing to the reference being renamed */ if ((error = git_reference_symbolic_set_target(&updated, head, payload->new_name, NULL)) < 0) { git_error_set(GIT_ERROR_REFERENCE, "failed to update HEAD after renaming reference"); goto out; } out: git_reference_free(updated); git_reference_free(head); return error; } int git_reference_rename( git_reference **out, git_reference *ref, const char *new_name, int force, const char *log_message) { refs_update_head_payload payload; git_signature *signature = NULL; git_repository *repo; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(ref); repo = git_reference_owner(ref); if ((error = git_reference__log_signature(&signature, repo)) < 0 || (error = reference_normalize_for_repo(payload.new_name, repo, new_name, true)) < 0 || (error = git_refdb_rename(out, ref->db, ref->name, payload.new_name, force, signature, log_message)) < 0) goto out; payload.old_name = ref->name; /* We may have to update any HEAD that was pointing to the renamed reference. */ if ((error = git_repository_foreach_worktree(repo, refs_update_head, &payload)) < 0) goto out; out: git_signature_free(signature); return error; } int git_reference_resolve(git_reference **ref_out, const git_reference *ref) { switch (git_reference_type(ref)) { case GIT_REFERENCE_DIRECT: return git_reference_lookup(ref_out, ref->db->repo, ref->name); case GIT_REFERENCE_SYMBOLIC: return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1); default: git_error_set(GIT_ERROR_REFERENCE, "invalid reference"); return -1; } } int git_reference_foreach( git_repository *repo, git_reference_foreach_cb callback, void *payload) { git_reference_iterator *iter; git_reference *ref; int error; if ((error = git_reference_iterator_new(&iter, repo)) < 0) return error; while (!(error = git_reference_next(&ref, iter))) { if ((error = callback(ref, payload)) != 0) { git_error_set_after_callback(error); break; } } if (error == GIT_ITEROVER) error = 0; git_reference_iterator_free(iter); return error; } int git_reference_foreach_name( git_repository *repo, git_reference_foreach_name_cb callback, void *payload) { git_reference_iterator *iter; const char *refname; int error; if ((error = git_reference_iterator_new(&iter, repo)) < 0) return error; while (!(error = git_reference_next_name(&refname, iter))) { if ((error = callback(refname, payload)) != 0) { git_error_set_after_callback(error); break; } } if (error == GIT_ITEROVER) error = 0; git_reference_iterator_free(iter); return error; } int git_reference_foreach_glob( git_repository *repo, const char *glob, git_reference_foreach_name_cb callback, void *payload) { git_reference_iterator *iter; const char *refname; int error; if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) return error; while (!(error = git_reference_next_name(&refname, iter))) { if ((error = callback(refname, payload)) != 0) { git_error_set_after_callback(error); break; } } if (error == GIT_ITEROVER) error = 0; git_reference_iterator_free(iter); return error; } int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo) { git_refdb *refdb; if (git_repository_refdb__weakptr(&refdb, repo) < 0) return -1; return git_refdb_iterator(out, refdb, NULL); } int git_reference_iterator_glob_new( git_reference_iterator **out, git_repository *repo, const char *glob) { git_refdb *refdb; if (git_repository_refdb__weakptr(&refdb, repo) < 0) return -1; return git_refdb_iterator(out, refdb, glob); } int git_reference_next(git_reference **out, git_reference_iterator *iter) { return git_refdb_iterator_next(out, iter); } int git_reference_next_name(const char **out, git_reference_iterator *iter) { return git_refdb_iterator_next_name(out, iter); } void git_reference_iterator_free(git_reference_iterator *iter) { if (iter == NULL) return; git_refdb_iterator_free(iter); } static int cb__reflist_add(const char *ref, void *data) { char *name = git__strdup(ref); GIT_ERROR_CHECK_ALLOC(name); return git_vector_insert((git_vector *)data, name); } int git_reference_list( git_strarray *array, git_repository *repo) { git_vector ref_list; GIT_ASSERT_ARG(array); GIT_ASSERT_ARG(repo); array->strings = NULL; array->count = 0; if (git_vector_init(&ref_list, 8, NULL) < 0) return -1; if (git_reference_foreach_name( repo, &cb__reflist_add, (void *)&ref_list) < 0) { git_vector_free(&ref_list); return -1; } array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list); return 0; } static int is_valid_ref_char(char ch) { if ((unsigned) ch <= ' ') return 0; switch (ch) { case '~': case '^': case ':': case '\\': case '?': case '[': return 0; default: return 1; } } static int ensure_segment_validity(const char *name, char may_contain_glob) { const char *current = name; char prev = '\0'; const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION); int segment_len; if (*current == '.') return -1; /* Refname starts with "." */ for (current = name; ; current++) { if (*current == '\0' || *current == '/') break; if (!is_valid_ref_char(*current)) return -1; /* Illegal character in refname */ if (prev == '.' && *current == '.') return -1; /* Refname contains ".." */ if (prev == '@' && *current == '{') return -1; /* Refname contains "@{" */ if (*current == '*') { if (!may_contain_glob) return -1; may_contain_glob = 0; } prev = *current; } segment_len = (int)(current - name); /* A refname component can not end with ".lock" */ if (segment_len >= lock_len && !memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len)) return -1; return segment_len; } static bool is_all_caps_and_underscore(const char *name, size_t len) { size_t i; char c; GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(len > 0); for (i = 0; i < len; i++) { c = name[i]; if ((c < 'A' || c > 'Z') && c != '_') return false; } if (*name == '_' || name[len - 1] == '_') return false; return true; } /* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */ int git_reference__normalize_name( git_buf *buf, const char *name, unsigned int flags) { const char *current; int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; unsigned int process_flags; bool normalize = (buf != NULL); bool validate = (flags & GIT_REFERENCE_FORMAT__VALIDATION_DISABLE) == 0; #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; #endif GIT_ASSERT_ARG(name); process_flags = flags; current = (char *)name; if (validate && *current == '/') goto cleanup; if (normalize) git_buf_clear(buf); #ifdef GIT_USE_ICONV if ((flags & GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE) != 0) { size_t namelen = strlen(current); if ((error = git_path_iconv_init_precompose(&ic)) < 0 || (error = git_path_iconv(&ic, ¤t, &namelen)) < 0) goto cleanup; error = GIT_EINVALIDSPEC; } #endif if (!validate) { git_buf_sets(buf, current); error = git_buf_oom(buf) ? -1 : 0; goto cleanup; } while (true) { char may_contain_glob = process_flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN; segment_len = ensure_segment_validity(current, may_contain_glob); if (segment_len < 0) goto cleanup; if (segment_len > 0) { /* * There may only be one glob in a pattern, thus we reset * the pattern-flag in case the current segment has one. */ if (memchr(current, '*', segment_len)) process_flags &= ~GIT_REFERENCE_FORMAT_REFSPEC_PATTERN; if (normalize) { size_t cur_len = git_buf_len(buf); git_buf_joinpath(buf, git_buf_cstr(buf), current); git_buf_truncate(buf, cur_len + segment_len + (segments_count ? 1 : 0)); if (git_buf_oom(buf)) { error = -1; goto cleanup; } } segments_count++; } /* No empty segment is allowed when not normalizing */ if (segment_len == 0 && !normalize) goto cleanup; if (current[segment_len] == '\0') break; current += segment_len + 1; } /* A refname can not be empty */ if (segment_len == 0 && segments_count == 0) goto cleanup; /* A refname can not end with "." */ if (current[segment_len - 1] == '.') goto cleanup; /* A refname can not end with "/" */ if (current[segment_len - 1] == '/') goto cleanup; if ((segments_count == 1 ) && !(flags & GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL)) goto cleanup; if ((segments_count == 1 ) && !(flags & GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND) && !(is_all_caps_and_underscore(name, (size_t)segment_len) || ((flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name)))) goto cleanup; if ((segments_count > 1) && (is_all_caps_and_underscore(name, strchr(name, '/') - name))) goto cleanup; error = 0; cleanup: if (error == GIT_EINVALIDSPEC) git_error_set( GIT_ERROR_REFERENCE, "the given reference name '%s' is not valid", name); if (error && normalize) git_buf_dispose(buf); #ifdef GIT_USE_ICONV git_path_iconv_clear(&ic); #endif return error; } int git_reference_normalize_name( char *buffer_out, size_t buffer_size, const char *name, unsigned int flags) { git_buf buf = GIT_BUF_INIT; int error; if ((error = git_reference__normalize_name(&buf, name, flags)) < 0) goto cleanup; if (git_buf_len(&buf) > buffer_size - 1) { git_error_set( GIT_ERROR_REFERENCE, "the provided buffer is too short to hold the normalization of '%s'", name); error = GIT_EBUFS; goto cleanup; } if ((error = git_buf_copy_cstr(buffer_out, buffer_size, &buf)) < 0) goto cleanup; error = 0; cleanup: git_buf_dispose(&buf); return error; } #define GIT_REFERENCE_TYPEMASK (GIT_REFERENCE_DIRECT | GIT_REFERENCE_SYMBOLIC) int git_reference_cmp( const git_reference *ref1, const git_reference *ref2) { git_reference_t type1, type2; GIT_ASSERT_ARG(ref1); GIT_ASSERT_ARG(ref2); type1 = git_reference_type(ref1); type2 = git_reference_type(ref2); /* let's put symbolic refs before OIDs */ if (type1 != type2) return (type1 == GIT_REFERENCE_SYMBOLIC) ? -1 : 1; if (type1 == GIT_REFERENCE_SYMBOLIC) return strcmp(ref1->target.symbolic, ref2->target.symbolic); return git_oid__cmp(&ref1->target.oid, &ref2->target.oid); } /* * Starting with the reference given by `ref_name`, follows symbolic * references until a direct reference is found and updated the OID * on that direct reference to `oid`. */ int git_reference__update_terminal( git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *sig, const char *log_message) { git_reference *ref = NULL, *ref2 = NULL; git_signature *who = NULL; git_refdb *refdb = NULL; const git_signature *to_use; int error = 0; if (!sig && (error = git_reference__log_signature(&who, repo)) < 0) goto out; to_use = sig ? sig : who; if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) goto out; if ((error = git_refdb_resolve(&ref, refdb, ref_name, -1)) < 0) { if (error == GIT_ENOTFOUND) { git_error_clear(); error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use, log_message, NULL, NULL); } goto out; } /* In case the resolved reference is symbolic, then it's a dangling symref. */ if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { error = reference__create(&ref2, repo, ref->target.symbolic, oid, NULL, 0, to_use, log_message, NULL, NULL); } else { error = reference__create(&ref2, repo, ref->name, oid, NULL, 1, to_use, log_message, &ref->target.oid, NULL); } out: git_reference_free(ref2); git_reference_free(ref); git_signature_free(who); return error; } static const char *commit_type(const git_commit *commit) { unsigned int count = git_commit_parentcount(commit); if (count >= 2) return " (merge)"; else if (count == 0) return " (initial)"; else return ""; } int git_reference__update_for_commit( git_repository *repo, git_reference *ref, const char *ref_name, const git_oid *id, const char *operation) { git_reference *ref_new = NULL; git_commit *commit = NULL; git_buf reflog_msg = GIT_BUF_INIT; const git_signature *who; int error; if ((error = git_commit_lookup(&commit, repo, id)) < 0 || (error = git_buf_printf(&reflog_msg, "%s%s: %s", operation ? operation : "commit", commit_type(commit), git_commit_summary(commit))) < 0) goto done; who = git_commit_committer(commit); if (ref) { if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0) return error; error = reference__create(&ref_new, repo, ref->name, id, NULL, 1, who, git_buf_cstr(&reflog_msg), &ref->target.oid, NULL); } else error = git_reference__update_terminal( repo, ref_name, id, who, git_buf_cstr(&reflog_msg)); done: git_reference_free(ref_new); git_buf_dispose(&reflog_msg); git_commit_free(commit); return error; } int git_reference_has_log(git_repository *repo, const char *refname) { int error; git_refdb *refdb; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(refname); if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) return error; return git_refdb_has_log(refdb, refname); } int git_reference_ensure_log(git_repository *repo, const char *refname) { int error; git_refdb *refdb; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(refname); if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) return error; return git_refdb_ensure_log(refdb, refname); } int git_reference__is_branch(const char *ref_name) { return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0; } int git_reference_is_branch(const git_reference *ref) { GIT_ASSERT_ARG(ref); return git_reference__is_branch(ref->name); } int git_reference__is_remote(const char *ref_name) { return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0; } int git_reference_is_remote(const git_reference *ref) { GIT_ASSERT_ARG(ref); return git_reference__is_remote(ref->name); } int git_reference__is_tag(const char *ref_name) { return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0; } int git_reference_is_tag(const git_reference *ref) { GIT_ASSERT_ARG(ref); return git_reference__is_tag(ref->name); } int git_reference__is_note(const char *ref_name) { return git__prefixcmp(ref_name, GIT_REFS_NOTES_DIR) == 0; } int git_reference_is_note(const git_reference *ref) { GIT_ASSERT_ARG(ref); return git_reference__is_note(ref->name); } static int peel_error(int error, const git_reference *ref, const char *msg) { git_error_set( GIT_ERROR_INVALID, "the reference '%s' cannot be peeled - %s", git_reference_name(ref), msg); return error; } int git_reference_peel( git_object **peeled, const git_reference *ref, git_object_t target_type) { const git_reference *resolved = NULL; git_reference *allocated = NULL; git_object *target = NULL; int error; GIT_ASSERT_ARG(ref); if (ref->type == GIT_REFERENCE_DIRECT) { resolved = ref; } else { if ((error = git_reference_resolve(&allocated, ref)) < 0) return peel_error(error, ref, "Cannot resolve reference"); resolved = allocated; } /* * If we try to peel an object to a tag, we cannot use * the fully peeled object, as that will always resolve * to a commit. So we only want to use the peeled value * if it is not zero and the target is not a tag. */ if (target_type != GIT_OBJECT_TAG && !git_oid_is_zero(&resolved->peel)) { error = git_object_lookup(&target, git_reference_owner(ref), &resolved->peel, GIT_OBJECT_ANY); } else { error = git_object_lookup(&target, git_reference_owner(ref), &resolved->target.oid, GIT_OBJECT_ANY); } if (error < 0) { peel_error(error, ref, "Cannot retrieve reference target"); goto cleanup; } if (target_type == GIT_OBJECT_ANY && git_object_type(target) != GIT_OBJECT_TAG) error = git_object_dup(peeled, target); else error = git_object_peel(peeled, target, target_type); cleanup: git_object_free(target); git_reference_free(allocated); return error; } int git_reference__name_is_valid( int *valid, const char *refname, unsigned int flags) { int error; GIT_ASSERT(valid && refname); *valid = 0; error = git_reference__normalize_name(NULL, refname, flags); if (!error) *valid = 1; else if (error == GIT_EINVALIDSPEC) error = 0; return error; } int git_reference_name_is_valid(int *valid, const char *refname) { return git_reference__name_is_valid(valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL); } const char *git_reference__shorthand(const char *name) { if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR)) return name + strlen(GIT_REFS_HEADS_DIR); else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR)) return name + strlen(GIT_REFS_TAGS_DIR); else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR)) return name + strlen(GIT_REFS_REMOTES_DIR); else if (!git__prefixcmp(name, GIT_REFS_DIR)) return name + strlen(GIT_REFS_DIR); /* No shorthands are available, so just return the name. */ return name; } const char *git_reference_shorthand(const git_reference *ref) { return git_reference__shorthand(ref->name); } int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo) { int error; git_reference *tmp_ref; GIT_ASSERT_ARG(unborn); GIT_ASSERT_ARG(ref); GIT_ASSERT_ARG(repo); if (ref->type == GIT_REFERENCE_DIRECT) { *unborn = 0; return 0; } error = git_reference_lookup_resolved(&tmp_ref, repo, ref->name, -1); git_reference_free(tmp_ref); if (error != 0 && error != GIT_ENOTFOUND) return error; else if (error == GIT_ENOTFOUND && git__strcmp(ref->name, GIT_HEAD_FILE) == 0) *unborn = true; else *unborn = false; return 0; } /* Deprecated functions */ #ifndef GIT_DEPRECATE_HARD int git_reference_is_valid_name(const char *refname) { int valid = 0; git_reference__name_is_valid(&valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL); return valid; } #endif git2r/src/libgit2/src/net.h0000644000175000017500000000404314125111754015306 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_net_h__ #define INCLUDE_net_h__ #include "common.h" typedef struct git_net_url { char *scheme; char *host; char *port; char *path; char *query; char *username; char *password; } git_net_url; #define GIT_NET_URL_INIT { NULL } /** Duplicate a URL */ extern int git_net_url_dup(git_net_url *out, git_net_url *in); /** Parses a string containing a URL into a structure. */ extern int git_net_url_parse(git_net_url *url, const char *str); /** Appends a path and/or query string to the given URL */ extern int git_net_url_joinpath( git_net_url *out, git_net_url *in, const char *path); /** Ensures that a URL is minimally valid (contains a host, port and path) */ extern bool git_net_url_valid(git_net_url *url); /** Returns true if the URL is on the default port. */ extern bool git_net_url_is_default_port(git_net_url *url); /** Returns true if the host portion of the URL is an ipv6 address. */ extern bool git_net_url_is_ipv6(git_net_url *url); /* Applies a redirect to the URL with a git-aware service suffix. */ extern int git_net_url_apply_redirect( git_net_url *url, const char *redirect_location, const char *service_suffix); /** Swaps the contents of one URL for another. */ extern void git_net_url_swap(git_net_url *a, git_net_url *b); /** Places the URL into the given buffer. */ extern int git_net_url_fmt(git_buf *out, git_net_url *url); /** Place the path and query string into the given buffer. */ extern int git_net_url_fmt_path(git_buf *buf, git_net_url *url); /** Determines if the url matches given pattern or pattern list */ extern bool git_net_url_matches_pattern( git_net_url *url, const char *pattern); extern bool git_net_url_matches_pattern_list( git_net_url *url, const char *pattern_list); /** Disposes the contents of the structure. */ extern void git_net_url_dispose(git_net_url *url); #endif git2r/src/libgit2/src/utf8.h0000644000175000017500000000323414125111754015407 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_utf8_h__ #define INCLUDE_utf8_h__ #include "common.h" /* * Iterate through an UTF-8 string, yielding one codepoint at a time. * * @param out pointer where to store the current codepoint * @param str current position in the string * @param str_len size left in the string * @return length in bytes of the read codepoint; -1 if the codepoint was invalid */ extern int git_utf8_iterate(uint32_t *out, const char *str, size_t str_len); /** * Returns the number of characters in the given string. * * This function will count invalid codepoints; if any given byte is * not part of a valid UTF-8 codepoint, then it will be counted toward * the length in characters. * * In other words: * 0x24 (U+0024 "$") has length 1 * 0xc2 0xa2 (U+00A2 "¢") has length 1 * 0x24 0xc2 0xa2 (U+0024 U+00A2 "$¢") has length 2 * 0xf0 0x90 0x8d 0x88 (U+10348 "𐍈") has length 1 * 0x24 0xc0 0xc1 0x34 (U+0024 "4) has length 4 * * @param str string to scan * @param str_len size of the string * @return length in characters of the string */ extern size_t git_utf8_char_length(const char *str, size_t str_len); /** * Iterate through an UTF-8 string and stops after finding any invalid UTF-8 * codepoints. * * @param str string to scan * @param str_len size of the string * @return length in bytes of the string that contains valid data */ extern size_t git_utf8_valid_buf_length(const char *str, size_t str_len); #endif git2r/src/libgit2/src/config_backend.h0000644000175000017500000000506014125111754017434 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_config_file_h__ #define INCLUDE_config_file_h__ #include "common.h" #include "git2/sys/config.h" #include "git2/config.h" /** * Create a configuration file backend for ondisk files * * These are the normal `.gitconfig` files that Core Git * processes. Note that you first have to add this file to a * configuration object before you can query it for configuration * variables. * * @param out the new backend * @param path where the config file is located */ extern int git_config_backend_from_file(git_config_backend **out, const char *path); /** * Create a readonly configuration file backend from another backend * * This copies the complete contents of the source backend to the * new backend. The new backend will be completely read-only and * cannot be modified. * * @param out the new snapshotted backend * @param source the backend to copy */ extern int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source); /** * Create an in-memory configuration file backend * * @param out the new backend * @param cfg the configuration that is to be parsed * @param len the length of the string pointed to by `cfg` */ extern int git_config_backend_from_string(git_config_backend **out, const char *cfg, size_t len); GIT_INLINE(int) git_config_backend_open(git_config_backend *cfg, unsigned int level, const git_repository *repo) { return cfg->open(cfg, level, repo); } GIT_INLINE(void) git_config_backend_free(git_config_backend *cfg) { if (cfg) cfg->free(cfg); } GIT_INLINE(int) git_config_backend_get_string( git_config_entry **out, git_config_backend *cfg, const char *name) { return cfg->get(cfg, name, out); } GIT_INLINE(int) git_config_backend_set_string( git_config_backend *cfg, const char *name, const char *value) { return cfg->set(cfg, name, value); } GIT_INLINE(int) git_config_backend_delete( git_config_backend *cfg, const char *name) { return cfg->del(cfg, name); } GIT_INLINE(int) git_config_backend_foreach( git_config_backend *cfg, int (*fn)(const git_config_entry *entry, void *data), void *data) { return git_config_backend_foreach_match(cfg, NULL, fn, data); } GIT_INLINE(int) git_config_backend_lock(git_config_backend *cfg) { return cfg->lock(cfg); } GIT_INLINE(int) git_config_backend_unlock(git_config_backend *cfg, int success) { return cfg->unlock(cfg, success); } #endif git2r/src/libgit2/src/config.h0000644000175000017500000000553114125111754015770 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_config_h__ #define INCLUDE_config_h__ #include "common.h" #include "git2.h" #include "git2/config.h" #include "vector.h" #include "repository.h" #define GIT_CONFIG_FILENAME_PROGRAMDATA "config" #define GIT_CONFIG_FILENAME_SYSTEM "gitconfig" #define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig" #define GIT_CONFIG_FILENAME_XDG "config" #define GIT_CONFIG_FILENAME_INREPO "config" #define GIT_CONFIG_FILE_MODE 0666 struct git_config { git_refcount rc; git_vector backends; }; extern int git_config__global_location(git_buf *buf); extern int git_config_rename_section( git_repository *repo, const char *old_section_name, /* eg "branch.dummy" */ const char *new_section_name); /* NULL to drop the old section */ extern int git_config__normalize_name(const char *in, char **out); /* internal only: does not normalize key and sets out to NULL if not found */ extern int git_config__lookup_entry( git_config_entry **out, const git_config *cfg, const char *key, bool no_errors); /* internal only: update and/or delete entry string with constraints */ extern int git_config__update_entry( git_config *cfg, const char *key, const char *value, bool overwrite_existing, bool only_if_existing); /* * Lookup functions that cannot fail. These functions look up a config * value and return a fallback value if the value is missing or if any * failures occur while trying to access the value. */ extern char *git_config__get_string_force( const git_config *cfg, const char *key, const char *fallback_value); extern int git_config__get_bool_force( const git_config *cfg, const char *key, int fallback_value); extern int git_config__get_int_force( const git_config *cfg, const char *key, int fallback_value); /* API for repository configmap-style lookups from config - not cached, but * uses configmap value maps and fallbacks */ extern int git_config__configmap_lookup( int *out, git_config *config, git_configmap_item item); /** * The opposite of git_config_lookup_map_value, we take an enum value * and map it to the string or bool value on the config. */ int git_config_lookup_map_enum(git_configmap_t *type_out, const char **str_out, const git_configmap *maps, size_t map_n, int enum_val); /** * Unlock the backend with the highest priority * * Unlocking will allow other writers to updat the configuration * file. Optionally, any changes performed since the lock will be * applied to the configuration. * * @param cfg the configuration * @param commit boolean which indicates whether to commit any changes * done since locking * @return 0 or an error code */ GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit); #endif git2r/src/libgit2/src/blame_git.c0000644000175000017500000004177414125111754016452 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "blame_git.h" #include "commit.h" #include "blob.h" #include "xdiff/xinclude.h" #include "diff_xdiff.h" /* * Origin is refcounted and usually we keep the blob contents to be * reused. */ static git_blame__origin *origin_incref(git_blame__origin *o) { if (o) o->refcnt++; return o; } static void origin_decref(git_blame__origin *o) { if (o && --o->refcnt <= 0) { if (o->previous) origin_decref(o->previous); git_blob_free(o->blob); git_commit_free(o->commit); git__free(o); } } /* Given a commit and a path in it, create a new origin structure. */ static int make_origin(git_blame__origin **out, git_commit *commit, const char *path) { git_blame__origin *o; git_object *blob; size_t path_len = strlen(path), alloc_len; int error = 0; if ((error = git_object_lookup_bypath(&blob, (git_object*)commit, path, GIT_OBJECT_BLOB)) < 0) return error; GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1); o = git__calloc(1, alloc_len); GIT_ERROR_CHECK_ALLOC(o); o->commit = commit; o->blob = (git_blob *) blob; o->refcnt = 1; strcpy(o->path, path); *out = o; return 0; } /* Locate an existing origin or create a new one. */ int git_blame__get_origin( git_blame__origin **out, git_blame *blame, git_commit *commit, const char *path) { git_blame__entry *e; for (e = blame->ent; e; e = e->next) { if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) { *out = origin_incref(e->suspect); } } return make_origin(out, commit, path); } typedef struct blame_chunk_cb_data { git_blame *blame; git_blame__origin *target; git_blame__origin *parent; long tlno; long plno; }blame_chunk_cb_data; static bool same_suspect(git_blame__origin *a, git_blame__origin *b) { if (a == b) return true; if (git_oid_cmp(git_commit_id(a->commit), git_commit_id(b->commit))) return false; return 0 == strcmp(a->path, b->path); } /* find the line number of the last line the target is suspected for */ static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target) { git_blame__entry *e; size_t last_in_target = 0; bool found = false; *out = 0; for (e=blame->ent; e; e=e->next) { if (e->guilty || !same_suspect(e->suspect, target)) continue; if (last_in_target < e->s_lno + e->num_lines) { found = true; last_in_target = e->s_lno + e->num_lines; } } *out = last_in_target; return found; } /* * It is known that lines between tlno to same came from parent, and e * has an overlap with that range. it also is known that parent's * line plno corresponds to e's line tlno. * * <---- e -----> * <------> (entirely within) * <------------> (extends past) * <------------> (starts before) * <------------------> (entirely encloses) * * Split e into potentially three parts; before this chunk, the chunk * to be blamed for the parent, and after that portion. */ static void split_overlap(git_blame__entry *split, git_blame__entry *e, size_t tlno, size_t plno, size_t same, git_blame__origin *parent) { size_t chunk_end_lno; if (e->s_lno < tlno) { /* there is a pre-chunk part not blamed on the parent */ split[0].suspect = origin_incref(e->suspect); split[0].lno = e->lno; split[0].s_lno = e->s_lno; split[0].num_lines = tlno - e->s_lno; split[1].lno = e->lno + tlno - e->s_lno; split[1].s_lno = plno; } else { split[1].lno = e->lno; split[1].s_lno = plno + (e->s_lno - tlno); } if (same < e->s_lno + e->num_lines) { /* there is a post-chunk part not blamed on parent */ split[2].suspect = origin_incref(e->suspect); split[2].lno = e->lno + (same - e->s_lno); split[2].s_lno = e->s_lno + (same - e->s_lno); split[2].num_lines = e->s_lno + e->num_lines - same; chunk_end_lno = split[2].lno; } else { chunk_end_lno = e->lno + e->num_lines; } split[1].num_lines = chunk_end_lno - split[1].lno; /* * if it turns out there is nothing to blame the parent for, forget about * the splitting. !split[1].suspect signals this. */ if (split[1].num_lines < 1) return; split[1].suspect = origin_incref(parent); } /* * Link in a new blame entry to the scoreboard. Entries that cover the same * line range have been removed from the scoreboard previously. */ static void add_blame_entry(git_blame *blame, git_blame__entry *e) { git_blame__entry *ent, *prev = NULL; origin_incref(e->suspect); for (ent = blame->ent; ent && ent->lno < e->lno; ent = ent->next) prev = ent; /* prev, if not NULL, is the last one that is below e */ e->prev = prev; if (prev) { e->next = prev->next; prev->next = e; } else { e->next = blame->ent; blame->ent = e; } if (e->next) e->next->prev = e; } /* * src typically is on-stack; we want to copy the information in it to * a malloced blame_entry that is already on the linked list of the scoreboard. * The origin of dst loses a refcnt while the origin of src gains one. */ static void dup_entry(git_blame__entry *dst, git_blame__entry *src) { git_blame__entry *p, *n; p = dst->prev; n = dst->next; origin_incref(src->suspect); origin_decref(dst->suspect); memcpy(dst, src, sizeof(*src)); dst->prev = p; dst->next = n; dst->score = 0; } /* * split_overlap() divided an existing blame e into up to three parts in split. * Adjust the linked list of blames in the scoreboard to reflect the split. */ static int split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e) { git_blame__entry *new_entry; if (split[0].suspect && split[2].suspect) { /* The first part (reuse storage for the existing entry e */ dup_entry(e, &split[0]); /* The last part -- me */ new_entry = git__malloc(sizeof(*new_entry)); GIT_ERROR_CHECK_ALLOC(new_entry); memcpy(new_entry, &(split[2]), sizeof(git_blame__entry)); add_blame_entry(blame, new_entry); /* ... and the middle part -- parent */ new_entry = git__malloc(sizeof(*new_entry)); GIT_ERROR_CHECK_ALLOC(new_entry); memcpy(new_entry, &(split[1]), sizeof(git_blame__entry)); add_blame_entry(blame, new_entry); } else if (!split[0].suspect && !split[2].suspect) { /* * The parent covers the entire area; reuse storage for e and replace it * with the parent */ dup_entry(e, &split[1]); } else if (split[0].suspect) { /* me and then parent */ dup_entry(e, &split[0]); new_entry = git__malloc(sizeof(*new_entry)); GIT_ERROR_CHECK_ALLOC(new_entry); memcpy(new_entry, &(split[1]), sizeof(git_blame__entry)); add_blame_entry(blame, new_entry); } else { /* parent and then me */ dup_entry(e, &split[1]); new_entry = git__malloc(sizeof(*new_entry)); GIT_ERROR_CHECK_ALLOC(new_entry); memcpy(new_entry, &(split[2]), sizeof(git_blame__entry)); add_blame_entry(blame, new_entry); } return 0; } /* * After splitting the blame, the origins used by the on-stack blame_entry * should lose one refcnt each. */ static void decref_split(git_blame__entry *split) { int i; for (i=0; i<3; i++) origin_decref(split[i].suspect); } /* * Helper for blame_chunk(). blame_entry e is known to overlap with the patch * hunk; split it and pass blame to the parent. */ static int blame_overlap( git_blame *blame, git_blame__entry *e, size_t tlno, size_t plno, size_t same, git_blame__origin *parent) { git_blame__entry split[3] = {{0}}; split_overlap(split, e, tlno, plno, same, parent); if (split[1].suspect) if (split_blame(blame, split, e) < 0) return -1; decref_split(split); return 0; } /* * Process one hunk from the patch between the current suspect for blame_entry * e and its parent. Find and split the overlap, and pass blame to the * overlapping part to the parent. */ static int blame_chunk( git_blame *blame, size_t tlno, size_t plno, size_t same, git_blame__origin *target, git_blame__origin *parent) { git_blame__entry *e; for (e = blame->ent; e; e = e->next) { if (e->guilty || !same_suspect(e->suspect, target)) continue; if (same <= e->s_lno) continue; if (tlno < e->s_lno + e->num_lines) { if (blame_overlap(blame, e, tlno, plno, same, parent) < 0) return -1; } } return 0; } static int my_emit( long start_a, long count_a, long start_b, long count_b, void *cb_data) { blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data; if (blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent) < 0) return -1; d->plno = start_a + count_a; d->tlno = start_b + count_b; return 0; } static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx) { const int blk = 1024; long trimmed = 0, recovered = 0; char *ap = a->ptr + a->size; char *bp = b->ptr + b->size; long smaller = (long)((a->size < b->size) ? a->size : b->size); if (ctx) return; while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) { trimmed += blk; ap -= blk; bp -= blk; } while (recovered < trimmed) if (ap[recovered++] == '\n') break; a->size -= trimmed - recovered; b->size -= trimmed - recovered; } static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data, git_blame_options *options) { xdemitconf_t xecfg = {0}; xdemitcb_t ecb = {0}; xpparam_t xpp = {0}; if (options->flags & GIT_BLAME_IGNORE_WHITESPACE) xpp.flags |= XDF_IGNORE_WHITESPACE; xecfg.hunk_func = my_emit; ecb.priv = cb_data; trim_common_tail(&file_a, &file_b, 0); if (file_a.size > GIT_XDIFF_MAX_SIZE || file_b.size > GIT_XDIFF_MAX_SIZE) { git_error_set(GIT_ERROR_INVALID, "file too large to blame"); return -1; } return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb); } static void fill_origin_blob(git_blame__origin *o, mmfile_t *file) { memset(file, 0, sizeof(*file)); if (o->blob) { file->ptr = (char*)git_blob_rawcontent(o->blob); file->size = (size_t)git_blob_rawsize(o->blob); } } static int pass_blame_to_parent( git_blame *blame, git_blame__origin *target, git_blame__origin *parent) { size_t last_in_target; mmfile_t file_p, file_o; blame_chunk_cb_data d = { blame, target, parent, 0, 0 }; if (!find_last_in_target(&last_in_target, blame, target)) return 1; /* nothing remains for this target */ fill_origin_blob(parent, &file_p); fill_origin_blob(target, &file_o); if (diff_hunks(file_p, file_o, &d, &blame->options) < 0) return -1; /* The reset (i.e. anything after tlno) are the same as the parent */ if (blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent) < 0) return -1; return 0; } static int paths_on_dup(void **old, void *new) { GIT_UNUSED(old); git__free(new); return -1; } static git_blame__origin *find_origin( git_blame *blame, git_commit *parent, git_blame__origin *origin) { git_blame__origin *porigin = NULL; git_diff *difflist = NULL; git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; git_tree *otree=NULL, *ptree=NULL; /* Get the trees from this commit and its parent */ if (0 != git_commit_tree(&otree, origin->commit) || 0 != git_commit_tree(&ptree, parent)) goto cleanup; /* Configure the diff */ diffopts.context_lines = 0; diffopts.flags = GIT_DIFF_SKIP_BINARY_CHECK; /* Check to see if files we're interested have changed */ diffopts.pathspec.count = blame->paths.length; diffopts.pathspec.strings = (char**)blame->paths.contents; if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts)) goto cleanup; if (!git_diff_num_deltas(difflist)) { /* No changes; copy data */ git_blame__get_origin(&porigin, blame, parent, origin->path); } else { git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; int i; /* Generate a full diff between the two trees */ git_diff_free(difflist); diffopts.pathspec.count = 0; if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts)) goto cleanup; /* Let diff find renames */ findopts.flags = GIT_DIFF_FIND_RENAMES; if (0 != git_diff_find_similar(difflist, &findopts)) goto cleanup; /* Find one that matches */ for (i=0; i<(int)git_diff_num_deltas(difflist); i++) { const git_diff_delta *delta = git_diff_get_delta(difflist, i); if (!git_vector_bsearch(NULL, &blame->paths, delta->new_file.path)) { git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path), paths_on_dup); make_origin(&porigin, parent, delta->old_file.path); } } } cleanup: git_diff_free(difflist); git_tree_free(otree); git_tree_free(ptree); return porigin; } /* * The blobs of origin and porigin exactly match, so everything origin is * suspected for can be blamed on the parent. */ static int pass_whole_blame(git_blame *blame, git_blame__origin *origin, git_blame__origin *porigin) { git_blame__entry *e; if (!porigin->blob && git_object_lookup((git_object**)&porigin->blob, blame->repository, git_blob_id(origin->blob), GIT_OBJECT_BLOB) < 0) return -1; for (e=blame->ent; e; e=e->next) { if (!same_suspect(e->suspect, origin)) continue; origin_incref(porigin); origin_decref(e->suspect); e->suspect = porigin; } return 0; } static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) { git_commit *commit = origin->commit; int i, num_parents; git_blame__origin *sg_buf[16]; git_blame__origin *porigin, **sg_origin = sg_buf; int ret, error = 0; num_parents = git_commit_parentcount(commit); if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit)) /* Stop at oldest specified commit */ num_parents = 0; else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1) /* Limit search to the first parent */ num_parents = 1; if (!num_parents) { git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit)); goto finish; } else if (num_parents < (int)ARRAY_SIZE(sg_buf)) memset(sg_buf, 0, sizeof(sg_buf)); else { sg_origin = git__calloc(num_parents, sizeof(*sg_origin)); GIT_ERROR_CHECK_ALLOC(sg_origin); } for (i=0; icommit, i)) < 0) goto finish; porigin = find_origin(blame, p, origin); if (!porigin) { /* * We only have to decrement the parent's * reference count when no porigin has * been created, as otherwise the commit * is assigned to the created object. */ git_commit_free(p); continue; } if (porigin->blob && origin->blob && !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) { error = pass_whole_blame(blame, origin, porigin); origin_decref(porigin); goto finish; } for (j = same = 0; jblob), git_blob_id(porigin->blob))) { same = 1; break; } if (!same) sg_origin[i] = porigin; else origin_decref(porigin); } /* Standard blame */ for (i=0; iprevious) { origin_incref(porigin); origin->previous = porigin; } if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) { if (ret < 0) error = -1; goto finish; } } /* TODO: optionally find moves in parents' files */ /* TODO: optionally find copies in parents' files */ finish: for (i=0; i pair), * merge them together. */ static void coalesce(git_blame *blame) { git_blame__entry *ent, *next; for (ent=blame->ent; ent && (next = ent->next); ent = next) { if (same_suspect(ent->suspect, next->suspect) && ent->guilty == next->guilty && ent->s_lno + ent->num_lines == next->s_lno) { ent->num_lines += next->num_lines; ent->next = next->next; if (ent->next) ent->next->prev = ent; origin_decref(next->suspect); git__free(next); ent->score = 0; next = ent; /* again */ } } } int git_blame__like_git(git_blame *blame, uint32_t opt) { int error = 0; while (true) { git_blame__entry *ent; git_blame__origin *suspect = NULL; /* Find a suspect to break down */ for (ent = blame->ent; !suspect && ent; ent = ent->next) if (!ent->guilty) suspect = ent->suspect; if (!suspect) break; /* We'll use this suspect later in the loop, so hold on to it for now. */ origin_incref(suspect); if ((error = pass_blame(blame, suspect, opt)) < 0) break; /* Take responsibility for the remaining entries */ for (ent = blame->ent; ent; ent = ent->next) { if (same_suspect(ent->suspect, suspect)) { ent->guilty = true; ent->is_boundary = !git_oid_cmp( git_commit_id(suspect->commit), &blame->options.oldest_commit); } } origin_decref(suspect); } if (!error) coalesce(blame); return error; } void git_blame__free_entry(git_blame__entry *ent) { if (!ent) return; origin_decref(ent->suspect); git__free(ent); } git2r/src/libgit2/src/diff.h0000644000175000017500000000333214125111754015430 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_diff_h__ #define INCLUDE_diff_h__ #include "common.h" #include "git2/diff.h" #include "git2/patch.h" #include "git2/sys/diff.h" #include "git2/oid.h" #include #include "vector.h" #include "buffer.h" #include "iterator.h" #include "repository.h" #include "pool.h" #include "odb.h" #define DIFF_OLD_PREFIX_DEFAULT "a/" #define DIFF_NEW_PREFIX_DEFAULT "b/" typedef enum { GIT_DIFF_TYPE_UNKNOWN = 0, GIT_DIFF_TYPE_GENERATED = 1, GIT_DIFF_TYPE_PARSED = 2, } git_diff_origin_t; struct git_diff { git_refcount rc; git_repository *repo; git_attr_session attrsession; git_diff_origin_t type; git_diff_options opts; git_vector deltas; /* vector of git_diff_delta */ git_pool pool; git_iterator_t old_src; git_iterator_t new_src; git_diff_perfdata perf; int (*strcomp)(const char *, const char *); int (*strncomp)(const char *, const char *, size_t); int (*pfxcomp)(const char *str, const char *pfx); int (*entrycomp)(const void *a, const void *b); int (*patch_fn)(git_patch **out, git_diff *diff, size_t idx); void (*free_fn)(git_diff *diff); }; extern int git_diff_delta__format_file_header( git_buf *out, const git_diff_delta *delta, const char *oldpfx, const char *newpfx, int oid_strlen, bool print_index); extern int git_diff_delta__cmp(const void *a, const void *b); extern int git_diff_delta__casecmp(const void *a, const void *b); extern int git_diff__entry_cmp(const void *a, const void *b); extern int git_diff__entry_icmp(const void *a, const void *b); #endif git2r/src/libgit2/src/blob.h0000644000175000017500000000217714125111754015444 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_blob_h__ #define INCLUDE_blob_h__ #include "common.h" #include "git2/blob.h" #include "repository.h" #include "odb.h" #include "futils.h" struct git_blob { git_object object; union { git_odb_object *odb; struct { const char *data; git_object_size_t size; } raw; } data; unsigned int raw:1; }; #define GIT_ERROR_CHECK_BLOBSIZE(n) \ do { \ if (!git__is_sizet(n)) { \ git_error_set(GIT_ERROR_NOMEMORY, "blob contents too large to fit in memory"); \ return -1; \ } \ } while(0) void git_blob__free(void *blob); int git_blob__parse(void *blob, git_odb_object *obj); int git_blob__parse_raw(void *blob, const char *data, size_t size); int git_blob__getbuf(git_buf *buffer, git_blob *blob); extern int git_blob__create_from_paths( git_oid *out_oid, struct stat *out_st, git_repository *repo, const char *full_path, const char *hint_path, mode_t hint_mode, bool apply_filters); #endif git2r/src/libgit2/src/merge_file.c0000644000175000017500000002015014125111754016606 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "repository.h" #include "posix.h" #include "futils.h" #include "index.h" #include "diff_xdiff.h" #include "merge.h" #include "git2/repository.h" #include "git2/object.h" #include "git2/index.h" #include "git2/merge.h" #include "xdiff/xdiff.h" /* only examine the first 8000 bytes for binaryness. * https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197 */ #define GIT_MERGE_FILE_BINARY_SIZE 8000 #define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0) static int merge_file_input_from_index( git_merge_file_input *input_out, git_odb_object **odb_object_out, git_odb *odb, const git_index_entry *entry) { int error = 0; GIT_ASSERT_ARG(input_out); GIT_ASSERT_ARG(odb_object_out); GIT_ASSERT_ARG(odb); GIT_ASSERT_ARG(entry); if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0) goto done; input_out->path = entry->path; input_out->mode = entry->mode; input_out->ptr = (char *)git_odb_object_data(*odb_object_out); input_out->size = git_odb_object_size(*odb_object_out); done: return error; } static void merge_file_normalize_opts( git_merge_file_options *out, const git_merge_file_options *given_opts) { if (given_opts) memcpy(out, given_opts, sizeof(git_merge_file_options)); else { git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT; memcpy(out, &default_opts, sizeof(git_merge_file_options)); } } static int merge_file__xdiff( git_merge_file_result *out, const git_merge_file_input *ancestor, const git_merge_file_input *ours, const git_merge_file_input *theirs, const git_merge_file_options *given_opts) { xmparam_t xmparam; mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0}; mmbuffer_t mmbuffer; git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT; const char *path; int xdl_result; int error = 0; memset(out, 0x0, sizeof(git_merge_file_result)); merge_file_normalize_opts(&options, given_opts); memset(&xmparam, 0x0, sizeof(xmparam_t)); if (ancestor) { xmparam.ancestor = (options.ancestor_label) ? options.ancestor_label : ancestor->path; ancestor_mmfile.ptr = (char *)ancestor->ptr; ancestor_mmfile.size = ancestor->size; } xmparam.file1 = (options.our_label) ? options.our_label : ours->path; our_mmfile.ptr = (char *)ours->ptr; our_mmfile.size = ours->size; xmparam.file2 = (options.their_label) ? options.their_label : theirs->path; their_mmfile.ptr = (char *)theirs->ptr; their_mmfile.size = theirs->size; if (options.favor == GIT_MERGE_FILE_FAVOR_OURS) xmparam.favor = XDL_MERGE_FAVOR_OURS; else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS) xmparam.favor = XDL_MERGE_FAVOR_THEIRS; else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION) xmparam.favor = XDL_MERGE_FAVOR_UNION; xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ? XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS; if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3) xmparam.style = XDL_MERGE_DIFF3; if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE) xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE; if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE) xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_CHANGE; if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL) xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; if (options.flags & GIT_MERGE_FILE_DIFF_PATIENCE) xmparam.xpp.flags |= XDF_PATIENCE_DIFF; if (options.flags & GIT_MERGE_FILE_DIFF_MINIMAL) xmparam.xpp.flags |= XDF_NEED_MINIMAL; xmparam.marker_size = options.marker_size; if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile, &their_mmfile, &xmparam, &mmbuffer)) < 0) { git_error_set(GIT_ERROR_MERGE, "failed to merge files"); error = -1; goto done; } path = git_merge_file__best_path( ancestor ? ancestor->path : NULL, ours->path, theirs->path); if (path != NULL && (out->path = git__strdup(path)) == NULL) { error = -1; goto done; } out->automergeable = (xdl_result == 0); out->ptr = (const char *)mmbuffer.ptr; out->len = mmbuffer.size; out->mode = git_merge_file__best_mode( ancestor ? ancestor->mode : 0, ours->mode, theirs->mode); done: if (error < 0) git_merge_file_result_free(out); return error; } static bool merge_file__is_binary(const git_merge_file_input *file) { size_t len = file ? file->size : 0; if (len > GIT_XDIFF_MAX_SIZE) return true; if (len > GIT_MERGE_FILE_BINARY_SIZE) len = GIT_MERGE_FILE_BINARY_SIZE; return len ? (memchr(file->ptr, 0, len) != NULL) : false; } static int merge_file__binary( git_merge_file_result *out, const git_merge_file_input *ours, const git_merge_file_input *theirs, const git_merge_file_options *given_opts) { const git_merge_file_input *favored = NULL; memset(out, 0x0, sizeof(git_merge_file_result)); if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS) favored = ours; else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS) favored = theirs; else goto done; if ((out->path = git__strdup(favored->path)) == NULL || (out->ptr = git__malloc(favored->size)) == NULL) goto done; memcpy((char *)out->ptr, favored->ptr, favored->size); out->len = favored->size; out->mode = favored->mode; out->automergeable = 1; done: return 0; } static int merge_file__from_inputs( git_merge_file_result *out, const git_merge_file_input *ancestor, const git_merge_file_input *ours, const git_merge_file_input *theirs, const git_merge_file_options *given_opts) { if (merge_file__is_binary(ancestor) || merge_file__is_binary(ours) || merge_file__is_binary(theirs)) return merge_file__binary(out, ours, theirs, given_opts); return merge_file__xdiff(out, ancestor, ours, theirs, given_opts); } static git_merge_file_input *git_merge_file__normalize_inputs( git_merge_file_input *out, const git_merge_file_input *given) { memcpy(out, given, sizeof(git_merge_file_input)); if (!out->path) out->path = "file.txt"; if (!out->mode) out->mode = 0100644; return out; } int git_merge_file( git_merge_file_result *out, const git_merge_file_input *ancestor, const git_merge_file_input *ours, const git_merge_file_input *theirs, const git_merge_file_options *options) { git_merge_file_input inputs[3] = { {0} }; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(ours); GIT_ASSERT_ARG(theirs); memset(out, 0x0, sizeof(git_merge_file_result)); if (ancestor) ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor); ours = git_merge_file__normalize_inputs(&inputs[1], ours); theirs = git_merge_file__normalize_inputs(&inputs[2], theirs); return merge_file__from_inputs(out, ancestor, ours, theirs, options); } int git_merge_file_from_index( git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *options) { git_merge_file_input *ancestor_ptr = NULL, ancestor_input = {0}, our_input = {0}, their_input = {0}; git_odb *odb = NULL; git_odb_object *odb_object[3] = { 0 }; int error = 0; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(ours); GIT_ASSERT_ARG(theirs); memset(out, 0x0, sizeof(git_merge_file_result)); if ((error = git_repository_odb(&odb, repo)) < 0) goto done; if (ancestor) { if ((error = merge_file_input_from_index( &ancestor_input, &odb_object[0], odb, ancestor)) < 0) goto done; ancestor_ptr = &ancestor_input; } if ((error = merge_file_input_from_index(&our_input, &odb_object[1], odb, ours)) < 0 || (error = merge_file_input_from_index(&their_input, &odb_object[2], odb, theirs)) < 0) goto done; error = merge_file__from_inputs(out, ancestor_ptr, &our_input, &their_input, options); done: git_odb_object_free(odb_object[0]); git_odb_object_free(odb_object[1]); git_odb_object_free(odb_object[2]); git_odb_free(odb); return error; } void git_merge_file_result_free(git_merge_file_result *result) { if (result == NULL) return; git__free((char *)result->path); git__free((char *)result->ptr); } git2r/src/libgit2/src/cc-compat.h0000644000175000017500000000474114125111754016373 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_cc_compat_h__ #define INCLUDE_cc_compat_h__ #include /* * See if our compiler is known to support flexible array members. */ #ifndef GIT_FLEX_ARRAY # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # define GIT_FLEX_ARRAY /* empty */ # elif defined(__GNUC__) # if (__GNUC__ >= 3) # define GIT_FLEX_ARRAY /* empty */ # else # define GIT_FLEX_ARRAY 0 /* older GNU extension */ # endif # endif /* Default to safer but a bit wasteful traditional style */ # ifndef GIT_FLEX_ARRAY # define GIT_FLEX_ARRAY 1 # endif #endif #if defined(__GNUC__) # define GIT_ALIGN(x,size) x __attribute__ ((aligned(size))) #elif defined(_MSC_VER) # define GIT_ALIGN(x,size) __declspec(align(size)) x #else # define GIT_ALIGN(x,size) x #endif #if defined(__GNUC__) # define GIT_UNUSED(x) \ do { \ __typeof__(x) _unused __attribute__((unused)); \ _unused = (x); \ } while (0) #else # define GIT_UNUSED(x) ((void)(x)) #endif /* Define the printf format specifier to use for size_t output */ #if defined(_MSC_VER) || defined(__MINGW32__) /* Visual Studio 2012 and prior lack PRId64 entirely */ # ifndef PRId64 # define PRId64 "I64d" # endif /* The first block is needed to avoid warnings on MingW amd64 */ # if (SIZE_MAX == ULLONG_MAX) # define PRIuZ "I64u" # define PRIxZ "I64x" # define PRIXZ "I64X" # define PRIdZ "I64d" # else # define PRIuZ "Iu" # define PRIxZ "Ix" # define PRIXZ "IX" # define PRIdZ "Id" # endif #else # define PRIuZ "zu" # define PRIxZ "zx" # define PRIXZ "zX" # define PRIdZ "zd" #endif /* Micosoft Visual C/C++ */ #if defined(_MSC_VER) /* disable "deprecated function" warnings */ # pragma warning ( disable : 4996 ) /* disable "conditional expression is constant" level 4 warnings */ # pragma warning ( disable : 4127 ) #endif #if defined (_MSC_VER) typedef unsigned char bool; # ifndef true # define true 1 # endif # ifndef false # define false 0 # endif #else # include #endif #ifndef va_copy # ifdef __va_copy # define va_copy(dst, src) __va_copy(dst, src) # else # define va_copy(dst, src) ((dst) = (src)) # endif #endif #endif git2r/src/libgit2/src/repository.h0000644000175000017500000001773014125111754016746 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_repository_h__ #define INCLUDE_repository_h__ #include "common.h" #include "git2/common.h" #include "git2/oid.h" #include "git2/odb.h" #include "git2/repository.h" #include "git2/object.h" #include "git2/config.h" #include "array.h" #include "cache.h" #include "refs.h" #include "buffer.h" #include "object.h" #include "attrcache.h" #include "submodule.h" #include "diff_driver.h" #define DOT_GIT ".git" #define GIT_DIR DOT_GIT "/" #define GIT_DIR_MODE 0755 #define GIT_BARE_DIR_MODE 0777 /* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */ #define GIT_DIR_SHORTNAME "GIT~1" extern bool git_repository__fsync_gitdir; /** Cvar cache identifiers */ typedef enum { GIT_CONFIGMAP_AUTO_CRLF = 0, /* core.autocrlf */ GIT_CONFIGMAP_EOL, /* core.eol */ GIT_CONFIGMAP_SYMLINKS, /* core.symlinks */ GIT_CONFIGMAP_IGNORECASE, /* core.ignorecase */ GIT_CONFIGMAP_FILEMODE, /* core.filemode */ GIT_CONFIGMAP_IGNORESTAT, /* core.ignorestat */ GIT_CONFIGMAP_TRUSTCTIME, /* core.trustctime */ GIT_CONFIGMAP_ABBREV, /* core.abbrev */ GIT_CONFIGMAP_PRECOMPOSE, /* core.precomposeunicode */ GIT_CONFIGMAP_SAFE_CRLF, /* core.safecrlf */ GIT_CONFIGMAP_LOGALLREFUPDATES, /* core.logallrefupdates */ GIT_CONFIGMAP_PROTECTHFS, /* core.protectHFS */ GIT_CONFIGMAP_PROTECTNTFS, /* core.protectNTFS */ GIT_CONFIGMAP_FSYNCOBJECTFILES, /* core.fsyncObjectFiles */ GIT_CONFIGMAP_LONGPATHS, /* core.longpaths */ GIT_CONFIGMAP_CACHE_MAX } git_configmap_item; /** * Configuration map value enumerations * * These are the values that are actually stored in the configmap cache, * instead of their string equivalents. These values are internal and * symbolic; make sure that none of them is set to `-1`, since that is * the unique identifier for "not cached" */ typedef enum { /* The value hasn't been loaded from the cache yet */ GIT_CONFIGMAP_NOT_CACHED = -1, /* core.safecrlf: false, 'fail', 'warn' */ GIT_SAFE_CRLF_FALSE = 0, GIT_SAFE_CRLF_FAIL = 1, GIT_SAFE_CRLF_WARN = 2, /* core.autocrlf: false, true, 'input; */ GIT_AUTO_CRLF_FALSE = 0, GIT_AUTO_CRLF_TRUE = 1, GIT_AUTO_CRLF_INPUT = 2, GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE, /* core.eol: unset, 'crlf', 'lf', 'native' */ GIT_EOL_UNSET = 0, GIT_EOL_CRLF = 1, GIT_EOL_LF = 2, #ifdef GIT_WIN32 GIT_EOL_NATIVE = GIT_EOL_CRLF, #else GIT_EOL_NATIVE = GIT_EOL_LF, #endif GIT_EOL_DEFAULT = GIT_EOL_NATIVE, /* core.symlinks: bool */ GIT_SYMLINKS_DEFAULT = GIT_CONFIGMAP_TRUE, /* core.ignorecase */ GIT_IGNORECASE_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.filemode */ GIT_FILEMODE_DEFAULT = GIT_CONFIGMAP_TRUE, /* core.ignorestat */ GIT_IGNORESTAT_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.trustctime */ GIT_TRUSTCTIME_DEFAULT = GIT_CONFIGMAP_TRUE, /* core.abbrev */ GIT_ABBREV_DEFAULT = 7, /* core.precomposeunicode */ GIT_PRECOMPOSE_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.safecrlf */ GIT_SAFE_CRLF_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.logallrefupdates */ GIT_LOGALLREFUPDATES_FALSE = GIT_CONFIGMAP_FALSE, GIT_LOGALLREFUPDATES_TRUE = GIT_CONFIGMAP_TRUE, GIT_LOGALLREFUPDATES_UNSET = 2, GIT_LOGALLREFUPDATES_ALWAYS = 3, GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET, /* core.protectHFS */ GIT_PROTECTHFS_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.protectNTFS */ GIT_PROTECTNTFS_DEFAULT = GIT_CONFIGMAP_TRUE, /* core.fsyncObjectFiles */ GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.longpaths */ GIT_LONGPATHS_DEFAULT = GIT_CONFIGMAP_FALSE, } git_configmap_value; /* internal repository init flags */ enum { GIT_REPOSITORY_INIT__HAS_DOTGIT = (1u << 16), GIT_REPOSITORY_INIT__NATURAL_WD = (1u << 17), GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18), }; /** Internal structure for repository object */ struct git_repository { git_odb *_odb; git_refdb *_refdb; git_config *_config; git_index *_index; git_cache objects; git_attr_cache *attrcache; git_diff_driver_registry *diff_drivers; char *gitlink; char *gitdir; char *commondir; char *workdir; char *namespace; char *ident_name; char *ident_email; git_array_t(git_buf) reserved_names; unsigned is_bare:1; unsigned is_worktree:1; unsigned int lru_counter; git_atomic32 attr_session_key; intptr_t configmap_cache[GIT_CONFIGMAP_CACHE_MAX]; git_strmap *submodule_cache; }; GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) { return repo->attrcache; } int git_repository_head_tree(git_tree **tree, git_repository *repo); int git_repository_create_head(const char *git_dir, const char *ref_name); typedef int (*git_repository_foreach_worktree_cb)(git_repository *, void *); int git_repository_foreach_worktree(git_repository *repo, git_repository_foreach_worktree_cb cb, void *payload); /* * Weak pointers to repository internals. * * The returned pointers do not need to be freed. Do not keep * permanent references to these (i.e. between API calls), since they may * become invalidated if the user replaces a repository internal. */ int git_repository_config__weakptr(git_config **out, git_repository *repo); int git_repository_odb__weakptr(git_odb **out, git_repository *repo); int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo); int git_repository_index__weakptr(git_index **out, git_repository *repo); /* * Configuration map cache * * Efficient access to the most used config variables of a repository. * The cache is cleared every time the config backend is replaced. */ int git_repository__configmap_lookup(int *out, git_repository *repo, git_configmap_item item); void git_repository__configmap_lookup_cache_clear(git_repository *repo); GIT_INLINE(int) git_repository__ensure_not_bare( git_repository *repo, const char *operation_name) { if (!git_repository_is_bare(repo)) return 0; git_error_set( GIT_ERROR_REPOSITORY, "cannot %s. This operation is not allowed against bare repositories.", operation_name); return GIT_EBAREREPO; } int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head); int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len); /* The default "reserved names" for a repository */ extern git_buf git_repository__reserved_names_win32[]; extern size_t git_repository__reserved_names_win32_len; extern git_buf git_repository__reserved_names_posix[]; extern size_t git_repository__reserved_names_posix_len; /* * Gets any "reserved names" in the repository. This will return paths * that should not be allowed in the repository (like ".git") to avoid * conflicting with the repository path, or with alternate mechanisms to * the repository path (eg, "GIT~1"). Every attempt will be made to look * up all possible reserved names - if there was a conflict for the shortname * GIT~1, for example, this function will try to look up the alternate * shortname. If that fails, this function returns false, but out and outlen * will still be populated with good defaults. */ bool git_repository__reserved_names( git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs); /* * The default branch for the repository; the `init.defaultBranch` * configuration option, if set, or `master` if it is not. */ int git_repository_initialbranch(git_buf *out, git_repository *repo); /* * Given a relative `path`, this makes it absolute based on the * repository's working directory. This will perform validation * to ensure that the path is not longer than MAX_PATH on Windows * (unless `core.longpaths` is set in the repo config). */ int git_repository_workdir_path(git_buf *out, git_repository *repo, const char *path); int git_repository__extensions(char ***out, size_t *out_len); int git_repository__set_extensions(const char **extensions, size_t len); void git_repository__free_extensions(void); #endif git2r/src/libgit2/src/pack.c0000644000175000017500000011502014125111754015427 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "pack.h" #include "delta.h" #include "futils.h" #include "mwindow.h" #include "odb.h" #include "oid.h" #include "oidarray.h" /* Option to bypass checking existence of '.keep' files */ bool git_disable_pack_keep_file_checks = false; static int packfile_open_locked(struct git_pack_file *p); static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n); static int packfile_unpack_compressed( git_rawobj *obj, struct git_pack_file *p, git_mwindow **w_curs, off64_t *curpos, size_t size, git_object_t type); /* Can find the offset of an object given * a prefix of an identifier. * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid * is ambiguous within the pack. * This method assumes that len is between * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ. */ static int pack_entry_find_offset( off64_t *offset_out, git_oid *found_oid, struct git_pack_file *p, const git_oid *short_oid, size_t len); static int packfile_error(const char *message) { git_error_set(GIT_ERROR_ODB, "invalid pack file - %s", message); return -1; } /******************** * Delta base cache ********************/ static git_pack_cache_entry *new_cache_object(git_rawobj *source) { git_pack_cache_entry *e = git__calloc(1, sizeof(git_pack_cache_entry)); if (!e) return NULL; git_atomic32_inc(&e->refcount); memcpy(&e->raw, source, sizeof(git_rawobj)); return e; } static void free_cache_object(void *o) { git_pack_cache_entry *e = (git_pack_cache_entry *)o; if (e != NULL) { git__free(e->raw.data); git__free(e); } } static void cache_free(git_pack_cache *cache) { git_pack_cache_entry *entry; if (cache->entries) { git_offmap_foreach_value(cache->entries, entry, { free_cache_object(entry); }); git_offmap_free(cache->entries); cache->entries = NULL; } } static int cache_init(git_pack_cache *cache) { if (git_offmap_new(&cache->entries) < 0) return -1; cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT; if (git_mutex_init(&cache->lock)) { git_error_set(GIT_ERROR_OS, "failed to initialize pack cache mutex"); git__free(cache->entries); cache->entries = NULL; return -1; } return 0; } static git_pack_cache_entry *cache_get(git_pack_cache *cache, off64_t offset) { git_pack_cache_entry *entry; if (git_mutex_lock(&cache->lock) < 0) return NULL; if ((entry = git_offmap_get(cache->entries, offset)) != NULL) { git_atomic32_inc(&entry->refcount); entry->last_usage = cache->use_ctr++; } git_mutex_unlock(&cache->lock); return entry; } /* Run with the cache lock held */ static void free_lowest_entry(git_pack_cache *cache) { off64_t offset; git_pack_cache_entry *entry; git_offmap_foreach(cache->entries, offset, entry, { if (entry && git_atomic32_get(&entry->refcount) == 0) { cache->memory_used -= entry->raw.len; git_offmap_delete(cache->entries, offset); free_cache_object(entry); } }); } static int cache_add( git_pack_cache_entry **cached_out, git_pack_cache *cache, git_rawobj *base, off64_t offset) { git_pack_cache_entry *entry; int exists; if (base->len > GIT_PACK_CACHE_SIZE_LIMIT) return -1; entry = new_cache_object(base); if (entry) { if (git_mutex_lock(&cache->lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock cache"); git__free(entry); return -1; } /* Add it to the cache if nobody else has */ exists = git_offmap_exists(cache->entries, offset); if (!exists) { while (cache->memory_used + base->len > cache->memory_limit) free_lowest_entry(cache); git_offmap_set(cache->entries, offset, entry); cache->memory_used += entry->raw.len; *cached_out = entry; } git_mutex_unlock(&cache->lock); /* Somebody beat us to adding it into the cache */ if (exists) { git__free(entry); return -1; } } return 0; } /*********************************************************** * * PACK INDEX METHODS * ***********************************************************/ static void pack_index_free(struct git_pack_file *p) { if (p->oids) { git__free(p->oids); p->oids = NULL; } if (p->index_map.data) { git_futils_mmap_free(&p->index_map); p->index_map.data = NULL; } } /* Run with the packfile lock held */ static int pack_index_check_locked(const char *path, struct git_pack_file *p) { struct git_pack_idx_header *hdr; uint32_t version, nr, i, *index; void *idx_map; size_t idx_size; struct stat st; int error; /* TODO: properly open the file without access time using O_NOATIME */ git_file fd = git_futils_open_ro(path); if (fd < 0) return fd; if (p_fstat(fd, &st) < 0) { p_close(fd); git_error_set(GIT_ERROR_OS, "unable to stat pack index '%s'", path); return -1; } if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size) || (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20) { p_close(fd); git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path); return -1; } error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size); p_close(fd); if (error < 0) return error; hdr = idx_map = p->index_map.data; if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) { version = ntohl(hdr->idx_version); if (version < 2 || version > 2) { git_futils_mmap_free(&p->index_map); return packfile_error("unsupported index version"); } } else version = 1; nr = 0; index = idx_map; if (version > 1) index += 2; /* skip index header */ for (i = 0; i < 256; i++) { uint32_t n = ntohl(index[i]); if (n < nr) { git_futils_mmap_free(&p->index_map); return packfile_error("index is non-monotonic"); } nr = n; } if (version == 1) { /* * Total size: * - 256 index entries 4 bytes each * - 24-byte entries * nr (20-byte sha1 + 4-byte offset) * - 20-byte SHA1 of the packfile * - 20-byte SHA1 file checksum */ if (idx_size != 4*256 + nr * 24 + 20 + 20) { git_futils_mmap_free(&p->index_map); return packfile_error("index is corrupted"); } } else if (version == 2) { /* * Minimum size: * - 8 bytes of header * - 256 index entries 4 bytes each * - 20-byte sha1 entry * nr * - 4-byte crc entry * nr * - 4-byte offset entry * nr * - 20-byte SHA1 of the packfile * - 20-byte SHA1 file checksum * And after the 4-byte offset table might be a * variable sized table containing 8-byte entries * for offsets larger than 2^31. */ unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20; unsigned long max_size = min_size; if (nr) max_size += (nr - 1)*8; if (idx_size < min_size || idx_size > max_size) { git_futils_mmap_free(&p->index_map); return packfile_error("wrong index size"); } } p->num_objects = nr; p->index_version = version; return 0; } /* Run with the packfile lock held */ static int pack_index_open_locked(struct git_pack_file *p) { int error = 0; size_t name_len; git_buf idx_name = GIT_BUF_INIT; if (p->index_version > -1) goto cleanup; /* checked by git_pack_file alloc */ name_len = strlen(p->pack_name); GIT_ASSERT(name_len > strlen(".pack")); if ((error = git_buf_init(&idx_name, name_len)) < 0) goto cleanup; git_buf_put(&idx_name, p->pack_name, name_len - strlen(".pack")); git_buf_puts(&idx_name, ".idx"); if (git_buf_oom(&idx_name)) { error = -1; goto cleanup; } if (p->index_version == -1) error = pack_index_check_locked(idx_name.ptr, p); cleanup: git_buf_dispose(&idx_name); return error; } static unsigned char *pack_window_open( struct git_pack_file *p, git_mwindow **w_cursor, off64_t offset, unsigned int *left) { unsigned char *pack_data = NULL; if (git_mutex_lock(&p->lock) < 0) { git_error_set(GIT_ERROR_THREAD, "unable to lock packfile"); return NULL; } if (git_mutex_lock(&p->mwf.lock) < 0) { git_mutex_unlock(&p->lock); git_error_set(GIT_ERROR_THREAD, "unable to lock packfile"); return NULL; } if (p->mwf.fd == -1 && packfile_open_locked(p) < 0) goto cleanup; /* Since packfiles end in a hash of their content and it's * pointless to ask for an offset into the middle of that * hash, and the pack_window_contains function above wouldn't match * don't allow an offset too close to the end of the file. * * Don't allow a negative offset, as that means we've wrapped * around. */ if (offset > (p->mwf.size - 20)) goto cleanup; if (offset < 0) goto cleanup; pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, 20, left); cleanup: git_mutex_unlock(&p->mwf.lock); git_mutex_unlock(&p->lock); return pack_data; } /* * The per-object header is a pretty dense thing, which is * - first byte: low four bits are "size", * then three bits of "type", * with the high bit being "size continues". * - each byte afterwards: low seven bits are size continuation, * with the high bit being "size continues" */ int git_packfile__object_header(size_t *out, unsigned char *hdr, size_t size, git_object_t type) { unsigned char *hdr_base; unsigned char c; GIT_ASSERT_ARG(type >= GIT_OBJECT_COMMIT && type <= GIT_OBJECT_REF_DELTA); /* TODO: add support for chunked objects; see git.git 6c0d19b1 */ c = (unsigned char)((type << 4) | (size & 15)); size >>= 4; hdr_base = hdr; while (size) { *hdr++ = c | 0x80; c = size & 0x7f; size >>= 7; } *hdr++ = c; *out = (hdr - hdr_base); return 0; } static int packfile_unpack_header1( unsigned long *usedp, size_t *sizep, git_object_t *type, const unsigned char *buf, unsigned long len) { unsigned shift; unsigned long size, c; unsigned long used = 0; c = buf[used++]; *type = (c >> 4) & 7; size = c & 15; shift = 4; while (c & 0x80) { if (len <= used) { git_error_set(GIT_ERROR_ODB, "buffer too small"); return GIT_EBUFS; } if (bitsizeof(long) <= shift) { *usedp = 0; git_error_set(GIT_ERROR_ODB, "packfile corrupted"); return -1; } c = buf[used++]; size += (c & 0x7f) << shift; shift += 7; } *sizep = (size_t)size; *usedp = used; return 0; } int git_packfile_unpack_header( size_t *size_p, git_object_t *type_p, struct git_pack_file *p, git_mwindow **w_curs, off64_t *curpos) { unsigned char *base; unsigned int left; unsigned long used; int error; if ((error = git_mutex_lock(&p->lock)) < 0) return error; if ((error = git_mutex_lock(&p->mwf.lock)) < 0) { git_mutex_unlock(&p->lock); return error; } if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) { git_mutex_unlock(&p->lock); git_mutex_unlock(&p->mwf.lock); return error; } /* pack_window_open() assures us we have [base, base + 20) available * as a range that we can look at at. (Its actually the hash * size that is assured.) With our object header encoding * the maximum deflated object size is 2^137, which is just * insane, so we know won't exceed what we have been given. */ base = git_mwindow_open(&p->mwf, w_curs, *curpos, 20, &left); git_mutex_unlock(&p->lock); git_mutex_unlock(&p->mwf.lock); if (base == NULL) return GIT_EBUFS; error = packfile_unpack_header1(&used, size_p, type_p, base, left); git_mwindow_close(w_curs); if (error == GIT_EBUFS) return error; else if (error < 0) return packfile_error("header length is zero"); *curpos += used; return 0; } int git_packfile_resolve_header( size_t *size_p, git_object_t *type_p, struct git_pack_file *p, off64_t offset) { git_mwindow *w_curs = NULL; off64_t curpos = offset; size_t size; git_object_t type; off64_t base_offset; int error; error = git_mutex_lock(&p->lock); if (error < 0) { git_error_set(GIT_ERROR_OS, "failed to lock packfile reader"); return error; } error = git_mutex_lock(&p->mwf.lock); if (error < 0) { git_error_set(GIT_ERROR_OS, "failed to lock packfile reader"); git_mutex_unlock(&p->lock); return error; } if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) { git_mutex_unlock(&p->mwf.lock); git_mutex_unlock(&p->lock); return error; } git_mutex_unlock(&p->mwf.lock); git_mutex_unlock(&p->lock); error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos); if (error < 0) return error; if (type == GIT_OBJECT_OFS_DELTA || type == GIT_OBJECT_REF_DELTA) { size_t base_size; git_packfile_stream stream; error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, offset); git_mwindow_close(&w_curs); if (error < 0) return error; if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0) return error; error = git_delta_read_header_fromstream(&base_size, size_p, &stream); git_packfile_stream_dispose(&stream); if (error < 0) return error; } else { *size_p = size; base_offset = 0; } while (type == GIT_OBJECT_OFS_DELTA || type == GIT_OBJECT_REF_DELTA) { curpos = base_offset; error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos); if (error < 0) return error; if (type != GIT_OBJECT_OFS_DELTA && type != GIT_OBJECT_REF_DELTA) break; error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, base_offset); git_mwindow_close(&w_curs); if (error < 0) return error; } *type_p = type; return error; } #define SMALL_STACK_SIZE 64 /** * Generate the chain of dependencies which we need to get to the * object at `off`. `chain` is used a stack, popping gives the right * order to apply deltas on. If an object is found in the pack's base * cache, we stop calculating there. */ static int pack_dependency_chain(git_dependency_chain *chain_out, git_pack_cache_entry **cached_out, off64_t *cached_off, struct pack_chain_elem *small_stack, size_t *stack_sz, struct git_pack_file *p, off64_t obj_offset) { git_dependency_chain chain = GIT_ARRAY_INIT; git_mwindow *w_curs = NULL; off64_t curpos = obj_offset, base_offset; int error = 0, use_heap = 0; size_t size, elem_pos; git_object_t type; elem_pos = 0; while (true) { struct pack_chain_elem *elem; git_pack_cache_entry *cached = NULL; /* if we have a base cached, we can stop here instead */ if ((cached = cache_get(&p->bases, obj_offset)) != NULL) { *cached_out = cached; *cached_off = obj_offset; break; } /* if we run out of space on the small stack, use the array */ if (elem_pos == SMALL_STACK_SIZE) { git_array_init_to_size(chain, elem_pos); GIT_ERROR_CHECK_ARRAY(chain); memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem)); chain.size = elem_pos; use_heap = 1; } curpos = obj_offset; if (!use_heap) { elem = &small_stack[elem_pos]; } else { elem = git_array_alloc(chain); if (!elem) { error = -1; goto on_error; } } elem->base_key = obj_offset; error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos); if (error < 0) goto on_error; elem->offset = curpos; elem->size = size; elem->type = type; elem->base_key = obj_offset; if (type != GIT_OBJECT_OFS_DELTA && type != GIT_OBJECT_REF_DELTA) break; error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, obj_offset); git_mwindow_close(&w_curs); if (error < 0) goto on_error; /* we need to pass the pos *after* the delta-base bit */ elem->offset = curpos; /* go through the loop again, but with the new object */ obj_offset = base_offset; elem_pos++; } *stack_sz = elem_pos + 1; *chain_out = chain; return error; on_error: git_array_clear(chain); return error; } int git_packfile_unpack( git_rawobj *obj, struct git_pack_file *p, off64_t *obj_offset) { git_mwindow *w_curs = NULL; off64_t curpos = *obj_offset; int error, free_base = 0; git_dependency_chain chain = GIT_ARRAY_INIT; struct pack_chain_elem *elem = NULL, *stack; git_pack_cache_entry *cached = NULL; struct pack_chain_elem small_stack[SMALL_STACK_SIZE]; size_t stack_size = 0, elem_pos, alloclen; git_object_t base_type; error = git_mutex_lock(&p->lock); if (error < 0) { git_error_set(GIT_ERROR_OS, "failed to lock packfile reader"); return error; } error = git_mutex_lock(&p->mwf.lock); if (error < 0) { git_error_set(GIT_ERROR_OS, "failed to lock packfile reader"); git_mutex_unlock(&p->lock); return error; } if (p->mwf.fd == -1) error = packfile_open_locked(p); git_mutex_unlock(&p->mwf.lock); git_mutex_unlock(&p->lock); if (error < 0) return error; /* * TODO: optionally check the CRC on the packfile */ error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset); if (error < 0) return error; obj->data = NULL; obj->len = 0; obj->type = GIT_OBJECT_INVALID; /* let's point to the right stack */ stack = chain.ptr ? chain.ptr : small_stack; elem_pos = stack_size; if (cached) { memcpy(obj, &cached->raw, sizeof(git_rawobj)); base_type = obj->type; elem_pos--; /* stack_size includes the base, which isn't actually there */ } else { elem = &stack[--elem_pos]; base_type = elem->type; } switch (base_type) { case GIT_OBJECT_COMMIT: case GIT_OBJECT_TREE: case GIT_OBJECT_BLOB: case GIT_OBJECT_TAG: if (!cached) { curpos = elem->offset; error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type); git_mwindow_close(&w_curs); base_type = elem->type; } if (error < 0) goto cleanup; break; case GIT_OBJECT_OFS_DELTA: case GIT_OBJECT_REF_DELTA: error = packfile_error("dependency chain ends in a delta"); goto cleanup; default: error = packfile_error("invalid packfile type in header"); goto cleanup; } /* * Finding the object we want a cached base element is * problematic, as we need to make sure we don't accidentally * give the caller the cached object, which it would then feel * free to free, so we need to copy the data. */ if (cached && stack_size == 1) { void *data = obj->data; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, obj->len, 1); obj->data = git__malloc(alloclen); GIT_ERROR_CHECK_ALLOC(obj->data); memcpy(obj->data, data, obj->len + 1); git_atomic32_dec(&cached->refcount); goto cleanup; } /* we now apply each consecutive delta until we run out */ while (elem_pos > 0 && !error) { git_rawobj base, delta; /* * We can now try to add the base to the cache, as * long as it's not already the cached one. */ if (!cached) free_base = !!cache_add(&cached, &p->bases, obj, elem->base_key); elem = &stack[elem_pos - 1]; curpos = elem->offset; error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type); git_mwindow_close(&w_curs); if (error < 0) { /* We have transferred ownership of the data to the cache. */ obj->data = NULL; break; } /* the current object becomes the new base, on which we apply the delta */ base = *obj; obj->data = NULL; obj->len = 0; obj->type = GIT_OBJECT_INVALID; error = git_delta_apply(&obj->data, &obj->len, base.data, base.len, delta.data, delta.len); obj->type = base_type; /* * We usually don't want to free the base at this * point, as we put it into the cache in the previous * iteration. free_base lets us know that we got the * base object directly from the packfile, so we can free it. */ git__free(delta.data); if (free_base) { free_base = 0; git__free(base.data); } if (cached) { git_atomic32_dec(&cached->refcount); cached = NULL; } if (error < 0) break; elem_pos--; } cleanup: if (error < 0) { git__free(obj->data); if (cached) git_atomic32_dec(&cached->refcount); } if (elem) *obj_offset = curpos; git_array_clear(chain); return error; } int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, off64_t curpos) { memset(obj, 0, sizeof(git_packfile_stream)); obj->curpos = curpos; obj->p = p; if (git_zstream_init(&obj->zstream, GIT_ZSTREAM_INFLATE) < 0) { git_error_set(GIT_ERROR_ZLIB, "failed to init packfile stream"); return -1; } return 0; } ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len) { unsigned int window_len; unsigned char *in; int error; if (obj->done) return 0; if ((in = pack_window_open(obj->p, &obj->mw, obj->curpos, &window_len)) == NULL) return GIT_EBUFS; if ((error = git_zstream_set_input(&obj->zstream, in, window_len)) < 0 || (error = git_zstream_get_output_chunk(buffer, &len, &obj->zstream)) < 0) { git_mwindow_close(&obj->mw); git_error_set(GIT_ERROR_ZLIB, "error reading from the zlib stream"); return -1; } git_mwindow_close(&obj->mw); obj->curpos += window_len - obj->zstream.in_len; if (git_zstream_eos(&obj->zstream)) obj->done = 1; /* If we didn't write anything out but we're not done, we need more data */ if (!len && !git_zstream_eos(&obj->zstream)) return GIT_EBUFS; return len; } void git_packfile_stream_dispose(git_packfile_stream *obj) { git_zstream_free(&obj->zstream); } static int packfile_unpack_compressed( git_rawobj *obj, struct git_pack_file *p, git_mwindow **mwindow, off64_t *position, size_t size, git_object_t type) { git_zstream zstream = GIT_ZSTREAM_INIT; size_t buffer_len, total = 0; char *data = NULL; int error; GIT_ERROR_CHECK_ALLOC_ADD(&buffer_len, size, 1); data = git__calloc(1, buffer_len); GIT_ERROR_CHECK_ALLOC(data); if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0) { git_error_set(GIT_ERROR_ZLIB, "failed to init zlib stream on unpack"); goto out; } do { size_t bytes = buffer_len - total; unsigned int window_len, consumed; unsigned char *in; if ((in = pack_window_open(p, mwindow, *position, &window_len)) == NULL) { error = -1; goto out; } if ((error = git_zstream_set_input(&zstream, in, window_len)) < 0 || (error = git_zstream_get_output_chunk(data + total, &bytes, &zstream)) < 0) { git_mwindow_close(mwindow); goto out; } git_mwindow_close(mwindow); consumed = window_len - (unsigned int)zstream.in_len; if (!bytes && !consumed) { git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream"); error = -1; goto out; } *position += consumed; total += bytes; } while (!git_zstream_eos(&zstream)); if (total != size || !git_zstream_eos(&zstream)) { git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream"); error = -1; goto out; } obj->type = type; obj->len = size; obj->data = data; out: git_zstream_free(&zstream); if (error) git__free(data); return error; } /* * curpos is where the data starts, delta_obj_offset is the where the * header starts */ int get_delta_base( off64_t *delta_base_out, struct git_pack_file *p, git_mwindow **w_curs, off64_t *curpos, git_object_t type, off64_t delta_obj_offset) { unsigned int left = 0; unsigned char *base_info; off64_t base_offset; git_oid unused; GIT_ASSERT_ARG(delta_base_out); base_info = pack_window_open(p, w_curs, *curpos, &left); /* Assumption: the only reason this would fail is because the file is too small */ if (base_info == NULL) return GIT_EBUFS; /* pack_window_open() assured us we have [base_info, base_info + 20) * as a range that we can look at without walking off the * end of the mapped window. Its actually the hash size * that is assured. An OFS_DELTA longer than the hash size * is stupid, as then a REF_DELTA would be smaller to store. */ if (type == GIT_OBJECT_OFS_DELTA) { unsigned used = 0; unsigned char c = base_info[used++]; size_t unsigned_base_offset = c & 127; while (c & 128) { if (left <= used) return GIT_EBUFS; unsigned_base_offset += 1; if (!unsigned_base_offset || MSB(unsigned_base_offset, 7)) return packfile_error("overflow"); c = base_info[used++]; unsigned_base_offset = (unsigned_base_offset << 7) + (c & 127); } if (unsigned_base_offset == 0 || (size_t)delta_obj_offset <= unsigned_base_offset) return packfile_error("out of bounds"); base_offset = delta_obj_offset - unsigned_base_offset; *curpos += used; } else if (type == GIT_OBJECT_REF_DELTA) { /* If we have the cooperative cache, search in it first */ if (p->has_cache) { struct git_pack_entry *entry; git_oid oid; git_oid_fromraw(&oid, base_info); if ((entry = git_oidmap_get(p->idx_cache, &oid)) != NULL) { if (entry->offset == 0) return packfile_error("delta offset is zero"); *curpos += 20; *delta_base_out = entry->offset; return 0; } else { /* If we're building an index, don't try to find the pack * entry; we just haven't seen it yet. We'll make * progress again in the next loop. */ return GIT_PASSTHROUGH; } } /* The base entry _must_ be in the same pack */ if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0) return packfile_error("base entry delta is not in the same pack"); *curpos += 20; } else return packfile_error("unknown object type"); if (base_offset == 0) return packfile_error("delta offset is zero"); *delta_base_out = base_offset; return 0; } /*********************************************************** * * PACKFILE METHODS * ***********************************************************/ void git_packfile_free(struct git_pack_file *p, bool unlink_packfile) { bool locked = true; if (!p) return; cache_free(&p->bases); if (git_mutex_lock(&p->lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock packfile"); locked = false; } if (p->mwf.fd >= 0) { git_mwindow_free_all(&p->mwf); p_close(p->mwf.fd); p->mwf.fd = -1; } if (locked) git_mutex_unlock(&p->lock); if (unlink_packfile) p_unlink(p->pack_name); pack_index_free(p); git__free(p->bad_object_sha1); git_mutex_free(&p->bases.lock); git_mutex_free(&p->mwf.lock); git_mutex_free(&p->lock); git__free(p); } /* Run with the packfile and mwf locks held */ static int packfile_open_locked(struct git_pack_file *p) { struct stat st; struct git_pack_header hdr; git_oid sha1; unsigned char *idx_sha1; if (pack_index_open_locked(p) < 0) return git_odb__error_notfound("failed to open packfile", NULL, 0); if (p->mwf.fd >= 0) return 0; /* TODO: open with noatime */ p->mwf.fd = git_futils_open_ro(p->pack_name); if (p->mwf.fd < 0) goto cleanup; if (p_fstat(p->mwf.fd, &st) < 0) { git_error_set(GIT_ERROR_OS, "could not stat packfile"); goto cleanup; } /* If we created the struct before we had the pack we lack size. */ if (!p->mwf.size) { if (!S_ISREG(st.st_mode)) goto cleanup; p->mwf.size = (off64_t)st.st_size; } else if (p->mwf.size != st.st_size) goto cleanup; #if 0 /* We leave these file descriptors open with sliding mmap; * there is no point keeping them open across exec(), though. */ fd_flag = fcntl(p->mwf.fd, F_GETFD, 0); if (fd_flag < 0) goto cleanup; fd_flag |= FD_CLOEXEC; if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1) goto cleanup; #endif /* Verify we recognize this pack file format. */ if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 || hdr.hdr_signature != htonl(PACK_SIGNATURE) || !pack_version_ok(hdr.hdr_version)) goto cleanup; /* Verify the pack matches its index. */ if (p->num_objects != ntohl(hdr.hdr_entries) || p_pread(p->mwf.fd, sha1.id, GIT_OID_RAWSZ, p->mwf.size - GIT_OID_RAWSZ) < 0) goto cleanup; idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40; if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) != 0) goto cleanup; if (git_mwindow_file_register(&p->mwf) < 0) goto cleanup; return 0; cleanup: git_error_set(GIT_ERROR_OS, "invalid packfile '%s'", p->pack_name); if (p->mwf.fd >= 0) p_close(p->mwf.fd); p->mwf.fd = -1; return -1; } int git_packfile__name(char **out, const char *path) { size_t path_len; git_buf buf = GIT_BUF_INIT; path_len = strlen(path); if (path_len < strlen(".idx")) return git_odb__error_notfound("invalid packfile path", NULL, 0); if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0) return -1; *out = git_buf_detach(&buf); return 0; } int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) { struct stat st; struct git_pack_file *p; size_t path_len = path ? strlen(path) : 0, alloc_len; *pack_out = NULL; if (path_len < strlen(".idx")) return git_odb__error_notfound("invalid packfile path", NULL, 0); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); p = git__calloc(1, alloc_len); GIT_ERROR_CHECK_ALLOC(p); memcpy(p->pack_name, path, path_len + 1); /* * Make sure a corresponding .pack file exists and that * the index looks sane. */ if (git__suffixcmp(path, ".idx") == 0) { size_t root_len = path_len - strlen(".idx"); if (!git_disable_pack_keep_file_checks) { memcpy(p->pack_name + root_len, ".keep", sizeof(".keep")); if (git_path_exists(p->pack_name) == true) p->pack_keep = 1; } memcpy(p->pack_name + root_len, ".pack", sizeof(".pack")); } if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) { git__free(p); return git_odb__error_notfound("packfile not found", NULL, 0); } /* ok, it looks sane as far as we can check without * actually mapping the pack file. */ p->mwf.fd = -1; p->mwf.size = st.st_size; p->pack_local = 1; p->mtime = (git_time_t)st.st_mtime; p->index_version = -1; if (git_mutex_init(&p->lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to initialize packfile mutex"); git__free(p); return -1; } if (git_mutex_init(&p->mwf.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to initialize packfile window mutex"); git_mutex_free(&p->lock); git__free(p); return -1; } if (cache_init(&p->bases) < 0) { git_mutex_free(&p->mwf.lock); git_mutex_free(&p->lock); git__free(p); return -1; } *pack_out = p; return 0; } /*********************************************************** * * PACKFILE ENTRY SEARCH INTERNALS * ***********************************************************/ static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n) { const unsigned char *index, *end; uint32_t off32; index = p->index_map.data; end = index + p->index_map.len; index += 4 * 256; if (p->index_version == 1) return ntohl(*((uint32_t *)(index + 24 * n))); index += 8 + p->num_objects * (20 + 4); off32 = ntohl(*((uint32_t *)(index + 4 * n))); if (!(off32 & 0x80000000)) return off32; index += p->num_objects * 4 + (off32 & 0x7fffffff) * 8; /* Make sure we're not being sent out of bounds */ if (index >= end - 8) return -1; return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) | ntohl(*((uint32_t *)(index + 4))); } static int git__memcmp4(const void *a, const void *b) { return memcmp(a, b, 4); } int git_pack_foreach_entry( struct git_pack_file *p, git_odb_foreach_cb cb, void *data) { const unsigned char *index, *current; uint32_t i; int error = 0; git_array_oid_t oids = GIT_ARRAY_INIT; git_oid *oid; if (git_mutex_lock(&p->lock) < 0) return packfile_error("failed to get lock for git_pack_foreach_entry"); if ((error = pack_index_open_locked(p)) < 0) { git_mutex_unlock(&p->lock); return error; } if (!p->index_map.data) { git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL"); git_mutex_unlock(&p->lock); return -1; } index = p->index_map.data; if (p->index_version > 1) index += 8; index += 4 * 256; if (p->oids == NULL) { git_vector offsets, oids; if ((error = git_vector_init(&oids, p->num_objects, NULL))) { git_mutex_unlock(&p->lock); return error; } if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4))) { git_mutex_unlock(&p->lock); return error; } if (p->index_version > 1) { const unsigned char *off = index + 24 * p->num_objects; for (i = 0; i < p->num_objects; i++) git_vector_insert(&offsets, (void*)&off[4 * i]); git_vector_sort(&offsets); git_vector_foreach(&offsets, i, current) git_vector_insert(&oids, (void*)&index[5 * (current - off)]); } else { for (i = 0; i < p->num_objects; i++) git_vector_insert(&offsets, (void*)&index[24 * i]); git_vector_sort(&offsets); git_vector_foreach(&offsets, i, current) git_vector_insert(&oids, (void*)¤t[4]); } git_vector_free(&offsets); p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids); } /* We need to copy the OIDs to another array before we relinquish the lock to avoid races. */ git_array_init_to_size(oids, p->num_objects); if (!oids.ptr) { git_mutex_unlock(&p->lock); git_array_clear(oids); GIT_ERROR_CHECK_ARRAY(oids); } for (i = 0; i < p->num_objects; i++) { oid = git_array_alloc(oids); if (!oid) { git_mutex_unlock(&p->lock); git_array_clear(oids); GIT_ERROR_CHECK_ALLOC(oid); } git_oid_cpy(oid, p->oids[i]); } git_mutex_unlock(&p->lock); git_array_foreach(oids, i, oid) { if ((error = cb(oid, data)) != 0) { git_error_set_after_callback(error); break; } } git_array_clear(oids); return error; } int git_pack_foreach_entry_offset( struct git_pack_file *p, git_pack_foreach_entry_offset_cb cb, void *data) { const unsigned char *index; off64_t current_offset; const git_oid *current_oid; uint32_t i; int error = 0; if (git_mutex_lock(&p->lock) < 0) return packfile_error("failed to get lock for git_pack_foreach_entry_offset"); index = p->index_map.data; if (index == NULL) { if ((error = pack_index_open_locked(p)) < 0) goto cleanup; if (!p->index_map.data) { git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL"); goto cleanup; } index = p->index_map.data; } if (p->index_version > 1) index += 8; index += 4 * 256; /* all offsets should have been validated by pack_index_check_locked */ if (p->index_version > 1) { const unsigned char *offsets = index + 24 * p->num_objects; const unsigned char *large_offset_ptr; const unsigned char *large_offsets = index + 28 * p->num_objects; const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - 20; for (i = 0; i < p->num_objects; i++) { current_offset = ntohl(*(const uint32_t *)(offsets + 4 * i)); if (current_offset & 0x80000000) { large_offset_ptr = large_offsets + (current_offset & 0x7fffffff) * 8; if (large_offset_ptr >= large_offsets_end) { error = packfile_error("invalid large offset"); goto cleanup; } current_offset = (((off64_t)ntohl(*((uint32_t *)(large_offset_ptr + 0)))) << 32) | ntohl(*((uint32_t *)(large_offset_ptr + 4))); } current_oid = (const git_oid *)(index + 20 * i); if ((error = cb(current_oid, current_offset, data)) != 0) { error = git_error_set_after_callback(error); goto cleanup; } } } else { for (i = 0; i < p->num_objects; i++) { current_offset = ntohl(*(const uint32_t *)(index + 24 * i)); current_oid = (const git_oid *)(index + 24 * i + 4); if ((error = cb(current_oid, current_offset, data)) != 0) { error = git_error_set_after_callback(error); goto cleanup; } } } cleanup: git_mutex_unlock(&p->lock); return error; } int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo, unsigned hi, const unsigned char *oid_prefix) { const unsigned char *base = oid_lookup_table; while (lo < hi) { unsigned mi = (lo + hi) / 2; int cmp = git_oid__hashcmp(base + mi * stride, oid_prefix); if (!cmp) return mi; if (cmp > 0) hi = mi; else lo = mi+1; } return -((int)lo)-1; } static int pack_entry_find_offset( off64_t *offset_out, git_oid *found_oid, struct git_pack_file *p, const git_oid *short_oid, size_t len) { const uint32_t *level1_ofs; const unsigned char *index; unsigned hi, lo, stride; int pos, found = 0; off64_t offset; const unsigned char *current = 0; int error = 0; *offset_out = 0; if (git_mutex_lock(&p->lock) < 0) return packfile_error("failed to get lock for pack_entry_find_offset"); if ((error = pack_index_open_locked(p)) < 0) goto cleanup; if (!p->index_map.data) { git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL"); goto cleanup; } index = p->index_map.data; level1_ofs = p->index_map.data; if (p->index_version > 1) { level1_ofs += 2; index += 8; } index += 4 * 256; hi = ntohl(level1_ofs[(int)short_oid->id[0]]); lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1])); if (p->index_version > 1) { stride = 20; } else { stride = 24; index += 4; } #ifdef INDEX_DEBUG_LOOKUP printf("%02x%02x%02x... lo %u hi %u nr %d\n", short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects); #endif pos = git_pack__lookup_sha1(index, stride, lo, hi, short_oid->id); if (pos >= 0) { /* An object matching exactly the oid was found */ found = 1; current = index + pos * stride; } else { /* No object was found */ /* pos refers to the object with the "closest" oid to short_oid */ pos = - 1 - pos; if (pos < (int)p->num_objects) { current = index + pos * stride; if (!git_oid_ncmp(short_oid, (const git_oid *)current, len)) found = 1; } } if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)p->num_objects) { /* Check for ambiguousity */ const unsigned char *next = current + stride; if (!git_oid_ncmp(short_oid, (const git_oid *)next, len)) { found = 2; } } if (!found) { error = git_odb__error_notfound("failed to find offset for pack entry", short_oid, len); goto cleanup; } if (found > 1) { error = git_odb__error_ambiguous("found multiple offsets for pack entry"); goto cleanup; } if ((offset = nth_packed_object_offset_locked(p, pos)) < 0) { git_error_set(GIT_ERROR_ODB, "packfile index is corrupt"); error = -1; goto cleanup; } *offset_out = offset; git_oid_fromraw(found_oid, current); #ifdef INDEX_DEBUG_LOOKUP { unsigned char hex_sha1[GIT_OID_HEXSZ + 1]; git_oid_fmt(hex_sha1, found_oid); hex_sha1[GIT_OID_HEXSZ] = '\0'; printf("found lo=%d %s\n", lo, hex_sha1); } #endif cleanup: git_mutex_unlock(&p->lock); return error; } int git_pack_entry_find( struct git_pack_entry *e, struct git_pack_file *p, const git_oid *short_oid, size_t len) { off64_t offset; git_oid found_oid; int error; GIT_ASSERT_ARG(p); if (len == GIT_OID_HEXSZ && p->num_bad_objects) { unsigned i; for (i = 0; i < p->num_bad_objects; i++) if (git_oid__cmp(short_oid, &p->bad_object_sha1[i]) == 0) return packfile_error("bad object found in packfile"); } error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len); if (error < 0) return error; error = git_mutex_lock(&p->lock); if (error < 0) { git_error_set(GIT_ERROR_OS, "failed to lock packfile reader"); return error; } error = git_mutex_lock(&p->mwf.lock); if (error < 0) { git_mutex_unlock(&p->lock); git_error_set(GIT_ERROR_OS, "failed to lock packfile reader"); return error; } /* we found a unique entry in the index; * make sure the packfile backing the index * still exists on disk */ if (p->mwf.fd == -1) error = packfile_open_locked(p); git_mutex_unlock(&p->mwf.lock); git_mutex_unlock(&p->lock); if (error < 0) return error; e->offset = offset; e->p = p; git_oid_cpy(&e->sha1, &found_oid); return 0; } git2r/src/libgit2/src/strmap.c0000644000175000017500000000354114125111754016023 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "strmap.h" #define kmalloc git__malloc #define kcalloc git__calloc #define krealloc git__realloc #define kreallocarray git__reallocarray #define kfree git__free #include "khash.h" __KHASH_TYPE(str, const char *, void *) __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) int git_strmap_new(git_strmap **out) { *out = kh_init(str); GIT_ERROR_CHECK_ALLOC(*out); return 0; } void git_strmap_free(git_strmap *map) { kh_destroy(str, map); } void git_strmap_clear(git_strmap *map) { kh_clear(str, map); } size_t git_strmap_size(git_strmap *map) { return kh_size(map); } void *git_strmap_get(git_strmap *map, const char *key) { size_t idx = kh_get(str, map, key); if (idx == kh_end(map) || !kh_exist(map, idx)) return NULL; return kh_val(map, idx); } int git_strmap_set(git_strmap *map, const char *key, void *value) { size_t idx; int rval; idx = kh_put(str, map, key, &rval); if (rval < 0) return -1; if (rval == 0) kh_key(map, idx) = key; kh_val(map, idx) = value; return 0; } int git_strmap_delete(git_strmap *map, const char *key) { khiter_t idx = kh_get(str, map, key); if (idx == kh_end(map)) return GIT_ENOTFOUND; kh_del(str, map, idx); return 0; } int git_strmap_exists(git_strmap *map, const char *key) { return kh_get(str, map, key) != kh_end(map); } int git_strmap_iterate(void **value, git_strmap *map, size_t *iter, const char **key) { size_t i = *iter; while (i < map->n_buckets && !kh_exist(map, i)) i++; if (i >= map->n_buckets) return GIT_ITEROVER; if (key) *key = kh_key(map, i); if (value) *value = kh_val(map, i); *iter = ++i; return 0; } git2r/src/libgit2/src/netops.c0000644000175000017500000000520714125111754016026 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "netops.h" #include #include "git2/errors.h" #include "posix.h" #include "buffer.h" #include "http_parser.h" #include "runtime.h" int gitno_recv(gitno_buffer *buf) { return buf->recv(buf); } void gitno_buffer_setup_callback( gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data) { memset(data, 0x0, len); buf->data = data; buf->len = len; buf->offset = 0; buf->recv = recv; buf->cb_data = cb_data; } static int recv_stream(gitno_buffer *buf) { git_stream *io = (git_stream *) buf->cb_data; size_t readlen = buf->len - buf->offset; ssize_t ret; readlen = min(readlen, INT_MAX); ret = git_stream_read(io, buf->data + buf->offset, (int)readlen); if (ret < 0) return -1; buf->offset += ret; return (int)ret; } void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len) { memset(data, 0x0, len); buf->data = data; buf->len = len; buf->offset = 0; buf->recv = recv_stream; buf->cb_data = st; } /* Consume up to ptr and move the rest of the buffer to the beginning */ int gitno_consume(gitno_buffer *buf, const char *ptr) { size_t consumed; GIT_ASSERT(ptr - buf->data >= 0); GIT_ASSERT(ptr - buf->data <= (int) buf->len); consumed = ptr - buf->data; memmove(buf->data, ptr, buf->offset - consumed); memset(buf->data + buf->offset, 0x0, buf->len - buf->offset); buf->offset -= consumed; return 0; } /* Consume const bytes and move the rest of the buffer to the beginning */ void gitno_consume_n(gitno_buffer *buf, size_t cons) { memmove(buf->data, buf->data + cons, buf->len - buf->offset); memset(buf->data + cons, 0x0, buf->len - buf->offset); buf->offset -= cons; } /* Match host names according to RFC 2818 rules */ int gitno__match_host(const char *pattern, const char *host) { for (;;) { char c = git__tolower(*pattern++); if (c == '\0') return *host ? -1 : 0; if (c == '*') { c = *pattern; /* '*' at the end matches everything left */ if (c == '\0') return 0; /* * We've found a pattern, so move towards the next matching * char. The '.' is handled specially because wildcards aren't * allowed to cross subdomains. */ while(*host) { char h = git__tolower(*host); if (c == h) return gitno__match_host(pattern, host++); if (h == '.') return gitno__match_host(pattern, host); host++; } return -1; } if (c != git__tolower(*host++)) return -1; } return -1; } git2r/src/libgit2/src/apply.h0000644000175000017500000000103614125111754015644 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_apply_h__ #define INCLUDE_apply_h__ #include "common.h" #include "git2/patch.h" #include "git2/apply.h" #include "buffer.h" extern int git_apply__patch( git_buf *out, char **filename, unsigned int *mode, const char *source, size_t source_len, git_patch *patch, const git_apply_options *opts); #endif git2r/src/libgit2/src/midx.c0000644000175000017500000005654014125111754015465 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "midx.h" #include "array.h" #include "buffer.h" #include "filebuf.h" #include "futils.h" #include "hash.h" #include "odb.h" #include "pack.h" #include "path.h" #include "repository.h" #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */ #define MIDX_VERSION 1 #define MIDX_OBJECT_ID_VERSION 1 struct git_midx_header { uint32_t signature; uint8_t version; uint8_t object_id_version; uint8_t chunks; uint8_t base_midx_files; uint32_t packfiles; }; #define MIDX_PACKFILE_NAMES_ID 0x504e414d /* "PNAM" */ #define MIDX_OID_FANOUT_ID 0x4f494446 /* "OIDF" */ #define MIDX_OID_LOOKUP_ID 0x4f49444c /* "OIDL" */ #define MIDX_OBJECT_OFFSETS_ID 0x4f4f4646 /* "OOFF" */ #define MIDX_OBJECT_LARGE_OFFSETS_ID 0x4c4f4646 /* "LOFF" */ struct git_midx_chunk { off64_t offset; size_t length; }; typedef int (*midx_write_cb)(const char *buf, size_t size, void *cb_data); static int midx_error(const char *message) { git_error_set(GIT_ERROR_ODB, "invalid multi-pack-index file - %s", message); return -1; } static int midx_parse_packfile_names( git_midx_file *idx, const unsigned char *data, uint32_t packfiles, struct git_midx_chunk *chunk) { int error; uint32_t i; char *packfile_name = (char *)(data + chunk->offset); size_t chunk_size = chunk->length, len; if (chunk->offset == 0) return midx_error("missing Packfile Names chunk"); if (chunk->length == 0) return midx_error("empty Packfile Names chunk"); if ((error = git_vector_init(&idx->packfile_names, packfiles, git__strcmp_cb)) < 0) return error; for (i = 0; i < packfiles; ++i) { len = p_strnlen(packfile_name, chunk_size); if (len == 0) return midx_error("empty packfile name"); if (len + 1 > chunk_size) return midx_error("unterminated packfile name"); git_vector_insert(&idx->packfile_names, packfile_name); if (i && strcmp(git_vector_get(&idx->packfile_names, i - 1), packfile_name) >= 0) return midx_error("packfile names are not sorted"); if (strlen(packfile_name) <= strlen(".idx") || git__suffixcmp(packfile_name, ".idx") != 0) return midx_error("non-.idx packfile name"); if (strchr(packfile_name, '/') != NULL || strchr(packfile_name, '\\') != NULL) return midx_error("non-local packfile"); packfile_name += len + 1; chunk_size -= len + 1; } return 0; } static int midx_parse_oid_fanout( git_midx_file *idx, const unsigned char *data, struct git_midx_chunk *chunk_oid_fanout) { uint32_t i, nr; if (chunk_oid_fanout->offset == 0) return midx_error("missing OID Fanout chunk"); if (chunk_oid_fanout->length == 0) return midx_error("empty OID Fanout chunk"); if (chunk_oid_fanout->length != 256 * 4) return midx_error("OID Fanout chunk has wrong length"); idx->oid_fanout = (const uint32_t *)(data + chunk_oid_fanout->offset); nr = 0; for (i = 0; i < 256; ++i) { uint32_t n = ntohl(idx->oid_fanout[i]); if (n < nr) return midx_error("index is non-monotonic"); nr = n; } idx->num_objects = nr; return 0; } static int midx_parse_oid_lookup( git_midx_file *idx, const unsigned char *data, struct git_midx_chunk *chunk_oid_lookup) { uint32_t i; git_oid *oid, *prev_oid, zero_oid = {{0}}; if (chunk_oid_lookup->offset == 0) return midx_error("missing OID Lookup chunk"); if (chunk_oid_lookup->length == 0) return midx_error("empty OID Lookup chunk"); if (chunk_oid_lookup->length != idx->num_objects * GIT_OID_RAWSZ) return midx_error("OID Lookup chunk has wrong length"); idx->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset); prev_oid = &zero_oid; for (i = 0; i < idx->num_objects; ++i, ++oid) { if (git_oid_cmp(prev_oid, oid) >= 0) return midx_error("OID Lookup index is non-monotonic"); prev_oid = oid; } return 0; } static int midx_parse_object_offsets( git_midx_file *idx, const unsigned char *data, struct git_midx_chunk *chunk_object_offsets) { if (chunk_object_offsets->offset == 0) return midx_error("missing Object Offsets chunk"); if (chunk_object_offsets->length == 0) return midx_error("empty Object Offsets chunk"); if (chunk_object_offsets->length != idx->num_objects * 8) return midx_error("Object Offsets chunk has wrong length"); idx->object_offsets = data + chunk_object_offsets->offset; return 0; } static int midx_parse_object_large_offsets( git_midx_file *idx, const unsigned char *data, struct git_midx_chunk *chunk_object_large_offsets) { if (chunk_object_large_offsets->length == 0) return 0; if (chunk_object_large_offsets->length % 8 != 0) return midx_error("malformed Object Large Offsets chunk"); idx->object_large_offsets = data + chunk_object_large_offsets->offset; idx->num_object_large_offsets = chunk_object_large_offsets->length / 8; return 0; } int git_midx_parse( git_midx_file *idx, const unsigned char *data, size_t size) { struct git_midx_header *hdr; const unsigned char *chunk_hdr; struct git_midx_chunk *last_chunk; uint32_t i; off64_t last_chunk_offset, chunk_offset, trailer_offset; git_oid idx_checksum = {{0}}; int error; struct git_midx_chunk chunk_packfile_names = {0}, chunk_oid_fanout = {0}, chunk_oid_lookup = {0}, chunk_object_offsets = {0}, chunk_object_large_offsets = {0}; GIT_ASSERT_ARG(idx); if (size < sizeof(struct git_midx_header) + GIT_OID_RAWSZ) return midx_error("multi-pack index is too short"); hdr = ((struct git_midx_header *)data); if (hdr->signature != htonl(MIDX_SIGNATURE) || hdr->version != MIDX_VERSION || hdr->object_id_version != MIDX_OBJECT_ID_VERSION) { return midx_error("unsupported multi-pack index version"); } if (hdr->chunks == 0) return midx_error("no chunks in multi-pack index"); /* * The very first chunk's offset should be after the header, all the chunk * headers, and a special zero chunk. */ last_chunk_offset = sizeof(struct git_midx_header) + (1 + hdr->chunks) * 12; trailer_offset = size - GIT_OID_RAWSZ; if (trailer_offset < last_chunk_offset) return midx_error("wrong index size"); git_oid_cpy(&idx->checksum, (git_oid *)(data + trailer_offset)); if (git_hash_buf(&idx_checksum, data, (size_t)trailer_offset) < 0) return midx_error("could not calculate signature"); if (!git_oid_equal(&idx_checksum, &idx->checksum)) return midx_error("index signature mismatch"); chunk_hdr = data + sizeof(struct git_midx_header); last_chunk = NULL; for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) { chunk_offset = ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) << 32 | ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 8)))); if (chunk_offset < last_chunk_offset) return midx_error("chunks are non-monotonic"); if (chunk_offset >= trailer_offset) return midx_error("chunks extend beyond the trailer"); if (last_chunk != NULL) last_chunk->length = (size_t)(chunk_offset - last_chunk_offset); last_chunk_offset = chunk_offset; switch (ntohl(*((uint32_t *)(chunk_hdr + 0)))) { case MIDX_PACKFILE_NAMES_ID: chunk_packfile_names.offset = last_chunk_offset; last_chunk = &chunk_packfile_names; break; case MIDX_OID_FANOUT_ID: chunk_oid_fanout.offset = last_chunk_offset; last_chunk = &chunk_oid_fanout; break; case MIDX_OID_LOOKUP_ID: chunk_oid_lookup.offset = last_chunk_offset; last_chunk = &chunk_oid_lookup; break; case MIDX_OBJECT_OFFSETS_ID: chunk_object_offsets.offset = last_chunk_offset; last_chunk = &chunk_object_offsets; break; case MIDX_OBJECT_LARGE_OFFSETS_ID: chunk_object_large_offsets.offset = last_chunk_offset; last_chunk = &chunk_object_large_offsets; break; default: return midx_error("unrecognized chunk ID"); } } last_chunk->length = (size_t)(trailer_offset - last_chunk_offset); error = midx_parse_packfile_names( idx, data, ntohl(hdr->packfiles), &chunk_packfile_names); if (error < 0) return error; error = midx_parse_oid_fanout(idx, data, &chunk_oid_fanout); if (error < 0) return error; error = midx_parse_oid_lookup(idx, data, &chunk_oid_lookup); if (error < 0) return error; error = midx_parse_object_offsets(idx, data, &chunk_object_offsets); if (error < 0) return error; error = midx_parse_object_large_offsets(idx, data, &chunk_object_large_offsets); if (error < 0) return error; return 0; } int git_midx_open( git_midx_file **idx_out, const char *path) { git_midx_file *idx; git_file fd = -1; size_t idx_size; struct stat st; int error; /* TODO: properly open the file without access time using O_NOATIME */ fd = git_futils_open_ro(path); if (fd < 0) return fd; if (p_fstat(fd, &st) < 0) { p_close(fd); git_error_set(GIT_ERROR_ODB, "multi-pack-index file not found - '%s'", path); return -1; } if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size)) { p_close(fd); git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path); return -1; } idx_size = (size_t)st.st_size; idx = git__calloc(1, sizeof(git_midx_file)); GIT_ERROR_CHECK_ALLOC(idx); error = git_buf_sets(&idx->filename, path); if (error < 0) return error; error = git_futils_mmap_ro(&idx->index_map, fd, 0, idx_size); p_close(fd); if (error < 0) { git_midx_free(idx); return error; } if ((error = git_midx_parse(idx, idx->index_map.data, idx_size)) < 0) { git_midx_free(idx); return error; } *idx_out = idx; return 0; } bool git_midx_needs_refresh( const git_midx_file *idx, const char *path) { git_file fd = -1; struct stat st; ssize_t bytes_read; git_oid idx_checksum = {{0}}; /* TODO: properly open the file without access time using O_NOATIME */ fd = git_futils_open_ro(path); if (fd < 0) return true; if (p_fstat(fd, &st) < 0) { p_close(fd); return true; } if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size) || (size_t)st.st_size != idx->index_map.len) { p_close(fd); return true; } bytes_read = p_pread(fd, &idx_checksum, GIT_OID_RAWSZ, st.st_size - GIT_OID_RAWSZ); p_close(fd); if (bytes_read != GIT_OID_RAWSZ) return true; return !git_oid_equal(&idx_checksum, &idx->checksum); } int git_midx_entry_find( git_midx_entry *e, git_midx_file *idx, const git_oid *short_oid, size_t len) { int pos, found = 0; size_t pack_index; uint32_t hi, lo; const git_oid *current = NULL; const unsigned char *object_offset; off64_t offset; GIT_ASSERT_ARG(idx); hi = ntohl(idx->oid_fanout[(int)short_oid->id[0]]); lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(idx->oid_fanout[(int)short_oid->id[0] - 1])); pos = git_pack__lookup_sha1(idx->oid_lookup, GIT_OID_RAWSZ, lo, hi, short_oid->id); if (pos >= 0) { /* An object matching exactly the oid was found */ found = 1; current = idx->oid_lookup + pos; } else { /* No object was found */ /* pos refers to the object with the "closest" oid to short_oid */ pos = -1 - pos; if (pos < (int)idx->num_objects) { current = idx->oid_lookup + pos; if (!git_oid_ncmp(short_oid, current, len)) found = 1; } } if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)idx->num_objects) { /* Check for ambiguousity */ const git_oid *next = current + 1; if (!git_oid_ncmp(short_oid, next, len)) { found = 2; } } if (!found) return git_odb__error_notfound("failed to find offset for multi-pack index entry", short_oid, len); if (found > 1) return git_odb__error_ambiguous("found multiple offsets for multi-pack index entry"); object_offset = idx->object_offsets + pos * 8; offset = ntohl(*((uint32_t *)(object_offset + 4))); if (offset & 0x80000000) { uint32_t object_large_offsets_pos = offset & 0x7fffffff; const unsigned char *object_large_offsets_index = idx->object_large_offsets; /* Make sure we're not being sent out of bounds */ if (object_large_offsets_pos >= idx->num_object_large_offsets) return git_odb__error_notfound("invalid index into the object large offsets table", short_oid, len); object_large_offsets_index += 8 * object_large_offsets_pos; offset = (((uint64_t)ntohl(*((uint32_t *)(object_large_offsets_index + 0)))) << 32) | ntohl(*((uint32_t *)(object_large_offsets_index + 4))); } pack_index = ntohl(*((uint32_t *)(object_offset + 0))); if (pack_index >= git_vector_length(&idx->packfile_names)) return midx_error("invalid index into the packfile names table"); e->pack_index = pack_index; e->offset = offset; git_oid_cpy(&e->sha1, current); return 0; } int git_midx_foreach_entry( git_midx_file *idx, git_odb_foreach_cb cb, void *data) { size_t i; int error; GIT_ASSERT_ARG(idx); for (i = 0; i < idx->num_objects; ++i) { if ((error = cb(&idx->oid_lookup[i], data)) != 0) return git_error_set_after_callback(error); } return error; } int git_midx_close(git_midx_file *idx) { GIT_ASSERT_ARG(idx); if (idx->index_map.data) git_futils_mmap_free(&idx->index_map); git_vector_free(&idx->packfile_names); return 0; } void git_midx_free(git_midx_file *idx) { if (!idx) return; git_buf_dispose(&idx->filename); git_midx_close(idx); git__free(idx); } static int packfile__cmp(const void *a_, const void *b_) { const struct git_pack_file *a = a_; const struct git_pack_file *b = b_; return strcmp(a->pack_name, b->pack_name); } int git_midx_writer_new( git_midx_writer **out, const char *pack_dir) { git_midx_writer *w = git__calloc(1, sizeof(git_midx_writer)); GIT_ERROR_CHECK_ALLOC(w); if (git_buf_sets(&w->pack_dir, pack_dir) < 0) { git__free(w); return -1; } git_path_squash_slashes(&w->pack_dir); if (git_vector_init(&w->packs, 0, packfile__cmp) < 0) { git_buf_dispose(&w->pack_dir); git__free(w); return -1; } *out = w; return 0; } void git_midx_writer_free(git_midx_writer *w) { struct git_pack_file *p; size_t i; if (!w) return; git_vector_foreach (&w->packs, i, p) git_mwindow_put_pack(p); git_vector_free(&w->packs); git_buf_dispose(&w->pack_dir); git__free(w); } int git_midx_writer_add( git_midx_writer *w, const char *idx_path) { git_buf idx_path_buf = GIT_BUF_INIT; int error; struct git_pack_file *p; error = git_path_prettify(&idx_path_buf, idx_path, git_buf_cstr(&w->pack_dir)); if (error < 0) return error; error = git_mwindow_get_pack(&p, git_buf_cstr(&idx_path_buf)); git_buf_dispose(&idx_path_buf); if (error < 0) return error; error = git_vector_insert(&w->packs, p); if (error < 0) { git_mwindow_put_pack(p); return error; } return 0; } typedef git_array_t(git_midx_entry) object_entry_array_t; struct object_entry_cb_state { uint32_t pack_index; object_entry_array_t *object_entries_array; }; static int object_entry__cb(const git_oid *oid, off64_t offset, void *data) { struct object_entry_cb_state *state = (struct object_entry_cb_state *)data; git_midx_entry *entry = git_array_alloc(*state->object_entries_array); GIT_ERROR_CHECK_ALLOC(entry); git_oid_cpy(&entry->sha1, oid); entry->offset = offset; entry->pack_index = state->pack_index; return 0; } static int object_entry__cmp(const void *a_, const void *b_) { const git_midx_entry *a = (const git_midx_entry *)a_; const git_midx_entry *b = (const git_midx_entry *)b_; return git_oid_cmp(&a->sha1, &b->sha1); } static int write_offset(off64_t offset, midx_write_cb write_cb, void *cb_data) { int error; uint32_t word; word = htonl((uint32_t)((offset >> 32) & 0xffffffffu)); error = write_cb((const char *)&word, sizeof(word), cb_data); if (error < 0) return error; word = htonl((uint32_t)((offset >> 0) & 0xffffffffu)); error = write_cb((const char *)&word, sizeof(word), cb_data); if (error < 0) return error; return 0; } static int write_chunk_header(int chunk_id, off64_t offset, midx_write_cb write_cb, void *cb_data) { uint32_t word = htonl(chunk_id); int error = write_cb((const char *)&word, sizeof(word), cb_data); if (error < 0) return error; return write_offset(offset, write_cb, cb_data); return 0; } static int midx_write_buf(const char *buf, size_t size, void *data) { git_buf *b = (git_buf *)data; return git_buf_put(b, buf, size); } struct midx_write_hash_context { midx_write_cb write_cb; void *cb_data; git_hash_ctx *ctx; }; static int midx_write_hash(const char *buf, size_t size, void *data) { struct midx_write_hash_context *ctx = (struct midx_write_hash_context *)data; int error; error = git_hash_update(ctx->ctx, buf, size); if (error < 0) return error; return ctx->write_cb(buf, size, ctx->cb_data); } static int midx_write( git_midx_writer *w, midx_write_cb write_cb, void *cb_data) { int error = 0; size_t i; struct git_pack_file *p; struct git_midx_header hdr = {0}; uint32_t oid_fanout_count; uint32_t object_large_offsets_count; uint32_t oid_fanout[256]; off64_t offset; git_buf packfile_names = GIT_BUF_INIT, oid_lookup = GIT_BUF_INIT, object_offsets = GIT_BUF_INIT, object_large_offsets = GIT_BUF_INIT; git_oid idx_checksum = {{0}}; git_midx_entry *entry; object_entry_array_t object_entries_array = GIT_ARRAY_INIT; git_vector object_entries = GIT_VECTOR_INIT; git_hash_ctx ctx; struct midx_write_hash_context hash_cb_data = {0}; hdr.signature = htonl(MIDX_SIGNATURE); hdr.version = MIDX_VERSION; hdr.object_id_version = MIDX_OBJECT_ID_VERSION; hdr.base_midx_files = 0; hash_cb_data.write_cb = write_cb; hash_cb_data.cb_data = cb_data; hash_cb_data.ctx = &ctx; error = git_hash_ctx_init(&ctx); if (error < 0) return error; cb_data = &hash_cb_data; write_cb = midx_write_hash; git_vector_sort(&w->packs); git_vector_foreach (&w->packs, i, p) { git_buf relative_index = GIT_BUF_INIT; struct object_entry_cb_state state = {0}; size_t path_len; state.pack_index = (uint32_t)i; state.object_entries_array = &object_entries_array; error = git_buf_sets(&relative_index, p->pack_name); if (error < 0) goto cleanup; error = git_path_make_relative(&relative_index, git_buf_cstr(&w->pack_dir)); if (error < 0) { git_buf_dispose(&relative_index); goto cleanup; } path_len = git_buf_len(&relative_index); if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_cstr(&relative_index), ".pack") != 0) { git_buf_dispose(&relative_index); git_error_set(GIT_ERROR_INVALID, "invalid packfile name: '%s'", p->pack_name); error = -1; goto cleanup; } path_len -= strlen(".pack"); git_buf_put(&packfile_names, git_buf_cstr(&relative_index), path_len); git_buf_puts(&packfile_names, ".idx"); git_buf_putc(&packfile_names, '\0'); git_buf_dispose(&relative_index); error = git_pack_foreach_entry_offset(p, object_entry__cb, &state); if (error < 0) goto cleanup; } /* Sort the object entries. */ error = git_vector_init(&object_entries, git_array_size(object_entries_array), object_entry__cmp); if (error < 0) goto cleanup; git_array_foreach (object_entries_array, i, entry) { if ((error = git_vector_set(NULL, &object_entries, i, entry)) < 0) goto cleanup; } git_vector_set_sorted(&object_entries, 0); git_vector_sort(&object_entries); git_vector_uniq(&object_entries, NULL); /* Pad the packfile names so it is a multiple of four. */ while (git_buf_len(&packfile_names) & 3) git_buf_putc(&packfile_names, '\0'); /* Fill the OID Fanout table. */ oid_fanout_count = 0; for (i = 0; i < 256; i++) { while (oid_fanout_count < git_vector_length(&object_entries) && ((const git_midx_entry *)git_vector_get(&object_entries, oid_fanout_count))->sha1.id[0] <= i) ++oid_fanout_count; oid_fanout[i] = htonl(oid_fanout_count); } /* Fill the OID Lookup table. */ git_vector_foreach (&object_entries, i, entry) { error = git_buf_put(&oid_lookup, (const char *)&entry->sha1, sizeof(entry->sha1)); if (error < 0) goto cleanup; } /* Fill the Object Offsets and Object Large Offsets tables. */ object_large_offsets_count = 0; git_vector_foreach (&object_entries, i, entry) { uint32_t word; word = htonl((uint32_t)entry->pack_index); error = git_buf_put(&object_offsets, (const char *)&word, sizeof(word)); if (error < 0) goto cleanup; if (entry->offset >= 0x80000000l) { word = htonl(0x80000000u | object_large_offsets_count++); if ((error = write_offset(entry->offset, midx_write_buf, &object_large_offsets)) < 0) goto cleanup; } else { word = htonl((uint32_t)entry->offset & 0x7fffffffu); } error = git_buf_put(&object_offsets, (const char *)&word, sizeof(word)); if (error < 0) goto cleanup; } /* Write the header. */ hdr.packfiles = htonl((uint32_t)git_vector_length(&w->packs)); hdr.chunks = 4; if (git_buf_len(&object_large_offsets) > 0) hdr.chunks++; error = write_cb((const char *)&hdr, sizeof(hdr), cb_data); if (error < 0) goto cleanup; /* Write the chunk headers. */ offset = sizeof(hdr) + (hdr.chunks + 1) * 12; error = write_chunk_header(MIDX_PACKFILE_NAMES_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += git_buf_len(&packfile_names); error = write_chunk_header(MIDX_OID_FANOUT_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += sizeof(oid_fanout); error = write_chunk_header(MIDX_OID_LOOKUP_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += git_buf_len(&oid_lookup); error = write_chunk_header(MIDX_OBJECT_OFFSETS_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += git_buf_len(&object_offsets); if (git_buf_len(&object_large_offsets) > 0) { error = write_chunk_header(MIDX_OBJECT_LARGE_OFFSETS_ID, offset, write_cb, cb_data); if (error < 0) goto cleanup; offset += git_buf_len(&object_large_offsets); } error = write_chunk_header(0, offset, write_cb, cb_data); if (error < 0) goto cleanup; /* Write all the chunks. */ error = write_cb(git_buf_cstr(&packfile_names), git_buf_len(&packfile_names), cb_data); if (error < 0) goto cleanup; error = write_cb((const char *)oid_fanout, sizeof(oid_fanout), cb_data); if (error < 0) goto cleanup; error = write_cb(git_buf_cstr(&oid_lookup), git_buf_len(&oid_lookup), cb_data); if (error < 0) goto cleanup; error = write_cb(git_buf_cstr(&object_offsets), git_buf_len(&object_offsets), cb_data); if (error < 0) goto cleanup; error = write_cb(git_buf_cstr(&object_large_offsets), git_buf_len(&object_large_offsets), cb_data); if (error < 0) goto cleanup; /* Finalize the checksum and write the trailer. */ error = git_hash_final(&idx_checksum, &ctx); if (error < 0) goto cleanup; error = write_cb((const char *)&idx_checksum, sizeof(idx_checksum), cb_data); if (error < 0) goto cleanup; cleanup: git_array_clear(object_entries_array); git_vector_free(&object_entries); git_buf_dispose(&packfile_names); git_buf_dispose(&oid_lookup); git_buf_dispose(&object_offsets); git_buf_dispose(&object_large_offsets); git_hash_ctx_cleanup(&ctx); return error; } static int midx_write_filebuf(const char *buf, size_t size, void *data) { git_filebuf *f = (git_filebuf *)data; return git_filebuf_write(f, buf, size); } int git_midx_writer_commit( git_midx_writer *w) { int error; int filebuf_flags = GIT_FILEBUF_DO_NOT_BUFFER; git_buf midx_path = GIT_BUF_INIT; git_filebuf output = GIT_FILEBUF_INIT; error = git_buf_joinpath(&midx_path, git_buf_cstr(&w->pack_dir), "multi-pack-index"); if (error < 0) return error; if (git_repository__fsync_gitdir) filebuf_flags |= GIT_FILEBUF_FSYNC; error = git_filebuf_open(&output, git_buf_cstr(&midx_path), filebuf_flags, 0644); git_buf_dispose(&midx_path); if (error < 0) return error; error = midx_write(w, midx_write_filebuf, &output); if (error < 0) { git_filebuf_cleanup(&output); return error; } return git_filebuf_commit(&output); } int git_midx_writer_dump( git_buf *midx, git_midx_writer *w) { return midx_write(w, midx_write_buf, midx); } git2r/src/libgit2/src/merge.h0000644000175000017500000001336614125111754015627 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_merge_h__ #define INCLUDE_merge_h__ #include "common.h" #include "vector.h" #include "commit_list.h" #include "pool.h" #include "iterator.h" #include "git2/types.h" #include "git2/merge.h" #include "git2/sys/merge.h" #define GIT_MERGE_MSG_FILE "MERGE_MSG" #define GIT_MERGE_MODE_FILE "MERGE_MODE" #define GIT_MERGE_FILE_MODE 0666 #define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50 #define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000 /** Internal merge flags. */ enum { /** The merge is for a virtual base in a recursive merge. */ GIT_MERGE__VIRTUAL_BASE = (1 << 31), }; enum { /** Accept the conflict file, staging it as the merge result. */ GIT_MERGE_FILE_FAVOR__CONFLICTED = 4, }; /** Types of changes when files are merged from branch to branch. */ typedef enum { /* No conflict - a change only occurs in one branch. */ GIT_MERGE_DIFF_NONE = 0, /* Occurs when a file is modified in both branches. */ GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0), /* Occurs when a file is added in both branches. */ GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1), /* Occurs when a file is deleted in both branches. */ GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2), /* Occurs when a file is modified in one branch and deleted in the other. */ GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3), /* Occurs when a file is renamed in one branch and modified in the other. */ GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4), /* Occurs when a file is renamed in one branch and deleted in the other. */ GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5), /* Occurs when a file is renamed in one branch and a file with the same * name is added in the other. Eg, A->B and new file B. Core git calls * this a "rename/delete". */ GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6), /* Occurs when both a file is renamed to the same name in the ours and * theirs branches. Eg, A->B and A->B in both. Automergeable. */ GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7), /* Occurs when a file is renamed to different names in the ours and theirs * branches. Eg, A->B and A->C. */ GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8), /* Occurs when two files are renamed to the same name in the ours and * theirs branches. Eg, A->C and B->C. */ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9), /* Occurs when an item at a path in one branch is a directory, and an * item at the same path in a different branch is a file. */ GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10), /* The child of a folder that is in a directory/file conflict. */ GIT_MERGE_DIFF_DF_CHILD = (1 << 11), } git_merge_diff_t; typedef struct { git_repository *repo; git_pool pool; /* Vector of git_index_entry that represent the merged items that * have been staged, either because only one side changed, or because * the two changes were non-conflicting and mergeable. These items * will be written as staged entries in the main index. */ git_vector staged; /* Vector of git_merge_diff entries that represent the conflicts that * have not been automerged. These items will be written to high-stage * entries in the main index. */ git_vector conflicts; /* Vector of git_merge_diff that have been automerged. These items * will be written to the REUC when the index is produced. */ git_vector resolved; } git_merge_diff_list; /** * Description of changes to one file across three trees. */ typedef struct { git_merge_diff_t type; git_index_entry ancestor_entry; git_index_entry our_entry; git_delta_t our_status; git_index_entry their_entry; git_delta_t their_status; } git_merge_diff; int git_merge__bases_many( git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos, uint32_t minimum_generation); /* * Three-way tree differencing */ git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo); int git_merge_diff_list__find_differences( git_merge_diff_list *merge_diff_list, git_iterator *ancestor_iterator, git_iterator *ours_iter, git_iterator *theirs_iter); int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_options *opts); void git_merge_diff_list__free(git_merge_diff_list *diff_list); /* Merge metadata setup */ int git_merge__setup( git_repository *repo, const git_annotated_commit *our_head, const git_annotated_commit *heads[], size_t heads_len); int git_merge__iterators( git_index **out, git_repository *repo, git_iterator *ancestor_iter, git_iterator *our_iter, git_iterator *their_iter, const git_merge_options *given_opts); int git_merge__check_result(git_repository *repo, git_index *index_new); int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index); /* Merge files */ GIT_INLINE(const char *) git_merge_file__best_path( const char *ancestor, const char *ours, const char *theirs) { if (!ancestor) { if (ours && theirs && strcmp(ours, theirs) == 0) return ours; return NULL; } if (ours && strcmp(ancestor, ours) == 0) return theirs; else if(theirs && strcmp(ancestor, theirs) == 0) return ours; return NULL; } GIT_INLINE(uint32_t) git_merge_file__best_mode( uint32_t ancestor, uint32_t ours, uint32_t theirs) { /* * If ancestor didn't exist and either ours or theirs is executable, * assume executable. Otherwise, if any mode changed from the ancestor, * use that one. */ if (!ancestor) { if (ours == GIT_FILEMODE_BLOB_EXECUTABLE || theirs == GIT_FILEMODE_BLOB_EXECUTABLE) return GIT_FILEMODE_BLOB_EXECUTABLE; return GIT_FILEMODE_BLOB; } else if (ours && theirs) { if (ancestor == ours) return theirs; return ours; } return 0; } #endif git2r/src/libgit2/src/object.h0000644000175000017500000000337414125111754015774 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_object_h__ #define INCLUDE_object_h__ #include "common.h" #include "repository.h" #define GIT_OBJECT_SIZE_MAX UINT64_MAX extern bool git_object__strict_input_validation; /** Base git object for inheritance */ struct git_object { git_cached_obj cached; git_repository *repo; }; /* fully free the object; internal method, DO NOT EXPORT */ void git_object__free(void *object); /* * Parse object from raw data. Note that the resulting object is * tied to the lifetime of the data, as some objects simply point * into it. */ int git_object__from_raw( git_object **object_out, const char *data, size_t size, git_object_t type); int git_object__from_odb_object( git_object **object_out, git_repository *repo, git_odb_object *odb_obj, git_object_t type); int git_object__resolve_to_type(git_object **obj, git_object_t type); git_object_t git_object_stringn2type(const char *str, size_t len); int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header); void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); bool git_object__is_valid( git_repository *repo, const git_oid *id, git_object_t expected_type); GIT_INLINE(git_object_t) git_object__type_from_filemode(git_filemode_t mode) { switch (mode) { case GIT_FILEMODE_TREE: return GIT_OBJECT_TREE; case GIT_FILEMODE_COMMIT: return GIT_OBJECT_COMMIT; case GIT_FILEMODE_BLOB: case GIT_FILEMODE_BLOB_EXECUTABLE: case GIT_FILEMODE_LINK: return GIT_OBJECT_BLOB; default: return GIT_OBJECT_INVALID; } } #endif git2r/src/libgit2/src/clone.h0000644000175000017500000000103414125111754015615 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_clone_h__ #define INCLUDE_clone_h__ #include "common.h" #include "git2/clone.h" extern int git_clone__submodule(git_repository **out, const char *url, const char *local_path, const git_clone_options *_options); extern int git_clone__should_clone_local(const char *url, git_clone_local_t local); #endif git2r/src/libgit2/src/revwalk.c0000644000175000017500000004524514125111754016177 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "revwalk.h" #include "commit.h" #include "odb.h" #include "pool.h" #include "git2/revparse.h" #include "merge.h" #include "vector.h" static int get_revision(git_commit_list_node **out, git_revwalk *walk, git_commit_list **list); git_commit_list_node *git_revwalk__commit_lookup( git_revwalk *walk, const git_oid *oid) { git_commit_list_node *commit; /* lookup and reserve space if not already present */ if ((commit = git_oidmap_get(walk->commits, oid)) != NULL) return commit; commit = git_commit_list_alloc_node(walk); if (commit == NULL) return NULL; git_oid_cpy(&commit->oid, oid); if ((git_oidmap_set(walk->commits, &commit->oid, commit)) < 0) return NULL; return commit; } int git_revwalk__push_commit(git_revwalk *walk, const git_oid *oid, const git_revwalk__push_options *opts) { git_oid commit_id; int error; git_object *obj, *oobj; git_commit_list_node *commit; git_commit_list *list; if ((error = git_object_lookup(&oobj, walk->repo, oid, GIT_OBJECT_ANY)) < 0) return error; error = git_object_peel(&obj, oobj, GIT_OBJECT_COMMIT); git_object_free(oobj); if (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC || error == GIT_EPEEL) { /* If this comes from e.g. push_glob("tags"), ignore this */ if (opts->from_glob) return 0; git_error_set(GIT_ERROR_INVALID, "object is not a committish"); return error; } if (error < 0) return error; git_oid_cpy(&commit_id, git_object_id(obj)); git_object_free(obj); commit = git_revwalk__commit_lookup(walk, &commit_id); if (commit == NULL) return -1; /* error already reported by failed lookup */ /* A previous hide already told us we don't want this commit */ if (commit->uninteresting) return 0; if (opts->uninteresting) { walk->limited = 1; walk->did_hide = 1; } else { walk->did_push = 1; } commit->uninteresting = opts->uninteresting; list = walk->user_input; if ((opts->insert_by_date && git_commit_list_insert_by_date(commit, &list) == NULL) || git_commit_list_insert(commit, &list) == NULL) { git_error_set_oom(); return -1; } walk->user_input = list; return 0; } int git_revwalk_push(git_revwalk *walk, const git_oid *oid) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(oid); return git_revwalk__push_commit(walk, oid, &opts); } int git_revwalk_hide(git_revwalk *walk, const git_oid *oid) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(oid); opts.uninteresting = 1; return git_revwalk__push_commit(walk, oid, &opts); } int git_revwalk__push_ref(git_revwalk *walk, const char *refname, const git_revwalk__push_options *opts) { git_oid oid; if (git_reference_name_to_id(&oid, walk->repo, refname) < 0) return -1; return git_revwalk__push_commit(walk, &oid, opts); } int git_revwalk__push_glob(git_revwalk *walk, const char *glob, const git_revwalk__push_options *given_opts) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; int error = 0; git_buf buf = GIT_BUF_INIT; git_reference *ref; git_reference_iterator *iter; size_t wildcard; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(glob); if (given_opts) memcpy(&opts, given_opts, sizeof(opts)); /* refs/ is implied if not given in the glob */ if (git__prefixcmp(glob, GIT_REFS_DIR) != 0) git_buf_joinpath(&buf, GIT_REFS_DIR, glob); else git_buf_puts(&buf, glob); GIT_ERROR_CHECK_ALLOC_BUF(&buf); /* If no '?', '*' or '[' exist, we append '/ *' to the glob */ wildcard = strcspn(glob, "?*["); if (!glob[wildcard]) git_buf_put(&buf, "/*", 2); if ((error = git_reference_iterator_glob_new(&iter, walk->repo, buf.ptr)) < 0) goto out; opts.from_glob = true; while ((error = git_reference_next(&ref, iter)) == 0) { error = git_revwalk__push_ref(walk, git_reference_name(ref), &opts); git_reference_free(ref); if (error < 0) break; } git_reference_iterator_free(iter); if (error == GIT_ITEROVER) error = 0; out: git_buf_dispose(&buf); return error; } int git_revwalk_push_glob(git_revwalk *walk, const char *glob) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(glob); return git_revwalk__push_glob(walk, glob, &opts); } int git_revwalk_hide_glob(git_revwalk *walk, const char *glob) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(glob); opts.uninteresting = 1; return git_revwalk__push_glob(walk, glob, &opts); } int git_revwalk_push_head(git_revwalk *walk) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); return git_revwalk__push_ref(walk, GIT_HEAD_FILE, &opts); } int git_revwalk_hide_head(git_revwalk *walk) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); opts.uninteresting = 1; return git_revwalk__push_ref(walk, GIT_HEAD_FILE, &opts); } int git_revwalk_push_ref(git_revwalk *walk, const char *refname) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(refname); return git_revwalk__push_ref(walk, refname, &opts); } int git_revwalk_push_range(git_revwalk *walk, const char *range) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; git_revspec revspec; int error = 0; if ((error = git_revparse(&revspec, walk->repo, range))) return error; if (!revspec.to) { git_error_set(GIT_ERROR_INVALID, "invalid revspec: range not provided"); error = GIT_EINVALIDSPEC; goto out; } if (revspec.flags & GIT_REVSPEC_MERGE_BASE) { /* TODO: support "..." */ git_error_set(GIT_ERROR_INVALID, "symmetric differences not implemented in revwalk"); error = GIT_EINVALIDSPEC; goto out; } opts.uninteresting = 1; if ((error = git_revwalk__push_commit(walk, git_object_id(revspec.from), &opts))) goto out; opts.uninteresting = 0; error = git_revwalk__push_commit(walk, git_object_id(revspec.to), &opts); out: git_object_free(revspec.from); git_object_free(revspec.to); return error; } int git_revwalk_hide_ref(git_revwalk *walk, const char *refname) { git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(refname); opts.uninteresting = 1; return git_revwalk__push_ref(walk, refname, &opts); } static int revwalk_enqueue_timesort(git_revwalk *walk, git_commit_list_node *commit) { return git_pqueue_insert(&walk->iterator_time, commit); } static int revwalk_enqueue_unsorted(git_revwalk *walk, git_commit_list_node *commit) { return git_commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1; } static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk *walk) { git_commit_list_node *next; while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) { /* Some commits might become uninteresting after being added to the list */ if (!next->uninteresting) { *object_out = next; return 0; } } git_error_clear(); return GIT_ITEROVER; } static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk *walk) { int error; git_commit_list_node *next; while (!(error = get_revision(&next, walk, &walk->iterator_rand))) { /* Some commits might become uninteresting after being added to the list */ if (!next->uninteresting) { *object_out = next; return 0; } } return error; } static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk) { int error; git_commit_list_node *next; while (!(error = get_revision(&next, walk, &walk->iterator_topo))) { /* Some commits might become uninteresting after being added to the list */ if (!next->uninteresting) { *object_out = next; return 0; } } return error; } static int revwalk_next_reverse(git_commit_list_node **object_out, git_revwalk *walk) { *object_out = git_commit_list_pop(&walk->iterator_reverse); return *object_out ? 0 : GIT_ITEROVER; } static void mark_parents_uninteresting(git_commit_list_node *commit) { unsigned short i; git_commit_list *parents = NULL; for (i = 0; i < commit->out_degree; i++) git_commit_list_insert(commit->parents[i], &parents); while (parents) { commit = git_commit_list_pop(&parents); while (commit) { if (commit->uninteresting) break; commit->uninteresting = 1; /* * If we've reached this commit some other way * already, we need to mark its parents uninteresting * as well. */ if (!commit->parents) break; for (i = 0; i < commit->out_degree; i++) git_commit_list_insert(commit->parents[i], &parents); commit = commit->parents[0]; } } } static int add_parents_to_list(git_revwalk *walk, git_commit_list_node *commit, git_commit_list **list) { unsigned short i; int error; if (commit->added) return 0; commit->added = 1; /* * Go full on in the uninteresting case as we want to include * as many of these as we can. * * Usually we haven't parsed the parent of a parent, but if we * have it we reached it via other means so we want to mark * its parents recursively too. */ if (commit->uninteresting) { for (i = 0; i < commit->out_degree; i++) { git_commit_list_node *p = commit->parents[i]; p->uninteresting = 1; /* git does it gently here, but we don't like missing objects */ if ((error = git_commit_list_parse(walk, p)) < 0) return error; if (p->parents) mark_parents_uninteresting(p); p->seen = 1; git_commit_list_insert_by_date(p, list); } return 0; } /* * Now on to what we do if the commit is indeed * interesting. Here we do want things like first-parent take * effect as this is what we'll be showing. */ for (i = 0; i < commit->out_degree; i++) { git_commit_list_node *p = commit->parents[i]; if ((error = git_commit_list_parse(walk, p)) < 0) return error; if (walk->hide_cb && walk->hide_cb(&p->oid, walk->hide_cb_payload)) continue; if (!p->seen) { p->seen = 1; git_commit_list_insert_by_date(p, list); } if (walk->first_parent) break; } return 0; } /* How many unintersting commits we want to look at after we run out of interesting ones */ #define SLOP 5 static int still_interesting(git_commit_list *list, int64_t time, int slop) { /* The empty list is pretty boring */ if (!list) return 0; /* * If the destination list has commits with an earlier date than our * source, we want to reset the slop counter as we're not done. */ if (time <= list->item->time) return SLOP; for (; list; list = list->next) { /* * If the destination list still contains interesting commits we * want to continue looking. */ if (!list->item->uninteresting || list->item->time > time) return SLOP; } /* Everything's uninteresting, reduce the count */ return slop - 1; } static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list *commits) { int error, slop = SLOP; int64_t time = INT64_MAX; git_commit_list *list = commits; git_commit_list *newlist = NULL; git_commit_list **p = &newlist; while (list) { git_commit_list_node *commit = git_commit_list_pop(&list); if ((error = add_parents_to_list(walk, commit, &list)) < 0) return error; if (commit->uninteresting) { mark_parents_uninteresting(commit); slop = still_interesting(list, time, slop); if (slop) continue; break; } if (walk->hide_cb && walk->hide_cb(&commit->oid, walk->hide_cb_payload)) continue; time = commit->time; p = &git_commit_list_insert(commit, p)->next; } git_commit_list_free(&list); *out = newlist; return 0; } static int get_revision(git_commit_list_node **out, git_revwalk *walk, git_commit_list **list) { int error; git_commit_list_node *commit; commit = git_commit_list_pop(list); if (!commit) { git_error_clear(); return GIT_ITEROVER; } /* * If we did not run limit_list and we must add parents to the * list ourselves. */ if (!walk->limited) { if ((error = add_parents_to_list(walk, commit, list)) < 0) return error; } *out = commit; return 0; } static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, git_commit_list *list) { git_commit_list *ll = NULL, *newlist, **pptr; git_commit_list_node *next; git_pqueue queue; git_vector_cmp queue_cmp = NULL; unsigned short i; int error; if (walk->sorting & GIT_SORT_TIME) queue_cmp = git_commit_list_time_cmp; if ((error = git_pqueue_init(&queue, 0, 8, queue_cmp))) return error; /* * Start by resetting the in-degree to 1 for the commits in * our list. We want to go through this list again, so we * store it in the commit list as we extract it from the lower * machinery. */ for (ll = list; ll; ll = ll->next) { ll->item->in_degree = 1; } /* * Count up how many children each commit has. We limit * ourselves to those commits in the original list (in-degree * of 1) avoiding setting it for any parent that was hidden. */ for(ll = list; ll; ll = ll->next) { for (i = 0; i < ll->item->out_degree; ++i) { git_commit_list_node *parent = ll->item->parents[i]; if (parent->in_degree) parent->in_degree++; } } /* * Now we find the tips i.e. those not reachable from any other node * i.e. those which still have an in-degree of 1. */ for(ll = list; ll; ll = ll->next) { if (ll->item->in_degree == 1) { if ((error = git_pqueue_insert(&queue, ll->item))) goto cleanup; } } /* * We need to output the tips in the order that they came out of the * traversal, so if we're not doing time-sorting, we need to reverse the * pqueue in order to get them to come out as we inserted them. */ if ((walk->sorting & GIT_SORT_TIME) == 0) git_pqueue_reverse(&queue); pptr = &newlist; newlist = NULL; while ((next = git_pqueue_pop(&queue)) != NULL) { for (i = 0; i < next->out_degree; ++i) { git_commit_list_node *parent = next->parents[i]; if (parent->in_degree == 0) continue; if (--parent->in_degree == 1) { if ((error = git_pqueue_insert(&queue, parent))) goto cleanup; } } /* All the children of 'item' have been emitted (since we got to it via the priority queue) */ next->in_degree = 0; pptr = &git_commit_list_insert(next, pptr)->next; } *out = newlist; error = 0; cleanup: git_pqueue_free(&queue); return error; } static int prepare_walk(git_revwalk *walk) { int error = 0; git_commit_list *list, *commits = NULL; git_commit_list_node *next; /* If there were no pushes, we know that the walk is already over */ if (!walk->did_push) { git_error_clear(); return GIT_ITEROVER; } for (list = walk->user_input; list; list = list->next) { git_commit_list_node *commit = list->item; if ((error = git_commit_list_parse(walk, commit)) < 0) return error; if (commit->uninteresting) mark_parents_uninteresting(commit); if (!commit->seen) { commit->seen = 1; git_commit_list_insert(commit, &commits); } } if (walk->limited && (error = limit_list(&commits, walk, commits)) < 0) return error; if (walk->sorting & GIT_SORT_TOPOLOGICAL) { error = sort_in_topological_order(&walk->iterator_topo, walk, commits); git_commit_list_free(&commits); if (error < 0) return error; walk->get_next = &revwalk_next_toposort; } else if (walk->sorting & GIT_SORT_TIME) { for (list = commits; list && !error; list = list->next) error = walk->enqueue(walk, list->item); git_commit_list_free(&commits); if (error < 0) return error; } else { walk->iterator_rand = commits; walk->get_next = revwalk_next_unsorted; } if (walk->sorting & GIT_SORT_REVERSE) { while ((error = walk->get_next(&next, walk)) == 0) if (git_commit_list_insert(next, &walk->iterator_reverse) == NULL) return -1; if (error != GIT_ITEROVER) return error; walk->get_next = &revwalk_next_reverse; } walk->walking = 1; return 0; } int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) { git_revwalk *walk = git__calloc(1, sizeof(git_revwalk)); GIT_ERROR_CHECK_ALLOC(walk); if (git_oidmap_new(&walk->commits) < 0 || git_pqueue_init(&walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0 || git_pool_init(&walk->commit_pool, COMMIT_ALLOC) < 0) return -1; walk->get_next = &revwalk_next_unsorted; walk->enqueue = &revwalk_enqueue_unsorted; walk->repo = repo; if (git_repository_odb(&walk->odb, repo) < 0) { git_revwalk_free(walk); return -1; } *revwalk_out = walk; return 0; } void git_revwalk_free(git_revwalk *walk) { if (walk == NULL) return; git_revwalk_reset(walk); git_odb_free(walk->odb); git_oidmap_free(walk->commits); git_pool_clear(&walk->commit_pool); git_pqueue_free(&walk->iterator_time); git__free(walk); } git_repository *git_revwalk_repository(git_revwalk *walk) { GIT_ASSERT_ARG_WITH_RETVAL(walk, NULL); return walk->repo; } int git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode) { GIT_ASSERT_ARG(walk); if (walk->walking) git_revwalk_reset(walk); walk->sorting = sort_mode; if (walk->sorting & GIT_SORT_TIME) { walk->get_next = &revwalk_next_timesort; walk->enqueue = &revwalk_enqueue_timesort; } else { walk->get_next = &revwalk_next_unsorted; walk->enqueue = &revwalk_enqueue_unsorted; } if (walk->sorting != GIT_SORT_NONE) walk->limited = 1; return 0; } int git_revwalk_simplify_first_parent(git_revwalk *walk) { walk->first_parent = 1; return 0; } int git_revwalk_next(git_oid *oid, git_revwalk *walk) { int error; git_commit_list_node *next; GIT_ASSERT_ARG(walk); GIT_ASSERT_ARG(oid); if (!walk->walking) { if ((error = prepare_walk(walk)) < 0) return error; } error = walk->get_next(&next, walk); if (error == GIT_ITEROVER) { git_revwalk_reset(walk); git_error_clear(); return GIT_ITEROVER; } if (!error) git_oid_cpy(oid, &next->oid); return error; } int git_revwalk_reset(git_revwalk *walk) { git_commit_list_node *commit; GIT_ASSERT_ARG(walk); git_oidmap_foreach_value(walk->commits, commit, { commit->seen = 0; commit->in_degree = 0; commit->topo_delay = 0; commit->uninteresting = 0; commit->added = 0; commit->flags = 0; }); git_pqueue_clear(&walk->iterator_time); git_commit_list_free(&walk->iterator_topo); git_commit_list_free(&walk->iterator_rand); git_commit_list_free(&walk->iterator_reverse); git_commit_list_free(&walk->user_input); walk->first_parent = 0; walk->walking = 0; walk->limited = 0; walk->did_push = walk->did_hide = 0; walk->sorting = GIT_SORT_NONE; return 0; } int git_revwalk_add_hide_cb( git_revwalk *walk, git_revwalk_hide_cb hide_cb, void *payload) { GIT_ASSERT_ARG(walk); if (walk->walking) git_revwalk_reset(walk); walk->hide_cb = hide_cb; walk->hide_cb_payload = payload; if (hide_cb) walk->limited = 1; return 0; } git2r/src/libgit2/src/errors.h0000644000175000017500000000413414125111754016035 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_errors_h__ #define INCLUDE_errors_h__ #include "common.h" /* * Set the error message for this thread, formatting as needed. */ void git_error_set(int error_class, const char *fmt, ...) GIT_FORMAT_PRINTF(2, 3); void git_error_vset(int error_class, const char *fmt, va_list ap); /** * Set error message for user callback if needed. * * If the error code in non-zero and no error message is set, this * sets a generic error message. * * @return This always returns the `error_code` parameter. */ GIT_INLINE(int) git_error_set_after_callback_function( int error_code, const char *action) { if (error_code) { const git_error *e = git_error_last(); if (!e || !e->message) git_error_set(e ? e->klass : GIT_ERROR_CALLBACK, "%s callback returned %d", action, error_code); } return error_code; } #ifdef GIT_WIN32 #define git_error_set_after_callback(code) \ git_error_set_after_callback_function((code), __FUNCTION__) #else #define git_error_set_after_callback(code) \ git_error_set_after_callback_function((code), __func__) #endif /** * Gets the system error code for this thread. */ int git_error_system_last(void); /** * Sets the system error code for this thread. */ void git_error_system_set(int code); /** * Structure to preserve libgit2 error state */ typedef struct { int error_code; unsigned int oom : 1; git_error error_msg; } git_error_state; /** * Capture current error state to restore later, returning error code. * If `error_code` is zero, this does not clear the current error state. * You must either restore this error state, or free it. */ extern int git_error_state_capture(git_error_state *state, int error_code); /** * Restore error state to a previous value, returning saved error code. */ extern int git_error_state_restore(git_error_state *state); /** Free an error state. */ extern void git_error_state_free(git_error_state *state); #endif git2r/src/libgit2/src/filebuf.c0000644000175000017500000003165614125111754016141 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "filebuf.h" #include "futils.h" static const size_t WRITE_BUFFER_SIZE = (4096 * 2); enum buferr_t { BUFERR_OK = 0, BUFERR_WRITE, BUFERR_ZLIB, BUFERR_MEM }; #define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; } static int verify_last_error(git_filebuf *file) { switch (file->last_error) { case BUFERR_WRITE: git_error_set(GIT_ERROR_OS, "failed to write out file"); return -1; case BUFERR_MEM: git_error_set_oom(); return -1; case BUFERR_ZLIB: git_error_set(GIT_ERROR_ZLIB, "Buffer error when writing out ZLib data"); return -1; default: return 0; } } static int lock_file(git_filebuf *file, int flags, mode_t mode) { if (git_path_exists(file->path_lock) == true) { git_error_clear(); /* actual OS error code just confuses */ git_error_set(GIT_ERROR_OS, "failed to lock file '%s' for writing", file->path_lock); return GIT_ELOCKED; } /* create path to the file buffer is required */ if (flags & GIT_FILEBUF_CREATE_LEADING_DIRS) { /* XXX: Should dirmode here be configurable? Or is 0777 always fine? */ file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, mode); } else { file->fd = git_futils_creat_locked(file->path_lock, mode); } if (file->fd < 0) return file->fd; file->fd_is_open = true; if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) { git_file source; char buffer[FILEIO_BUFSIZE]; ssize_t read_bytes; int error = 0; source = p_open(file->path_original, O_RDONLY); if (source < 0) { git_error_set(GIT_ERROR_OS, "failed to open file '%s' for reading", file->path_original); return -1; } while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) { if ((error = p_write(file->fd, buffer, read_bytes)) < 0) break; if (file->compute_digest) git_hash_update(&file->digest, buffer, read_bytes); } p_close(source); if (read_bytes < 0) { git_error_set(GIT_ERROR_OS, "failed to read file '%s'", file->path_original); return -1; } else if (error < 0) { git_error_set(GIT_ERROR_OS, "failed to write file '%s'", file->path_lock); return -1; } } return 0; } void git_filebuf_cleanup(git_filebuf *file) { if (file->fd_is_open && file->fd >= 0) p_close(file->fd); if (file->created_lock && !file->did_rename && file->path_lock && git_path_exists(file->path_lock)) p_unlink(file->path_lock); if (file->compute_digest) { git_hash_ctx_cleanup(&file->digest); file->compute_digest = 0; } if (file->buffer) git__free(file->buffer); /* use the presence of z_buf to decide if we need to deflateEnd */ if (file->z_buf) { git__free(file->z_buf); deflateEnd(&file->zs); } if (file->path_original) git__free(file->path_original); if (file->path_lock) git__free(file->path_lock); memset(file, 0x0, sizeof(git_filebuf)); file->fd = -1; } GIT_INLINE(int) flush_buffer(git_filebuf *file) { int result = file->write(file, file->buffer, file->buf_pos); file->buf_pos = 0; return result; } int git_filebuf_flush(git_filebuf *file) { return flush_buffer(file); } static int write_normal(git_filebuf *file, void *source, size_t len) { if (len > 0) { if (p_write(file->fd, (void *)source, len) < 0) { file->last_error = BUFERR_WRITE; return -1; } if (file->compute_digest) git_hash_update(&file->digest, source, len); } return 0; } static int write_deflate(git_filebuf *file, void *source, size_t len) { z_stream *zs = &file->zs; if (len > 0 || file->flush_mode == Z_FINISH) { zs->next_in = source; zs->avail_in = (uInt)len; do { size_t have; zs->next_out = file->z_buf; zs->avail_out = (uInt)file->buf_size; if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) { file->last_error = BUFERR_ZLIB; return -1; } have = file->buf_size - (size_t)zs->avail_out; if (p_write(file->fd, file->z_buf, have) < 0) { file->last_error = BUFERR_WRITE; return -1; } } while (zs->avail_out == 0); GIT_ASSERT(zs->avail_in == 0); if (file->compute_digest) git_hash_update(&file->digest, source, len); } return 0; } #define MAX_SYMLINK_DEPTH 5 static int resolve_symlink(git_buf *out, const char *path) { int i, error, root; ssize_t ret; struct stat st; git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT; if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 || (error = git_buf_puts(&curpath, path)) < 0) return error; for (i = 0; i < MAX_SYMLINK_DEPTH; i++) { error = p_lstat(curpath.ptr, &st); if (error < 0 && errno == ENOENT) { error = git_buf_puts(out, curpath.ptr); goto cleanup; } if (error < 0) { git_error_set(GIT_ERROR_OS, "failed to stat '%s'", curpath.ptr); error = -1; goto cleanup; } if (!S_ISLNK(st.st_mode)) { error = git_buf_puts(out, curpath.ptr); goto cleanup; } ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX); if (ret < 0) { git_error_set(GIT_ERROR_OS, "failed to read symlink '%s'", curpath.ptr); error = -1; goto cleanup; } if (ret == GIT_PATH_MAX) { git_error_set(GIT_ERROR_INVALID, "symlink target too long"); error = -1; goto cleanup; } /* readlink(2) won't NUL-terminate for us */ target.ptr[ret] = '\0'; target.size = ret; root = git_path_root(target.ptr); if (root >= 0) { if ((error = git_buf_sets(&curpath, target.ptr)) < 0) goto cleanup; } else { git_buf dir = GIT_BUF_INIT; if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0) goto cleanup; git_buf_swap(&curpath, &dir); git_buf_dispose(&dir); if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0) goto cleanup; } } git_error_set(GIT_ERROR_INVALID, "maximum symlink depth reached"); error = -1; cleanup: git_buf_dispose(&curpath); git_buf_dispose(&target); return error; } int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode) { return git_filebuf_open_withsize(file, path, flags, mode, WRITE_BUFFER_SIZE); } int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size) { int compression, error = -1; size_t path_len, alloc_len; GIT_ASSERT_ARG(file); GIT_ASSERT_ARG(path); GIT_ASSERT(file->buffer == NULL); memset(file, 0x0, sizeof(git_filebuf)); if (flags & GIT_FILEBUF_DO_NOT_BUFFER) file->do_not_buffer = true; if (flags & GIT_FILEBUF_FSYNC) file->do_fsync = true; file->buf_size = size; file->buf_pos = 0; file->fd = -1; file->last_error = BUFERR_OK; /* Allocate the main cache buffer */ if (!file->do_not_buffer) { file->buffer = git__malloc(file->buf_size); GIT_ERROR_CHECK_ALLOC(file->buffer); } /* If we are hashing on-write, allocate a new hash context */ if (flags & GIT_FILEBUF_HASH_CONTENTS) { file->compute_digest = 1; if (git_hash_ctx_init(&file->digest) < 0) goto cleanup; } compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT; /* If we are deflating on-write, */ if (compression != 0) { /* Initialize the ZLib stream */ if (deflateInit(&file->zs, compression) != Z_OK) { git_error_set(GIT_ERROR_ZLIB, "failed to initialize zlib"); goto cleanup; } /* Allocate the Zlib cache buffer */ file->z_buf = git__malloc(file->buf_size); GIT_ERROR_CHECK_ALLOC(file->z_buf); /* Never flush */ file->flush_mode = Z_NO_FLUSH; file->write = &write_deflate; } else { file->write = &write_normal; } /* If we are writing to a temp file */ if (flags & GIT_FILEBUF_TEMPORARY) { git_buf tmp_path = GIT_BUF_INIT; /* Open the file as temporary for locking */ file->fd = git_futils_mktmp(&tmp_path, path, mode); if (file->fd < 0) { git_buf_dispose(&tmp_path); goto cleanup; } file->fd_is_open = true; file->created_lock = true; /* No original path */ file->path_original = NULL; file->path_lock = git_buf_detach(&tmp_path); GIT_ERROR_CHECK_ALLOC(file->path_lock); } else { git_buf resolved_path = GIT_BUF_INIT; if ((error = resolve_symlink(&resolved_path, path)) < 0) goto cleanup; /* Save the original path of the file */ path_len = resolved_path.size; file->path_original = git_buf_detach(&resolved_path); /* create the locking path by appending ".lock" to the original */ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH); file->path_lock = git__malloc(alloc_len); GIT_ERROR_CHECK_ALLOC(file->path_lock); memcpy(file->path_lock, file->path_original, path_len); memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); if (git_path_isdir(file->path_original)) { git_error_set(GIT_ERROR_FILESYSTEM, "path '%s' is a directory", file->path_original); error = GIT_EDIRECTORY; goto cleanup; } /* open the file for locking */ if ((error = lock_file(file, flags, mode)) < 0) goto cleanup; file->created_lock = true; } return 0; cleanup: git_filebuf_cleanup(file); return error; } int git_filebuf_hash(git_oid *oid, git_filebuf *file) { GIT_ASSERT_ARG(oid); GIT_ASSERT_ARG(file); GIT_ASSERT_ARG(file->compute_digest); flush_buffer(file); if (verify_last_error(file) < 0) return -1; git_hash_final(oid, &file->digest); git_hash_ctx_cleanup(&file->digest); file->compute_digest = 0; return 0; } int git_filebuf_commit_at(git_filebuf *file, const char *path) { git__free(file->path_original); file->path_original = git__strdup(path); GIT_ERROR_CHECK_ALLOC(file->path_original); return git_filebuf_commit(file); } int git_filebuf_commit(git_filebuf *file) { /* temporary files cannot be committed */ GIT_ASSERT_ARG(file); GIT_ASSERT(file->path_original); file->flush_mode = Z_FINISH; flush_buffer(file); if (verify_last_error(file) < 0) goto on_error; file->fd_is_open = false; if (file->do_fsync && p_fsync(file->fd) < 0) { git_error_set(GIT_ERROR_OS, "failed to fsync '%s'", file->path_lock); goto on_error; } if (p_close(file->fd) < 0) { git_error_set(GIT_ERROR_OS, "failed to close file at '%s'", file->path_lock); goto on_error; } file->fd = -1; if (p_rename(file->path_lock, file->path_original) < 0) { git_error_set(GIT_ERROR_OS, "failed to rename lockfile to '%s'", file->path_original); goto on_error; } if (file->do_fsync && git_futils_fsync_parent(file->path_original) < 0) goto on_error; file->did_rename = true; git_filebuf_cleanup(file); return 0; on_error: git_filebuf_cleanup(file); return -1; } GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len) { memcpy(file->buffer + file->buf_pos, buf, len); file->buf_pos += len; } int git_filebuf_write(git_filebuf *file, const void *buff, size_t len) { const unsigned char *buf = buff; ENSURE_BUF_OK(file); if (file->do_not_buffer) return file->write(file, (void *)buff, len); for (;;) { size_t space_left = file->buf_size - file->buf_pos; /* cache if it's small */ if (space_left > len) { add_to_cache(file, buf, len); return 0; } add_to_cache(file, buf, space_left); if (flush_buffer(file) < 0) return -1; len -= space_left; buf += space_left; } } int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len) { size_t space_left = file->buf_size - file->buf_pos; *buffer = NULL; ENSURE_BUF_OK(file); if (len > file->buf_size) { file->last_error = BUFERR_MEM; return -1; } if (space_left <= len) { if (flush_buffer(file) < 0) return -1; } *buffer = (file->buffer + file->buf_pos); file->buf_pos += len; return 0; } int git_filebuf_printf(git_filebuf *file, const char *format, ...) { va_list arglist; size_t space_left, len, alloclen; int written, res; char *tmp_buffer; ENSURE_BUF_OK(file); space_left = file->buf_size - file->buf_pos; do { va_start(arglist, format); written = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist); va_end(arglist); if (written < 0) { file->last_error = BUFERR_MEM; return -1; } len = written; if (len + 1 <= space_left) { file->buf_pos += len; return 0; } if (flush_buffer(file) < 0) return -1; space_left = file->buf_size - file->buf_pos; } while (len + 1 <= space_left); if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, 1) || !(tmp_buffer = git__malloc(alloclen))) { file->last_error = BUFERR_MEM; return -1; } va_start(arglist, format); written = p_vsnprintf(tmp_buffer, len + 1, format, arglist); va_end(arglist); if (written < 0) { git__free(tmp_buffer); file->last_error = BUFERR_MEM; return -1; } res = git_filebuf_write(file, tmp_buffer, len); git__free(tmp_buffer); return res; } int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file) { int res; struct stat st; if (file->fd_is_open) res = p_fstat(file->fd, &st); else res = p_stat(file->path_original, &st); if (res < 0) { git_error_set(GIT_ERROR_OS, "could not get stat info for '%s'", file->path_original); return res; } if (mtime) *mtime = st.st_mtime; if (size) *size = (size_t)st.st_size; return 0; } git2r/src/libgit2/src/khash.h0000644000175000017500000005147114125111754015625 0ustar nileshnilesh/* The MIT License Copyright (c) 2008, 2009, 2011 by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* An example: #include "khash.h" KHASH_MAP_INIT_INT(32, char) int main() { int ret, is_missing; khiter_t k; khash_t(32) *h = kh_init(32); k = kh_put(32, h, 5, &ret); kh_value(h, k) = 10; k = kh_get(32, h, 10); is_missing = (k == kh_end(h)); k = kh_get(32, h, 5); kh_del(32, h, k); for (k = kh_begin(h); k != kh_end(h); ++k) if (kh_exist(h, k)) kh_value(h, k) = 1; kh_destroy(32, h); return 0; } */ /* 2013-05-02 (0.2.8): * Use quadratic probing. When the capacity is power of 2, stepping function i*(i+1)/2 guarantees to traverse each bucket. It is better than double hashing on cache performance and is more robust than linear probing. In theory, double hashing should be more robust than quadratic probing. However, my implementation is probably not for large hash tables, because the second hash function is closely tied to the first hash function, which reduce the effectiveness of double hashing. Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php 2011-12-29 (0.2.7): * Minor code clean up; no actual effect. 2011-09-16 (0.2.6): * The capacity is a power of 2. This seems to dramatically improve the speed for simple keys. Thank Zilong Tan for the suggestion. Reference: - http://code.google.com/p/ulib/ - http://nothings.org/computer/judy/ * Allow to optionally use linear probing which usually has better performance for random input. Double hashing is still the default as it is more robust to certain non-random input. * Added Wang's integer hash function (not used by default). This hash function is more robust to certain non-random input. 2011-02-14 (0.2.5): * Allow to declare global functions. 2009-09-26 (0.2.4): * Improve portability 2008-09-19 (0.2.3): * Corrected the example * Improved interfaces 2008-09-11 (0.2.2): * Improved speed a little in kh_put() 2008-09-10 (0.2.1): * Added kh_clear() * Fixed a compiling error 2008-09-02 (0.2.0): * Changed to token concatenation which increases flexibility. 2008-08-31 (0.1.2): * Fixed a bug in kh_get(), which has not been tested previously. 2008-08-31 (0.1.1): * Added destructor */ #ifndef __AC_KHASH_H #define __AC_KHASH_H /*! @header Generic hash table library. */ #define AC_VERSION_KHASH_H "0.2.8" #include #include #include /* compiler specific configuration */ typedef uint32_t khint32_t; typedef uint64_t khint64_t; #ifndef kh_inline #ifdef _MSC_VER #define kh_inline __inline #elif defined(__GNUC__) #define kh_inline __inline__ #else #define kh_inline #endif #endif /* kh_inline */ typedef khint32_t khint_t; typedef khint_t khiter_t; #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) #define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) #define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) #define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) #define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) #define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) #define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) #ifndef kroundup32 #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #endif #ifndef kcalloc #define kcalloc(N,Z) calloc(N,Z) #endif #ifndef kmalloc #define kmalloc(Z) malloc(Z) #endif #ifndef krealloc #define krealloc(P,Z) realloc(P,Z) #endif #ifndef kreallocarray #define kreallocarray(P,N,Z) ((SIZE_MAX - N < Z) ? NULL : krealloc(P, (N*Z))) #endif #ifndef kfree #define kfree(P) free(P) #endif static const double __ac_HASH_UPPER = 0.77; #define __KHASH_TYPE(name, khkey_t, khval_t) \ typedef struct kh_##name##_s { \ khint_t n_buckets, size, n_occupied, upper_bound; \ khint32_t *flags; \ khkey_t *keys; \ khval_t *vals; \ } kh_##name##_t; #define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ extern kh_##name##_t *kh_init_##name(void); \ extern void kh_destroy_##name(kh_##name##_t *h); \ extern void kh_clear_##name(kh_##name##_t *h); \ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ extern void kh_del_##name(kh_##name##_t *h, khint_t x); #define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ SCOPE kh_##name##_t *kh_init_##name(void) { \ return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ } \ SCOPE void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ kfree((void *)h->keys); kfree(h->flags); \ kfree((void *)h->vals); \ kfree(h); \ } \ } \ SCOPE void kh_clear_##name(kh_##name##_t *h) \ { \ if (h && h->flags) { \ memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ h->size = h->n_occupied = 0; \ } \ } \ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ { \ if (h->n_buckets) { \ khint_t k, i, last, mask, step = 0; \ mask = h->n_buckets - 1; \ k = __hash_func(key); i = k & mask; \ last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ i = (i + (++step)) & mask; \ if (i == last) return h->n_buckets; \ } \ return __ac_iseither(h->flags, i)? h->n_buckets : i; \ } else return 0; \ } \ SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ khint32_t *new_flags = 0; \ khint_t j = 1; \ { \ kroundup32(new_n_buckets); \ if (new_n_buckets < 4) new_n_buckets = 4; \ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ else { /* hash table size to be changed (shrink or expand); rehash */ \ new_flags = (khint32_t*)kreallocarray(NULL, __ac_fsize(new_n_buckets), sizeof(khint32_t)); \ if (!new_flags) return -1; \ memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ if (h->n_buckets < new_n_buckets) { /* expand */ \ khkey_t *new_keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \ if (!new_keys) { kfree(new_flags); return -1; } \ h->keys = new_keys; \ if (kh_is_map) { \ khval_t *new_vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \ if (!new_vals) { kfree(new_flags); return -1; } \ h->vals = new_vals; \ } \ } /* otherwise shrink */ \ } \ } \ if (j) { /* rehashing is needed */ \ for (j = 0; j != h->n_buckets; ++j) { \ if (__ac_iseither(h->flags, j) == 0) { \ khkey_t key = h->keys[j]; \ khval_t val; \ khint_t new_mask; \ new_mask = new_n_buckets - 1; \ if (kh_is_map) val = h->vals[j]; \ __ac_set_isdel_true(h->flags, j); \ while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ khint_t k, i, step = 0; \ k = __hash_func(key); \ i = k & new_mask; \ while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ __ac_set_isempty_false(new_flags, i); \ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ } else { /* write the element and jump out of the loop */ \ h->keys[i] = key; \ if (kh_is_map) h->vals[i] = val; \ break; \ } \ } \ } \ } \ if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ h->keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \ if (kh_is_map) h->vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \ } \ kfree(h->flags); /* free the working space */ \ h->flags = new_flags; \ h->n_buckets = new_n_buckets; \ h->n_occupied = h->size; \ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ } \ return 0; \ } \ SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ { \ khint_t x; \ if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ if (h->n_buckets > (h->size<<1)) { \ if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ *ret = -1; return h->n_buckets; \ } \ } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ *ret = -1; return h->n_buckets; \ } \ } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ { \ khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ else { \ last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (__ac_isdel(h->flags, i)) site = i; \ i = (i + (++step)) & mask; \ if (i == last) { x = site; break; } \ } \ if (x == h->n_buckets) { \ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ else x = i; \ } \ } \ } \ if (__ac_isempty(h->flags, x)) { /* not present at all */ \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; ++h->n_occupied; \ *ret = 1; \ } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; \ *ret = 2; \ } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ return x; \ } \ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ { \ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ __ac_set_isdel_true(h->flags, x); \ --h->size; \ } \ } #define KHASH_DECLARE(name, khkey_t, khval_t) \ __KHASH_TYPE(name, khkey_t, khval_t) \ __KHASH_PROTOTYPES(name, khkey_t, khval_t) #define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ __KHASH_TYPE(name, khkey_t, khval_t) \ __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) /* --- BEGIN OF HASH FUNCTIONS --- */ /*! @function @abstract Integer hash function @param key The integer [khint32_t] @return The hash value [khint_t] */ #define kh_int_hash_func(key) (khint32_t)(key) /*! @function @abstract Integer comparison function */ #define kh_int_hash_equal(a, b) ((a) == (b)) /*! @function @abstract 64-bit integer hash function @param key The integer [khint64_t] @return The hash value [khint_t] */ #define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) /*! @function @abstract 64-bit integer comparison function */ #define kh_int64_hash_equal(a, b) ((a) == (b)) /*! @function @abstract const char* hash function @param s Pointer to a null terminated string @return The hash value */ static kh_inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = (khint_t)*s; if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; return h; } /*! @function @abstract Another interface to const char* hash function @param key Pointer to a null terminated string [const char*] @return The hash value [khint_t] */ #define kh_str_hash_func(key) __ac_X31_hash_string(key) /*! @function @abstract Const char* comparison function */ #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) static kh_inline khint_t __ac_Wang_hash(khint_t key) { key += ~(key << 15); key ^= (key >> 10); key += (key << 3); key ^= (key >> 6); key += ~(key << 11); key ^= (key >> 16); return key; } #define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) /* --- END OF HASH FUNCTIONS --- */ /* Other convenient macros... */ /*! @abstract Type of the hash table. @param name Name of the hash table [symbol] */ #define khash_t(name) kh_##name##_t /*! @function @abstract Initiate a hash table. @param name Name of the hash table [symbol] @return Pointer to the hash table [khash_t(name)*] */ #define kh_init(name) kh_init_##name() /*! @function @abstract Destroy a hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] */ #define kh_destroy(name, h) kh_destroy_##name(h) /*! @function @abstract Reset a hash table without deallocating memory. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] */ #define kh_clear(name, h) kh_clear_##name(h) /*! @function @abstract Resize a hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param s New size [khint_t] */ #define kh_resize(name, h, s) kh_resize_##name(h, s) /*! @function @abstract Insert a key to the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Key [type of keys] @param r Extra return code: -1 if the operation failed; 0 if the key is present in the hash table; 1 if the bucket is empty (never used); 2 if the element in the bucket has been deleted [int*] @return Iterator to the inserted element [khint_t] */ #define kh_put(name, h, k, r) kh_put_##name(h, k, r) /*! @function @abstract Retrieve a key from the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Key [type of keys] @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] */ #define kh_get(name, h, k) kh_get_##name(h, k) /*! @function @abstract Remove a key from the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Iterator to the element to be deleted [khint_t] */ #define kh_del(name, h, k) kh_del_##name(h, k) /*! @function @abstract Test whether a bucket contains data. @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return 1 if containing data; 0 otherwise [int] */ #define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) /*! @function @abstract Get key given an iterator @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return Key [type of keys] */ #define kh_key(h, x) ((h)->keys[x]) /*! @function @abstract Get value given an iterator @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return Value [type of values] @discussion For hash sets, calling this results in segfault. */ #define kh_val(h, x) ((h)->vals[x]) /*! @function @abstract Alias of kh_val() */ #define kh_value(h, x) ((h)->vals[x]) /*! @function @abstract Get the start iterator @param h Pointer to the hash table [khash_t(name)*] @return The start iterator [khint_t] */ #define kh_begin(h) (khint_t)(0) /*! @function @abstract Get the end iterator @param h Pointer to the hash table [khash_t(name)*] @return The end iterator [khint_t] */ #define kh_end(h) ((h)->n_buckets) /*! @function @abstract Get the number of elements in the hash table @param h Pointer to the hash table [khash_t(name)*] @return Number of elements in the hash table [khint_t] */ #define kh_size(h) ((h)->size) /*! @function @abstract Get the number of buckets in the hash table @param h Pointer to the hash table [khash_t(name)*] @return Number of buckets in the hash table [khint_t] */ #define kh_n_buckets(h) ((h)->n_buckets) /*! @function @abstract Iterate over the entries in the hash table @param h Pointer to the hash table [khash_t(name)*] @param kvar Variable to which key will be assigned @param vvar Variable to which value will be assigned @param code Block of code to execute */ #define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ if (!kh_exist(h,__i)) continue; \ (kvar) = kh_key(h,__i); \ (vvar) = kh_val(h,__i); \ code; \ } } /*! @function @abstract Iterate over the values in the hash table @param h Pointer to the hash table [khash_t(name)*] @param vvar Variable to which value will be assigned @param code Block of code to execute */ #define kh_foreach_value(h, vvar, code) { khint_t __i; \ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ if (!kh_exist(h,__i)) continue; \ (vvar) = kh_val(h,__i); \ code; \ } } /* More conenient interfaces */ /*! @function @abstract Instantiate a hash set containing integer keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_INT(name) \ KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) /*! @function @abstract Instantiate a hash map containing integer keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_INT(name, khval_t) \ KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) /*! @function @abstract Instantiate a hash map containing 64-bit integer keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_INT64(name) \ KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) /*! @function @abstract Instantiate a hash map containing 64-bit integer keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_INT64(name, khval_t) \ KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) typedef const char *kh_cstr_t; /*! @function @abstract Instantiate a hash map containing const char* keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_STR(name) \ KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) /*! @function @abstract Instantiate a hash map containing const char* keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_STR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) #endif /* __AC_KHASH_H */ git2r/src/libgit2/src/varint.h0000644000175000017500000000070714125111754016026 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_varint_h__ #define INCLUDE_varint_h__ #include "common.h" #include extern int git_encode_varint(unsigned char *, size_t, uintmax_t); extern uintmax_t git_decode_varint(const unsigned char *, size_t *); #endif git2r/src/libgit2/src/libgit2.c0000644000175000017500000002147214125111754016054 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "libgit2.h" #include #include "alloc.h" #include "cache.h" #include "common.h" #include "filter.h" #include "hash.h" #include "index.h" #include "merge_driver.h" #include "pool.h" #include "mwindow.h" #include "object.h" #include "odb.h" #include "refs.h" #include "runtime.h" #include "sysdir.h" #include "thread.h" #include "threadstate.h" #include "git2/global.h" #include "streams/registry.h" #include "streams/mbedtls.h" #include "streams/openssl.h" #include "transports/smart.h" #include "transports/http.h" #include "transports/ssh.h" #ifdef GIT_WIN32 # include "win32/w32_leakcheck.h" #endif /* Declarations for tuneable settings */ extern size_t git_mwindow__window_size; extern size_t git_mwindow__mapped_limit; extern size_t git_mwindow__file_limit; extern size_t git_indexer__max_objects; extern bool git_disable_pack_keep_file_checks; extern int git_odb__packed_priority; extern int git_odb__loose_priority; char *git__user_agent; char *git__ssl_ciphers; static void libgit2_settings_global_shutdown(void) { git__free(git__user_agent); git__free(git__ssl_ciphers); git_repository__free_extensions(); } static int git_libgit2_settings_global_init(void) { return git_runtime_shutdown_register(libgit2_settings_global_shutdown); } int git_libgit2_init(void) { static git_runtime_init_fn init_fns[] = { #ifdef GIT_WIN32 git_win32_leakcheck_global_init, #endif git_allocator_global_init, git_threadstate_global_init, git_threads_global_init, git_hash_global_init, git_sysdir_global_init, git_filter_global_init, git_merge_driver_global_init, git_transport_ssh_global_init, git_stream_registry_global_init, git_openssl_stream_global_init, git_mbedtls_stream_global_init, git_mwindow_global_init, git_pool_global_init, git_libgit2_settings_global_init }; return git_runtime_init(init_fns, ARRAY_SIZE(init_fns)); } int git_libgit2_init_count(void) { return git_runtime_init_count(); } int git_libgit2_shutdown(void) { return git_runtime_shutdown(); } int git_libgit2_version(int *major, int *minor, int *rev) { *major = LIBGIT2_VER_MAJOR; *minor = LIBGIT2_VER_MINOR; *rev = LIBGIT2_VER_REVISION; return 0; } int git_libgit2_features(void) { return 0 #ifdef GIT_THREADS | GIT_FEATURE_THREADS #endif #ifdef GIT_HTTPS | GIT_FEATURE_HTTPS #endif #if defined(GIT_SSH) | GIT_FEATURE_SSH #endif #if defined(GIT_USE_NSEC) | GIT_FEATURE_NSEC #endif ; } static int config_level_to_sysdir(int *out, int config_level) { switch (config_level) { case GIT_CONFIG_LEVEL_SYSTEM: *out = GIT_SYSDIR_SYSTEM; return 0; case GIT_CONFIG_LEVEL_XDG: *out = GIT_SYSDIR_XDG; return 0; case GIT_CONFIG_LEVEL_GLOBAL: *out = GIT_SYSDIR_GLOBAL; return 0; case GIT_CONFIG_LEVEL_PROGRAMDATA: *out = GIT_SYSDIR_PROGRAMDATA; return 0; default: break; } git_error_set( GIT_ERROR_INVALID, "invalid config path selector %d", config_level); return -1; } const char *git_libgit2__user_agent(void) { return git__user_agent; } const char *git_libgit2__ssl_ciphers(void) { return git__ssl_ciphers; } int git_libgit2_opts(int key, ...) { int error = 0; va_list ap; va_start(ap, key); switch (key) { case GIT_OPT_SET_MWINDOW_SIZE: git_mwindow__window_size = va_arg(ap, size_t); break; case GIT_OPT_GET_MWINDOW_SIZE: *(va_arg(ap, size_t *)) = git_mwindow__window_size; break; case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT: git_mwindow__mapped_limit = va_arg(ap, size_t); break; case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT: *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit; break; case GIT_OPT_SET_MWINDOW_FILE_LIMIT: git_mwindow__file_limit = va_arg(ap, size_t); break; case GIT_OPT_GET_MWINDOW_FILE_LIMIT: *(va_arg(ap, size_t *)) = git_mwindow__file_limit; break; case GIT_OPT_GET_SEARCH_PATH: { int sysdir = va_arg(ap, int); git_buf *out = va_arg(ap, git_buf *); const git_buf *tmp; int level; if ((error = config_level_to_sysdir(&level, sysdir)) < 0 || (error = git_buf_sanitize(out)) < 0 || (error = git_sysdir_get(&tmp, level)) < 0) break; error = git_buf_sets(out, tmp->ptr); } break; case GIT_OPT_SET_SEARCH_PATH: { int level; if ((error = config_level_to_sysdir(&level, va_arg(ap, int))) >= 0) error = git_sysdir_set(level, va_arg(ap, const char *)); } break; case GIT_OPT_SET_CACHE_OBJECT_LIMIT: { git_object_t type = (git_object_t)va_arg(ap, int); size_t size = va_arg(ap, size_t); error = git_cache_set_max_object_size(type, size); break; } case GIT_OPT_SET_CACHE_MAX_SIZE: git_cache__max_storage = va_arg(ap, ssize_t); break; case GIT_OPT_ENABLE_CACHING: git_cache__enabled = (va_arg(ap, int) != 0); break; case GIT_OPT_GET_CACHED_MEMORY: *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val; *(va_arg(ap, ssize_t *)) = git_cache__max_storage; break; case GIT_OPT_GET_TEMPLATE_PATH: { git_buf *out = va_arg(ap, git_buf *); const git_buf *tmp; if ((error = git_buf_sanitize(out)) < 0 || (error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0) break; error = git_buf_sets(out, tmp->ptr); } break; case GIT_OPT_SET_TEMPLATE_PATH: error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *)); break; case GIT_OPT_SET_SSL_CERT_LOCATIONS: #ifdef GIT_OPENSSL { const char *file = va_arg(ap, const char *); const char *path = va_arg(ap, const char *); error = git_openssl__set_cert_location(file, path); } #elif defined(GIT_MBEDTLS) { const char *file = va_arg(ap, const char *); const char *path = va_arg(ap, const char *); error = git_mbedtls__set_cert_location(file, path); } #else git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support certificate locations"); error = -1; #endif break; case GIT_OPT_SET_USER_AGENT: git__free(git__user_agent); git__user_agent = git__strdup(va_arg(ap, const char *)); if (!git__user_agent) { git_error_set_oom(); error = -1; } break; case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION: git_object__strict_input_validation = (va_arg(ap, int) != 0); break; case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION: git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0); break; case GIT_OPT_SET_SSL_CIPHERS: #if (GIT_OPENSSL || GIT_MBEDTLS) { git__free(git__ssl_ciphers); git__ssl_ciphers = git__strdup(va_arg(ap, const char *)); if (!git__ssl_ciphers) { git_error_set_oom(); error = -1; } } #else git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support custom ciphers"); error = -1; #endif break; case GIT_OPT_GET_USER_AGENT: { git_buf *out = va_arg(ap, git_buf *); if ((error = git_buf_sanitize(out)) < 0) break; error = git_buf_sets(out, git__user_agent); } break; case GIT_OPT_ENABLE_OFS_DELTA: git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0); break; case GIT_OPT_ENABLE_FSYNC_GITDIR: git_repository__fsync_gitdir = (va_arg(ap, int) != 0); break; case GIT_OPT_GET_WINDOWS_SHAREMODE: #ifdef GIT_WIN32 *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode; #endif break; case GIT_OPT_SET_WINDOWS_SHAREMODE: #ifdef GIT_WIN32 git_win32__createfile_sharemode = va_arg(ap, unsigned long); #endif break; case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION: git_odb__strict_hash_verification = (va_arg(ap, int) != 0); break; case GIT_OPT_SET_ALLOCATOR: error = git_allocator_setup(va_arg(ap, git_allocator *)); break; case GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY: git_index__enforce_unsaved_safety = (va_arg(ap, int) != 0); break; case GIT_OPT_SET_PACK_MAX_OBJECTS: git_indexer__max_objects = va_arg(ap, size_t); break; case GIT_OPT_GET_PACK_MAX_OBJECTS: *(va_arg(ap, size_t *)) = git_indexer__max_objects; break; case GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS: git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0); break; case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE: git_http__expect_continue = (va_arg(ap, int) != 0); break; case GIT_OPT_SET_ODB_PACKED_PRIORITY: git_odb__packed_priority = va_arg(ap, int); break; case GIT_OPT_SET_ODB_LOOSE_PRIORITY: git_odb__loose_priority = va_arg(ap, int); break; case GIT_OPT_SET_EXTENSIONS: { const char **extensions = va_arg(ap, const char **); size_t len = va_arg(ap, size_t); error = git_repository__set_extensions(extensions, len); } break; case GIT_OPT_GET_EXTENSIONS: { git_strarray *out = va_arg(ap, git_strarray *); char **extensions; size_t len; if ((error = git_repository__extensions(&extensions, &len)) < 0) break; out->strings = extensions; out->count = len; } break; default: git_error_set(GIT_ERROR_INVALID, "invalid option key"); error = -1; } va_end(ap); return error; } git2r/src/libgit2/src/net.c0000644000175000017500000003156314125111754015310 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "net.h" #include "netops.h" #include #include "git2/errors.h" #include "posix.h" #include "buffer.h" #include "http_parser.h" #include "runtime.h" #define DEFAULT_PORT_HTTP "80" #define DEFAULT_PORT_HTTPS "443" #define DEFAULT_PORT_GIT "9418" #define DEFAULT_PORT_SSH "22" static const char *default_port_for_scheme(const char *scheme) { if (strcmp(scheme, "http") == 0) return DEFAULT_PORT_HTTP; else if (strcmp(scheme, "https") == 0) return DEFAULT_PORT_HTTPS; else if (strcmp(scheme, "git") == 0) return DEFAULT_PORT_GIT; else if (strcmp(scheme, "ssh") == 0) return DEFAULT_PORT_SSH; return NULL; } int git_net_url_dup(git_net_url *out, git_net_url *in) { if (in->scheme) { out->scheme = git__strdup(in->scheme); GIT_ERROR_CHECK_ALLOC(out->scheme); } if (in->host) { out->host = git__strdup(in->host); GIT_ERROR_CHECK_ALLOC(out->host); } if (in->port) { out->port = git__strdup(in->port); GIT_ERROR_CHECK_ALLOC(out->port); } if (in->path) { out->path = git__strdup(in->path); GIT_ERROR_CHECK_ALLOC(out->path); } if (in->query) { out->query = git__strdup(in->query); GIT_ERROR_CHECK_ALLOC(out->query); } if (in->username) { out->username = git__strdup(in->username); GIT_ERROR_CHECK_ALLOC(out->username); } if (in->password) { out->password = git__strdup(in->password); GIT_ERROR_CHECK_ALLOC(out->password); } return 0; } int git_net_url_parse(git_net_url *url, const char *given) { struct http_parser_url u = {0}; bool has_scheme, has_host, has_port, has_path, has_query, has_userinfo; git_buf scheme = GIT_BUF_INIT, host = GIT_BUF_INIT, port = GIT_BUF_INIT, path = GIT_BUF_INIT, username = GIT_BUF_INIT, password = GIT_BUF_INIT, query = GIT_BUF_INIT; int error = GIT_EINVALIDSPEC; if (http_parser_parse_url(given, strlen(given), false, &u)) { git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given); goto done; } has_scheme = !!(u.field_set & (1 << UF_SCHEMA)); has_host = !!(u.field_set & (1 << UF_HOST)); has_port = !!(u.field_set & (1 << UF_PORT)); has_path = !!(u.field_set & (1 << UF_PATH)); has_query = !!(u.field_set & (1 << UF_QUERY)); has_userinfo = !!(u.field_set & (1 << UF_USERINFO)); if (has_scheme) { const char *url_scheme = given + u.field_data[UF_SCHEMA].off; size_t url_scheme_len = u.field_data[UF_SCHEMA].len; git_buf_put(&scheme, url_scheme, url_scheme_len); git__strntolower(scheme.ptr, scheme.size); } else { git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given); goto done; } if (has_host) { const char *url_host = given + u.field_data[UF_HOST].off; size_t url_host_len = u.field_data[UF_HOST].len; git_buf_decode_percent(&host, url_host, url_host_len); } if (has_port) { const char *url_port = given + u.field_data[UF_PORT].off; size_t url_port_len = u.field_data[UF_PORT].len; git_buf_put(&port, url_port, url_port_len); } else { const char *default_port = default_port_for_scheme(scheme.ptr); if (default_port == NULL) { git_error_set(GIT_ERROR_NET, "unknown scheme for URL '%s'", given); goto done; } git_buf_puts(&port, default_port); } if (has_path) { const char *url_path = given + u.field_data[UF_PATH].off; size_t url_path_len = u.field_data[UF_PATH].len; git_buf_put(&path, url_path, url_path_len); } else { git_buf_puts(&path, "/"); } if (has_query) { const char *url_query = given + u.field_data[UF_QUERY].off; size_t url_query_len = u.field_data[UF_QUERY].len; git_buf_decode_percent(&query, url_query, url_query_len); } if (has_userinfo) { const char *url_userinfo = given + u.field_data[UF_USERINFO].off; size_t url_userinfo_len = u.field_data[UF_USERINFO].len; const char *colon = memchr(url_userinfo, ':', url_userinfo_len); if (colon) { const char *url_username = url_userinfo; size_t url_username_len = colon - url_userinfo; const char *url_password = colon + 1; size_t url_password_len = url_userinfo_len - (url_username_len + 1); git_buf_decode_percent(&username, url_username, url_username_len); git_buf_decode_percent(&password, url_password, url_password_len); } else { git_buf_decode_percent(&username, url_userinfo, url_userinfo_len); } } if (git_buf_oom(&scheme) || git_buf_oom(&host) || git_buf_oom(&port) || git_buf_oom(&path) || git_buf_oom(&query) || git_buf_oom(&username) || git_buf_oom(&password)) return -1; url->scheme = git_buf_detach(&scheme); url->host = git_buf_detach(&host); url->port = git_buf_detach(&port); url->path = git_buf_detach(&path); url->query = git_buf_detach(&query); url->username = git_buf_detach(&username); url->password = git_buf_detach(&password); error = 0; done: git_buf_dispose(&scheme); git_buf_dispose(&host); git_buf_dispose(&port); git_buf_dispose(&path); git_buf_dispose(&query); git_buf_dispose(&username); git_buf_dispose(&password); return error; } int git_net_url_joinpath( git_net_url *out, git_net_url *one, const char *two) { git_buf path = GIT_BUF_INIT; const char *query; size_t one_len, two_len; git_net_url_dispose(out); if ((query = strchr(two, '?')) != NULL) { two_len = query - two; if (*(++query) != '\0') { out->query = git__strdup(query); GIT_ERROR_CHECK_ALLOC(out->query); } } else { two_len = strlen(two); } /* Strip all trailing `/`s from the first path */ one_len = one->path ? strlen(one->path) : 0; while (one_len && one->path[one_len - 1] == '/') one_len--; /* Strip all leading `/`s from the second path */ while (*two == '/') { two++; two_len--; } git_buf_put(&path, one->path, one_len); git_buf_putc(&path, '/'); git_buf_put(&path, two, two_len); if (git_buf_oom(&path)) return -1; out->path = git_buf_detach(&path); if (one->scheme) { out->scheme = git__strdup(one->scheme); GIT_ERROR_CHECK_ALLOC(out->scheme); } if (one->host) { out->host = git__strdup(one->host); GIT_ERROR_CHECK_ALLOC(out->host); } if (one->port) { out->port = git__strdup(one->port); GIT_ERROR_CHECK_ALLOC(out->port); } if (one->username) { out->username = git__strdup(one->username); GIT_ERROR_CHECK_ALLOC(out->username); } if (one->password) { out->password = git__strdup(one->password); GIT_ERROR_CHECK_ALLOC(out->password); } return 0; } /* * Some servers strip the query parameters from the Location header * when sending a redirect. Others leave it in place. * Check for both, starting with the stripped case first, * since it appears to be more common. */ static void remove_service_suffix( git_net_url *url, const char *service_suffix) { const char *service_query = strchr(service_suffix, '?'); size_t full_suffix_len = strlen(service_suffix); size_t suffix_len = service_query ? (size_t)(service_query - service_suffix) : full_suffix_len; size_t path_len = strlen(url->path); ssize_t truncate = -1; /* * Check for a redirect without query parameters, * like "/newloc/info/refs"' */ if (suffix_len && path_len >= suffix_len) { size_t suffix_offset = path_len - suffix_len; if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 && (!service_query || git__strcmp(url->query, service_query + 1) == 0)) { truncate = suffix_offset; } } /* * If we haven't already found where to truncate to remove the * suffix, check for a redirect with query parameters, like * "/newloc/info/refs?service=git-upload-pack" */ if (truncate < 0 && git__suffixcmp(url->path, service_suffix) == 0) truncate = path_len - full_suffix_len; /* Ensure we leave a minimum of '/' as the path */ if (truncate == 0) truncate++; if (truncate > 0) { url->path[truncate] = '\0'; git__free(url->query); url->query = NULL; } } int git_net_url_apply_redirect( git_net_url *url, const char *redirect_location, const char *service_suffix) { git_net_url tmp = GIT_NET_URL_INIT; int error = 0; GIT_ASSERT(url); GIT_ASSERT(redirect_location); if (redirect_location[0] == '/') { git__free(url->path); if ((url->path = git__strdup(redirect_location)) == NULL) { error = -1; goto done; } } else { git_net_url *original = url; if ((error = git_net_url_parse(&tmp, redirect_location)) < 0) goto done; /* Validate that this is a legal redirection */ if (original->scheme && strcmp(original->scheme, tmp.scheme) != 0 && strcmp(tmp.scheme, "https") != 0) { git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'", original->scheme, tmp.scheme); error = -1; goto done; } if (original->host && git__strcasecmp(original->host, tmp.host) != 0) { git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'", original->host, tmp.host); error = -1; goto done; } git_net_url_swap(url, &tmp); } /* Remove the service suffix if it was given to us */ if (service_suffix) remove_service_suffix(url, service_suffix); done: git_net_url_dispose(&tmp); return error; } bool git_net_url_valid(git_net_url *url) { return (url->host && url->port && url->path); } bool git_net_url_is_default_port(git_net_url *url) { const char *default_port; if ((default_port = default_port_for_scheme(url->scheme)) != NULL) return (strcmp(url->port, default_port) == 0); else return false; } bool git_net_url_is_ipv6(git_net_url *url) { return (strchr(url->host, ':') != NULL); } void git_net_url_swap(git_net_url *a, git_net_url *b) { git_net_url tmp = GIT_NET_URL_INIT; memcpy(&tmp, a, sizeof(git_net_url)); memcpy(a, b, sizeof(git_net_url)); memcpy(b, &tmp, sizeof(git_net_url)); } int git_net_url_fmt(git_buf *buf, git_net_url *url) { GIT_ASSERT_ARG(url); GIT_ASSERT_ARG(url->scheme); GIT_ASSERT_ARG(url->host); git_buf_puts(buf, url->scheme); git_buf_puts(buf, "://"); if (url->username) { git_buf_puts(buf, url->username); if (url->password) { git_buf_puts(buf, ":"); git_buf_puts(buf, url->password); } git_buf_putc(buf, '@'); } git_buf_puts(buf, url->host); if (url->port && !git_net_url_is_default_port(url)) { git_buf_putc(buf, ':'); git_buf_puts(buf, url->port); } git_buf_puts(buf, url->path ? url->path : "/"); if (url->query) { git_buf_putc(buf, '?'); git_buf_puts(buf, url->query); } return git_buf_oom(buf) ? -1 : 0; } int git_net_url_fmt_path(git_buf *buf, git_net_url *url) { git_buf_puts(buf, url->path ? url->path : "/"); if (url->query) { git_buf_putc(buf, '?'); git_buf_puts(buf, url->query); } return git_buf_oom(buf) ? -1 : 0; } static bool matches_pattern( git_net_url *url, const char *pattern, size_t pattern_len) { const char *domain, *port = NULL, *colon; size_t host_len, domain_len, port_len = 0, wildcard = 0; GIT_UNUSED(url); GIT_UNUSED(pattern); if (!pattern_len) return false; else if (pattern_len == 1 && pattern[0] == '*') return true; else if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.') wildcard = 2; else if (pattern[0] == '.') wildcard = 1; domain = pattern + wildcard; domain_len = pattern_len - wildcard; if ((colon = memchr(domain, ':', domain_len)) != NULL) { domain_len = colon - domain; port = colon + 1; port_len = pattern_len - wildcard - domain_len - 1; } /* A pattern's port *must* match if it's specified */ if (port_len && git__strlcmp(url->port, port, port_len) != 0) return false; /* No wildcard? Host must match exactly. */ if (!wildcard) return !git__strlcmp(url->host, domain, domain_len); /* Wildcard: ensure there's (at least) a suffix match */ if ((host_len = strlen(url->host)) < domain_len || memcmp(url->host + (host_len - domain_len), domain, domain_len)) return false; /* The pattern is *.domain and the host is simply domain */ if (host_len == domain_len) return true; /* The pattern is *.domain and the host is foo.domain */ return (url->host[host_len - domain_len - 1] == '.'); } bool git_net_url_matches_pattern(git_net_url *url, const char *pattern) { return matches_pattern(url, pattern, strlen(pattern)); } bool git_net_url_matches_pattern_list( git_net_url *url, const char *pattern_list) { const char *pattern, *pattern_end, *sep; for (pattern = pattern_list; pattern && *pattern; pattern = sep ? sep + 1 : NULL) { sep = strchr(pattern, ','); pattern_end = sep ? sep : strchr(pattern, '\0'); if (matches_pattern(url, pattern, (pattern_end - pattern))) return true; } return false; } void git_net_url_dispose(git_net_url *url) { if (url->username) git__memzero(url->username, strlen(url->username)); if (url->password) git__memzero(url->password, strlen(url->password)); git__free(url->scheme); url->scheme = NULL; git__free(url->host); url->host = NULL; git__free(url->port); url->port = NULL; git__free(url->path); url->path = NULL; git__free(url->query); url->query = NULL; git__free(url->username); url->username = NULL; git__free(url->password); url->password = NULL; } git2r/src/libgit2/src/reader.c0000644000175000017500000001275014125111754015761 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "reader.h" #include "futils.h" #include "blob.h" #include "git2/tree.h" #include "git2/blob.h" #include "git2/index.h" #include "git2/repository.h" /* tree reader */ typedef struct { git_reader reader; git_tree *tree; } tree_reader; static int tree_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *_reader, const char *filename) { tree_reader *reader = (tree_reader *)_reader; git_tree_entry *tree_entry = NULL; git_blob *blob = NULL; git_object_size_t blobsize; int error; if ((error = git_tree_entry_bypath(&tree_entry, reader->tree, filename)) < 0 || (error = git_blob_lookup(&blob, git_tree_owner(reader->tree), git_tree_entry_id(tree_entry))) < 0) goto done; blobsize = git_blob_rawsize(blob); GIT_ERROR_CHECK_BLOBSIZE(blobsize); if ((error = git_buf_set(out, git_blob_rawcontent(blob), (size_t)blobsize)) < 0) goto done; if (out_id) git_oid_cpy(out_id, git_tree_entry_id(tree_entry)); if (out_filemode) *out_filemode = git_tree_entry_filemode(tree_entry); done: git_blob_free(blob); git_tree_entry_free(tree_entry); return error; } int git_reader_for_tree(git_reader **out, git_tree *tree) { tree_reader *reader; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(tree); reader = git__calloc(1, sizeof(tree_reader)); GIT_ERROR_CHECK_ALLOC(reader); reader->reader.read = tree_reader_read; reader->tree = tree; *out = (git_reader *)reader; return 0; } /* workdir reader */ typedef struct { git_reader reader; git_repository *repo; git_index *index; } workdir_reader; static int workdir_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *_reader, const char *filename) { workdir_reader *reader = (workdir_reader *)_reader; git_buf path = GIT_BUF_INIT; struct stat st; git_filemode_t filemode; git_filter_list *filters = NULL; const git_index_entry *idx_entry; git_oid id; int error; if ((error = git_repository_workdir_path(&path, reader->repo, filename)) < 0) goto done; if ((error = p_lstat(path.ptr, &st)) < 0) { if (error == -1 && errno == ENOENT) error = GIT_ENOTFOUND; git_error_set(GIT_ERROR_OS, "could not stat '%s'", path.ptr); goto done; } filemode = git_futils_canonical_mode(st.st_mode); /* * Patch application - for example - uses the filtered version of * the working directory data to match git. So we will run the * workdir -> ODB filter on the contents in this workdir reader. */ if ((error = git_filter_list_load(&filters, reader->repo, NULL, filename, GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT)) < 0) goto done; if ((error = git_filter_list_apply_to_file(out, filters, reader->repo, path.ptr)) < 0) goto done; if (out_id || reader->index) { if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB)) < 0) goto done; } if (reader->index) { if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) || filemode != idx_entry->mode || !git_oid_equal(&id, &idx_entry->id)) { error = GIT_READER_MISMATCH; goto done; } } if (out_id) git_oid_cpy(out_id, &id); if (out_filemode) *out_filemode = filemode; done: git_filter_list_free(filters); git_buf_dispose(&path); return error; } int git_reader_for_workdir( git_reader **out, git_repository *repo, bool validate_index) { workdir_reader *reader; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); reader = git__calloc(1, sizeof(workdir_reader)); GIT_ERROR_CHECK_ALLOC(reader); reader->reader.read = workdir_reader_read; reader->repo = repo; if (validate_index && (error = git_repository_index__weakptr(&reader->index, repo)) < 0) { git__free(reader); return error; } *out = (git_reader *)reader; return 0; } /* index reader */ typedef struct { git_reader reader; git_repository *repo; git_index *index; } index_reader; static int index_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *_reader, const char *filename) { index_reader *reader = (index_reader *)_reader; const git_index_entry *entry; git_blob *blob; int error; if ((entry = git_index_get_bypath(reader->index, filename, 0)) == NULL) return GIT_ENOTFOUND; if ((error = git_blob_lookup(&blob, reader->repo, &entry->id)) < 0) goto done; if (out_id) git_oid_cpy(out_id, &entry->id); if (out_filemode) *out_filemode = entry->mode; error = git_blob__getbuf(out, blob); done: git_blob_free(blob); return error; } int git_reader_for_index( git_reader **out, git_repository *repo, git_index *index) { index_reader *reader; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); reader = git__calloc(1, sizeof(index_reader)); GIT_ERROR_CHECK_ALLOC(reader); reader->reader.read = index_reader_read; reader->repo = repo; if (index) { reader->index = index; } else if ((error = git_repository_index__weakptr(&reader->index, repo)) < 0) { git__free(reader); return error; } *out = (git_reader *)reader; return 0; } /* generic */ int git_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *reader, const char *filename) { GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(reader); GIT_ASSERT_ARG(filename); return reader->read(out, out_id, out_filemode, reader, filename); } void git_reader_free(git_reader *reader) { if (!reader) return; git__free(reader); } git2r/src/libgit2/src/commit_list.h0000644000175000017500000000320114125111754017036 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_commit_list_h__ #define INCLUDE_commit_list_h__ #include "common.h" #include "git2/oid.h" #define PARENT1 (1 << 0) #define PARENT2 (1 << 1) #define RESULT (1 << 2) #define STALE (1 << 3) #define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT) #define PARENTS_PER_COMMIT 2 #define COMMIT_ALLOC \ (sizeof(git_commit_list_node) + PARENTS_PER_COMMIT * sizeof(git_commit_list_node *)) #define FLAG_BITS 4 typedef struct git_commit_list_node { git_oid oid; int64_t time; uint32_t generation; unsigned int seen:1, uninteresting:1, topo_delay:1, parsed:1, added:1, flags : FLAG_BITS; uint16_t in_degree; uint16_t out_degree; struct git_commit_list_node **parents; } git_commit_list_node; typedef struct git_commit_list { git_commit_list_node *item; struct git_commit_list *next; } git_commit_list; git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk); int git_commit_list_generation_cmp(const void *a, const void *b); int git_commit_list_time_cmp(const void *a, const void *b); void git_commit_list_free(git_commit_list **list_p); git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p); git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p); int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit); git_commit_list_node *git_commit_list_pop(git_commit_list **stack); #endif git2r/src/libgit2/src/sysdir.c0000644000175000017500000001715714125111754016042 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "sysdir.h" #include "runtime.h" #include "buffer.h" #include "path.h" #include #if GIT_WIN32 #include "win32/findfile.h" #else #include #include #endif static int git_sysdir_guess_programdata_dirs(git_buf *out) { #ifdef GIT_WIN32 return git_win32__find_programdata_dirs(out); #else git_buf_clear(out); return 0; #endif } static int git_sysdir_guess_system_dirs(git_buf *out) { #ifdef GIT_WIN32 return git_win32__find_system_dirs(out, L"etc\\"); #else return git_buf_sets(out, "/etc"); #endif } #ifndef GIT_WIN32 static int get_passwd_home(git_buf *out, uid_t uid) { struct passwd pwd, *pwdptr; char *buf = NULL; long buflen; int error; GIT_ASSERT_ARG(out); if ((buflen = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) buflen = 1024; do { buf = git__realloc(buf, buflen); error = getpwuid_r(uid, &pwd, buf, buflen, &pwdptr); buflen *= 2; } while (error == ERANGE && buflen <= 8192); if (error) { git_error_set(GIT_ERROR_OS, "failed to get passwd entry"); goto out; } if (!pwdptr) { git_error_set(GIT_ERROR_OS, "no passwd entry found for user"); goto out; } if ((error = git_buf_puts(out, pwdptr->pw_dir)) < 0) goto out; out: git__free(buf); return error; } #endif static int git_sysdir_guess_global_dirs(git_buf *out) { #ifdef GIT_WIN32 return git_win32__find_global_dirs(out); #else int error; uid_t uid, euid; const char *sandbox_id; uid = getuid(); euid = geteuid(); /** * If APP_SANDBOX_CONTAINER_ID is set, we are running in a * sandboxed environment on macOS. */ sandbox_id = getenv("APP_SANDBOX_CONTAINER_ID"); /* * In case we are running setuid, use the configuration * of the effective user. * * If we are running in a sandboxed environment on macOS, * we have to get the HOME dir from the password entry file. */ if (!sandbox_id && uid == euid) error = git__getenv(out, "HOME"); else error = get_passwd_home(out, euid); if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } return error; #endif } static int git_sysdir_guess_xdg_dirs(git_buf *out) { #ifdef GIT_WIN32 return git_win32__find_xdg_dirs(out); #else git_buf env = GIT_BUF_INIT; int error; uid_t uid, euid; uid = getuid(); euid = geteuid(); /* * In case we are running setuid, only look up passwd * directory of the effective user. */ if (uid == euid) { if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0) error = git_buf_joinpath(out, env.ptr, "git"); if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0) error = git_buf_joinpath(out, env.ptr, ".config/git"); } else { if ((error = get_passwd_home(&env, euid)) == 0) error = git_buf_joinpath(out, env.ptr, ".config/git"); } if (error == GIT_ENOTFOUND) { git_error_clear(); error = 0; } git_buf_dispose(&env); return error; #endif } static int git_sysdir_guess_template_dirs(git_buf *out) { #ifdef GIT_WIN32 return git_win32__find_system_dirs(out, L"share\\git-core\\templates"); #else return git_buf_sets(out, "/usr/share/git-core/templates"); #endif } struct git_sysdir__dir { git_buf buf; int (*guess)(git_buf *out); }; static struct git_sysdir__dir git_sysdir__dirs[] = { { GIT_BUF_INIT, git_sysdir_guess_system_dirs }, { GIT_BUF_INIT, git_sysdir_guess_global_dirs }, { GIT_BUF_INIT, git_sysdir_guess_xdg_dirs }, { GIT_BUF_INIT, git_sysdir_guess_programdata_dirs }, { GIT_BUF_INIT, git_sysdir_guess_template_dirs }, }; static void git_sysdir_global_shutdown(void) { size_t i; for (i = 0; i < ARRAY_SIZE(git_sysdir__dirs); ++i) git_buf_dispose(&git_sysdir__dirs[i].buf); } int git_sysdir_global_init(void) { size_t i; int error = 0; for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++) error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); return git_runtime_shutdown_register(git_sysdir_global_shutdown); } static int git_sysdir_check_selector(git_sysdir_t which) { if (which < ARRAY_SIZE(git_sysdir__dirs)) return 0; git_error_set(GIT_ERROR_INVALID, "config directory selector out of range"); return -1; } int git_sysdir_get(const git_buf **out, git_sysdir_t which) { GIT_ASSERT_ARG(out); *out = NULL; GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which)); *out = &git_sysdir__dirs[which].buf; return 0; } #define PATH_MAGIC "$PATH" int git_sysdir_set(git_sysdir_t which, const char *search_path) { const char *expand_path = NULL; git_buf merge = GIT_BUF_INIT; GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which)); if (search_path != NULL) expand_path = strstr(search_path, PATH_MAGIC); /* reset the default if this path has been cleared */ if (!search_path) git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf); /* if $PATH is not referenced, then just set the path */ if (!expand_path) { if (search_path) git_buf_sets(&git_sysdir__dirs[which].buf, search_path); goto done; } /* otherwise set to join(before $PATH, old value, after $PATH) */ if (expand_path > search_path) git_buf_set(&merge, search_path, expand_path - search_path); if (git_buf_len(&git_sysdir__dirs[which].buf)) git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, git_sysdir__dirs[which].buf.ptr); expand_path += strlen(PATH_MAGIC); if (*expand_path) git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path); git_buf_swap(&git_sysdir__dirs[which].buf, &merge); git_buf_dispose(&merge); done: if (git_buf_oom(&git_sysdir__dirs[which].buf)) return -1; return 0; } static int git_sysdir_find_in_dirlist( git_buf *path, const char *name, git_sysdir_t which, const char *label) { size_t len; const char *scan, *next = NULL; const git_buf *syspath; GIT_ERROR_CHECK_ERROR(git_sysdir_get(&syspath, which)); if (!syspath || !git_buf_len(syspath)) goto done; for (scan = git_buf_cstr(syspath); scan; scan = next) { /* find unescaped separator or end of string */ for (next = scan; *next; ++next) { if (*next == GIT_PATH_LIST_SEPARATOR && (next <= scan || next[-1] != '\\')) break; } len = (size_t)(next - scan); next = (*next ? next + 1 : NULL); if (!len) continue; GIT_ERROR_CHECK_ERROR(git_buf_set(path, scan, len)); if (name) GIT_ERROR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name)); if (git_path_exists(path->ptr)) return 0; } done: if (name) git_error_set(GIT_ERROR_OS, "the %s file '%s' doesn't exist", label, name); else git_error_set(GIT_ERROR_OS, "the %s directory doesn't exist", label); git_buf_dispose(path); return GIT_ENOTFOUND; } int git_sysdir_find_system_file(git_buf *path, const char *filename) { return git_sysdir_find_in_dirlist( path, filename, GIT_SYSDIR_SYSTEM, "system"); } int git_sysdir_find_global_file(git_buf *path, const char *filename) { return git_sysdir_find_in_dirlist( path, filename, GIT_SYSDIR_GLOBAL, "global"); } int git_sysdir_find_xdg_file(git_buf *path, const char *filename) { return git_sysdir_find_in_dirlist( path, filename, GIT_SYSDIR_XDG, "global/xdg"); } int git_sysdir_find_programdata_file(git_buf *path, const char *filename) { return git_sysdir_find_in_dirlist( path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData"); } int git_sysdir_find_template_dir(git_buf *path) { return git_sysdir_find_in_dirlist( path, NULL, GIT_SYSDIR_TEMPLATE, "template"); } int git_sysdir_expand_global_file(git_buf *path, const char *filename) { int error; if ((error = git_sysdir_find_global_file(path, NULL)) == 0) { if (filename) error = git_buf_joinpath(path, path->ptr, filename); } return error; } git2r/src/libgit2/src/attr.h0000644000175000017500000000052514125111754015473 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_attr_h__ #define INCLUDE_attr_h__ #include "common.h" #include "attr_file.h" #include "attrcache.h" #endif git2r/src/libgit2/src/filter.h0000644000175000017500000000311514125111754016004 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_filter_h__ #define INCLUDE_filter_h__ #include "common.h" #include "attr_file.h" #include "git2/filter.h" #include "git2/sys/filter.h" /* Amount of file to examine for NUL byte when checking binary-ness */ #define GIT_FILTER_BYTES_TO_CHECK_NUL 8000 typedef struct { git_filter_options options; git_attr_session *attr_session; git_buf *temp_buf; } git_filter_session; #define GIT_FILTER_SESSION_INIT {GIT_FILTER_OPTIONS_INIT, 0} extern int git_filter_global_init(void); extern void git_filter_free(git_filter *filter); extern int git_filter_list__load( git_filter_list **filters, git_repository *repo, git_blob *blob, /* can be NULL */ const char *path, git_filter_mode_t mode, git_filter_session *filter_session); /* * The given input buffer will be converted to the given output buffer. * The input buffer will be freed (_if_ it was allocated). */ extern int git_filter_list__convert_buf( git_buf *out, git_filter_list *filters, git_buf *in); /* * Available filters */ extern git_filter *git_crlf_filter_new(void); extern git_filter *git_ident_filter_new(void); extern int git_filter_buffered_stream_new( git_writestream **out, git_filter *filter, int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *), git_buf *temp_buf, void **payload, const git_filter_source *source, git_writestream *target); #endif git2r/src/libgit2/src/delta.c0000644000175000017500000004507614125111754015617 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "delta.h" /* maximum hash entry list for the same hash bucket */ #define HASH_LIMIT 64 #define RABIN_SHIFT 23 #define RABIN_WINDOW 16 static const unsigned int T[256] = { 0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344, 0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259, 0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85, 0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2, 0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a, 0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db, 0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753, 0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964, 0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8, 0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5, 0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81, 0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6, 0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e, 0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77, 0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff, 0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8, 0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc, 0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1, 0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d, 0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a, 0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2, 0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02, 0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a, 0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd, 0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61, 0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c, 0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08, 0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f, 0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7, 0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe, 0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76, 0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141, 0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65, 0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78, 0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4, 0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93, 0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b, 0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa, 0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872, 0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645, 0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99, 0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84, 0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811 }; static const unsigned int U[256] = { 0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a, 0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48, 0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511, 0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d, 0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8, 0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe, 0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb, 0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937, 0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e, 0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c, 0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d, 0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1, 0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4, 0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa, 0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef, 0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263, 0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302, 0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000, 0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59, 0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5, 0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90, 0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7, 0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2, 0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e, 0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467, 0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765, 0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604, 0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88, 0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd, 0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3, 0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996, 0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a, 0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b, 0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609, 0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50, 0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc, 0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99, 0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf, 0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa, 0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176, 0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f, 0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d, 0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a }; struct index_entry { const unsigned char *ptr; unsigned int val; struct index_entry *next; }; struct git_delta_index { unsigned long memsize; const void *src_buf; size_t src_size; unsigned int hash_mask; struct index_entry *hash[GIT_FLEX_ARRAY]; }; static int lookup_index_alloc( void **out, unsigned long *out_len, size_t entries, size_t hash_count) { size_t entries_len, hash_len, index_len; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&entries_len, entries, sizeof(struct index_entry)); GIT_ERROR_CHECK_ALLOC_MULTIPLY(&hash_len, hash_count, sizeof(struct index_entry *)); GIT_ERROR_CHECK_ALLOC_ADD(&index_len, sizeof(struct git_delta_index), entries_len); GIT_ERROR_CHECK_ALLOC_ADD(&index_len, index_len, hash_len); if (!git__is_ulong(index_len)) { git_error_set(GIT_ERROR_NOMEMORY, "overly large delta"); return -1; } *out = git__malloc(index_len); GIT_ERROR_CHECK_ALLOC(*out); *out_len = (unsigned long)index_len; return 0; } int git_delta_index_init( git_delta_index **out, const void *buf, size_t bufsize) { unsigned int i, hsize, hmask, entries, prev_val, *hash_count; const unsigned char *data, *buffer = buf; struct git_delta_index *index; struct index_entry *entry, **hash; void *mem; unsigned long memsize; *out = NULL; if (!buf || !bufsize) return 0; /* Determine index hash size. Note that indexing skips the first byte to allow for optimizing the rabin polynomial initialization in create_delta(). */ entries = (unsigned int)(bufsize - 1) / RABIN_WINDOW; if (bufsize >= 0xffffffffUL) { /* * Current delta format can't encode offsets into * reference buffer with more than 32 bits. */ entries = 0xfffffffeU / RABIN_WINDOW; } hsize = entries / 4; for (i = 4; i < 31 && (1u << i) < hsize; i++); hsize = 1 << i; hmask = hsize - 1; if (lookup_index_alloc(&mem, &memsize, entries, hsize) < 0) return -1; index = mem; mem = index->hash; hash = mem; mem = hash + hsize; entry = mem; index->memsize = memsize; index->src_buf = buf; index->src_size = bufsize; index->hash_mask = hmask; memset(hash, 0, hsize * sizeof(*hash)); /* allocate an array to count hash entries */ hash_count = git__calloc(hsize, sizeof(*hash_count)); if (!hash_count) { git__free(index); return -1; } /* then populate the index */ prev_val = ~0; for (data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW; data >= buffer; data -= RABIN_WINDOW) { unsigned int val = 0; for (i = 1; i <= RABIN_WINDOW; i++) val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT]; if (val == prev_val) { /* keep the lowest of consecutive identical blocks */ entry[-1].ptr = data + RABIN_WINDOW; } else { prev_val = val; i = val & hmask; entry->ptr = data + RABIN_WINDOW; entry->val = val; entry->next = hash[i]; hash[i] = entry++; hash_count[i]++; } } /* * Determine a limit on the number of entries in the same hash * bucket. This guard us against patological data sets causing * really bad hash distribution with most entries in the same hash * bucket that would bring us to O(m*n) computing costs (m and n * corresponding to reference and target buffer sizes). * * Make sure none of the hash buckets has more entries than * we're willing to test. Otherwise we cull the entry list * uniformly to still preserve a good repartition across * the reference buffer. */ for (i = 0; i < hsize; i++) { if (hash_count[i] < HASH_LIMIT) continue; entry = hash[i]; do { struct index_entry *keep = entry; int skip = hash_count[i] / HASH_LIMIT / 2; do { entry = entry->next; } while(--skip && entry); keep->next = entry; } while (entry); } git__free(hash_count); *out = index; return 0; } void git_delta_index_free(git_delta_index *index) { git__free(index); } size_t git_delta_index_size(git_delta_index *index) { GIT_ASSERT_ARG(index); return index->memsize; } /* * The maximum size for any opcode sequence, including the initial header * plus rabin window plus biggest copy. */ #define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7) int git_delta_create_from_index( void **out, size_t *out_len, const struct git_delta_index *index, const void *trg_buf, size_t trg_size, size_t max_size) { unsigned int i, bufpos, bufsize, moff, msize, val; int inscnt; const unsigned char *ref_data, *ref_top, *data, *top; unsigned char *buf; *out = NULL; *out_len = 0; if (!trg_buf || !trg_size) return 0; if (index->src_size > UINT_MAX || trg_size > UINT_MAX || max_size > (UINT_MAX - MAX_OP_SIZE - 1)) { git_error_set(GIT_ERROR_INVALID, "buffer sizes too large for delta processing"); return -1; } bufpos = 0; bufsize = 8192; if (max_size && bufsize >= max_size) bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1); buf = git__malloc(bufsize); GIT_ERROR_CHECK_ALLOC(buf); /* store reference buffer size */ i = (unsigned int)index->src_size; while (i >= 0x80) { buf[bufpos++] = i | 0x80; i >>= 7; } buf[bufpos++] = i; /* store target buffer size */ i = (unsigned int)trg_size; while (i >= 0x80) { buf[bufpos++] = i | 0x80; i >>= 7; } buf[bufpos++] = i; ref_data = index->src_buf; ref_top = ref_data + index->src_size; data = trg_buf; top = (const unsigned char *) trg_buf + trg_size; bufpos++; val = 0; for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) { buf[bufpos++] = *data; val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT]; } inscnt = i; moff = 0; msize = 0; while (data < top) { if (msize < 4096) { struct index_entry *entry; val ^= U[data[-RABIN_WINDOW]]; val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT]; i = val & index->hash_mask; for (entry = index->hash[i]; entry; entry = entry->next) { const unsigned char *ref = entry->ptr; const unsigned char *src = data; unsigned int ref_size = (unsigned int)(ref_top - ref); if (entry->val != val) continue; if (ref_size > (unsigned int)(top - src)) ref_size = (unsigned int)(top - src); if (ref_size <= msize) break; while (ref_size-- && *src++ == *ref) ref++; if (msize < (unsigned int)(ref - entry->ptr)) { /* this is our best match so far */ msize = (unsigned int)(ref - entry->ptr); moff = (unsigned int)(entry->ptr - ref_data); if (msize >= 4096) /* good enough */ break; } } } if (msize < 4) { if (!inscnt) bufpos++; buf[bufpos++] = *data++; inscnt++; if (inscnt == 0x7f) { buf[bufpos - inscnt - 1] = inscnt; inscnt = 0; } msize = 0; } else { unsigned int left; unsigned char *op; if (inscnt) { while (moff && ref_data[moff-1] == data[-1]) { /* we can match one byte back */ msize++; moff--; data--; bufpos--; if (--inscnt) continue; bufpos--; /* remove count slot */ inscnt--; /* make it -1 */ break; } buf[bufpos - inscnt - 1] = inscnt; inscnt = 0; } /* A copy op is currently limited to 64KB (pack v2) */ left = (msize < 0x10000) ? 0 : (msize - 0x10000); msize -= left; op = buf + bufpos++; i = 0x80; if (moff & 0x000000ff) buf[bufpos++] = moff >> 0, i |= 0x01; if (moff & 0x0000ff00) buf[bufpos++] = moff >> 8, i |= 0x02; if (moff & 0x00ff0000) buf[bufpos++] = moff >> 16, i |= 0x04; if (moff & 0xff000000) buf[bufpos++] = moff >> 24, i |= 0x08; if (msize & 0x00ff) buf[bufpos++] = msize >> 0, i |= 0x10; if (msize & 0xff00) buf[bufpos++] = msize >> 8, i |= 0x20; *op = i; data += msize; moff += msize; msize = left; if (msize < 4096) { int j; val = 0; for (j = -RABIN_WINDOW; j < 0; j++) val = ((val << 8) | data[j]) ^ T[val >> RABIN_SHIFT]; } } if (bufpos >= bufsize - MAX_OP_SIZE) { void *tmp = buf; bufsize = bufsize * 3 / 2; if (max_size && bufsize >= max_size) bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1); if (max_size && bufpos > max_size) break; buf = git__realloc(buf, bufsize); if (!buf) { git__free(tmp); return -1; } } } if (inscnt) buf[bufpos - inscnt - 1] = inscnt; if (max_size && bufpos > max_size) { git_error_set(GIT_ERROR_NOMEMORY, "delta would be larger than maximum size"); git__free(buf); return GIT_EBUFS; } *out_len = bufpos; *out = buf; return 0; } /* * Delta application was heavily cribbed from BinaryDelta.java in JGit, which * itself was heavily cribbed from patch-delta.c in the * GIT project. The original delta patching code was written by * Nicolas Pitre . */ static int hdr_sz( size_t *size, const unsigned char **delta, const unsigned char *end) { const unsigned char *d = *delta; size_t r = 0; unsigned int c, shift = 0; do { if (d == end) { git_error_set(GIT_ERROR_INVALID, "truncated delta"); return -1; } c = *d++; r |= (c & 0x7f) << shift; shift += 7; } while (c & 0x80); *delta = d; *size = r; return 0; } int git_delta_read_header( size_t *base_out, size_t *result_out, const unsigned char *delta, size_t delta_len) { const unsigned char *delta_end = delta + delta_len; if ((hdr_sz(base_out, &delta, delta_end) < 0) || (hdr_sz(result_out, &delta, delta_end) < 0)) return -1; return 0; } #define DELTA_HEADER_BUFFER_LEN 16 int git_delta_read_header_fromstream( size_t *base_sz, size_t *res_sz, git_packfile_stream *stream) { static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN; unsigned char buffer[DELTA_HEADER_BUFFER_LEN]; const unsigned char *delta, *delta_end; size_t len; ssize_t read; len = read = 0; while (len < buffer_len) { read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len); if (read == 0) break; if (read == GIT_EBUFS) continue; len += read; } delta = buffer; delta_end = delta + len; if ((hdr_sz(base_sz, &delta, delta_end) < 0) || (hdr_sz(res_sz, &delta, delta_end) < 0)) return -1; return 0; } int git_delta_apply( void **out, size_t *out_len, const unsigned char *base, size_t base_len, const unsigned char *delta, size_t delta_len) { const unsigned char *delta_end = delta + delta_len; size_t base_sz, res_sz, alloc_sz; unsigned char *res_dp; *out = NULL; *out_len = 0; /* * Check that the base size matches the data we were given; * if not we would underflow while accessing data from the * base object, resulting in data corruption or segfault. */ if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) { git_error_set(GIT_ERROR_INVALID, "failed to apply delta: base size does not match given data"); return -1; } if (hdr_sz(&res_sz, &delta, delta_end) < 0) { git_error_set(GIT_ERROR_INVALID, "failed to apply delta: base size does not match given data"); return -1; } GIT_ERROR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1); res_dp = git__malloc(alloc_sz); GIT_ERROR_CHECK_ALLOC(res_dp); res_dp[res_sz] = '\0'; *out = res_dp; *out_len = res_sz; while (delta < delta_end) { unsigned char cmd = *delta++; if (cmd & 0x80) { /* cmd is a copy instruction; copy from the base. */ size_t off = 0, len = 0, end; #define ADD_DELTA(o, shift) { if (delta < delta_end) (o) |= ((unsigned) *delta++ << shift); else goto fail; } if (cmd & 0x01) ADD_DELTA(off, 0UL); if (cmd & 0x02) ADD_DELTA(off, 8UL); if (cmd & 0x04) ADD_DELTA(off, 16UL); if (cmd & 0x08) ADD_DELTA(off, 24UL); if (cmd & 0x10) ADD_DELTA(len, 0UL); if (cmd & 0x20) ADD_DELTA(len, 8UL); if (cmd & 0x40) ADD_DELTA(len, 16UL); if (!len) len = 0x10000; #undef ADD_DELTA if (GIT_ADD_SIZET_OVERFLOW(&end, off, len) || base_len < end || res_sz < len) goto fail; memcpy(res_dp, base + off, len); res_dp += len; res_sz -= len; } else if (cmd) { /* * cmd is a literal insert instruction; copy from * the delta stream itself. */ if (delta_end - delta < cmd || res_sz < cmd) goto fail; memcpy(res_dp, delta, cmd); delta += cmd; res_dp += cmd; res_sz -= cmd; } else { /* cmd == 0 is reserved for future encodings. */ goto fail; } } if (delta != delta_end || res_sz) goto fail; return 0; fail: git__free(*out); *out = NULL; *out_len = 0; git_error_set(GIT_ERROR_INVALID, "failed to apply delta"); return -1; } git2r/src/libgit2/src/hash.h0000644000175000017500000000174714125111754015453 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_hash_h__ #define INCLUDE_hash_h__ #include "common.h" #include "git2/oid.h" typedef struct { void *data; size_t len; } git_buf_vec; typedef enum { GIT_HASH_ALGO_UNKNOWN = 0, GIT_HASH_ALGO_SHA1, } git_hash_algo_t; #include "hash/sha1.h" typedef struct git_hash_ctx { union { git_hash_sha1_ctx sha1; } ctx; git_hash_algo_t algo; } git_hash_ctx; int git_hash_global_init(void); int git_hash_ctx_init(git_hash_ctx *ctx); void git_hash_ctx_cleanup(git_hash_ctx *ctx); int git_hash_init(git_hash_ctx *c); int git_hash_update(git_hash_ctx *c, const void *data, size_t len); int git_hash_final(git_oid *out, git_hash_ctx *c); int git_hash_buf(git_oid *out, const void *data, size_t len); int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n); #endif git2r/src/libgit2/src/common.h0000644000175000017500000001331114125111754016006 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_common_h__ #define INCLUDE_common_h__ #ifndef LIBGIT2_NO_FEATURES_H # include "git2/sys/features.h" #endif #include "git2/common.h" #include "cc-compat.h" /** Declare a function as always inlined. */ #if defined(_MSC_VER) # define GIT_INLINE(type) static __inline type #elif defined(__GNUC__) # define GIT_INLINE(type) static __inline__ type #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # define GIT_INLINE(type) static inline type #else # define GIT_INLINE(type) static type #endif /** Support for gcc/clang __has_builtin intrinsic */ #ifndef __has_builtin # define __has_builtin(x) 0 #endif /** * Declare that a function's return value must be used. * * Used mostly to guard against potential silent bugs at runtime. This is * recommended to be added to functions that: * * - Allocate / reallocate memory. This prevents memory leaks or errors where * buffers are expected to have grown to a certain size, but could not be * resized. * - Acquire locks. When a lock cannot be acquired, that will almost certainly * cause a data race / undefined behavior. */ #if defined(__GNUC__) # define GIT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else # define GIT_WARN_UNUSED_RESULT #endif #include #include #include #include #include #include #include #include #ifdef GIT_WIN32 # include # include # include # include # include # include "win32/msvc-compat.h" # include "win32/mingw-compat.h" # include "win32/w32_common.h" # include "win32/win32-compat.h" # include "win32/error.h" # include "win32/version.h" # ifdef GIT_THREADS # include "win32/thread.h" # endif #else # include # include # ifdef GIT_THREADS # include # include # endif #define GIT_LIBGIT2_CALL #define GIT_SYSTEM_CALL #ifdef GIT_USE_STAT_ATIMESPEC # define st_atim st_atimespec # define st_ctim st_ctimespec # define st_mtim st_mtimespec #endif # include #endif #include "git2/types.h" #include "git2/errors.h" #include "errors.h" #include "thread.h" #include "integer.h" #include "assert_safe.h" #include "utf8.h" /* * Include the declarations for deprecated functions; this ensures * that they're decorated with the proper extern/visibility attributes. */ #include "git2/deprecated.h" #include "posix.h" #define DEFAULT_BUFSIZE 65536 #define FILEIO_BUFSIZE DEFAULT_BUFSIZE #define FILTERIO_BUFSIZE DEFAULT_BUFSIZE #define NETIO_BUFSIZE DEFAULT_BUFSIZE /** * Check a pointer allocation result, returning -1 if it failed. */ #define GIT_ERROR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; } /** * Check a buffer allocation result, returning -1 if it failed. */ #define GIT_ERROR_CHECK_ALLOC_BUF(buf) if ((void *)(buf) == NULL || git_buf_oom(buf)) { return -1; } /** * Check a return value and propagate result if non-zero. */ #define GIT_ERROR_CHECK_ERROR(code) \ do { int _err = (code); if (_err) return _err; } while (0) /** * Check a versioned structure for validity */ GIT_INLINE(int) git_error__check_version(const void *structure, unsigned int expected_max, const char *name) { unsigned int actual; if (!structure) return 0; actual = *(const unsigned int*)structure; if (actual > 0 && actual <= expected_max) return 0; git_error_set(GIT_ERROR_INVALID, "invalid version %d on %s", actual, name); return -1; } #define GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) return -1 /** * Initialize a structure with a version. */ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version) { memset(structure, 0, len); *((int*)structure) = version; } #define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V) #define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \ TYPE _tmpl = TPL; \ GIT_ERROR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \ memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0) /** Check for additive overflow, setting an error if would occur. */ #define GIT_ADD_SIZET_OVERFLOW(out, one, two) \ (git__add_sizet_overflow(out, one, two) ? (git_error_set_oom(), 1) : 0) /** Check for additive overflow, setting an error if would occur. */ #define GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize) \ (git__multiply_sizet_overflow(out, nelem, elsize) ? (git_error_set_oom(), 1) : 0) /** Check for additive overflow, failing if it would occur. */ #define GIT_ERROR_CHECK_ALLOC_ADD(out, one, two) \ if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; } #define GIT_ERROR_CHECK_ALLOC_ADD3(out, one, two, three) \ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { return -1; } #define GIT_ERROR_CHECK_ALLOC_ADD4(out, one, two, three, four) \ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \ GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; } #define GIT_ERROR_CHECK_ALLOC_ADD5(out, one, two, three, four, five) \ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \ GIT_ADD_SIZET_OVERFLOW(out, *(out), four) || \ GIT_ADD_SIZET_OVERFLOW(out, *(out), five)) { return -1; } /** Check for multiplicative overflow, failing if it would occur. */ #define GIT_ERROR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; } /* NOTE: other git_error functions are in the public errors.h header file */ #include "util.h" #endif git2r/src/libgit2/src/filebuf.h0000644000175000017500000000561414125111754016141 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_filebuf_h__ #define INCLUDE_filebuf_h__ #include "common.h" #include "futils.h" #include "hash.h" #include #ifdef GIT_THREADS # define GIT_FILEBUF_THREADS #endif #define GIT_FILEBUF_HASH_CONTENTS (1 << 0) #define GIT_FILEBUF_APPEND (1 << 2) #define GIT_FILEBUF_CREATE_LEADING_DIRS (1 << 3) #define GIT_FILEBUF_TEMPORARY (1 << 4) #define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5) #define GIT_FILEBUF_FSYNC (1 << 6) #define GIT_FILEBUF_DEFLATE_SHIFT (7) #define GIT_FILELOCK_EXTENSION ".lock\0" #define GIT_FILELOCK_EXTLENGTH 6 typedef struct git_filebuf git_filebuf; struct git_filebuf { char *path_original; char *path_lock; int (*write)(git_filebuf *file, void *source, size_t len); bool compute_digest; git_hash_ctx digest; unsigned char *buffer; unsigned char *z_buf; z_stream zs; int flush_mode; size_t buf_size, buf_pos; git_file fd; bool fd_is_open; bool created_lock; bool did_rename; bool do_not_buffer; bool do_fsync; int last_error; }; #define GIT_FILEBUF_INIT {0} /* * The git_filebuf object lifecycle is: * - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT. * * - Call git_filebuf_open() to initialize the filebuf for use. * * - Make as many calls to git_filebuf_write(), git_filebuf_printf(), * git_filebuf_reserve() as you like. The error codes for these * functions don't need to be checked. They are stored internally * by the file buffer. * * - While you are writing, you may call git_filebuf_hash() to get * the hash of all you have written so far. This function will * fail if any of the previous writes to the buffer failed. * * - To close the git_filebuf, you may call git_filebuf_commit() or * git_filebuf_commit_at() to save the file, or * git_filebuf_cleanup() to abandon the file. All of these will * free the git_filebuf object. Likewise, all of these will fail * if any of the previous writes to the buffer failed, and set * an error code accordingly. */ int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len); int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len); int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); int git_filebuf_open(git_filebuf *lock, const char *path, int flags, mode_t mode); int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size); int git_filebuf_commit(git_filebuf *lock); int git_filebuf_commit_at(git_filebuf *lock, const char *path); void git_filebuf_cleanup(git_filebuf *lock); int git_filebuf_hash(git_oid *oid, git_filebuf *file); int git_filebuf_flush(git_filebuf *file); int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file); #endif git2r/src/libgit2/src/posix.c0000644000175000017500000001307014125111754015655 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "posix.h" #include "path.h" #include #include size_t p_fsync__cnt = 0; #ifndef GIT_WIN32 #ifdef NO_ADDRINFO int p_getaddrinfo( const char *host, const char *port, struct addrinfo *hints, struct addrinfo **info) { struct addrinfo *ainfo, *ai; int p = 0; GIT_UNUSED(hints); if ((ainfo = git__malloc(sizeof(struct addrinfo))) == NULL) return -1; if ((ainfo->ai_hostent = gethostbyname(host)) == NULL) { git__free(ainfo); return -2; } ainfo->ai_servent = getservbyname(port, 0); if (ainfo->ai_servent) ainfo->ai_port = ainfo->ai_servent->s_port; else ainfo->ai_port = htons(atol(port)); memcpy(&ainfo->ai_addr_in.sin_addr, ainfo->ai_hostent->h_addr_list[0], ainfo->ai_hostent->h_length); ainfo->ai_protocol = 0; ainfo->ai_socktype = hints->ai_socktype; ainfo->ai_family = ainfo->ai_hostent->h_addrtype; ainfo->ai_addr_in.sin_family = ainfo->ai_family; ainfo->ai_addr_in.sin_port = ainfo->ai_port; ainfo->ai_addr = (struct addrinfo *)&ainfo->ai_addr_in; ainfo->ai_addrlen = sizeof(struct sockaddr_in); *info = ainfo; if (ainfo->ai_hostent->h_addr_list[1] == NULL) { ainfo->ai_next = NULL; return 0; } ai = ainfo; for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) { if (!(ai->ai_next = git__malloc(sizeof(struct addrinfo)))) { p_freeaddrinfo(ainfo); return -1; } memcpy(ai->ai_next, ainfo, sizeof(struct addrinfo)); memcpy(&ai->ai_next->ai_addr_in.sin_addr, ainfo->ai_hostent->h_addr_list[p], ainfo->ai_hostent->h_length); ai->ai_next->ai_addr = (struct addrinfo *)&ai->ai_next->ai_addr_in; ai = ai->ai_next; } ai->ai_next = NULL; return 0; } void p_freeaddrinfo(struct addrinfo *info) { struct addrinfo *p, *next; p = info; while(p != NULL) { next = p->ai_next; git__free(p); p = next; } } const char *p_gai_strerror(int ret) { switch(ret) { case -1: return "Out of memory"; break; case -2: return "Address lookup failed"; break; default: return "Unknown error"; break; } } #endif /* NO_ADDRINFO */ int p_open(const char *path, volatile int flags, ...) { mode_t mode = 0; #ifdef GIT_DEBUG_STRICT_OPEN if (strstr(path, "//") != NULL) { errno = EACCES; return -1; } #endif if (flags & O_CREAT) { va_list arg_list; va_start(arg_list, flags); mode = (mode_t)va_arg(arg_list, int); va_end(arg_list); } return open(path, flags | O_BINARY | O_CLOEXEC, mode); } int p_creat(const char *path, mode_t mode) { return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC, mode); } int p_getcwd(char *buffer_out, size_t size) { char *cwd_buffer; GIT_ASSERT_ARG(buffer_out); GIT_ASSERT_ARG(size > 0); cwd_buffer = getcwd(buffer_out, size); if (cwd_buffer == NULL) return -1; git_path_mkposix(buffer_out); git_path_string_to_dir(buffer_out, size); /* append trailing slash */ return 0; } int p_rename(const char *from, const char *to) { if (!link(from, to)) { p_unlink(from); return 0; } if (!rename(from, to)) return 0; return -1; } #endif /* GIT_WIN32 */ ssize_t p_read(git_file fd, void *buf, size_t cnt) { char *b = buf; if (!git__is_ssizet(cnt)) { #ifdef GIT_WIN32 SetLastError(ERROR_INVALID_PARAMETER); #endif errno = EINVAL; return -1; } while (cnt) { ssize_t r; #ifdef GIT_WIN32 r = read(fd, b, cnt > INT_MAX ? INT_MAX : (unsigned int)cnt); #else r = read(fd, b, cnt); #endif if (r < 0) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } if (!r) break; cnt -= r; b += r; } return (b - (char *)buf); } int p_write(git_file fd, const void *buf, size_t cnt) { const char *b = buf; while (cnt) { ssize_t r; #ifdef GIT_WIN32 GIT_ASSERT((size_t)((unsigned int)cnt) == cnt); r = write(fd, b, (unsigned int)cnt); #else r = write(fd, b, cnt); #endif if (r < 0) { if (errno == EINTR || GIT_ISBLOCKED(errno)) continue; return -1; } if (!r) { errno = EPIPE; return -1; } cnt -= r; b += r; } return 0; } #ifdef NO_MMAP #include "map.h" int git__page_size(size_t *page_size) { /* dummy; here we don't need any alignment anyway */ *page_size = 4096; return 0; } int git__mmap_alignment(size_t *alignment) { /* dummy; here we don't need any alignment anyway */ *alignment = 4096; return 0; } int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset) { const char *ptr; size_t remaining_len; GIT_MMAP_VALIDATE(out, len, prot, flags); /* writes cannot be emulated without handling pagefaults since write happens by * writing to mapped memory */ if (prot & GIT_PROT_WRITE) { git_error_set(GIT_ERROR_OS, "trying to map %s-writeable", ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED) ? "shared": "private"); return -1; } if (!git__is_ssizet(len)) { errno = EINVAL; return -1; } out->len = 0; out->data = git__malloc(len); GIT_ERROR_CHECK_ALLOC(out->data); remaining_len = len; ptr = (const char *)out->data; while (remaining_len > 0) { ssize_t nb; HANDLE_EINTR(nb, p_pread(fd, (void *)ptr, remaining_len, offset)); if (nb <= 0) { git_error_set(GIT_ERROR_OS, "mmap emulation failed"); git__free(out->data); out->data = NULL; return -1; } ptr += nb; offset += nb; remaining_len -= nb; } out->len = len; return 0; } int p_munmap(git_map *map) { GIT_ASSERT_ARG(map); git__free(map->data); /* Initializing will help debug use-after-free */ map->len = 0; map->data = NULL; return 0; } #endif git2r/src/libgit2/src/config_cache.c0000644000175000017500000001110514125111754017100 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "futils.h" #include "repository.h" #include "config.h" #include "git2/config.h" #include "vector.h" #include "filter.h" struct map_data { const char *name; git_configmap *maps; size_t map_count; int default_value; }; /* * core.eol * Sets the line ending type to use in the working directory for * files that have the text property set. Alternatives are lf, crlf * and native, which uses the platform's native line ending. The default * value is native. See gitattributes(5) for more information on * end-of-line conversion. */ static git_configmap _configmap_eol[] = { {GIT_CONFIGMAP_FALSE, NULL, GIT_EOL_UNSET}, {GIT_CONFIGMAP_STRING, "lf", GIT_EOL_LF}, {GIT_CONFIGMAP_STRING, "crlf", GIT_EOL_CRLF}, {GIT_CONFIGMAP_STRING, "native", GIT_EOL_NATIVE} }; /* * core.autocrlf * Setting this variable to "true" is almost the same as setting * the text attribute to "auto" on all files except that text files are * not guaranteed to be normalized: files that contain CRLF in the * repository will not be touched. Use this setting if you want to have * CRLF line endings in your working directory even though the repository * does not have normalized line endings. This variable can be set to input, * in which case no output conversion is performed. */ static git_configmap _configmap_autocrlf[] = { {GIT_CONFIGMAP_FALSE, NULL, GIT_AUTO_CRLF_FALSE}, {GIT_CONFIGMAP_TRUE, NULL, GIT_AUTO_CRLF_TRUE}, {GIT_CONFIGMAP_STRING, "input", GIT_AUTO_CRLF_INPUT} }; static git_configmap _configmap_safecrlf[] = { {GIT_CONFIGMAP_FALSE, NULL, GIT_SAFE_CRLF_FALSE}, {GIT_CONFIGMAP_TRUE, NULL, GIT_SAFE_CRLF_FAIL}, {GIT_CONFIGMAP_STRING, "warn", GIT_SAFE_CRLF_WARN} }; static git_configmap _configmap_logallrefupdates[] = { {GIT_CONFIGMAP_FALSE, NULL, GIT_LOGALLREFUPDATES_FALSE}, {GIT_CONFIGMAP_TRUE, NULL, GIT_LOGALLREFUPDATES_TRUE}, {GIT_CONFIGMAP_STRING, "always", GIT_LOGALLREFUPDATES_ALWAYS}, }; /* * Generic map for integer values */ static git_configmap _configmap_int[] = { {GIT_CONFIGMAP_INT32, NULL, 0}, }; static struct map_data _configmaps[] = { {"core.autocrlf", _configmap_autocrlf, ARRAY_SIZE(_configmap_autocrlf), GIT_AUTO_CRLF_DEFAULT}, {"core.eol", _configmap_eol, ARRAY_SIZE(_configmap_eol), GIT_EOL_DEFAULT}, {"core.symlinks", NULL, 0, GIT_SYMLINKS_DEFAULT }, {"core.ignorecase", NULL, 0, GIT_IGNORECASE_DEFAULT }, {"core.filemode", NULL, 0, GIT_FILEMODE_DEFAULT }, {"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT }, {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT }, {"core.abbrev", _configmap_int, 1, GIT_ABBREV_DEFAULT }, {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT }, {"core.safecrlf", _configmap_safecrlf, ARRAY_SIZE(_configmap_safecrlf), GIT_SAFE_CRLF_DEFAULT}, {"core.logallrefupdates", _configmap_logallrefupdates, ARRAY_SIZE(_configmap_logallrefupdates), GIT_LOGALLREFUPDATES_DEFAULT}, {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT }, {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT }, {"core.fsyncobjectfiles", NULL, 0, GIT_FSYNCOBJECTFILES_DEFAULT }, {"core.longpaths", NULL, 0, GIT_LONGPATHS_DEFAULT }, }; int git_config__configmap_lookup(int *out, git_config *config, git_configmap_item item) { int error = 0; struct map_data *data = &_configmaps[(int)item]; git_config_entry *entry; if ((error = git_config__lookup_entry(&entry, config, data->name, false)) < 0) return error; if (!entry) *out = data->default_value; else if (data->maps) error = git_config_lookup_map_value( out, data->maps, data->map_count, entry->value); else error = git_config_parse_bool(out, entry->value); git_config_entry_free(entry); return error; } int git_repository__configmap_lookup(int *out, git_repository *repo, git_configmap_item item) { intptr_t value = (intptr_t)git_atomic_load(repo->configmap_cache[(int)item]); *out = (int)value; if (value == GIT_CONFIGMAP_NOT_CACHED) { git_config *config; intptr_t oldval = value; int error; if ((error = git_repository_config__weakptr(&config, repo)) < 0 || (error = git_config__configmap_lookup(out, config, item)) < 0) return error; value = *out; git_atomic_compare_and_swap(&repo->configmap_cache[(int)item], (void *)oldval, (void *)value); } return 0; } void git_repository__configmap_lookup_cache_clear(git_repository *repo) { int i; for (i = 0; i < GIT_CONFIGMAP_CACHE_MAX; ++i) repo->configmap_cache[i] = GIT_CONFIGMAP_NOT_CACHED; } git2r/src/libgit2/src/refdb.c0000644000175000017500000002173514125111754015604 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "refdb.h" #include "git2/object.h" #include "git2/refs.h" #include "git2/refdb.h" #include "git2/sys/refdb_backend.h" #include "hash.h" #include "refs.h" #include "reflog.h" #include "posix.h" #define DEFAULT_NESTING_LEVEL 5 #define MAX_NESTING_LEVEL 10 int git_refdb_new(git_refdb **out, git_repository *repo) { git_refdb *db; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); db = git__calloc(1, sizeof(*db)); GIT_ERROR_CHECK_ALLOC(db); db->repo = repo; *out = db; GIT_REFCOUNT_INC(db); return 0; } int git_refdb_open(git_refdb **out, git_repository *repo) { git_refdb *db; git_refdb_backend *dir; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); *out = NULL; if (git_refdb_new(&db, repo) < 0) return -1; /* Add the default (filesystem) backend */ if (git_refdb_backend_fs(&dir, repo) < 0) { git_refdb_free(db); return -1; } db->repo = repo; db->backend = dir; *out = db; return 0; } static void refdb_free_backend(git_refdb *db) { if (db->backend) db->backend->free(db->backend); } int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend) { GIT_ERROR_CHECK_VERSION(backend, GIT_REFDB_BACKEND_VERSION, "git_refdb_backend"); if (!backend->exists || !backend->lookup || !backend->iterator || !backend->write || !backend->rename || !backend->del || !backend->has_log || !backend->ensure_log || !backend->free || !backend->reflog_read || !backend->reflog_write || !backend->reflog_rename || !backend->reflog_delete || (backend->lock && !backend->unlock)) { git_error_set(GIT_ERROR_REFERENCE, "incomplete refdb backend implementation"); return GIT_EINVALID; } refdb_free_backend(db); db->backend = backend; return 0; } int git_refdb_compress(git_refdb *db) { GIT_ASSERT_ARG(db); if (db->backend->compress) return db->backend->compress(db->backend); return 0; } void git_refdb__free(git_refdb *db) { refdb_free_backend(db); git__memzero(db, sizeof(*db)); git__free(db); } void git_refdb_free(git_refdb *db) { if (db == NULL) return; GIT_REFCOUNT_DEC(db, git_refdb__free); } int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name) { GIT_ASSERT_ARG(exists); GIT_ASSERT_ARG(refdb); GIT_ASSERT_ARG(refdb->backend); return refdb->backend->exists(exists, refdb->backend, ref_name); } int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) { git_reference *ref; int error; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(db->backend); GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(ref_name); error = db->backend->lookup(&ref, db->backend, ref_name); if (error < 0) return error; GIT_REFCOUNT_INC(db); ref->db = db; *out = ref; return 0; } int git_refdb_resolve( git_reference **out, git_refdb *db, const char *ref_name, int max_nesting) { git_reference *ref = NULL; int error = 0, nesting; *out = NULL; if (max_nesting > MAX_NESTING_LEVEL) max_nesting = MAX_NESTING_LEVEL; else if (max_nesting < 0) max_nesting = DEFAULT_NESTING_LEVEL; if ((error = git_refdb_lookup(&ref, db, ref_name)) < 0) goto out; for (nesting = 0; nesting < max_nesting; nesting++) { git_reference *resolved; if (ref->type == GIT_REFERENCE_DIRECT) break; if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0) { /* If we found a symbolic reference with a nonexistent target, return it. */ if (error == GIT_ENOTFOUND) { error = 0; *out = ref; ref = NULL; } goto out; } git_reference_free(ref); ref = resolved; } if (ref->type != GIT_REFERENCE_DIRECT && max_nesting != 0) { git_error_set(GIT_ERROR_REFERENCE, "cannot resolve reference (>%u levels deep)", max_nesting); error = -1; goto out; } *out = ref; ref = NULL; out: git_reference_free(ref); return error; } int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob) { int error; if (!db->backend || !db->backend->iterator) { git_error_set(GIT_ERROR_REFERENCE, "this backend doesn't support iterators"); return -1; } if ((error = db->backend->iterator(out, db->backend, glob)) < 0) return error; GIT_REFCOUNT_INC(db); (*out)->db = db; return 0; } int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter) { int error; if ((error = iter->next(out, iter)) < 0) return error; GIT_REFCOUNT_INC(iter->db); (*out)->db = iter->db; return 0; } int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter) { return iter->next_name(out, iter); } void git_refdb_iterator_free(git_reference_iterator *iter) { GIT_REFCOUNT_DEC(iter->db, git_refdb__free); iter->free(iter); } int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target) { GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(db->backend); GIT_REFCOUNT_INC(db); ref->db = db; return db->backend->write(db->backend, ref, force, who, message, old_id, old_target); } int git_refdb_rename( git_reference **out, git_refdb *db, const char *old_name, const char *new_name, int force, const git_signature *who, const char *message) { int error; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(db->backend); error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message); if (error < 0) return error; if (out) { GIT_REFCOUNT_INC(db); (*out)->db = db; } return 0; } int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target) { GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(db->backend); return db->backend->del(db->backend, ref_name, old_id, old_target); } int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name) { int error; GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(db->backend); if ((error = db->backend->reflog_read(out, db->backend, name)) < 0) return error; GIT_REFCOUNT_INC(db); (*out)->db = db; return 0; } int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref) { int error, logall; error = git_repository__configmap_lookup(&logall, db->repo, GIT_CONFIGMAP_LOGALLREFUPDATES); if (error < 0) return error; /* Defaults to the opposite of the repo being bare */ if (logall == GIT_LOGALLREFUPDATES_UNSET) logall = !git_repository_is_bare(db->repo); *out = 0; switch (logall) { case GIT_LOGALLREFUPDATES_FALSE: *out = 0; break; case GIT_LOGALLREFUPDATES_TRUE: /* Only write if it already has a log, * or if it's under heads/, remotes/ or notes/ */ *out = git_refdb_has_log(db, ref->name) || !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) || !git__strcmp(ref->name, GIT_HEAD_FILE) || !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) || !git__prefixcmp(ref->name, GIT_REFS_NOTES_DIR); break; case GIT_LOGALLREFUPDATES_ALWAYS: *out = 1; break; } return 0; } int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref) { git_reference *head = NULL, *resolved = NULL; const char *name; int error; *out = 0; if (ref->type == GIT_REFERENCE_SYMBOLIC) { error = 0; goto out; } if ((error = git_refdb_lookup(&head, db, GIT_HEAD_FILE)) < 0) goto out; if (git_reference_type(head) == GIT_REFERENCE_DIRECT) goto out; /* Go down the symref chain until we find the branch */ if ((error = git_refdb_resolve(&resolved, db, git_reference_symbolic_target(head), -1)) < 0) { if (error != GIT_ENOTFOUND) goto out; error = 0; name = git_reference_symbolic_target(head); } else if (git_reference_type(resolved) == GIT_REFERENCE_SYMBOLIC) { name = git_reference_symbolic_target(resolved); } else { name = git_reference_name(resolved); } if (strcmp(name, ref->name)) goto out; *out = 1; out: git_reference_free(resolved); git_reference_free(head); return error; } int git_refdb_has_log(git_refdb *db, const char *refname) { GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(refname); return db->backend->has_log(db->backend, refname); } int git_refdb_ensure_log(git_refdb *db, const char *refname) { GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(refname); return db->backend->ensure_log(db->backend, refname); } int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE( backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT); return 0; } int git_refdb_lock(void **payload, git_refdb *db, const char *refname) { GIT_ASSERT_ARG(payload); GIT_ASSERT_ARG(db); GIT_ASSERT_ARG(refname); if (!db->backend->lock) { git_error_set(GIT_ERROR_REFERENCE, "backend does not support locking"); return -1; } return db->backend->lock(payload, db->backend, refname); } int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message) { GIT_ASSERT_ARG(db); return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message); } git2r/src/libgit2/src/indexer.h0000644000175000017500000000061414125111754016156 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_indexer_h__ #define INCLUDE_indexer_h__ #include "common.h" #include "git2/indexer.h" extern void git_indexer__set_fsync(git_indexer *idx, int do_fsync); #endif git2r/src/libgit2/src/filter.c0000644000175000017500000006242114125111754016004 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "filter.h" #include "common.h" #include "futils.h" #include "hash.h" #include "repository.h" #include "runtime.h" #include "git2/sys/filter.h" #include "git2/config.h" #include "blob.h" #include "attr_file.h" #include "array.h" struct git_filter_source { git_repository *repo; const char *path; git_oid oid; /* zero if unknown (which is likely) */ uint16_t filemode; /* zero if unknown */ git_filter_mode_t mode; git_filter_options options; }; typedef struct { const char *filter_name; git_filter *filter; void *payload; } git_filter_entry; struct git_filter_list { git_array_t(git_filter_entry) filters; git_filter_source source; git_buf *temp_buf; char path[GIT_FLEX_ARRAY]; }; typedef struct { char *filter_name; git_filter *filter; int priority; int initialized; size_t nattrs, nmatches; char *attrdata; const char *attrs[GIT_FLEX_ARRAY]; } git_filter_def; static int filter_def_priority_cmp(const void *a, const void *b) { int pa = ((const git_filter_def *)a)->priority; int pb = ((const git_filter_def *)b)->priority; return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; } struct git_filter_registry { git_rwlock lock; git_vector filters; }; static struct git_filter_registry filter_registry; static void git_filter_global_shutdown(void); static int filter_def_scan_attrs( git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) { const char *start, *scan = attr_str; int has_eq; *nattr = *nmatch = 0; if (!scan) return 0; while (*scan) { while (git__isspace(*scan)) scan++; for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) { if (*scan == '=') has_eq = 1; } if (scan > start) { (*nattr)++; if (has_eq || *start == '-' || *start == '+' || *start == '!') (*nmatch)++; if (has_eq) git_buf_putc(attrs, '='); git_buf_put(attrs, start, scan - start); git_buf_putc(attrs, '\0'); } } return 0; } static void filter_def_set_attrs(git_filter_def *fdef) { char *scan = fdef->attrdata; size_t i; for (i = 0; i < fdef->nattrs; ++i) { const char *name, *value; switch (*scan) { case '=': name = scan + 1; for (scan++; *scan != '='; scan++) /* find '=' */; *scan++ = '\0'; value = scan; break; case '-': name = scan + 1; value = git_attr__false; break; case '+': name = scan + 1; value = git_attr__true; break; case '!': name = scan + 1; value = git_attr__unset; break; default: name = scan; value = NULL; break; } fdef->attrs[i] = name; fdef->attrs[i + fdef->nattrs] = value; scan += strlen(scan) + 1; } } static int filter_def_name_key_check(const void *key, const void *fdef) { const char *name = fdef ? ((const git_filter_def *)fdef)->filter_name : NULL; return name ? git__strcmp(key, name) : -1; } static int filter_def_filter_key_check(const void *key, const void *fdef) { const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL; return (key == filter) ? 0 : -1; } /* Note: callers must lock the registry before calling this function */ static int filter_registry_insert( const char *name, git_filter *filter, int priority) { git_filter_def *fdef; size_t nattr = 0, nmatch = 0, alloc_len; git_buf attrs = GIT_BUF_INIT; if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) return -1; GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2); GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *)); GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def)); fdef = git__calloc(1, alloc_len); GIT_ERROR_CHECK_ALLOC(fdef); fdef->filter_name = git__strdup(name); GIT_ERROR_CHECK_ALLOC(fdef->filter_name); fdef->filter = filter; fdef->priority = priority; fdef->nattrs = nattr; fdef->nmatches = nmatch; fdef->attrdata = git_buf_detach(&attrs); filter_def_set_attrs(fdef); if (git_vector_insert(&filter_registry.filters, fdef) < 0) { git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); return -1; } git_vector_sort(&filter_registry.filters); return 0; } int git_filter_global_init(void) { git_filter *crlf = NULL, *ident = NULL; int error = 0; if (git_rwlock_init(&filter_registry.lock) < 0) return -1; if ((error = git_vector_init(&filter_registry.filters, 2, filter_def_priority_cmp)) < 0) goto done; if ((crlf = git_crlf_filter_new()) == NULL || filter_registry_insert( GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 || (ident = git_ident_filter_new()) == NULL || filter_registry_insert( GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) error = -1; if (!error) error = git_runtime_shutdown_register(git_filter_global_shutdown); done: if (error) { git_filter_free(crlf); git_filter_free(ident); } return error; } static void git_filter_global_shutdown(void) { size_t pos; git_filter_def *fdef; if (git_rwlock_wrlock(&filter_registry.lock) < 0) return; git_vector_foreach(&filter_registry.filters, pos, fdef) { if (fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); fdef->initialized = false; } git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); } git_vector_free(&filter_registry.filters); git_rwlock_wrunlock(&filter_registry.lock); git_rwlock_free(&filter_registry.lock); } /* Note: callers must lock the registry before calling this function */ static int filter_registry_find(size_t *pos, const char *name) { return git_vector_search2( pos, &filter_registry.filters, filter_def_name_key_check, name); } /* Note: callers must lock the registry before calling this function */ static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) { git_filter_def *fdef = NULL; if (!filter_registry_find(pos, name)) fdef = git_vector_get(&filter_registry.filters, *pos); return fdef; } int git_filter_register( const char *name, git_filter *filter, int priority) { int error; GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(filter); if (git_rwlock_wrlock(&filter_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); return -1; } if (!filter_registry_find(NULL, name)) { git_error_set( GIT_ERROR_FILTER, "attempt to reregister existing filter '%s'", name); error = GIT_EEXISTS; goto done; } error = filter_registry_insert(name, filter, priority); done: git_rwlock_wrunlock(&filter_registry.lock); return error; } int git_filter_unregister(const char *name) { size_t pos; git_filter_def *fdef; int error = 0; GIT_ASSERT_ARG(name); /* cannot unregister default filters */ if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) { git_error_set(GIT_ERROR_FILTER, "cannot unregister filter '%s'", name); return -1; } if (git_rwlock_wrlock(&filter_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); return -1; } if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { git_error_set(GIT_ERROR_FILTER, "cannot find filter '%s' to unregister", name); error = GIT_ENOTFOUND; goto done; } git_vector_remove(&filter_registry.filters, pos); if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); fdef->initialized = false; } git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); done: git_rwlock_wrunlock(&filter_registry.lock); return error; } static int filter_initialize(git_filter_def *fdef) { int error = 0; if (!fdef->initialized && fdef->filter && fdef->filter->initialize) { if ((error = fdef->filter->initialize(fdef->filter)) < 0) return error; } fdef->initialized = true; return 0; } git_filter *git_filter_lookup(const char *name) { size_t pos; git_filter_def *fdef; git_filter *filter = NULL; if (git_rwlock_rdlock(&filter_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); return NULL; } if ((fdef = filter_registry_lookup(&pos, name)) == NULL || (!fdef->initialized && filter_initialize(fdef) < 0)) goto done; filter = fdef->filter; done: git_rwlock_rdunlock(&filter_registry.lock); return filter; } void git_filter_free(git_filter *filter) { git__free(filter); } git_repository *git_filter_source_repo(const git_filter_source *src) { return src->repo; } const char *git_filter_source_path(const git_filter_source *src) { return src->path; } uint16_t git_filter_source_filemode(const git_filter_source *src) { return src->filemode; } const git_oid *git_filter_source_id(const git_filter_source *src) { return git_oid_is_zero(&src->oid) ? NULL : &src->oid; } git_filter_mode_t git_filter_source_mode(const git_filter_source *src) { return src->mode; } uint32_t git_filter_source_flags(const git_filter_source *src) { return src->options.flags; } static int filter_list_new( git_filter_list **out, const git_filter_source *src) { git_filter_list *fl = NULL; size_t pathlen = src->path ? strlen(src->path) : 0, alloclen; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); fl = git__calloc(1, alloclen); GIT_ERROR_CHECK_ALLOC(fl); if (src->path) memcpy(fl->path, src->path, pathlen); fl->source.repo = src->repo; fl->source.path = fl->path; fl->source.mode = src->mode; memcpy(&fl->source.options, &src->options, sizeof(git_filter_options)); *out = fl; return 0; } static int filter_list_check_attributes( const char ***out, git_repository *repo, git_filter_session *filter_session, git_filter_def *fdef, const git_filter_source *src) { const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT; size_t i; int error; GIT_ERROR_CHECK_ALLOC(strs); if ((src->options.flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM; if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0) attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD; if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) { attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_COMMIT; #ifndef GIT_DEPRECATE_HARD if (src->options.commit_id) git_oid_cpy(&attr_opts.attr_commit_id, src->options.commit_id); else #endif git_oid_cpy(&attr_opts.attr_commit_id, &src->options.attr_commit_id); } error = git_attr_get_many_with_session( strs, repo, filter_session->attr_session, &attr_opts, src->path, fdef->nattrs, fdef->attrs); /* if no values were found but no matches are needed, it's okay! */ if (error == GIT_ENOTFOUND && !fdef->nmatches) { git_error_clear(); git__free((void *)strs); return 0; } for (i = 0; !error && i < fdef->nattrs; ++i) { const char *want = fdef->attrs[fdef->nattrs + i]; git_attr_value_t want_type, found_type; if (!want) continue; want_type = git_attr_value(want); found_type = git_attr_value(strs[i]); if (want_type != found_type) error = GIT_ENOTFOUND; else if (want_type == GIT_ATTR_VALUE_STRING && strcmp(want, strs[i]) && strcmp(want, "*")) error = GIT_ENOTFOUND; } if (error) git__free((void *)strs); else *out = strs; return error; } int git_filter_list_new( git_filter_list **out, git_repository *repo, git_filter_mode_t mode, uint32_t flags) { git_filter_source src = { 0 }; src.repo = repo; src.path = NULL; src.mode = mode; src.options.flags = flags; return filter_list_new(out, &src); } int git_filter_list__load( git_filter_list **filters, git_repository *repo, git_blob *blob, /* can be NULL */ const char *path, git_filter_mode_t mode, git_filter_session *filter_session) { int error = 0; git_filter_list *fl = NULL; git_filter_source src = { 0 }; git_filter_entry *fe; size_t idx; git_filter_def *fdef; if (git_rwlock_rdlock(&filter_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); return -1; } src.repo = repo; src.path = path; src.mode = mode; memcpy(&src.options, &filter_session->options, sizeof(git_filter_options)); if (blob) git_oid_cpy(&src.oid, git_blob_id(blob)); git_vector_foreach(&filter_registry.filters, idx, fdef) { const char **values = NULL; void *payload = NULL; if (!fdef || !fdef->filter) continue; if (fdef->nattrs > 0) { error = filter_list_check_attributes( &values, repo, filter_session, fdef, &src); if (error == GIT_ENOTFOUND) { error = 0; continue; } else if (error < 0) break; } if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) break; if (fdef->filter->check) error = fdef->filter->check( fdef->filter, &payload, &src, values); git__free((void *)values); if (error == GIT_PASSTHROUGH) error = 0; else if (error < 0) break; else { if (!fl) { if ((error = filter_list_new(&fl, &src)) < 0) break; fl->temp_buf = filter_session->temp_buf; } fe = git_array_alloc(fl->filters); GIT_ERROR_CHECK_ALLOC(fe); fe->filter = fdef->filter; fe->filter_name = fdef->filter_name; fe->payload = payload; } } git_rwlock_rdunlock(&filter_registry.lock); if (error && fl != NULL) { git_array_clear(fl->filters); git__free(fl); fl = NULL; } *filters = fl; return error; } int git_filter_list_load_ext( git_filter_list **filters, git_repository *repo, git_blob *blob, /* can be NULL */ const char *path, git_filter_mode_t mode, git_filter_options *opts) { git_filter_session filter_session = GIT_FILTER_SESSION_INIT; if (opts) memcpy(&filter_session.options, opts, sizeof(git_filter_options)); return git_filter_list__load( filters, repo, blob, path, mode, &filter_session); } int git_filter_list_load( git_filter_list **filters, git_repository *repo, git_blob *blob, /* can be NULL */ const char *path, git_filter_mode_t mode, uint32_t flags) { git_filter_session filter_session = GIT_FILTER_SESSION_INIT; filter_session.options.flags = flags; return git_filter_list__load( filters, repo, blob, path, mode, &filter_session); } void git_filter_list_free(git_filter_list *fl) { uint32_t i; if (!fl) return; for (i = 0; i < git_array_size(fl->filters); ++i) { git_filter_entry *fe = git_array_get(fl->filters, i); if (fe->filter->cleanup) fe->filter->cleanup(fe->filter, fe->payload); } git_array_clear(fl->filters); git__free(fl); } int git_filter_list_contains( git_filter_list *fl, const char *name) { size_t i; GIT_ASSERT_ARG(name); if (!fl) return 0; for (i = 0; i < fl->filters.size; i++) { if (strcmp(fl->filters.ptr[i].filter_name, name) == 0) return 1; } return 0; } int git_filter_list_push( git_filter_list *fl, git_filter *filter, void *payload) { int error = 0; size_t pos; git_filter_def *fdef = NULL; git_filter_entry *fe; GIT_ASSERT_ARG(fl); GIT_ASSERT_ARG(filter); if (git_rwlock_rdlock(&filter_registry.lock) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); return -1; } if (git_vector_search2( &pos, &filter_registry.filters, filter_def_filter_key_check, filter) == 0) fdef = git_vector_get(&filter_registry.filters, pos); git_rwlock_rdunlock(&filter_registry.lock); if (fdef == NULL) { git_error_set(GIT_ERROR_FILTER, "cannot use an unregistered filter"); return -1; } if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) return error; fe = git_array_alloc(fl->filters); GIT_ERROR_CHECK_ALLOC(fe); fe->filter = filter; fe->payload = payload; return 0; } size_t git_filter_list_length(const git_filter_list *fl) { return fl ? git_array_size(fl->filters) : 0; } struct buf_stream { git_writestream parent; git_buf *target; bool complete; }; static int buf_stream_write( git_writestream *s, const char *buffer, size_t len) { struct buf_stream *buf_stream = (struct buf_stream *)s; GIT_ASSERT_ARG(buf_stream); GIT_ASSERT(buf_stream->complete == 0); return git_buf_put(buf_stream->target, buffer, len); } static int buf_stream_close(git_writestream *s) { struct buf_stream *buf_stream = (struct buf_stream *)s; GIT_ASSERT_ARG(buf_stream); GIT_ASSERT(buf_stream->complete == 0); buf_stream->complete = 1; return 0; } static void buf_stream_free(git_writestream *s) { GIT_UNUSED(s); } static void buf_stream_init(struct buf_stream *writer, git_buf *target) { memset(writer, 0, sizeof(struct buf_stream)); writer->parent.write = buf_stream_write; writer->parent.close = buf_stream_close; writer->parent.free = buf_stream_free; writer->target = target; git_buf_clear(target); } int git_filter_list_apply_to_buffer( git_buf *out, git_filter_list *filters, const char *in, size_t in_len) { struct buf_stream writer; int error; if ((error = git_buf_sanitize(out)) < 0) return error; buf_stream_init(&writer, out); if ((error = git_filter_list_stream_buffer(filters, in, in_len, &writer.parent)) < 0) return error; GIT_ASSERT(writer.complete); return error; } int git_filter_list__convert_buf( git_buf *out, git_filter_list *filters, git_buf *in) { int error; if (!filters || git_filter_list_length(filters) == 0) { git_buf_swap(out, in); git_buf_dispose(in); return 0; } error = git_filter_list_apply_to_buffer(out, filters, in->ptr, in->size); if (!error) git_buf_dispose(in); return error; } int git_filter_list_apply_to_file( git_buf *out, git_filter_list *filters, git_repository *repo, const char *path) { struct buf_stream writer; int error; buf_stream_init(&writer, out); if ((error = git_filter_list_stream_file( filters, repo, path, &writer.parent)) < 0) return error; GIT_ASSERT(writer.complete); return error; } static int buf_from_blob(git_buf *out, git_blob *blob) { git_object_size_t rawsize = git_blob_rawsize(blob); if (!git__is_sizet(rawsize)) { git_error_set(GIT_ERROR_OS, "blob is too large to filter"); return -1; } git_buf_attach_notowned(out, git_blob_rawcontent(blob), (size_t)rawsize); return 0; } int git_filter_list_apply_to_blob( git_buf *out, git_filter_list *filters, git_blob *blob) { struct buf_stream writer; int error; buf_stream_init(&writer, out); if ((error = git_filter_list_stream_blob( filters, blob, &writer.parent)) < 0) return error; GIT_ASSERT(writer.complete); return error; } struct buffered_stream { git_writestream parent; git_filter *filter; int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *); const git_filter_source *source; void **payload; git_buf input; git_buf temp_buf; git_buf *output; git_writestream *target; }; static int buffered_stream_write( git_writestream *s, const char *buffer, size_t len) { struct buffered_stream *buffered_stream = (struct buffered_stream *)s; GIT_ASSERT_ARG(buffered_stream); return git_buf_put(&buffered_stream->input, buffer, len); } static int buffered_stream_close(git_writestream *s) { struct buffered_stream *buffered_stream = (struct buffered_stream *)s; git_buf *writebuf; git_error_state error_state = {0}; int error; GIT_ASSERT_ARG(buffered_stream); error = buffered_stream->write_fn( buffered_stream->filter, buffered_stream->payload, buffered_stream->output, &buffered_stream->input, buffered_stream->source); if (error == GIT_PASSTHROUGH) { writebuf = &buffered_stream->input; } else if (error == 0) { if ((error = git_buf_sanitize(buffered_stream->output)) < 0) return error; writebuf = buffered_stream->output; } else { /* close stream before erroring out taking care * to preserve the original error */ git_error_state_capture(&error_state, error); buffered_stream->target->close(buffered_stream->target); git_error_state_restore(&error_state); return error; } if ((error = buffered_stream->target->write( buffered_stream->target, writebuf->ptr, writebuf->size)) == 0) error = buffered_stream->target->close(buffered_stream->target); return error; } static void buffered_stream_free(git_writestream *s) { struct buffered_stream *buffered_stream = (struct buffered_stream *)s; if (buffered_stream) { git_buf_dispose(&buffered_stream->input); git_buf_dispose(&buffered_stream->temp_buf); git__free(buffered_stream); } } int git_filter_buffered_stream_new( git_writestream **out, git_filter *filter, int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *), git_buf *temp_buf, void **payload, const git_filter_source *source, git_writestream *target) { struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream)); GIT_ERROR_CHECK_ALLOC(buffered_stream); buffered_stream->parent.write = buffered_stream_write; buffered_stream->parent.close = buffered_stream_close; buffered_stream->parent.free = buffered_stream_free; buffered_stream->filter = filter; buffered_stream->write_fn = write_fn; buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf; buffered_stream->payload = payload; buffered_stream->source = source; buffered_stream->target = target; if (temp_buf) git_buf_clear(temp_buf); *out = (git_writestream *)buffered_stream; return 0; } static int setup_stream( git_writestream **out, git_filter_entry *fe, git_filter_list *filters, git_writestream *last_stream) { #ifndef GIT_DEPRECATE_HARD GIT_ASSERT(fe->filter->stream || fe->filter->apply); /* * If necessary, create a stream that proxies the traditional * application. */ if (!fe->filter->stream) { /* Create a stream that proxies the one-shot apply */ return git_filter_buffered_stream_new(out, fe->filter, fe->filter->apply, filters->temp_buf, &fe->payload, &filters->source, last_stream); } #endif GIT_ASSERT(fe->filter->stream); return fe->filter->stream(out, fe->filter, &fe->payload, &filters->source, last_stream); } static int stream_list_init( git_writestream **out, git_vector *streams, git_filter_list *filters, git_writestream *target) { git_writestream *last_stream = target; size_t i; int error = 0; *out = NULL; if (!filters) { *out = target; return 0; } /* Create filters last to first to get the chaining direction */ for (i = 0; i < git_array_size(filters->filters); ++i) { size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ? git_array_size(filters->filters) - 1 - i : i; git_filter_entry *fe = git_array_get(filters->filters, filter_idx); git_writestream *filter_stream; error = setup_stream(&filter_stream, fe, filters, last_stream); if (error < 0) goto out; git_vector_insert(streams, filter_stream); last_stream = filter_stream; } out: if (error) last_stream->close(last_stream); else *out = last_stream; return error; } static void filter_streams_free(git_vector *streams) { git_writestream *stream; size_t i; git_vector_foreach(streams, i, stream) stream->free(stream); git_vector_free(streams); } int git_filter_list_stream_file( git_filter_list *filters, git_repository *repo, const char *path, git_writestream *target) { char buf[FILTERIO_BUFSIZE]; git_buf abspath = GIT_BUF_INIT; const char *base = repo ? git_repository_workdir(repo) : NULL; git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; ssize_t readlen; int fd = -1, error, initialized = 0; if ((error = stream_list_init( &stream_start, &filter_streams, filters, target)) < 0 || (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0 || (error = git_path_validate_workdir_buf(repo, &abspath)) < 0) goto done; initialized = 1; if ((fd = git_futils_open_ro(abspath.ptr)) < 0) { error = fd; goto done; } while ((readlen = p_read(fd, buf, sizeof(buf))) > 0) { if ((error = stream_start->write(stream_start, buf, readlen)) < 0) goto done; } if (readlen < 0) error = -1; done: if (initialized) error |= stream_start->close(stream_start); if (fd >= 0) p_close(fd); filter_streams_free(&filter_streams); git_buf_dispose(&abspath); return error; } int git_filter_list_stream_buffer( git_filter_list *filters, const char *buffer, size_t len, git_writestream *target) { git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; int error, initialized = 0; if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0) goto out; initialized = 1; if ((error = stream_start->write(stream_start, buffer, len)) < 0) goto out; out: if (initialized) error |= stream_start->close(stream_start); filter_streams_free(&filter_streams); return error; } int git_filter_list_stream_blob( git_filter_list *filters, git_blob *blob, git_writestream *target) { git_buf in = GIT_BUF_INIT; if (buf_from_blob(&in, blob) < 0) return -1; if (filters) git_oid_cpy(&filters->source.oid, git_blob_id(blob)); return git_filter_list_stream_buffer(filters, in.ptr, in.size, target); } int git_filter_init(git_filter *filter, unsigned int version) { GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter, version, git_filter, GIT_FILTER_INIT); return 0; } #ifndef GIT_DEPRECATE_HARD int git_filter_list_stream_data( git_filter_list *filters, git_buf *data, git_writestream *target) { int error; if ((error = git_buf_sanitize(data)) < 0) return error; return git_filter_list_stream_buffer(filters, data->ptr, data->size, target); } int git_filter_list_apply_to_data( git_buf *tgt, git_filter_list *filters, git_buf *src) { int error; if ((error = git_buf_sanitize(src)) < 0) return error; return git_filter_list_apply_to_buffer(tgt, filters, src->ptr, src->size); } #endif git2r/src/libgit2/src/attr.c0000644000175000017500000004045414125111754015473 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "attr.h" #include "repository.h" #include "sysdir.h" #include "config.h" #include "attr_file.h" #include "ignore.h" #include "git2/oid.h" #include const char *git_attr__true = "[internal]__TRUE__"; const char *git_attr__false = "[internal]__FALSE__"; const char *git_attr__unset = "[internal]__UNSET__"; git_attr_value_t git_attr_value(const char *attr) { if (attr == NULL || attr == git_attr__unset) return GIT_ATTR_VALUE_UNSPECIFIED; if (attr == git_attr__true) return GIT_ATTR_VALUE_TRUE; if (attr == git_attr__false) return GIT_ATTR_VALUE_FALSE; return GIT_ATTR_VALUE_STRING; } static int collect_attr_files( git_repository *repo, git_attr_session *attr_session, git_attr_options *opts, const char *path, git_vector *files); static void release_attr_files(git_vector *files); int git_attr_get_ext( const char **value, git_repository *repo, git_attr_options *opts, const char *pathname, const char *name) { int error; git_attr_path path; git_vector files = GIT_VECTOR_INIT; size_t i, j; git_attr_file *file; git_attr_name attr; git_attr_rule *rule; git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN; GIT_ASSERT_ARG(value); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options"); *value = NULL; if (git_repository_is_bare(repo)) dir_flag = GIT_DIR_FLAG_FALSE; if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0) return -1; if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0) goto cleanup; memset(&attr, 0, sizeof(attr)); attr.name = name; attr.name_hash = git_attr_file__name_hash(name); git_vector_foreach(&files, i, file) { git_attr_file__foreach_matching_rule(file, &path, j, rule) { size_t pos; if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) { *value = ((git_attr_assignment *)git_vector_get( &rule->assigns, pos))->value; goto cleanup; } } } cleanup: release_attr_files(&files); git_attr_path__free(&path); return error; } int git_attr_get( const char **value, git_repository *repo, uint32_t flags, const char *pathname, const char *name) { git_attr_options opts = GIT_ATTR_OPTIONS_INIT; opts.flags = flags; return git_attr_get_ext(value, repo, &opts, pathname, name); } typedef struct { git_attr_name name; git_attr_assignment *found; } attr_get_many_info; int git_attr_get_many_with_session( const char **values, git_repository *repo, git_attr_session *attr_session, git_attr_options *opts, const char *pathname, size_t num_attr, const char **names) { int error; git_attr_path path; git_vector files = GIT_VECTOR_INIT; size_t i, j, k; git_attr_file *file; git_attr_rule *rule; attr_get_many_info *info = NULL; size_t num_found = 0; git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN; if (!num_attr) return 0; GIT_ASSERT_ARG(values); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(pathname); GIT_ASSERT_ARG(names); GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options"); if (git_repository_is_bare(repo)) dir_flag = GIT_DIR_FLAG_FALSE; if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0) return -1; if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0) goto cleanup; info = git__calloc(num_attr, sizeof(attr_get_many_info)); GIT_ERROR_CHECK_ALLOC(info); git_vector_foreach(&files, i, file) { git_attr_file__foreach_matching_rule(file, &path, j, rule) { for (k = 0; k < num_attr; k++) { size_t pos; if (info[k].found != NULL) /* already found assignment */ continue; if (!info[k].name.name) { info[k].name.name = names[k]; info[k].name.name_hash = git_attr_file__name_hash(names[k]); } if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) { info[k].found = (git_attr_assignment *) git_vector_get(&rule->assigns, pos); values[k] = info[k].found->value; if (++num_found == num_attr) goto cleanup; } } } } for (k = 0; k < num_attr; k++) { if (!info[k].found) values[k] = NULL; } cleanup: release_attr_files(&files); git_attr_path__free(&path); git__free(info); return error; } int git_attr_get_many( const char **values, git_repository *repo, uint32_t flags, const char *pathname, size_t num_attr, const char **names) { git_attr_options opts = GIT_ATTR_OPTIONS_INIT; opts.flags = flags; return git_attr_get_many_with_session( values, repo, NULL, &opts, pathname, num_attr, names); } int git_attr_get_many_ext( const char **values, git_repository *repo, git_attr_options *opts, const char *pathname, size_t num_attr, const char **names) { return git_attr_get_many_with_session( values, repo, NULL, opts, pathname, num_attr, names); } int git_attr_foreach( git_repository *repo, uint32_t flags, const char *pathname, int (*callback)(const char *name, const char *value, void *payload), void *payload) { git_attr_options opts = GIT_ATTR_OPTIONS_INIT; opts.flags = flags; return git_attr_foreach_ext(repo, &opts, pathname, callback, payload); } int git_attr_foreach_ext( git_repository *repo, git_attr_options *opts, const char *pathname, int (*callback)(const char *name, const char *value, void *payload), void *payload) { int error; git_attr_path path; git_vector files = GIT_VECTOR_INIT; size_t i, j, k; git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; git_strmap *seen = NULL; git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(callback); GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options"); if (git_repository_is_bare(repo)) dir_flag = GIT_DIR_FLAG_FALSE; if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0) return -1; if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 || (error = git_strmap_new(&seen)) < 0) goto cleanup; git_vector_foreach(&files, i, file) { git_attr_file__foreach_matching_rule(file, &path, j, rule) { git_vector_foreach(&rule->assigns, k, assign) { /* skip if higher priority assignment was already seen */ if (git_strmap_exists(seen, assign->name)) continue; if ((error = git_strmap_set(seen, assign->name, assign)) < 0) goto cleanup; error = callback(assign->name, assign->value, payload); if (error) { git_error_set_after_callback(error); goto cleanup; } } } } cleanup: git_strmap_free(seen); release_attr_files(&files); git_attr_path__free(&path); return error; } static int preload_attr_source( git_repository *repo, git_attr_session *attr_session, git_attr_file_source *source) { int error; git_attr_file *preload = NULL; if (!source) return 0; error = git_attr_cache__get(&preload, repo, attr_session, source, git_attr_file__parse_buffer, true); if (!error) git_attr_file__free(preload); return error; } GIT_INLINE(int) preload_attr_file( git_repository *repo, git_attr_session *attr_session, const char *base, const char *filename) { git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE }; if (!filename) return 0; source.base = base; source.filename = filename; return preload_attr_source(repo, attr_session, &source); } static int system_attr_file( git_buf *out, git_attr_session *attr_session) { int error; if (!attr_session) { error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM); if (error == GIT_ENOTFOUND) git_error_clear(); return error; } if (!attr_session->init_sysdir) { error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM); if (error == GIT_ENOTFOUND) git_error_clear(); else if (error) return error; attr_session->init_sysdir = 1; } if (attr_session->sysdir.size == 0) return GIT_ENOTFOUND; /* We can safely provide a git_buf with no allocation (asize == 0) to * a consumer. This allows them to treat this as a regular `git_buf`, * but their call to `git_buf_dispose` will not attempt to free it. */ git_buf_attach_notowned( out, attr_session->sysdir.ptr, attr_session->sysdir.size); return 0; } static int attr_setup( git_repository *repo, git_attr_session *attr_session, git_attr_options *opts) { git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT; git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL }; git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_HEAD, NULL, GIT_ATTR_FILE, NULL }; git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL }; git_index *idx = NULL; const char *workdir; int error = 0; if (attr_session && attr_session->init_setup) return 0; if ((error = git_attr_cache__init(repo)) < 0) return error; /* * Preload attribute files that could contain macros so the * definitions will be available for later file parsing. */ if ((error = system_attr_file(&system, attr_session)) < 0 || (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) { if (error != GIT_ENOTFOUND) goto out; error = 0; } if ((error = preload_attr_file(repo, attr_session, NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0) goto out; if ((error = git_repository_item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 || (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) { if (error != GIT_ENOTFOUND) goto out; error = 0; } if ((workdir = git_repository_workdir(repo)) != NULL && (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0) goto out; if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || (error = preload_attr_source(repo, attr_session, &index_source)) < 0) goto out; if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) && (error = preload_attr_source(repo, attr_session, &head_source)) < 0) goto out; if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) { #ifndef GIT_DEPRECATE_HARD if (opts->commit_id) commit_source.commit_id = opts->commit_id; else #endif commit_source.commit_id = &opts->attr_commit_id; if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0) goto out; } if (attr_session) attr_session->init_setup = 1; out: git_buf_dispose(&system); git_buf_dispose(&info); return error; } int git_attr_add_macro( git_repository *repo, const char *name, const char *values) { int error; git_attr_rule *macro = NULL; git_pool *pool; GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); if ((error = git_attr_cache__init(repo)) < 0) return error; macro = git__calloc(1, sizeof(git_attr_rule)); GIT_ERROR_CHECK_ALLOC(macro); pool = &git_repository_attr_cache(repo)->pool; macro->match.pattern = git_pool_strdup(pool, name); GIT_ERROR_CHECK_ALLOC(macro->match.pattern); macro->match.length = strlen(macro->match.pattern); macro->match.flags = GIT_ATTR_FNMATCH_MACRO; error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values); if (!error) error = git_attr_cache__insert_macro(repo, macro); if (error < 0) git_attr_rule__free(macro); return error; } typedef struct { git_repository *repo; git_attr_session *attr_session; git_attr_options *opts; const char *workdir; git_index *index; git_vector *files; } attr_walk_up_info; static int attr_decide_sources( uint32_t flags, bool has_wd, bool has_index, git_attr_file_source_t *srcs) { int count = 0; switch (flags & 0x03) { case GIT_ATTR_CHECK_FILE_THEN_INDEX: if (has_wd) srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE; if (has_index) srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX; break; case GIT_ATTR_CHECK_INDEX_THEN_FILE: if (has_index) srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX; if (has_wd) srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE; break; case GIT_ATTR_CHECK_INDEX_ONLY: if (has_index) srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX; break; } if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) srcs[count++] = GIT_ATTR_FILE_SOURCE_HEAD; if ((flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0) srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT; return count; } static int push_attr_source( git_repository *repo, git_attr_session *attr_session, git_vector *list, git_attr_file_source *source, bool allow_macros) { int error = 0; git_attr_file *file = NULL; error = git_attr_cache__get(&file, repo, attr_session, source, git_attr_file__parse_buffer, allow_macros); if (error < 0) return error; if (file != NULL) { if ((error = git_vector_insert(list, file)) < 0) git_attr_file__free(file); } return error; } GIT_INLINE(int) push_attr_file( git_repository *repo, git_attr_session *attr_session, git_vector *list, const char *base, const char *filename) { git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename }; return push_attr_source(repo, attr_session, list, &source, true); } static int push_one_attr(void *ref, const char *path) { attr_walk_up_info *info = (attr_walk_up_info *)ref; git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES]; int error = 0, n_src, i; bool allow_macros; n_src = attr_decide_sources(info->opts ? info->opts->flags : 0, info->workdir != NULL, info->index != NULL, src); allow_macros = info->workdir ? !strcmp(info->workdir, path) : false; for (i = 0; !error && i < n_src; ++i) { git_attr_file_source source = { src[i], path, GIT_ATTR_FILE }; if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts) { #ifndef GIT_DEPRECATE_HARD if (info->opts->commit_id) source.commit_id = info->opts->commit_id; else #endif source.commit_id = &info->opts->attr_commit_id; } error = push_attr_source(info->repo, info->attr_session, info->files, &source, allow_macros); } return error; } static void release_attr_files(git_vector *files) { size_t i; git_attr_file *file; git_vector_foreach(files, i, file) { git_attr_file__free(file); files->contents[i] = NULL; } git_vector_free(files); } static int collect_attr_files( git_repository *repo, git_attr_session *attr_session, git_attr_options *opts, const char *path, git_vector *files) { int error = 0; git_buf dir = GIT_BUF_INIT, attrfile = GIT_BUF_INIT; const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; GIT_ASSERT(!git_path_is_absolute(path)); if ((error = attr_setup(repo, attr_session, opts)) < 0) return error; /* Resolve path in a non-bare repo */ if (workdir != NULL) { if (!(error = git_repository_workdir_path(&dir, repo, path))) error = git_path_find_dir(&dir); } else { error = git_path_dirname_r(&dir, path); } if (error < 0) goto cleanup; /* in precendence order highest to lowest: * - $GIT_DIR/info/attributes * - path components with .gitattributes * - config core.attributesfile * - $GIT_PREFIX/etc/gitattributes */ if ((error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 || (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) { if (error != GIT_ENOTFOUND) goto cleanup; } info.repo = repo; info.attr_session = attr_session; info.opts = opts; info.workdir = workdir; if (git_repository_index__weakptr(&info.index, repo) < 0) git_error_clear(); /* no error even if there is no index */ info.files = files; if (!strcmp(dir.ptr, ".")) error = push_one_attr(&info, ""); else error = git_path_walk_up(&dir, workdir, push_one_attr, &info); if (error < 0) goto cleanup; if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file); if (error < 0) goto cleanup; } if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { error = system_attr_file(&dir, attr_session); if (!error) error = push_attr_file(repo, attr_session, files, NULL, dir.ptr); else if (error == GIT_ENOTFOUND) error = 0; } cleanup: if (error < 0) release_attr_files(files); git_buf_dispose(&attrfile); git_buf_dispose(&dir); return error; } git2r/src/libgit2/src/offmap.c0000644000175000017500000000354214125111754015766 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "offmap.h" #define kmalloc git__malloc #define kcalloc git__calloc #define krealloc git__realloc #define kreallocarray git__reallocarray #define kfree git__free #include "khash.h" __KHASH_TYPE(off, off64_t, void *) __KHASH_IMPL(off, static kh_inline, off64_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal) int git_offmap_new(git_offmap **out) { *out = kh_init(off); GIT_ERROR_CHECK_ALLOC(*out); return 0; } void git_offmap_free(git_offmap *map) { kh_destroy(off, map); } void git_offmap_clear(git_offmap *map) { kh_clear(off, map); } size_t git_offmap_size(git_offmap *map) { return kh_size(map); } void *git_offmap_get(git_offmap *map, const off64_t key) { size_t idx = kh_get(off, map, key); if (idx == kh_end(map) || !kh_exist(map, idx)) return NULL; return kh_val(map, idx); } int git_offmap_set(git_offmap *map, const off64_t key, void *value) { size_t idx; int rval; idx = kh_put(off, map, key, &rval); if (rval < 0) return -1; if (rval == 0) kh_key(map, idx) = key; kh_val(map, idx) = value; return 0; } int git_offmap_delete(git_offmap *map, const off64_t key) { khiter_t idx = kh_get(off, map, key); if (idx == kh_end(map)) return GIT_ENOTFOUND; kh_del(off, map, idx); return 0; } int git_offmap_exists(git_offmap *map, const off64_t key) { return kh_get(off, map, key) != kh_end(map); } int git_offmap_iterate(void **value, git_offmap *map, size_t *iter, off64_t *key) { size_t i = *iter; while (i < map->n_buckets && !kh_exist(map, i)) i++; if (i >= map->n_buckets) return GIT_ITEROVER; if (key) *key = kh_key(map, i); if (value) *value = kh_value(map, i); *iter = ++i; return 0; } git2r/src/libgit2/src/crlf.c0000644000175000017500000002367114125111754015451 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "git2/attr.h" #include "git2/blob.h" #include "git2/index.h" #include "git2/sys/filter.h" #include "futils.h" #include "hash.h" #include "filter.h" #include "repository.h" typedef enum { GIT_CRLF_UNDEFINED, GIT_CRLF_BINARY, GIT_CRLF_TEXT, GIT_CRLF_TEXT_INPUT, GIT_CRLF_TEXT_CRLF, GIT_CRLF_AUTO, GIT_CRLF_AUTO_INPUT, GIT_CRLF_AUTO_CRLF, } git_crlf_t; struct crlf_attrs { int attr_action; /* the .gitattributes setting */ int crlf_action; /* the core.autocrlf setting */ int auto_crlf; int safe_crlf; int core_eol; }; struct crlf_filter { git_filter f; }; static git_crlf_t check_crlf(const char *value) { if (GIT_ATTR_IS_TRUE(value)) return GIT_CRLF_TEXT; else if (GIT_ATTR_IS_FALSE(value)) return GIT_CRLF_BINARY; else if (GIT_ATTR_IS_UNSPECIFIED(value)) ; else if (strcmp(value, "input") == 0) return GIT_CRLF_TEXT_INPUT; else if (strcmp(value, "auto") == 0) return GIT_CRLF_AUTO; return GIT_CRLF_UNDEFINED; } static git_configmap_value check_eol(const char *value) { if (GIT_ATTR_IS_UNSPECIFIED(value)) ; else if (strcmp(value, "lf") == 0) return GIT_EOL_LF; else if (strcmp(value, "crlf") == 0) return GIT_EOL_CRLF; return GIT_EOL_UNSET; } static int has_cr_in_index(const git_filter_source *src) { git_repository *repo = git_filter_source_repo(src); const char *path = git_filter_source_path(src); git_index *index; const git_index_entry *entry; git_blob *blob; const void *blobcontent; git_object_size_t blobsize; bool found_cr; if (!path) return false; if (git_repository_index__weakptr(&index, repo) < 0) { git_error_clear(); return false; } if (!(entry = git_index_get_bypath(index, path, 0)) && !(entry = git_index_get_bypath(index, path, 1))) return false; if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */ return true; if (git_blob_lookup(&blob, repo, &entry->id) < 0) return false; blobcontent = git_blob_rawcontent(blob); blobsize = git_blob_rawsize(blob); if (!git__is_sizet(blobsize)) blobsize = (size_t)-1; found_cr = (blobcontent != NULL && blobsize > 0 && memchr(blobcontent, '\r', (size_t)blobsize) != NULL); git_blob_free(blob); return found_cr; } static int text_eol_is_crlf(struct crlf_attrs *ca) { if (ca->auto_crlf == GIT_AUTO_CRLF_TRUE) return 1; else if (ca->auto_crlf == GIT_AUTO_CRLF_INPUT) return 0; if (ca->core_eol == GIT_EOL_CRLF) return 1; if (ca->core_eol == GIT_EOL_UNSET && GIT_EOL_NATIVE == GIT_EOL_CRLF) return 1; return 0; } static git_configmap_value output_eol(struct crlf_attrs *ca) { switch (ca->crlf_action) { case GIT_CRLF_BINARY: return GIT_EOL_UNSET; case GIT_CRLF_TEXT_CRLF: return GIT_EOL_CRLF; case GIT_CRLF_TEXT_INPUT: return GIT_EOL_LF; case GIT_CRLF_UNDEFINED: case GIT_CRLF_AUTO_CRLF: return GIT_EOL_CRLF; case GIT_CRLF_AUTO_INPUT: return GIT_EOL_LF; case GIT_CRLF_TEXT: case GIT_CRLF_AUTO: return text_eol_is_crlf(ca) ? GIT_EOL_CRLF : GIT_EOL_LF; } /* TODO: warn when available */ return ca->core_eol; } GIT_INLINE(int) check_safecrlf( struct crlf_attrs *ca, const git_filter_source *src, git_buf_text_stats *stats) { const char *filename = git_filter_source_path(src); if (!ca->safe_crlf) return 0; if (output_eol(ca) == GIT_EOL_LF) { /* * CRLFs would not be restored by checkout: * check if we'd remove CRLFs */ if (stats->crlf) { if (ca->safe_crlf == GIT_SAFE_CRLF_WARN) { /* TODO: issue a warning when available */ } else { if (filename && *filename) git_error_set( GIT_ERROR_FILTER, "CRLF would be replaced by LF in '%s'", filename); else git_error_set( GIT_ERROR_FILTER, "CRLF would be replaced by LF"); return -1; } } } else if (output_eol(ca) == GIT_EOL_CRLF) { /* * CRLFs would be added by checkout: * check if we have "naked" LFs */ if (stats->crlf != stats->lf) { if (ca->safe_crlf == GIT_SAFE_CRLF_WARN) { /* TODO: issue a warning when available */ } else { if (filename && *filename) git_error_set( GIT_ERROR_FILTER, "LF would be replaced by CRLF in '%s'", filename); else git_error_set( GIT_ERROR_FILTER, "LF would be replaced by CRLF"); return -1; } } } return 0; } static int crlf_apply_to_odb( struct crlf_attrs *ca, git_buf *to, const git_buf *from, const git_filter_source *src) { git_buf_text_stats stats; bool is_binary; int error; /* Binary attribute? Empty file? Nothing to do */ if (ca->crlf_action == GIT_CRLF_BINARY || !git_buf_len(from)) return GIT_PASSTHROUGH; is_binary = git_buf_gather_text_stats(&stats, from, false); /* Heuristics to see if we can skip the conversion. * Straight from Core Git. */ if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_AUTO_INPUT || ca->crlf_action == GIT_CRLF_AUTO_CRLF) { if (is_binary) return GIT_PASSTHROUGH; /* * If the file in the index has any CR in it, do not convert. * This is the new safer autocrlf handling. */ if (has_cr_in_index(src)) return GIT_PASSTHROUGH; } if ((error = check_safecrlf(ca, src, &stats)) < 0) return error; /* If there are no CR characters to filter out, then just pass */ if (!stats.crlf) return GIT_PASSTHROUGH; /* Actually drop the carriage returns */ return git_buf_crlf_to_lf(to, from); } static int crlf_apply_to_workdir( struct crlf_attrs *ca, git_buf *to, const git_buf *from) { git_buf_text_stats stats; bool is_binary; /* Empty file? Nothing to do. */ if (git_buf_len(from) == 0 || output_eol(ca) != GIT_EOL_CRLF) return GIT_PASSTHROUGH; is_binary = git_buf_gather_text_stats(&stats, from, false); /* If there are no LFs, or all LFs are part of a CRLF, nothing to do */ if (stats.lf == 0 || stats.lf == stats.crlf) return GIT_PASSTHROUGH; if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_AUTO_INPUT || ca->crlf_action == GIT_CRLF_AUTO_CRLF) { /* If we have any existing CR or CRLF line endings, do nothing */ if (stats.cr > 0) return GIT_PASSTHROUGH; /* Don't filter binary files */ if (is_binary) return GIT_PASSTHROUGH; } return git_buf_lf_to_crlf(to, from); } static int convert_attrs( struct crlf_attrs *ca, const char **attr_values, const git_filter_source *src) { int error; memset(ca, 0, sizeof(struct crlf_attrs)); if ((error = git_repository__configmap_lookup(&ca->auto_crlf, git_filter_source_repo(src), GIT_CONFIGMAP_AUTO_CRLF)) < 0 || (error = git_repository__configmap_lookup(&ca->safe_crlf, git_filter_source_repo(src), GIT_CONFIGMAP_SAFE_CRLF)) < 0 || (error = git_repository__configmap_lookup(&ca->core_eol, git_filter_source_repo(src), GIT_CONFIGMAP_EOL)) < 0) return error; /* downgrade FAIL to WARN if ALLOW_UNSAFE option is used */ if ((git_filter_source_flags(src) & GIT_FILTER_ALLOW_UNSAFE) && ca->safe_crlf == GIT_SAFE_CRLF_FAIL) ca->safe_crlf = GIT_SAFE_CRLF_WARN; if (attr_values) { /* load the text attribute */ ca->crlf_action = check_crlf(attr_values[2]); /* text */ if (ca->crlf_action == GIT_CRLF_UNDEFINED) ca->crlf_action = check_crlf(attr_values[0]); /* crlf */ if (ca->crlf_action != GIT_CRLF_BINARY) { /* load the eol attribute */ int eol_attr = check_eol(attr_values[1]); if (ca->crlf_action == GIT_CRLF_AUTO && eol_attr == GIT_EOL_LF) ca->crlf_action = GIT_CRLF_AUTO_INPUT; else if (ca->crlf_action == GIT_CRLF_AUTO && eol_attr == GIT_EOL_CRLF) ca->crlf_action = GIT_CRLF_AUTO_CRLF; else if (eol_attr == GIT_EOL_LF) ca->crlf_action = GIT_CRLF_TEXT_INPUT; else if (eol_attr == GIT_EOL_CRLF) ca->crlf_action = GIT_CRLF_TEXT_CRLF; } ca->attr_action = ca->crlf_action; } else { ca->crlf_action = GIT_CRLF_UNDEFINED; } if (ca->crlf_action == GIT_CRLF_TEXT) ca->crlf_action = text_eol_is_crlf(ca) ? GIT_CRLF_TEXT_CRLF : GIT_CRLF_TEXT_INPUT; if (ca->crlf_action == GIT_CRLF_UNDEFINED && ca->auto_crlf == GIT_AUTO_CRLF_FALSE) ca->crlf_action = GIT_CRLF_BINARY; if (ca->crlf_action == GIT_CRLF_UNDEFINED && ca->auto_crlf == GIT_AUTO_CRLF_TRUE) ca->crlf_action = GIT_CRLF_AUTO_CRLF; if (ca->crlf_action == GIT_CRLF_UNDEFINED && ca->auto_crlf == GIT_AUTO_CRLF_INPUT) ca->crlf_action = GIT_CRLF_AUTO_INPUT; return 0; } static int crlf_check( git_filter *self, void **payload, /* points to NULL ptr on entry, may be set */ const git_filter_source *src, const char **attr_values) { struct crlf_attrs ca; GIT_UNUSED(self); convert_attrs(&ca, attr_values, src); if (ca.crlf_action == GIT_CRLF_BINARY) return GIT_PASSTHROUGH; *payload = git__malloc(sizeof(ca)); GIT_ERROR_CHECK_ALLOC(*payload); memcpy(*payload, &ca, sizeof(ca)); return 0; } static int crlf_apply( git_filter *self, void **payload, /* may be read and/or set */ git_buf *to, const git_buf *from, const git_filter_source *src) { /* initialize payload in case `check` was bypassed */ if (!*payload) { int error = crlf_check(self, payload, src, NULL); if (error < 0) return error; } if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE) return crlf_apply_to_workdir(*payload, to, from); else return crlf_apply_to_odb(*payload, to, from, src); } static int crlf_stream( git_writestream **out, git_filter *self, void **payload, const git_filter_source *src, git_writestream *next) { return git_filter_buffered_stream_new(out, self, crlf_apply, NULL, payload, src, next); } static void crlf_cleanup( git_filter *self, void *payload) { GIT_UNUSED(self); git__free(payload); } git_filter *git_crlf_filter_new(void) { struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter)); if (f == NULL) return NULL; f->f.version = GIT_FILTER_VERSION; f->f.attributes = "crlf eol text"; f->f.initialize = NULL; f->f.shutdown = git_filter_free; f->f.check = crlf_check; f->f.stream = crlf_stream; f->f.cleanup = crlf_cleanup; return (git_filter *)f; } git2r/src/libgit2/src/mailmap.c0000644000175000017500000003057014125111754016137 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "mailmap.h" #include "common.h" #include "path.h" #include "repository.h" #include "signature.h" #include "git2/config.h" #include "git2/revparse.h" #include "blob.h" #include "parse.h" #define MM_FILE ".mailmap" #define MM_FILE_CONFIG "mailmap.file" #define MM_BLOB_CONFIG "mailmap.blob" #define MM_BLOB_DEFAULT "HEAD:" MM_FILE static void mailmap_entry_free(git_mailmap_entry *entry) { if (!entry) return; git__free(entry->real_name); git__free(entry->real_email); git__free(entry->replace_name); git__free(entry->replace_email); git__free(entry); } /* * First we sort by replace_email, then replace_name (if present). * Entries with names are greater than entries without. */ static int mailmap_entry_cmp(const void *a_raw, const void *b_raw) { const git_mailmap_entry *a = (const git_mailmap_entry *)a_raw; const git_mailmap_entry *b = (const git_mailmap_entry *)b_raw; int cmp; GIT_ASSERT_ARG(a && a->replace_email); GIT_ASSERT_ARG(b && b->replace_email); cmp = git__strcmp(a->replace_email, b->replace_email); if (cmp) return cmp; /* NULL replace_names are less than not-NULL ones */ if (a->replace_name == NULL || b->replace_name == NULL) return (int)(a->replace_name != NULL) - (int)(b->replace_name != NULL); return git__strcmp(a->replace_name, b->replace_name); } /* Replace the old entry with the new on duplicate. */ static int mailmap_entry_replace(void **old_raw, void *new_raw) { mailmap_entry_free((git_mailmap_entry *)*old_raw); *old_raw = new_raw; return GIT_EEXISTS; } /* Check if we're at the end of line, w/ comments */ static bool is_eol(git_parse_ctx *ctx) { char c; return git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE) < 0 || c == '#'; } static int advance_until( const char **start, size_t *len, git_parse_ctx *ctx, char needle) { *start = ctx->line; while (ctx->line_len > 0 && *ctx->line != '#' && *ctx->line != needle) git_parse_advance_chars(ctx, 1); if (ctx->line_len == 0 || *ctx->line == '#') return -1; /* end of line */ *len = ctx->line - *start; git_parse_advance_chars(ctx, 1); /* advance past needle */ return 0; } /* * Parse a single entry from a mailmap file. * * The output git_bufs will be non-owning, and should be copied before being * persisted. */ static int parse_mailmap_entry( git_buf *real_name, git_buf *real_email, git_buf *replace_name, git_buf *replace_email, git_parse_ctx *ctx) { const char *start; size_t len; git_buf_clear(real_name); git_buf_clear(real_email); git_buf_clear(replace_name); git_buf_clear(replace_email); git_parse_advance_ws(ctx); if (is_eol(ctx)) return -1; /* blank line */ /* Parse the real name */ if (advance_until(&start, &len, ctx, '<') < 0) return -1; git_buf_attach_notowned(real_name, start, len); git_buf_rtrim(real_name); /* * If this is the last email in the line, this is the email to replace, * otherwise, it's the real email. */ if (advance_until(&start, &len, ctx, '>') < 0) return -1; /* If we aren't at the end of the line, parse a second name and email */ if (!is_eol(ctx)) { git_buf_attach_notowned(real_email, start, len); git_parse_advance_ws(ctx); if (advance_until(&start, &len, ctx, '<') < 0) return -1; git_buf_attach_notowned(replace_name, start, len); git_buf_rtrim(replace_name); if (advance_until(&start, &len, ctx, '>') < 0) return -1; } git_buf_attach_notowned(replace_email, start, len); if (!is_eol(ctx)) return -1; return 0; } int git_mailmap_new(git_mailmap **out) { int error; git_mailmap *mm = git__calloc(1, sizeof(git_mailmap)); GIT_ERROR_CHECK_ALLOC(mm); error = git_vector_init(&mm->entries, 0, mailmap_entry_cmp); if (error < 0) { git__free(mm); return error; } *out = mm; return 0; } void git_mailmap_free(git_mailmap *mm) { size_t idx; git_mailmap_entry *entry; if (!mm) return; git_vector_foreach(&mm->entries, idx, entry) mailmap_entry_free(entry); git_vector_free(&mm->entries); git__free(mm); } static int mailmap_add_entry_unterminated( git_mailmap *mm, const char *real_name, size_t real_name_size, const char *real_email, size_t real_email_size, const char *replace_name, size_t replace_name_size, const char *replace_email, size_t replace_email_size) { int error; git_mailmap_entry *entry = git__calloc(1, sizeof(git_mailmap_entry)); GIT_ERROR_CHECK_ALLOC(entry); GIT_ASSERT_ARG(mm); GIT_ASSERT_ARG(replace_email && *replace_email); if (real_name_size > 0) { entry->real_name = git__substrdup(real_name, real_name_size); GIT_ERROR_CHECK_ALLOC(entry->real_name); } if (real_email_size > 0) { entry->real_email = git__substrdup(real_email, real_email_size); GIT_ERROR_CHECK_ALLOC(entry->real_email); } if (replace_name_size > 0) { entry->replace_name = git__substrdup(replace_name, replace_name_size); GIT_ERROR_CHECK_ALLOC(entry->replace_name); } entry->replace_email = git__substrdup(replace_email, replace_email_size); GIT_ERROR_CHECK_ALLOC(entry->replace_email); error = git_vector_insert_sorted(&mm->entries, entry, mailmap_entry_replace); if (error == GIT_EEXISTS) error = GIT_OK; else if (error < 0) mailmap_entry_free(entry); return error; } int git_mailmap_add_entry( git_mailmap *mm, const char *real_name, const char *real_email, const char *replace_name, const char *replace_email) { return mailmap_add_entry_unterminated( mm, real_name, real_name ? strlen(real_name) : 0, real_email, real_email ? strlen(real_email) : 0, replace_name, replace_name ? strlen(replace_name) : 0, replace_email, strlen(replace_email)); } static int mailmap_add_buffer(git_mailmap *mm, const char *buf, size_t len) { int error = 0; git_parse_ctx ctx; /* Scratch buffers containing the real parsed names & emails */ git_buf real_name = GIT_BUF_INIT; git_buf real_email = GIT_BUF_INIT; git_buf replace_name = GIT_BUF_INIT; git_buf replace_email = GIT_BUF_INIT; /* Buffers may not contain '\0's. */ if (memchr(buf, '\0', len) != NULL) return -1; git_parse_ctx_init(&ctx, buf, len); /* Run the parser */ while (ctx.remain_len > 0) { error = parse_mailmap_entry( &real_name, &real_email, &replace_name, &replace_email, &ctx); if (error < 0) { error = 0; /* Skip lines which don't contain a valid entry */ git_parse_advance_line(&ctx); continue; /* TODO: warn */ } /* NOTE: Can't use add_entry(...) as our buffers aren't terminated */ error = mailmap_add_entry_unterminated( mm, real_name.ptr, real_name.size, real_email.ptr, real_email.size, replace_name.ptr, replace_name.size, replace_email.ptr, replace_email.size); if (error < 0) goto cleanup; error = 0; } cleanup: git_buf_dispose(&real_name); git_buf_dispose(&real_email); git_buf_dispose(&replace_name); git_buf_dispose(&replace_email); return error; } int git_mailmap_from_buffer(git_mailmap **out, const char *data, size_t len) { int error = git_mailmap_new(out); if (error < 0) return error; error = mailmap_add_buffer(*out, data, len); if (error < 0) { git_mailmap_free(*out); *out = NULL; } return error; } static int mailmap_add_blob( git_mailmap *mm, git_repository *repo, const char *rev) { git_object *object = NULL; git_blob *blob = NULL; git_buf content = GIT_BUF_INIT; int error; GIT_ASSERT_ARG(mm); GIT_ASSERT_ARG(repo); error = git_revparse_single(&object, repo, rev); if (error < 0) goto cleanup; error = git_object_peel((git_object **)&blob, object, GIT_OBJECT_BLOB); if (error < 0) goto cleanup; error = git_blob__getbuf(&content, blob); if (error < 0) goto cleanup; error = mailmap_add_buffer(mm, content.ptr, content.size); if (error < 0) goto cleanup; cleanup: git_buf_dispose(&content); git_blob_free(blob); git_object_free(object); return error; } static int mailmap_add_file_ondisk( git_mailmap *mm, const char *path, git_repository *repo) { const char *base = repo ? git_repository_workdir(repo) : NULL; git_buf fullpath = GIT_BUF_INIT; git_buf content = GIT_BUF_INIT; int error; error = git_path_join_unrooted(&fullpath, path, base, NULL); if (error < 0) goto cleanup; error = git_path_validate_workdir_buf(repo, &fullpath); if (error < 0) goto cleanup; error = git_futils_readbuffer(&content, fullpath.ptr); if (error < 0) goto cleanup; error = mailmap_add_buffer(mm, content.ptr, content.size); if (error < 0) goto cleanup; cleanup: git_buf_dispose(&fullpath); git_buf_dispose(&content); return error; } /* NOTE: Only expose with an error return, currently never errors */ static void mailmap_add_from_repository(git_mailmap *mm, git_repository *repo) { git_config *config = NULL; git_buf rev_buf = GIT_BUF_INIT; git_buf path_buf = GIT_BUF_INIT; const char *rev = NULL; const char *path = NULL; /* If we're in a bare repo, default blob to 'HEAD:.mailmap' */ if (repo->is_bare) rev = MM_BLOB_DEFAULT; /* Try to load 'mailmap.file' and 'mailmap.blob' cfgs from the repo */ if (git_repository_config(&config, repo) == 0) { if (git_config_get_string_buf(&rev_buf, config, MM_BLOB_CONFIG) == 0) rev = rev_buf.ptr; if (git_config_get_path(&path_buf, config, MM_FILE_CONFIG) == 0) path = path_buf.ptr; } /* * Load mailmap files in order, overriding previous entries with new ones. * 1. The '.mailmap' file in the repository's workdir root, * 2. The blob described by the 'mailmap.blob' config (default HEAD:.mailmap), * 3. The file described by the 'mailmap.file' config. * * We ignore errors from these loads, as these files may not exist, or may * contain invalid information, and we don't want to report that error. * * XXX: Warn? */ if (!repo->is_bare) mailmap_add_file_ondisk(mm, MM_FILE, repo); if (rev != NULL) mailmap_add_blob(mm, repo, rev); if (path != NULL) mailmap_add_file_ondisk(mm, path, repo); git_buf_dispose(&rev_buf); git_buf_dispose(&path_buf); git_config_free(config); } int git_mailmap_from_repository(git_mailmap **out, git_repository *repo) { int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); if ((error = git_mailmap_new(out)) < 0) return error; mailmap_add_from_repository(*out, repo); return 0; } const git_mailmap_entry *git_mailmap_entry_lookup( const git_mailmap *mm, const char *name, const char *email) { int error; ssize_t fallback = -1; size_t idx; git_mailmap_entry *entry; /* The lookup needle we want to use only sets the replace_email. */ git_mailmap_entry needle = { NULL }; needle.replace_email = (char *)email; GIT_ASSERT_ARG_WITH_RETVAL(email, NULL); if (!mm) return NULL; /* * We want to find the place to start looking. so we do a binary search for * the "fallback" nameless entry. If we find it, we advance past it and record * the index. */ error = git_vector_bsearch(&idx, (git_vector *)&mm->entries, &needle); if (error >= 0) fallback = idx++; else if (error != GIT_ENOTFOUND) return NULL; /* do a linear search for an exact match */ for (; idx < git_vector_length(&mm->entries); ++idx) { entry = git_vector_get(&mm->entries, idx); if (git__strcmp(entry->replace_email, email)) break; /* it's a different email, so we're done looking */ /* should be specific */ GIT_ASSERT_WITH_RETVAL(entry->replace_name, NULL); if (!name || !git__strcmp(entry->replace_name, name)) return entry; } if (fallback < 0) return NULL; /* no fallback */ return git_vector_get(&mm->entries, fallback); } int git_mailmap_resolve( const char **real_name, const char **real_email, const git_mailmap *mailmap, const char *name, const char *email) { const git_mailmap_entry *entry = NULL; GIT_ASSERT(name); GIT_ASSERT(email); *real_name = name; *real_email = email; if ((entry = git_mailmap_entry_lookup(mailmap, name, email))) { if (entry->real_name) *real_name = entry->real_name; if (entry->real_email) *real_email = entry->real_email; } return 0; } int git_mailmap_resolve_signature( git_signature **out, const git_mailmap *mailmap, const git_signature *sig) { const char *name = NULL; const char *email = NULL; int error; if (!sig) return 0; error = git_mailmap_resolve(&name, &email, mailmap, sig->name, sig->email); if (error < 0) return error; error = git_signature_new(out, name, email, sig->when.time, sig->when.offset); if (error < 0) return error; /* Copy over the sign, as git_signature_new doesn't let you pass it. */ (*out)->when.sign = sig->when.sign; return 0; } git2r/src/libgit2/src/parse.h0000644000175000017500000000316514125111754015636 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_parse_h__ #define INCLUDE_parse_h__ #include "common.h" typedef struct { /* Original content buffer */ const char *content; size_t content_len; /* The remaining (unparsed) buffer */ const char *remain; size_t remain_len; const char *line; size_t line_len; size_t line_num; } git_parse_ctx; #define GIT_PARSE_CTX_INIT { 0 } int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len); void git_parse_ctx_clear(git_parse_ctx *ctx); #define git_parse_ctx_contains_s(ctx, str) \ git_parse_ctx_contains(ctx, str, sizeof(str) - 1) GIT_INLINE(bool) git_parse_ctx_contains( git_parse_ctx *ctx, const char *str, size_t len) { return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0); } void git_parse_advance_line(git_parse_ctx *ctx); void git_parse_advance_chars(git_parse_ctx *ctx, size_t char_cnt); int git_parse_advance_expected( git_parse_ctx *ctx, const char *expected, size_t expected_len); #define git_parse_advance_expected_str(ctx, str) \ git_parse_advance_expected(ctx, str, strlen(str)) int git_parse_advance_ws(git_parse_ctx *ctx); int git_parse_advance_nl(git_parse_ctx *ctx); int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base); int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx); enum GIT_PARSE_PEEK_FLAGS { GIT_PARSE_PEEK_SKIP_WHITESPACE = (1 << 0) }; int git_parse_peek(char *out, git_parse_ctx *ctx, int flags); #endif git2r/src/libgit2/src/mwindow.c0000644000175000017500000003123614125111754016203 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "mwindow.h" #include "vector.h" #include "futils.h" #include "map.h" #include "runtime.h" #include "strmap.h" #include "pack.h" #define DEFAULT_WINDOW_SIZE \ (sizeof(void*) >= 8 \ ? 1 * 1024 * 1024 * 1024 \ : 32 * 1024 * 1024) #define DEFAULT_MAPPED_LIMIT \ ((1024 * 1024) * (sizeof(void*) >= 8 ? UINT64_C(8192) : UINT64_C(256))) /* default is unlimited */ #define DEFAULT_FILE_LIMIT 0 size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE; size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT; size_t git_mwindow__file_limit = DEFAULT_FILE_LIMIT; /* Mutex to control access to `git_mwindow__mem_ctl` and `git__pack_cache`. */ git_mutex git__mwindow_mutex; /* Whenever you want to read or modify this, grab `git__mwindow_mutex` */ git_mwindow_ctl git_mwindow__mem_ctl; /* Global list of mwindow files, to open packs once across repos */ git_strmap *git__pack_cache = NULL; static void git_mwindow_global_shutdown(void) { git_strmap *tmp = git__pack_cache; git_mutex_free(&git__mwindow_mutex); git__pack_cache = NULL; git_strmap_free(tmp); } int git_mwindow_global_init(void) { int error; GIT_ASSERT(!git__pack_cache); if ((error = git_mutex_init(&git__mwindow_mutex)) < 0 || (error = git_strmap_new(&git__pack_cache)) < 0) return error; return git_runtime_shutdown_register(git_mwindow_global_shutdown); } int git_mwindow_get_pack(struct git_pack_file **out, const char *path) { struct git_pack_file *pack; char *packname; int error; if ((error = git_packfile__name(&packname, path)) < 0) return error; if (git_mutex_lock(&git__mwindow_mutex) < 0) { git_error_set(GIT_ERROR_OS, "failed to lock mwindow mutex"); return -1; } pack = git_strmap_get(git__pack_cache, packname); git__free(packname); if (pack != NULL) { git_atomic32_inc(&pack->refcount); git_mutex_unlock(&git__mwindow_mutex); *out = pack; return 0; } /* If we didn't find it, we need to create it */ if ((error = git_packfile_alloc(&pack, path)) < 0) { git_mutex_unlock(&git__mwindow_mutex); return error; } git_atomic32_inc(&pack->refcount); error = git_strmap_set(git__pack_cache, pack->pack_name, pack); git_mutex_unlock(&git__mwindow_mutex); if (error < 0) { git_packfile_free(pack, false); return error; } *out = pack; return 0; } int git_mwindow_put_pack(struct git_pack_file *pack) { int count, error; struct git_pack_file *pack_to_delete = NULL; if ((error = git_mutex_lock(&git__mwindow_mutex)) < 0) return error; /* put before get would be a corrupted state */ GIT_ASSERT(git__pack_cache); /* if we cannot find it, the state is corrupted */ GIT_ASSERT(git_strmap_exists(git__pack_cache, pack->pack_name)); count = git_atomic32_dec(&pack->refcount); if (count == 0) { git_strmap_delete(git__pack_cache, pack->pack_name); pack_to_delete = pack; } git_mutex_unlock(&git__mwindow_mutex); git_packfile_free(pack_to_delete, false); return 0; } /* * Free all the windows in a sequence, typically because we're done * with the file. Needs to hold the git__mwindow_mutex. */ static int git_mwindow_free_all_locked(git_mwindow_file *mwf) { git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; size_t i; /* * Remove these windows from the global list */ for (i = 0; i < ctl->windowfiles.length; ++i){ if (git_vector_get(&ctl->windowfiles, i) == mwf) { git_vector_remove(&ctl->windowfiles, i); break; } } if (ctl->windowfiles.length == 0) { git_vector_free(&ctl->windowfiles); ctl->windowfiles.contents = NULL; } while (mwf->windows) { git_mwindow *w = mwf->windows; GIT_ASSERT(w->inuse_cnt == 0); ctl->mapped -= w->window_map.len; ctl->open_windows--; git_futils_mmap_free(&w->window_map); mwf->windows = w->next; git__free(w); } return 0; } int git_mwindow_free_all(git_mwindow_file *mwf) { int error; if (git_mutex_lock(&git__mwindow_mutex)) { git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); return -1; } error = git_mwindow_free_all_locked(mwf); git_mutex_unlock(&git__mwindow_mutex); return error; } /* * Check if a window 'win' contains the address 'offset' */ int git_mwindow_contains(git_mwindow *win, off64_t offset) { off64_t win_off = win->offset; return win_off <= offset && offset <= (off64_t)(win_off + win->window_map.len); } #define GIT_MWINDOW__LRU -1 #define GIT_MWINDOW__MRU 1 /* * Find the least- or most-recently-used window in a file that is not currently * being used. The 'only_unused' flag controls whether the caller requires the * file to only have unused windows. If '*out_window' is non-null, it is used as * a starting point for the comparison. * * Returns whether such a window was found in the file. */ static bool git_mwindow_scan_recently_used( git_mwindow_file *mwf, git_mwindow **out_window, git_mwindow **out_last, bool only_unused, int comparison_sign) { git_mwindow *w, *w_last; git_mwindow *lru_window = NULL, *lru_last = NULL; bool found = false; GIT_ASSERT_ARG(mwf); GIT_ASSERT_ARG(out_window); lru_window = *out_window; if (out_last) lru_last = *out_last; for (w_last = NULL, w = mwf->windows; w; w_last = w, w = w->next) { if (w->inuse_cnt) { if (only_unused) return false; /* This window is currently being used. Skip it. */ continue; } /* * If the current one is more (or less) recent than the last one, * store it in the output parameter. If lru_window is NULL, * it's the first loop, so store it as well. */ if (!lru_window || (comparison_sign == GIT_MWINDOW__LRU && lru_window->last_used > w->last_used) || (comparison_sign == GIT_MWINDOW__MRU && lru_window->last_used < w->last_used)) { lru_window = w; lru_last = w_last; found = true; } } if (!found) return false; *out_window = lru_window; if (out_last) *out_last = lru_last; return true; } /* * Close the least recently used window (that is currently not being used) out * of all the files. Called under lock from new_window_locked. */ static int git_mwindow_close_lru_window_locked(void) { git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; git_mwindow_file *cur; size_t i; git_mwindow *lru_window = NULL, *lru_last = NULL, **list = NULL; git_vector_foreach(&ctl->windowfiles, i, cur) { if (git_mwindow_scan_recently_used( cur, &lru_window, &lru_last, false, GIT_MWINDOW__LRU)) { list = &cur->windows; } } if (!lru_window) { git_error_set(GIT_ERROR_OS, "failed to close memory window; couldn't find LRU"); return -1; } ctl->mapped -= lru_window->window_map.len; git_futils_mmap_free(&lru_window->window_map); if (lru_last) lru_last->next = lru_window->next; else *list = lru_window->next; git__free(lru_window); ctl->open_windows--; return 0; } /* * Finds the file that does not have any open windows AND whose * most-recently-used window is the least-recently used one across all * currently open files. * * Called under lock from new_window_locked. */ static int git_mwindow_find_lru_file_locked(git_mwindow_file **out) { git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; git_mwindow_file *lru_file = NULL, *current_file = NULL; git_mwindow *lru_window = NULL; size_t i; git_vector_foreach(&ctl->windowfiles, i, current_file) { git_mwindow *mru_window = NULL; if (!git_mwindow_scan_recently_used( current_file, &mru_window, NULL, true, GIT_MWINDOW__MRU)) { continue; } if (!lru_window || lru_window->last_used > mru_window->last_used) { lru_window = mru_window; lru_file = current_file; } } if (!lru_file) { git_error_set(GIT_ERROR_OS, "failed to close memory window file; couldn't find LRU"); return -1; } *out = lru_file; return 0; } /* This gets called under lock from git_mwindow_open */ static git_mwindow *new_window_locked( git_file fd, off64_t size, off64_t offset) { git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; size_t walign = git_mwindow__window_size / 2; off64_t len; git_mwindow *w; w = git__calloc(1, sizeof(*w)); if (w == NULL) return NULL; w->offset = (offset / walign) * walign; len = size - w->offset; if (len > (off64_t)git_mwindow__window_size) len = (off64_t)git_mwindow__window_size; ctl->mapped += (size_t)len; while (git_mwindow__mapped_limit < ctl->mapped && git_mwindow_close_lru_window_locked() == 0) /* nop */; /* * We treat `mapped_limit` as a soft limit. If we can't find a * window to close and are above the limit, we still mmap the new * window. */ if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) { /* * The first error might be down to memory fragmentation even if * we're below our soft limits, so free up what we can and try again. */ while (git_mwindow_close_lru_window_locked() == 0) /* nop */; if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) { git__free(w); return NULL; } } ctl->mmap_calls++; ctl->open_windows++; if (ctl->mapped > ctl->peak_mapped) ctl->peak_mapped = ctl->mapped; if (ctl->open_windows > ctl->peak_open_windows) ctl->peak_open_windows = ctl->open_windows; return w; } /* * Open a new window, closing the least recenty used until we have * enough space. Don't forget to add it to your list */ unsigned char *git_mwindow_open( git_mwindow_file *mwf, git_mwindow **cursor, off64_t offset, size_t extra, unsigned int *left) { git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; git_mwindow *w = *cursor; if (git_mutex_lock(&git__mwindow_mutex)) { git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); return NULL; } if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) { if (w) { w->inuse_cnt--; } for (w = mwf->windows; w; w = w->next) { if (git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra)) break; } /* * If there isn't a suitable window, we need to create a new * one. */ if (!w) { w = new_window_locked(mwf->fd, mwf->size, offset); if (w == NULL) { git_mutex_unlock(&git__mwindow_mutex); return NULL; } w->next = mwf->windows; mwf->windows = w; } } /* If we changed w, store it in the cursor */ if (w != *cursor) { w->last_used = ctl->used_ctr++; w->inuse_cnt++; *cursor = w; } offset -= w->offset; if (left) *left = (unsigned int)(w->window_map.len - offset); git_mutex_unlock(&git__mwindow_mutex); return (unsigned char *) w->window_map.data + offset; } int git_mwindow_file_register(git_mwindow_file *mwf) { git_vector closed_files = GIT_VECTOR_INIT; git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; int error; size_t i; git_mwindow_file *closed_file = NULL; if (git_mutex_lock(&git__mwindow_mutex)) { git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); return -1; } if (ctl->windowfiles.length == 0 && (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < 0) { git_mutex_unlock(&git__mwindow_mutex); goto cleanup; } if (git_mwindow__file_limit) { git_mwindow_file *lru_file; while (git_mwindow__file_limit <= ctl->windowfiles.length && git_mwindow_find_lru_file_locked(&lru_file) == 0) { if ((error = git_vector_insert(&closed_files, lru_file)) < 0) { /* * Exceeding the file limit seems preferrable to being open to * data races that can end up corrupting the heap. */ break; } git_mwindow_free_all_locked(lru_file); } } error = git_vector_insert(&ctl->windowfiles, mwf); git_mutex_unlock(&git__mwindow_mutex); if (error < 0) goto cleanup; /* * Once we have released the global windowfiles lock, we can close each * individual file. Before doing so, acquire that file's lock to avoid * closing a file that is currently being used. */ git_vector_foreach(&closed_files, i, closed_file) { error = git_mutex_lock(&closed_file->lock); if (error < 0) continue; p_close(closed_file->fd); closed_file->fd = -1; git_mutex_unlock(&closed_file->lock); } cleanup: git_vector_free(&closed_files); return error; } void git_mwindow_file_deregister(git_mwindow_file *mwf) { git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; git_mwindow_file *cur; size_t i; if (git_mutex_lock(&git__mwindow_mutex)) return; git_vector_foreach(&ctl->windowfiles, i, cur) { if (cur == mwf) { git_vector_remove(&ctl->windowfiles, i); git_mutex_unlock(&git__mwindow_mutex); return; } } git_mutex_unlock(&git__mwindow_mutex); } void git_mwindow_close(git_mwindow **window) { git_mwindow *w = *window; if (w) { if (git_mutex_lock(&git__mwindow_mutex)) { git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); return; } w->inuse_cnt--; git_mutex_unlock(&git__mwindow_mutex); *window = NULL; } } git2r/src/libgit2/src/message.c0000644000175000017500000000324314125111754016140 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "message.h" static size_t line_length_without_trailing_spaces(const char *line, size_t len) { while (len) { unsigned char c = line[len - 1]; if (!git__isspace(c)) break; len--; } return len; } /* Greatly inspired from git.git "stripspace" */ /* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */ int git_message_prettify(git_buf *message_out, const char *message, int strip_comments, char comment_char) { const size_t message_len = strlen(message); int consecutive_empty_lines = 0; size_t i, line_length, rtrimmed_line_length; char *next_newline; int error; if ((error = git_buf_sanitize(message_out)) < 0) return error; for (i = 0; i < strlen(message); i += line_length) { next_newline = memchr(message + i, '\n', message_len - i); if (next_newline != NULL) { line_length = next_newline - (message + i) + 1; } else { line_length = message_len - i; } if (strip_comments && line_length && message[i] == comment_char) continue; rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length); if (!rtrimmed_line_length) { consecutive_empty_lines++; continue; } if (consecutive_empty_lines > 0 && message_out->size > 0) git_buf_putc(message_out, '\n'); consecutive_empty_lines = 0; git_buf_put(message_out, message + i, rtrimmed_line_length); git_buf_putc(message_out, '\n'); } return git_buf_oom(message_out) ? -1 : 0; } git2r/src/libgit2/src/merge_driver.h0000644000175000017500000000316314125111754017174 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_merge_driver_h__ #define INCLUDE_merge_driver_h__ #include "common.h" #include "git2/merge.h" #include "git2/index.h" #include "git2/sys/merge.h" struct git_merge_driver_source { git_repository *repo; const char *default_driver; const git_merge_file_options *file_opts; const git_index_entry *ancestor; const git_index_entry *ours; const git_index_entry *theirs; }; typedef struct git_merge_driver__builtin { git_merge_driver base; git_merge_file_favor_t favor; } git_merge_driver__builtin; extern int git_merge_driver_global_init(void); extern int git_merge_driver_for_path( char **name_out, git_merge_driver **driver_out, git_repository *repo, const char *path); /* Merge driver configuration */ extern int git_merge_driver_for_source( const char **name_out, git_merge_driver **driver_out, const git_merge_driver_source *src); extern int git_merge_driver__builtin_apply( git_merge_driver *self, const char **path_out, uint32_t *mode_out, git_buf *merged_out, const char *filter_name, const git_merge_driver_source *src); /* Merge driver for text files, performs a standard three-way merge */ extern git_merge_driver__builtin git_merge_driver__text; /* Merge driver for union-style merging */ extern git_merge_driver__builtin git_merge_driver__union; /* Merge driver for unmergeable (binary) files: always produces conflicts */ extern git_merge_driver git_merge_driver__binary; #endif git2r/src/libgit2/src/diff_driver.c0000644000175000017500000003036414125111754017003 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "diff_driver.h" #include "git2/attr.h" #include "common.h" #include "diff.h" #include "strmap.h" #include "map.h" #include "config.h" #include "regexp.h" #include "repository.h" typedef enum { DIFF_DRIVER_AUTO = 0, DIFF_DRIVER_BINARY = 1, DIFF_DRIVER_TEXT = 2, DIFF_DRIVER_PATTERNLIST = 3, } git_diff_driver_t; typedef struct { git_regexp re; int flags; } git_diff_driver_pattern; enum { REG_NEGATE = (1 << 15) /* get out of the way of existing flags */ }; /* data for finding function context for a given file type */ struct git_diff_driver { git_diff_driver_t type; uint32_t binary_flags; uint32_t other_flags; git_array_t(git_diff_driver_pattern) fn_patterns; git_regexp word_pattern; char name[GIT_FLEX_ARRAY]; }; #include "userdiff.h" struct git_diff_driver_registry { git_strmap *drivers; }; #define FORCE_DIFFABLE (GIT_DIFF_FORCE_TEXT | GIT_DIFF_FORCE_BINARY) static git_diff_driver global_drivers[3] = { { DIFF_DRIVER_AUTO, 0, 0, }, { DIFF_DRIVER_BINARY, GIT_DIFF_FORCE_BINARY, 0 }, { DIFF_DRIVER_TEXT, GIT_DIFF_FORCE_TEXT, 0 }, }; git_diff_driver_registry *git_diff_driver_registry_new(void) { git_diff_driver_registry *reg = git__calloc(1, sizeof(git_diff_driver_registry)); if (!reg) return NULL; if (git_strmap_new(®->drivers) < 0) { git_diff_driver_registry_free(reg); return NULL; } return reg; } void git_diff_driver_registry_free(git_diff_driver_registry *reg) { git_diff_driver *drv; if (!reg) return; git_strmap_foreach_value(reg->drivers, drv, git_diff_driver_free(drv)); git_strmap_free(reg->drivers); git__free(reg); } static int diff_driver_add_patterns( git_diff_driver *drv, const char *regex_str, int regex_flags) { int error = 0; const char *scan, *end; git_diff_driver_pattern *pat = NULL; git_buf buf = GIT_BUF_INIT; for (scan = regex_str; scan; scan = end) { /* get pattern to fill in */ if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) { return -1; } pat->flags = regex_flags; if (*scan == '!') { pat->flags |= REG_NEGATE; ++scan; } if ((end = strchr(scan, '\n')) != NULL) { error = git_buf_set(&buf, scan, end - scan); end++; } else { error = git_buf_sets(&buf, scan); } if (error < 0) break; if ((error = git_regexp_compile(&pat->re, buf.ptr, regex_flags)) != 0) { /* * TODO: issue a warning */ } } if (error && pat != NULL) (void)git_array_pop(drv->fn_patterns); /* release last item */ git_buf_dispose(&buf); /* We want to ignore bad patterns, so return success regardless */ return 0; } static int diff_driver_xfuncname(const git_config_entry *entry, void *payload) { return diff_driver_add_patterns(payload, entry->value, 0); } static int diff_driver_funcname(const git_config_entry *entry, void *payload) { return diff_driver_add_patterns(payload, entry->value, 0); } static git_diff_driver_registry *git_repository_driver_registry( git_repository *repo) { git_diff_driver_registry *reg = git_atomic_load(repo->diff_drivers), *newreg; if (reg) return reg; newreg = git_diff_driver_registry_new(); if (!newreg) { git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry"); return newreg; } reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, newreg); if (!reg) { reg = newreg; } else { /* if we race, free losing allocation */ git_diff_driver_registry_free(newreg); } return reg; } static int diff_driver_alloc( git_diff_driver **out, size_t *namelen_out, const char *name) { git_diff_driver *driver; size_t driverlen = sizeof(git_diff_driver), namelen = strlen(name), alloclen; GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, driverlen, namelen); GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); driver = git__calloc(1, alloclen); GIT_ERROR_CHECK_ALLOC(driver); memcpy(driver->name, name, namelen); *out = driver; if (namelen_out) *namelen_out = namelen; return 0; } static int git_diff_driver_builtin( git_diff_driver **out, git_diff_driver_registry *reg, const char *driver_name) { git_diff_driver_definition *ddef = NULL; git_diff_driver *drv = NULL; int error = 0; size_t idx; for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) { if (!strcasecmp(driver_name, builtin_defs[idx].name)) { ddef = &builtin_defs[idx]; break; } } if (!ddef) goto done; if ((error = diff_driver_alloc(&drv, NULL, ddef->name)) < 0) goto done; drv->type = DIFF_DRIVER_PATTERNLIST; if (ddef->fns && (error = diff_driver_add_patterns( drv, ddef->fns, ddef->flags)) < 0) goto done; if (ddef->words && (error = git_regexp_compile(&drv->word_pattern, ddef->words, ddef->flags)) < 0) goto done; if ((error = git_strmap_set(reg->drivers, drv->name, drv)) < 0) goto done; done: if (error && drv) git_diff_driver_free(drv); else *out = drv; return error; } static int git_diff_driver_load( git_diff_driver **out, git_repository *repo, const char *driver_name) { int error = 0; git_diff_driver_registry *reg; git_diff_driver *drv; size_t namelen; git_config *cfg = NULL; git_buf name = GIT_BUF_INIT; git_config_entry *ce = NULL; bool found_driver = false; if ((reg = git_repository_driver_registry(repo)) == NULL) return -1; if ((drv = git_strmap_get(reg->drivers, driver_name)) != NULL) { *out = drv; return 0; } if ((error = diff_driver_alloc(&drv, &namelen, driver_name)) < 0) goto done; drv->type = DIFF_DRIVER_AUTO; /* if you can't read config for repo, just use default driver */ if (git_repository_config_snapshot(&cfg, repo) < 0) { git_error_clear(); goto done; } if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) goto done; switch (git_config__get_bool_force(cfg, name.ptr, -1)) { case true: /* if diff..binary is true, just return the binary driver */ *out = &global_drivers[DIFF_DRIVER_BINARY]; goto done; case false: /* if diff..binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ drv->binary_flags = GIT_DIFF_FORCE_TEXT; found_driver = true; break; default: /* diff..binary unspecified or "auto", so just continue */ break; } /* TODO: warn if diff..command or diff..textconv are set */ git_buf_truncate(&name, namelen + strlen("diff..")); if ((error = git_buf_PUTS(&name, "xfuncname")) < 0) goto done; if ((error = git_config_get_multivar_foreach( cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) { if (error != GIT_ENOTFOUND) goto done; git_error_clear(); /* no diff..xfuncname, so just continue */ } git_buf_truncate(&name, namelen + strlen("diff..")); if ((error = git_buf_PUTS(&name, "funcname")) < 0) goto done; if ((error = git_config_get_multivar_foreach( cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) { if (error != GIT_ENOTFOUND) goto done; git_error_clear(); /* no diff..funcname, so just continue */ } /* if we found any patterns, set driver type to use correct callback */ if (git_array_size(drv->fn_patterns) > 0) { drv->type = DIFF_DRIVER_PATTERNLIST; found_driver = true; } git_buf_truncate(&name, namelen + strlen("diff..")); if ((error = git_buf_PUTS(&name, "wordregex")) < 0) goto done; if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0) goto done; if (!ce || !ce->value) /* no diff..wordregex, so just continue */; else if (!(error = git_regexp_compile(&drv->word_pattern, ce->value, 0))) found_driver = true; else { /* TODO: warn about bad regex instead of failure */ goto done; } /* TODO: look up diff..algorithm to turn on minimal / patience * diff in drv->other_flags */ /* if no driver config found at all, fall back on AUTO driver */ if (!found_driver) goto done; /* store driver in registry */ if ((error = git_strmap_set(reg->drivers, drv->name, drv)) < 0) goto done; *out = drv; done: git_config_entry_free(ce); git_buf_dispose(&name); git_config_free(cfg); if (!*out) { int error2 = git_diff_driver_builtin(out, reg, driver_name); if (!error) error = error2; } if (drv && drv != *out) git_diff_driver_free(drv); return error; } int git_diff_driver_lookup( git_diff_driver **out, git_repository *repo, git_attr_session *attrsession, const char *path) { int error = 0; const char *values[1], *attrs[] = { "diff" }; GIT_ASSERT_ARG(out); *out = NULL; if (!repo || !path || !strlen(path)) /* just use the auto value */; else if ((error = git_attr_get_many_with_session(values, repo, attrsession, 0, path, 1, attrs)) < 0) /* return error below */; else if (GIT_ATTR_IS_UNSPECIFIED(values[0])) /* just use the auto value */; else if (GIT_ATTR_IS_FALSE(values[0])) *out = &global_drivers[DIFF_DRIVER_BINARY]; else if (GIT_ATTR_IS_TRUE(values[0])) *out = &global_drivers[DIFF_DRIVER_TEXT]; /* otherwise look for driver information in config and build driver */ else if ((error = git_diff_driver_load(out, repo, values[0])) < 0) { if (error == GIT_ENOTFOUND) { error = 0; git_error_clear(); } } if (!*out) *out = &global_drivers[DIFF_DRIVER_AUTO]; return error; } void git_diff_driver_free(git_diff_driver *driver) { git_diff_driver_pattern *pat; if (!driver) return; while ((pat = git_array_pop(driver->fn_patterns)) != NULL) git_regexp_dispose(&pat->re); git_array_clear(driver->fn_patterns); git_regexp_dispose(&driver->word_pattern); git__free(driver); } void git_diff_driver_update_options( uint32_t *option_flags, git_diff_driver *driver) { if ((*option_flags & FORCE_DIFFABLE) == 0) *option_flags |= driver->binary_flags; *option_flags |= driver->other_flags; } int git_diff_driver_content_is_binary( git_diff_driver *driver, const char *content, size_t content_len) { git_buf search = GIT_BUF_INIT; GIT_UNUSED(driver); git_buf_attach_notowned(&search, content, min(content_len, GIT_FILTER_BYTES_TO_CHECK_NUL)); /* TODO: provide encoding / binary detection callbacks that can * be UTF-8 aware, etc. For now, instead of trying to be smart, * let's just use the simple NUL-byte detection that core git uses. */ /* previously was: if (git_buf_is_binary(&search)) */ if (git_buf_contains_nul(&search)) return 1; return 0; } static int diff_context_line__simple( git_diff_driver *driver, git_buf *line) { char firstch = line->ptr[0]; GIT_UNUSED(driver); return (git__isalpha(firstch) || firstch == '_' || firstch == '$'); } static int diff_context_line__pattern_match( git_diff_driver *driver, git_buf *line) { size_t i, maxi = git_array_size(driver->fn_patterns); git_regmatch pmatch[2]; for (i = 0; i < maxi; ++i) { git_diff_driver_pattern *pat = git_array_get(driver->fn_patterns, i); if (!git_regexp_search(&pat->re, line->ptr, 2, pmatch)) { if (pat->flags & REG_NEGATE) return false; /* use pmatch data to trim line data */ i = (pmatch[1].start >= 0) ? 1 : 0; git_buf_consume(line, git_buf_cstr(line) + pmatch[i].start); git_buf_truncate(line, pmatch[i].end - pmatch[i].start); git_buf_rtrim(line); return true; } } return false; } static long diff_context_find( const char *line, long line_len, char *out, long out_size, void *payload) { git_diff_find_context_payload *ctxt = payload; if (git_buf_set(&ctxt->line, line, (size_t)line_len) < 0) return -1; git_buf_rtrim(&ctxt->line); if (!ctxt->line.size) return -1; if (!ctxt->match_line || !ctxt->match_line(ctxt->driver, &ctxt->line)) return -1; if (out_size > (long)ctxt->line.size) out_size = (long)ctxt->line.size; memcpy(out, ctxt->line.ptr, (size_t)out_size); return out_size; } void git_diff_find_context_init( git_diff_find_context_fn *findfn_out, git_diff_find_context_payload *payload_out, git_diff_driver *driver) { *findfn_out = driver ? diff_context_find : NULL; memset(payload_out, 0, sizeof(*payload_out)); if (driver) { payload_out->driver = driver; payload_out->match_line = (driver->type == DIFF_DRIVER_PATTERNLIST) ? diff_context_line__pattern_match : diff_context_line__simple; git_buf_init(&payload_out->line, 0); } } void git_diff_find_context_clear(git_diff_find_context_payload *payload) { if (payload) { git_buf_dispose(&payload->line); payload->driver = NULL; } } git2r/src/libgit2/src/path.h0000644000175000017500000005256114125111754015464 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_path_h__ #define INCLUDE_path_h__ #include "common.h" #include "posix.h" #include "buffer.h" #include "vector.h" #include "git2/sys/path.h" /** * Path manipulation utils * * These are path utilities that munge paths without actually * looking at the real filesystem. */ /* * The dirname() function shall take a pointer to a character string * that contains a pathname, and return a pointer to a string that is a * pathname of the parent directory of that file. Trailing '/' characters * in the path are not counted as part of the path. * * If path does not contain a '/', then dirname() shall return a pointer to * the string ".". If path is a null pointer or points to an empty string, * dirname() shall return a pointer to the string "." . * * The `git_path_dirname` implementation is thread safe. The returned * string must be manually free'd. * * The `git_path_dirname_r` implementation writes the dirname to a `git_buf` * if the buffer pointer is not NULL. * It returns an error code < 0 if there is an allocation error, otherwise * the length of the dirname (which will be > 0). */ extern char *git_path_dirname(const char *path); extern int git_path_dirname_r(git_buf *buffer, const char *path); /* * This function returns the basename of the file, which is the last * part of its full name given by fname, with the drive letter and * leading directories stripped off. For example, the basename of * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo. * * Trailing slashes and backslashes are significant: the basename of * c:/foo/bar/ is an empty string after the rightmost slash. * * The `git_path_basename` implementation is thread safe. The returned * string must be manually free'd. * * The `git_path_basename_r` implementation writes the basename to a `git_buf`. * It returns an error code < 0 if there is an allocation error, otherwise * the length of the basename (which will be >= 0). */ extern char *git_path_basename(const char *path); extern int git_path_basename_r(git_buf *buffer, const char *path); /* Return the offset of the start of the basename. Unlike the other * basename functions, this returns 0 if the path is empty. */ extern size_t git_path_basename_offset(git_buf *buffer); /** * Find offset to root of path if path has one. * * This will return a number >= 0 which is the offset to the start of the * path, if the path is rooted (i.e. "/rooted/path" returns 0 and * "c:/windows/rooted/path" returns 2). If the path is not rooted, this * returns -1. */ extern int git_path_root(const char *path); /** * Ensure path has a trailing '/'. */ extern int git_path_to_dir(git_buf *path); /** * Ensure string has a trailing '/' if there is space for it. */ extern void git_path_string_to_dir(char *path, size_t size); /** * Taken from git.git; returns nonzero if the given path is "." or "..". */ GIT_INLINE(int) git_path_is_dot_or_dotdot(const char *name) { return (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))); } #ifdef GIT_WIN32 GIT_INLINE(int) git_path_is_dot_or_dotdotW(const wchar_t *name) { return (name[0] == L'.' && (name[1] == L'\0' || (name[1] == L'.' && name[2] == L'\0'))); } #define git_path_is_absolute(p) \ (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/')) #define git_path_is_dirsep(p) \ ((p) == '/' || (p) == '\\') /** * Convert backslashes in path to forward slashes. */ GIT_INLINE(void) git_path_mkposix(char *path) { while (*path) { if (*path == '\\') *path = '/'; path++; } } #else # define git_path_mkposix(p) /* blank */ #define git_path_is_absolute(p) \ ((p)[0] == '/') #define git_path_is_dirsep(p) \ ((p) == '/') #endif /** * Check if string is a relative path (i.e. starts with "./" or "../") */ GIT_INLINE(int) git_path_is_relative(const char *p) { return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/'))); } /** * Check if string is at end of path segment (i.e. looking at '/' or '\0') */ GIT_INLINE(int) git_path_at_end_of_segment(const char *p) { return !*p || *p == '/'; } extern int git__percent_decode(git_buf *decoded_out, const char *input); /** * Extract path from file:// URL. */ extern int git_path_fromurl(git_buf *local_path_out, const char *file_url); /** * Path filesystem utils * * These are path utilities that actually access the filesystem. */ /** * Check if a file exists and can be accessed. * @return true or false */ extern bool git_path_exists(const char *path); /** * Check if the given path points to a directory. * @return true or false */ extern bool git_path_isdir(const char *path); /** * Check if the given path points to a regular file. * @return true or false */ extern bool git_path_isfile(const char *path); /** * Check if the given path points to a symbolic link. * @return true or false */ extern bool git_path_islink(const char *path); /** * Check if the given path is a directory, and is empty. */ extern bool git_path_is_empty_dir(const char *path); /** * Stat a file and/or link and set error if needed. */ extern int git_path_lstat(const char *path, struct stat *st); /** * Check if the parent directory contains the item. * * @param dir Directory to check. * @param item Item that might be in the directory. * @return 0 if item exists in directory, <0 otherwise. */ extern bool git_path_contains(git_buf *dir, const char *item); /** * Check if the given path contains the given subdirectory. * * @param parent Directory path that might contain subdir * @param subdir Subdirectory name to look for in parent * @return true if subdirectory exists, false otherwise. */ extern bool git_path_contains_dir(git_buf *parent, const char *subdir); /** * Determine the common directory length between two paths, including * the final path separator. For example, given paths 'a/b/c/1.txt * and 'a/b/c/d/2.txt', the common directory is 'a/b/c/', and this * will return the length of the string 'a/b/c/', which is 6. * * @param one The first path * @param two The second path * @return The length of the common directory */ extern size_t git_path_common_dirlen(const char *one, const char *two); /** * Make the path relative to the given parent path. * * @param path The path to make relative * @param parent The parent path to make path relative to * @return 0 if path was made relative, GIT_ENOTFOUND * if there was not common root between the paths, * or <0. */ extern int git_path_make_relative(git_buf *path, const char *parent); /** * Check if the given path contains the given file. * * @param dir Directory path that might contain file * @param file File name to look for in parent * @return true if file exists, false otherwise. */ extern bool git_path_contains_file(git_buf *dir, const char *file); /** * Prepend base to unrooted path or just copy path over. * * This will optionally return the index into the path where the "root" * is, either the end of the base directory prefix or the path root. */ extern int git_path_join_unrooted( git_buf *path_out, const char *path, const char *base, ssize_t *root_at); /** * Removes multiple occurrences of '/' in a row, squashing them into a * single '/'. */ extern void git_path_squash_slashes(git_buf *path); /** * Clean up path, prepending base if it is not already rooted. */ extern int git_path_prettify(git_buf *path_out, const char *path, const char *base); /** * Clean up path, prepending base if it is not already rooted and * appending a slash. */ extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base); /** * Get a directory from a path. * * If path is a directory, this acts like `git_path_prettify_dir` * (cleaning up path and appending a '/'). If path is a normal file, * this prettifies it, then removed the filename a la dirname and * appends the trailing '/'. If the path does not exist, it is * treated like a regular filename. */ extern int git_path_find_dir(git_buf *dir); /** * Resolve relative references within a path. * * This eliminates "./" and "../" relative references inside a path, * as well as condensing multiple slashes into single ones. It will * not touch the path before the "ceiling" length. * * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL * prefix and not touch that part of the path. */ extern int git_path_resolve_relative(git_buf *path, size_t ceiling); /** * Apply a relative path to base path. * * Note that the base path could be a filename or a URL and this * should still work. The relative path is walked segment by segment * with three rules: series of slashes will be condensed to a single * slash, "." will be eaten with no change, and ".." will remove a * segment from the base path. */ extern int git_path_apply_relative(git_buf *target, const char *relpath); enum { GIT_PATH_DIR_IGNORE_CASE = (1u << 0), GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1), GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2), }; /** * Walk each directory entry, except '.' and '..', calling fn(state). * * @param pathbuf Buffer the function reads the initial directory * path from, and updates with each successive entry's name. * @param flags Combination of GIT_PATH_DIR flags. * @param callback Callback for each entry. Passed the `payload` and each * successive path inside the directory as a full path. This may * safely append text to the pathbuf if needed. Return non-zero to * cancel iteration (and return value will be propagated back). * @param payload Passed to callback as first argument. * @return 0 on success or error code from OS error or from callback */ extern int git_path_direach( git_buf *pathbuf, uint32_t flags, int (*callback)(void *payload, git_buf *path), void *payload); /** * Sort function to order two paths */ extern int git_path_cmp( const char *name1, size_t len1, int isdir1, const char *name2, size_t len2, int isdir2, int (*compare)(const char *, const char *, size_t)); /** * Invoke callback up path directory by directory until the ceiling is * reached (inclusive of a final call at the root_path). * * Returning anything other than 0 from the callback function * will stop the iteration and propagate the error to the caller. * * @param pathbuf Buffer the function reads the directory from and * and updates with each successive name. * @param ceiling Prefix of path at which to stop walking up. If NULL, * this will walk all the way up to the root. If not a prefix of * pathbuf, the callback will be invoked a single time on the * original input path. * @param callback Function to invoke on each path. Passed the `payload` * and the buffer containing the current path. The path should not * be modified in any way. Return non-zero to stop iteration. * @param payload Passed to fn as the first ath. */ extern int git_path_walk_up( git_buf *pathbuf, const char *ceiling, int (*callback)(void *payload, const char *path), void *payload); enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 }; /* * Determines if a path is equal to or potentially a child of another. * @param parent The possible parent * @param child The possible child */ GIT_INLINE(int) git_path_equal_or_prefixed( const char *parent, const char *child, ssize_t *prefixlen) { const char *p = parent, *c = child; int lastslash = 0; while (*p && *c) { lastslash = (*p == '/'); if (*p++ != *c++) return GIT_PATH_NOTEQUAL; } if (*p != '\0') return GIT_PATH_NOTEQUAL; if (*c == '\0') { if (prefixlen) *prefixlen = p - parent; return GIT_PATH_EQUAL; } if (*c == '/' || lastslash) { if (prefixlen) *prefixlen = (p - parent) - lastslash; return GIT_PATH_PREFIX; } return GIT_PATH_NOTEQUAL; } /* translate errno to libgit2 error code and set error message */ extern int git_path_set_error( int errno_value, const char *path, const char *action); /* check if non-ascii characters are present in filename */ extern bool git_path_has_non_ascii(const char *path, size_t pathlen); #define GIT_PATH_REPO_ENCODING "UTF-8" #ifdef __APPLE__ #define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC" #else #define GIT_PATH_NATIVE_ENCODING "UTF-8" #endif #ifdef GIT_USE_ICONV #include typedef struct { iconv_t map; git_buf buf; } git_path_iconv_t; #define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT } /* Init iconv data for converting decomposed UTF-8 to precomposed */ extern int git_path_iconv_init_precompose(git_path_iconv_t *ic); /* Clear allocated iconv data */ extern void git_path_iconv_clear(git_path_iconv_t *ic); /* * Rewrite `in` buffer using iconv map if necessary, replacing `in` * pointer internal iconv buffer if rewrite happened. The `in` pointer * will be left unchanged if no rewrite was needed. */ extern int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen); #endif /* GIT_USE_ICONV */ extern bool git_path_does_fs_decompose_unicode(const char *root); typedef struct git_path_diriter git_path_diriter; #if defined(GIT_WIN32) && !defined(__MINGW32__) struct git_path_diriter { git_win32_path path; size_t parent_len; git_buf path_utf8; size_t parent_utf8_len; HANDLE handle; unsigned int flags; WIN32_FIND_DATAW current; unsigned int needs_next; }; #define GIT_PATH_DIRITER_INIT { {0}, 0, GIT_BUF_INIT, 0, INVALID_HANDLE_VALUE } #else struct git_path_diriter { git_buf path; size_t parent_len; unsigned int flags; DIR *dir; #ifdef GIT_USE_ICONV git_path_iconv_t ic; #endif }; #define GIT_PATH_DIRITER_INIT { GIT_BUF_INIT } #endif /** * Initialize a directory iterator. * * @param diriter Pointer to a diriter structure that will be setup. * @param path The path that will be iterated over * @param flags Directory reader flags * @return 0 or an error code */ extern int git_path_diriter_init( git_path_diriter *diriter, const char *path, unsigned int flags); /** * Advance the directory iterator. Will return GIT_ITEROVER when * the iteration has completed successfully. * * @param diriter The directory iterator * @return 0, GIT_ITEROVER, or an error code */ extern int git_path_diriter_next(git_path_diriter *diriter); /** * Returns the file name of the current item in the iterator. * * @param out Pointer to store the path in * @param out_len Pointer to store the length of the path in * @param diriter The directory iterator * @return 0 or an error code */ extern int git_path_diriter_filename( const char **out, size_t *out_len, git_path_diriter *diriter); /** * Returns the full path of the current item in the iterator; that * is the current filename plus the path of the directory that the * iterator was constructed with. * * @param out Pointer to store the path in * @param out_len Pointer to store the length of the path in * @param diriter The directory iterator * @return 0 or an error code */ extern int git_path_diriter_fullpath( const char **out, size_t *out_len, git_path_diriter *diriter); /** * Performs an `lstat` on the current item in the iterator. * * @param out Pointer to store the stat data in * @param diriter The directory iterator * @return 0 or an error code */ extern int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter); /** * Closes the directory iterator. * * @param diriter The directory iterator */ extern void git_path_diriter_free(git_path_diriter *diriter); /** * Load all directory entries (except '.' and '..') into a vector. * * For cases where `git_path_direach()` is not appropriate, this * allows you to load the filenames in a directory into a vector * of strings. That vector can then be sorted, iterated, or whatever. * Remember to free alloc of the allocated strings when you are done. * * @param contents Vector to fill with directory entry names. * @param path The directory to read from. * @param prefix_len When inserting entries, the trailing part of path * will be prefixed after this length. I.e. given path "/a/b" and * prefix_len 3, the entries will look like "b/e1", "b/e2", etc. * @param flags Combination of GIT_PATH_DIR flags. */ extern int git_path_dirload( git_vector *contents, const char *path, size_t prefix_len, uint32_t flags); /* Used for paths to repositories on the filesystem */ extern bool git_path_is_local_file_url(const char *file_url); extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path); /* Flags to determine path validity in `git_path_isvalid` */ #define GIT_PATH_REJECT_TRAVERSAL (1 << 0) #define GIT_PATH_REJECT_DOT_GIT (1 << 1) #define GIT_PATH_REJECT_SLASH (1 << 2) #define GIT_PATH_REJECT_BACKSLASH (1 << 3) #define GIT_PATH_REJECT_TRAILING_DOT (1 << 4) #define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5) #define GIT_PATH_REJECT_TRAILING_COLON (1 << 6) #define GIT_PATH_REJECT_DOS_PATHS (1 << 7) #define GIT_PATH_REJECT_NT_CHARS (1 << 8) #define GIT_PATH_REJECT_DOT_GIT_LITERAL (1 << 9) #define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 10) #define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 11) /* Default path safety for writing files to disk: since we use the * Win32 "File Namespace" APIs ("\\?\") we need to protect from * paths that the normal Win32 APIs would not write. */ #ifdef GIT_WIN32 # define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ GIT_PATH_REJECT_TRAVERSAL | \ GIT_PATH_REJECT_BACKSLASH | \ GIT_PATH_REJECT_TRAILING_DOT | \ GIT_PATH_REJECT_TRAILING_SPACE | \ GIT_PATH_REJECT_TRAILING_COLON | \ GIT_PATH_REJECT_DOS_PATHS | \ GIT_PATH_REJECT_NT_CHARS #else # define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ GIT_PATH_REJECT_TRAVERSAL #endif /* Paths that should never be written into the working directory. */ #define GIT_PATH_REJECT_WORKDIR_DEFAULTS \ GIT_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT /* Paths that should never be written to the index. */ #define GIT_PATH_REJECT_INDEX_DEFAULTS \ GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT /** * Validate a "bare" git path. This ensures that the given path is legal * to place in the index or a tree. This should be checked by mechanisms * like `git_index_add` and `git_treebuilder_insert` when taking user * data, and by `git_checkout` before constructing on-disk paths. * * This will ensure that a git path does not contain any "unsafe" components, * a '.' or '..' component, or a component that is ".git" (in any case). * * (Note: if you take or construct an on-disk path -- a workdir path, * a path to a git repository or a reference name that could be a loose * ref -- you should _also_ validate that with `git_path_validate_workdir`.) * * `repo` is optional. If specified, it will be used to determine the short * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified), * in addition to the default of "git~1". */ extern bool git_path_validate( git_repository *repo, const char *path, uint16_t mode, unsigned int flags); /** * Validate an on-disk path, taking into account that it will have a * suffix appended (eg, `.lock`). */ GIT_INLINE(int) git_path_validate_filesystem_with_suffix( const char *path, size_t path_len, size_t suffix_len) { #ifdef GIT_WIN32 size_t path_chars, total_chars; path_chars = git_utf8_char_length(path, path_len); if (GIT_ADD_SIZET_OVERFLOW(&total_chars, path_chars, suffix_len) || total_chars > MAX_PATH) { git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path); return -1; } return 0; #else GIT_UNUSED(path); GIT_UNUSED(path_len); GIT_UNUSED(suffix_len); return 0; #endif } /** * Validate an path on the filesystem. This ensures that the given * path is valid for the operating system/platform; for example, this * will ensure that the given absolute path is smaller than MAX_PATH on * Windows. * * For paths within the working directory, you should use ensure that * `core.longpaths` is obeyed. Use `git_path_validate_workdir`. */ GIT_INLINE(int) git_path_validate_filesystem( const char *path, size_t path_len) { return git_path_validate_filesystem_with_suffix(path, path_len, 0); } /** * Validate a path relative to the repo's worktree. This ensures that * the given working tree path is valid for the operating system/platform. * This will ensure that an absolute path is smaller than MAX_PATH on * Windows, while keeping `core.longpaths` configuration settings in mind. * * This should be checked by mechamisms like `git_checkout` after * contructing on-disk paths and before trying to write them. * * If the repository is null, no repository configuration is applied. */ extern int git_path_validate_workdir( git_repository *repo, const char *path); extern int git_path_validate_workdir_with_len( git_repository *repo, const char *path, size_t path_len); extern int git_path_validate_workdir_buf( git_repository *repo, git_buf *buf); /** * Convert any backslashes into slashes */ int git_path_normalize_slashes(git_buf *out, const char *path); bool git_path_supports_symlinks(const char *dir); /** * Validate a system file's ownership * * Verify that the file in question is owned by an administrator or system * account, or at least by the current user. * * This function returns 0 if successful. If the file is not owned by any of * these, or any other if there have been problems determining the file * ownership, it returns -1. */ int git_path_validate_system_file_ownership(const char *path); #endif git2r/src/libgit2/src/checkout.h0000644000175000017500000000130414125111754016322 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_checkout_h__ #define INCLUDE_checkout_h__ #include "common.h" #include "git2/checkout.h" #include "iterator.h" #define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12) /** * Update the working directory to match the target iterator. The * expected baseline value can be passed in via the checkout options * or else will default to the HEAD commit. */ extern int git_checkout_iterator( git_iterator *target, git_index *index, const git_checkout_options *opts); #endif git2r/src/libgit2/src/tsort.c0000644000175000017500000002034714125111754015673 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" /** * An array-of-pointers implementation of Python's Timsort * Based on code by Christopher Swenson under the MIT license * * Copyright (c) 2010 Christopher Swenson * Copyright (c) 2011 Vicent Marti */ #ifndef MAX # define MAX(x,y) (((x) > (y) ? (x) : (y))) #endif #ifndef MIN # define MIN(x,y) (((x) < (y) ? (x) : (y))) #endif static int binsearch( void **dst, const void *x, size_t size, git__sort_r_cmp cmp, void *payload) { int l, c, r; void *lx, *cx; l = 0; r = (int)size - 1; c = r >> 1; lx = dst[l]; /* check for beginning conditions */ if (cmp(x, lx, payload) < 0) return 0; else if (cmp(x, lx, payload) == 0) { int i = 1; while (cmp(x, dst[i], payload) == 0) i++; return i; } /* guaranteed not to be >= rx */ cx = dst[c]; while (1) { const int val = cmp(x, cx, payload); if (val < 0) { if (c - l <= 1) return c; r = c; } else if (val > 0) { if (r - c <= 1) return c + 1; l = c; lx = cx; } else { do { cx = dst[++c]; } while (cmp(x, cx, payload) == 0); return c; } c = l + ((r - l) >> 1); cx = dst[c]; } } /* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */ static void bisort( void **dst, size_t start, size_t size, git__sort_r_cmp cmp, void *payload) { size_t i; void *x; int location; for (i = start; i < size; i++) { int j; /* If this entry is already correct, just move along */ if (cmp(dst[i - 1], dst[i], payload) <= 0) continue; /* Else we need to find the right place, shift everything over, and squeeze in */ x = dst[i]; location = binsearch(dst, x, i, cmp, payload); for (j = (int)i - 1; j >= location; j--) { dst[j + 1] = dst[j]; } dst[location] = x; } } /* timsort implementation, based on timsort.txt */ struct tsort_run { ssize_t start; ssize_t length; }; struct tsort_store { size_t alloc; git__sort_r_cmp cmp; void *payload; void **storage; }; static void reverse_elements(void **dst, ssize_t start, ssize_t end) { while (start < end) { void *tmp = dst[start]; dst[start] = dst[end]; dst[end] = tmp; start++; end--; } } static ssize_t count_run( void **dst, ssize_t start, ssize_t size, struct tsort_store *store) { ssize_t curr = start + 2; if (size - start == 1) return 1; if (start >= size - 2) { if (store->cmp(dst[size - 2], dst[size - 1], store->payload) > 0) { void *tmp = dst[size - 1]; dst[size - 1] = dst[size - 2]; dst[size - 2] = tmp; } return 2; } if (store->cmp(dst[start], dst[start + 1], store->payload) <= 0) { while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr], store->payload) <= 0) curr++; return curr - start; } else { while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr], store->payload) > 0) curr++; /* reverse in-place */ reverse_elements(dst, start, curr - 1); return curr - start; } } static size_t compute_minrun(size_t n) { int r = 0; while (n >= 64) { r |= n & 1; n >>= 1; } return n + r; } static int check_invariant(struct tsort_run *stack, ssize_t stack_curr) { if (stack_curr < 2) return 1; else if (stack_curr == 2) { const ssize_t A = stack[stack_curr - 2].length; const ssize_t B = stack[stack_curr - 1].length; return (A > B); } else { const ssize_t A = stack[stack_curr - 3].length; const ssize_t B = stack[stack_curr - 2].length; const ssize_t C = stack[stack_curr - 1].length; return !((A <= B + C) || (B <= C)); } } static int resize(struct tsort_store *store, size_t new_size) { if (store->alloc < new_size) { void **tempstore; tempstore = git__reallocarray(store->storage, new_size, sizeof(void *)); /** * Do not propagate on OOM; this will abort the sort and * leave the array unsorted, but no error code will be * raised */ if (tempstore == NULL) return -1; store->storage = tempstore; store->alloc = new_size; } return 0; } static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store) { const ssize_t A = stack[stack_curr - 2].length; const ssize_t B = stack[stack_curr - 1].length; const ssize_t curr = stack[stack_curr - 2].start; void **storage; ssize_t i, j, k; if (resize(store, MIN(A, B)) < 0) return; storage = store->storage; /* left merge */ if (A < B) { memcpy(storage, &dst[curr], A * sizeof(void *)); i = 0; j = curr + A; for (k = curr; k < curr + A + B; k++) { if ((i < A) && (j < curr + A + B)) { if (store->cmp(storage[i], dst[j], store->payload) <= 0) dst[k] = storage[i++]; else dst[k] = dst[j++]; } else if (i < A) { dst[k] = storage[i++]; } else dst[k] = dst[j++]; } } else { memcpy(storage, &dst[curr + A], B * sizeof(void *)); i = B - 1; j = curr + A - 1; for (k = curr + A + B - 1; k >= curr; k--) { if ((i >= 0) && (j >= curr)) { if (store->cmp(dst[j], storage[i], store->payload) > 0) dst[k] = dst[j--]; else dst[k] = storage[i--]; } else if (i >= 0) dst[k] = storage[i--]; else dst[k] = dst[j--]; } } } static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store, ssize_t size) { ssize_t A, B, C; while (1) { /* if the stack only has one thing on it, we are done with the collapse */ if (stack_curr <= 1) break; /* if this is the last merge, just do it */ if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) { merge(dst, stack, stack_curr, store); stack[0].length += stack[1].length; stack_curr--; break; } /* check if the invariant is off for a stack of 2 elements */ else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) { merge(dst, stack, stack_curr, store); stack[0].length += stack[1].length; stack_curr--; break; } else if (stack_curr == 2) break; A = stack[stack_curr - 3].length; B = stack[stack_curr - 2].length; C = stack[stack_curr - 1].length; /* check first invariant */ if (A <= B + C) { if (A < C) { merge(dst, stack, stack_curr - 1, store); stack[stack_curr - 3].length += stack[stack_curr - 2].length; stack[stack_curr - 2] = stack[stack_curr - 1]; stack_curr--; } else { merge(dst, stack, stack_curr, store); stack[stack_curr - 2].length += stack[stack_curr - 1].length; stack_curr--; } } else if (B <= C) { merge(dst, stack, stack_curr, store); stack[stack_curr - 2].length += stack[stack_curr - 1].length; stack_curr--; } else break; } return stack_curr; } #define PUSH_NEXT() do {\ len = count_run(dst, curr, size, store);\ run = minrun;\ if (run > (ssize_t)size - curr) run = size - curr;\ if (run > len) {\ bisort(&dst[curr], len, run, cmp, payload);\ len = run;\ }\ run_stack[stack_curr].start = curr;\ run_stack[stack_curr++].length = len;\ curr += len;\ if (curr == (ssize_t)size) {\ /* finish up */ \ while (stack_curr > 1) { \ merge(dst, run_stack, stack_curr, store); \ run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \ stack_curr--; \ } \ if (store->storage != NULL) {\ git__free(store->storage);\ store->storage = NULL;\ }\ return;\ }\ }\ while (0) void git__tsort_r( void **dst, size_t size, git__sort_r_cmp cmp, void *payload) { struct tsort_store _store, *store = &_store; struct tsort_run run_stack[128]; ssize_t stack_curr = 0; ssize_t len, run; ssize_t curr = 0; ssize_t minrun; if (size < 64) { bisort(dst, 1, size, cmp, payload); return; } /* compute the minimum run length */ minrun = (ssize_t)compute_minrun(size); /* temporary storage for merges */ store->alloc = 0; store->storage = NULL; store->cmp = cmp; store->payload = payload; PUSH_NEXT(); PUSH_NEXT(); PUSH_NEXT(); while (1) { if (!check_invariant(run_stack, stack_curr)) { stack_curr = collapse(dst, run_stack, stack_curr, store, size); continue; } PUSH_NEXT(); } } static int tsort_r_cmp(const void *a, const void *b, void *payload) { return ((git__tsort_cmp)payload)(a, b); } void git__tsort(void **dst, size_t size, git__tsort_cmp cmp) { git__tsort_r(dst, size, tsort_r_cmp, cmp); } git2r/src/libgit2/src/libgit2.h0000644000175000017500000000064714125111754016062 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_libgit2_h__ #define INCLUDE_libgit2_h__ extern int git_libgit2_init_count(void); extern const char *git_libgit2__user_agent(void); extern const char *git_libgit2__ssl_ciphers(void); #endif git2r/src/libgit2/deps/0000755000175000017500000000000013651043437014517 5ustar nileshnileshgit2r/src/libgit2/deps/http-parser/0000755000175000017500000000000014145550337016771 5ustar nileshnileshgit2r/src/libgit2/deps/http-parser/http_parser.h0000644000175000017500000002616114125111754021475 0ustar nileshnilesh/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef http_parser_h #define http_parser_h #ifdef __cplusplus extern "C" { #endif #define HTTP_PARSER_VERSION_MAJOR 2 #define HTTP_PARSER_VERSION_MINOR 0 #include #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) #include typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; typedef SIZE_T size_t; typedef SSIZE_T ssize_t; #elif defined(__sun) || defined(__sun__) #include #else #include #endif /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run * faster */ #ifndef HTTP_PARSER_STRICT # define HTTP_PARSER_STRICT 1 #endif /* Maximium header size allowed */ #define HTTP_MAX_HEADER_SIZE (80*1024) typedef struct http_parser http_parser; typedef struct http_parser_settings http_parser_settings; /* Callbacks should return non-zero to indicate an error. The parser will * then halt execution. * * The one exception is on_headers_complete. In a HTTP_RESPONSE parser * returning '1' from on_headers_complete will tell the parser that it * should not expect a body. This is used when receiving a response to a * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: * chunked' headers that indicate the presence of a body. * * http_data_cb does not return data chunks. It will be call arbitrarally * many times for each string. E.G. you might get 10 callbacks for "on_url" * each providing just a few characters more data. */ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); typedef int (*http_cb) (http_parser*); /* Request Methods */ #define HTTP_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ XX(1, GET, GET) \ XX(2, HEAD, HEAD) \ XX(3, POST, POST) \ XX(4, PUT, PUT) \ /* pathological */ \ XX(5, CONNECT, CONNECT) \ XX(6, OPTIONS, OPTIONS) \ XX(7, TRACE, TRACE) \ /* webdav */ \ XX(8, COPY, COPY) \ XX(9, LOCK, LOCK) \ XX(10, MKCOL, MKCOL) \ XX(11, MOVE, MOVE) \ XX(12, PROPFIND, PROPFIND) \ XX(13, PROPPATCH, PROPPATCH) \ XX(14, SEARCH, SEARCH) \ XX(15, UNLOCK, UNLOCK) \ /* subversion */ \ XX(16, REPORT, REPORT) \ XX(17, MKACTIVITY, MKACTIVITY) \ XX(18, CHECKOUT, CHECKOUT) \ XX(19, MERGE, MERGE) \ /* upnp */ \ XX(20, MSEARCH, M-SEARCH) \ XX(21, NOTIFY, NOTIFY) \ XX(22, SUBSCRIBE, SUBSCRIBE) \ XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ /* RFC-5789 */ \ XX(24, PATCH, PATCH) \ XX(25, PURGE, PURGE) \ enum http_method { #define XX(num, name, string) HTTP_##name = num, HTTP_METHOD_MAP(XX) #undef XX }; enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; /* Flag values for http_parser.flags field */ enum flags { F_CHUNKED = 1 << 0 , F_CONNECTION_KEEP_ALIVE = 1 << 1 , F_CONNECTION_CLOSE = 1 << 2 , F_TRAILING = 1 << 3 , F_UPGRADE = 1 << 4 , F_SKIPBODY = 1 << 5 }; /* Map for errno-related constants * * The provided argument should be a macro that takes 2 arguments. */ #define HTTP_ERRNO_MAP(XX) \ /* No error */ \ XX(OK, "success") \ \ /* Callback-related errors */ \ XX(CB_message_begin, "the on_message_begin callback failed") \ XX(CB_url, "the on_url callback failed") \ XX(CB_header_field, "the on_header_field callback failed") \ XX(CB_header_value, "the on_header_value callback failed") \ XX(CB_headers_complete, "the on_headers_complete callback failed") \ XX(CB_body, "the on_body callback failed") \ XX(CB_message_complete, "the on_message_complete callback failed") \ \ /* Parsing-related errors */ \ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ XX(HEADER_OVERFLOW, \ "too many header bytes seen; overflow detected") \ XX(CLOSED_CONNECTION, \ "data received after completed connection: close message") \ XX(INVALID_VERSION, "invalid HTTP version") \ XX(INVALID_STATUS, "invalid HTTP status code") \ XX(INVALID_METHOD, "invalid HTTP method") \ XX(INVALID_URL, "invalid URL") \ XX(INVALID_HOST, "invalid host") \ XX(INVALID_PORT, "invalid port") \ XX(INVALID_PATH, "invalid path") \ XX(INVALID_QUERY_STRING, "invalid query string") \ XX(INVALID_FRAGMENT, "invalid fragment") \ XX(LF_EXPECTED, "LF character expected") \ XX(INVALID_HEADER_TOKEN, "invalid character in header") \ XX(INVALID_CONTENT_LENGTH, \ "invalid character in content-length header") \ XX(INVALID_CHUNK_SIZE, \ "invalid character in chunk size header") \ XX(INVALID_CONSTANT, "invalid constant string") \ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ XX(STRICT, "strict mode assertion failed") \ XX(PAUSED, "parser is paused") \ XX(UNKNOWN, "an unknown error occurred") /* Define HPE_* values for each errno value above */ #define HTTP_ERRNO_GEN(n, s) HPE_##n, enum http_errno { HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) }; #undef HTTP_ERRNO_GEN /* Get an http_errno value from an http_parser */ #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) struct http_parser { /** PRIVATE **/ unsigned char type : 2; /* enum http_parser_type */ unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */ unsigned char state; /* enum state from http_parser.c */ unsigned char header_state; /* enum header_state from http_parser.c */ unsigned char index; /* index into current matcher */ uint32_t nread; /* # bytes read in various scenarios */ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ /** READ-ONLY **/ unsigned short http_major; unsigned short http_minor; unsigned short status_code; /* responses only */ unsigned char method; /* requests only */ unsigned char http_errno : 7; /* 1 = Upgrade header was present and the parser has exited because of that. * 0 = No upgrade header present. * Should be checked when http_parser_execute() returns in addition to * error checking. */ unsigned char upgrade : 1; /** PUBLIC **/ void *data; /* A pointer to get hook to the "connection" or "socket" object */ }; struct http_parser_settings { http_cb on_message_begin; http_data_cb on_url; http_data_cb on_header_field; http_data_cb on_header_value; http_cb on_headers_complete; http_data_cb on_body; http_cb on_message_complete; }; enum http_parser_url_fields { UF_SCHEMA = 0 , UF_HOST = 1 , UF_PORT = 2 , UF_PATH = 3 , UF_QUERY = 4 , UF_FRAGMENT = 5 , UF_USERINFO = 6 , UF_MAX = 7 }; /* Result structure for http_parser_parse_url(). * * Callers should index into field_data[] with UF_* values iff field_set * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and * because we probably have padding left over), we convert any port to * a uint16_t. */ struct http_parser_url { uint16_t field_set; /* Bitmask of (1 << UF_*) values */ uint16_t port; /* Converted UF_PORT string */ struct { uint16_t off; /* Offset into buffer in which field starts */ uint16_t len; /* Length of run in buffer */ } field_data[UF_MAX]; }; void http_parser_init(http_parser *parser, enum http_parser_type type); size_t http_parser_execute(http_parser *parser, const http_parser_settings *settings, const char *data, size_t len); /* If http_should_keep_alive() in the on_headers_complete or * on_message_complete callback returns 0, then this should be * the last message on the connection. * If you are the server, respond with the "Connection: close" header. * If you are the client, close the connection. */ int http_should_keep_alive(const http_parser *parser); /* Returns a string version of the HTTP method. */ const char *http_method_str(enum http_method m); /* Return a string name of the given error */ const char *http_errno_name(enum http_errno err); /* Return a string description of the given error */ const char *http_errno_description(enum http_errno err); /* Parse a URL; return nonzero on failure */ int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u); /* Pause or un-pause the parser; a nonzero value pauses */ void http_parser_pause(http_parser *parser, int paused); /* Checks if this is the final chunk of the body. */ int http_body_is_final(const http_parser *parser); #ifdef __cplusplus } #endif #endif git2r/src/libgit2/deps/http-parser/COPYING0000644000175000017500000000234314125111754020020 0ustar nileshnileshhttp_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev. Additional changes are licensed under the same terms as NGINX and copyright Joyent, Inc. and other Node contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. git2r/src/libgit2/deps/http-parser/http_parser.c0000644000175000017500000017004414125111754021470 0ustar nileshnilesh/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev * * Additional changes are licensed under the same terms as NGINX and * copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "http_parser.h" #include #include #include #include #include #include #ifndef ULLONG_MAX # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ #endif #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef ARRAY_SIZE # define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #endif #ifndef BIT_AT # define BIT_AT(a, i) \ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ (1 << ((unsigned int) (i) & 7)))) #endif #ifndef ELEM_AT # define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) #endif #define SET_ERRNO(e) \ do { \ parser->http_errno = (e); \ } while(0) /* Run the notify callback FOR, returning ER if it fails */ #define CALLBACK_NOTIFY_(FOR, ER) \ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ if (settings->on_##FOR) { \ if (0 != settings->on_##FOR(parser)) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ \ /* We either errored above or got paused; get out */ \ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ return (ER); \ } \ } \ } while (0) /* Run the notify callback FOR and consume the current byte */ #define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) /* Run the notify callback FOR and don't consume the current byte */ #define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) /* Run data callback FOR with LEN bytes, returning ER if it fails */ #define CALLBACK_DATA_(FOR, LEN, ER) \ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ if (FOR##_mark) { \ if (settings->on_##FOR) { \ if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ \ /* We either errored above or got paused; get out */ \ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ return (ER); \ } \ } \ FOR##_mark = NULL; \ } \ } while (0) /* Run the data callback FOR and consume the current byte */ #define CALLBACK_DATA(FOR) \ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) /* Run the data callback FOR and don't consume the current byte */ #define CALLBACK_DATA_NOADVANCE(FOR) \ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) /* Set the mark FOR; non-destructive if mark is already set */ #define MARK(FOR) \ do { \ if (!FOR##_mark) { \ FOR##_mark = p; \ } \ } while (0) #define PROXY_CONNECTION "proxy-connection" #define CONNECTION "connection" #define CONTENT_LENGTH "content-length" #define TRANSFER_ENCODING "transfer-encoding" #define UPGRADE "upgrade" #define CHUNKED "chunked" #define KEEP_ALIVE "keep-alive" #define CLOSE "close" static const char *method_strings[] = { #define XX(num, name, string) #string, HTTP_METHOD_MAP(XX) #undef XX }; /* Tokens as defined by rfc 2616. Also lowercases them. * token = 1* * separators = "(" | ")" | "<" | ">" | "@" * | "," | ";" | ":" | "\" | <"> * | "/" | "[" | "]" | "?" | "=" * | "{" | "}" | SP | HT */ static const char tokens[256] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0, 0, 0, 0, 0, 0, 0, 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0, 0, 0, 0, 0, 0, 0, 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0, 0, 0, 0, 0, 0, 0, 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 0, '!', 0, '#', '$', '%', '&', '\'', /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 0, 0, '*', '+', 0, '-', '.', 0, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ '0', '1', '2', '3', '4', '5', '6', '7', /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ '8', '9', 0, 0, 0, 0, 0, 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 'x', 'y', 'z', 0, 0, 0, '^', '_', /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 'x', 'y', 'z', 0, '|', 0, '~', 0 }; static const int8_t unhex[256] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; #if HTTP_PARSER_STRICT # define T(v) 0 #else # define T(v) v #endif static const uint8_t normal_url_char[32] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; #undef T enum state { s_dead = 1 /* important that this is > 0 */ , s_start_req_or_res , s_res_or_resp_H , s_start_res , s_res_H , s_res_HT , s_res_HTT , s_res_HTTP , s_res_first_http_major , s_res_http_major , s_res_first_http_minor , s_res_http_minor , s_res_first_status_code , s_res_status_code , s_res_status , s_res_line_almost_done , s_start_req , s_req_method , s_req_spaces_before_url , s_req_schema , s_req_schema_slash , s_req_schema_slash_slash , s_req_server_start , s_req_server , s_req_server_with_at , s_req_path , s_req_query_string_start , s_req_query_string , s_req_fragment_start , s_req_fragment , s_req_http_start , s_req_http_H , s_req_http_HT , s_req_http_HTT , s_req_http_HTTP , s_req_first_http_major , s_req_http_major , s_req_first_http_minor , s_req_http_minor , s_req_line_almost_done , s_header_field_start , s_header_field , s_header_value_start , s_header_value , s_header_value_lws , s_header_almost_done , s_chunk_size_start , s_chunk_size , s_chunk_parameters , s_chunk_size_almost_done , s_headers_almost_done , s_headers_done /* Important: 's_headers_done' must be the last 'header' state. All * states beyond this must be 'body' states. It is used for overflow * checking. See the PARSING_HEADER() macro. */ , s_chunk_data , s_chunk_data_almost_done , s_chunk_data_done , s_body_identity , s_body_identity_eof , s_message_done }; #define PARSING_HEADER(state) (state <= s_headers_done) enum header_states { h_general = 0 , h_C , h_CO , h_CON , h_matching_connection , h_matching_proxy_connection , h_matching_content_length , h_matching_transfer_encoding , h_matching_upgrade , h_connection , h_content_length , h_transfer_encoding , h_upgrade , h_matching_transfer_encoding_chunked , h_matching_connection_keep_alive , h_matching_connection_close , h_transfer_encoding_chunked , h_connection_keep_alive , h_connection_close }; enum http_host_state { s_http_host_dead = 1 , s_http_userinfo_start , s_http_userinfo , s_http_host_start , s_http_host_v6_start , s_http_host , s_http_host_v6 , s_http_host_v6_end , s_http_host_port_start , s_http_host_port }; /* Macros for character classes; depends on strict-mode */ #define CR '\r' #define LF '\n' #define LOWER(c) (unsigned char)(c | 0x20) #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') #define IS_NUM(c) ((c) >= '0' && (c) <= '9') #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ (c) == ')') #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ (c) == '$' || (c) == ',') #if HTTP_PARSER_STRICT #define TOKEN(c) (tokens[(unsigned char)c]) #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') #else #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) #define IS_URL_CHAR(c) \ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) #define IS_HOST_CHAR(c) \ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') #endif #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) #if HTTP_PARSER_STRICT # define STRICT_CHECK(cond) \ do { \ if (cond) { \ SET_ERRNO(HPE_STRICT); \ goto error; \ } \ } while (0) # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) #else # define STRICT_CHECK(cond) # define NEW_MESSAGE() start_state #endif /* Map errno values to strings for human-readable output */ #define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, static struct { const char *name; const char *description; } http_strerror_tab[] = { HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) }; #undef HTTP_STRERROR_GEN int http_message_needs_eof(const http_parser *parser); /* Our URL parser. * * This is designed to be shared by http_parser_execute() for URL validation, * hence it has a state transition + byte-for-byte interface. In addition, it * is meant to be embedded in http_parser_parse_url(), which does the dirty * work of turning state transitions URL components for its API. * * This function should only be invoked with non-space characters. It is * assumed that the caller cares about (and can detect) the transition between * URL and non-URL states by looking for these. */ static enum state parse_url_char(enum state s, const char ch) { if (ch == ' ' || ch == '\r' || ch == '\n') { return s_dead; } #if HTTP_PARSER_STRICT if (ch == '\t' || ch == '\f') { return s_dead; } #endif switch (s) { case s_req_spaces_before_url: /* Proxied requests are followed by scheme of an absolute URI (alpha). * All methods except CONNECT are followed by '/' or '*'. */ if (ch == '/' || ch == '*') { return s_req_path; } /* The schema must start with an alpha character. After that, it may * consist of digits, '+', '-' or '.', followed by a ':'. */ if (IS_ALPHA(ch)) { return s_req_schema; } break; case s_req_schema: if (IS_ALPHANUM(ch) || ch == '+' || ch == '-' || ch == '.') { return s; } if (ch == ':') { return s_req_schema_slash; } break; case s_req_schema_slash: if (ch == '/') { return s_req_schema_slash_slash; } break; case s_req_schema_slash_slash: if (ch == '/') { return s_req_server_start; } break; case s_req_server_with_at: if (ch == '@') { return s_dead; } /* FALLTHROUGH */ case s_req_server_start: case s_req_server: if (ch == '/') { return s_req_path; } if (ch == '?') { return s_req_query_string_start; } if (ch == '@') { return s_req_server_with_at; } if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { return s_req_server; } break; case s_req_path: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': return s_req_query_string_start; case '#': return s_req_fragment_start; } break; case s_req_query_string_start: case s_req_query_string: if (IS_URL_CHAR(ch)) { return s_req_query_string; } switch (ch) { case '?': /* allow extra '?' in query string */ return s_req_query_string; case '#': return s_req_fragment_start; } break; case s_req_fragment_start: if (IS_URL_CHAR(ch)) { return s_req_fragment; } switch (ch) { case '?': return s_req_fragment; case '#': return s; } break; case s_req_fragment: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': case '#': return s; } break; default: break; } /* We should never fall out of the switch above unless there's an error */ return s_dead; } size_t http_parser_execute (http_parser *parser, const http_parser_settings *settings, const char *data, size_t len) { char c, ch; int8_t unhex_val; const char *p = data; const char *header_field_mark = 0; const char *header_value_mark = 0; const char *url_mark = 0; const char *body_mark = 0; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { return 0; } if (len == 0) { switch (parser->state) { case s_body_identity_eof: /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if * we got paused. */ CALLBACK_NOTIFY_NOADVANCE(message_complete); return 0; case s_dead: case s_start_req_or_res: case s_start_res: case s_start_req: return 0; default: SET_ERRNO(HPE_INVALID_EOF_STATE); return 1; } } if (parser->state == s_header_field) header_field_mark = data; if (parser->state == s_header_value) header_value_mark = data; switch (parser->state) { case s_req_path: case s_req_schema: case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: case s_req_server: case s_req_server_with_at: case s_req_query_string_start: case s_req_query_string: case s_req_fragment_start: case s_req_fragment: url_mark = data; break; } for (p=data; p != data + len; p++) { ch = *p; if (PARSING_HEADER(parser->state)) { ++parser->nread; /* Buffer overflow attack */ if (parser->nread > HTTP_MAX_HEADER_SIZE) { SET_ERRNO(HPE_HEADER_OVERFLOW); goto error; } } reexecute_byte: switch (parser->state) { case s_dead: /* this state is used after a 'Connection: close' message * the parser will error out if it reads another message */ if (ch == CR || ch == LF) break; SET_ERRNO(HPE_CLOSED_CONNECTION); goto error; case s_start_req_or_res: { if (ch == CR || ch == LF) break; parser->flags = 0; parser->content_length = ULLONG_MAX; if (ch == 'H') { parser->state = s_res_or_resp_H; CALLBACK_NOTIFY(message_begin); } else { parser->type = HTTP_REQUEST; parser->state = s_start_req; goto reexecute_byte; } break; } case s_res_or_resp_H: if (ch == 'T') { parser->type = HTTP_RESPONSE; parser->state = s_res_HT; } else { if (ch != 'E') { SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } parser->type = HTTP_REQUEST; parser->method = HTTP_HEAD; parser->index = 2; parser->state = s_req_method; } break; case s_start_res: { parser->flags = 0; parser->content_length = ULLONG_MAX; switch (ch) { case 'H': parser->state = s_res_H; break; case CR: case LF: break; default: SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } CALLBACK_NOTIFY(message_begin); break; } case s_res_H: STRICT_CHECK(ch != 'T'); parser->state = s_res_HT; break; case s_res_HT: STRICT_CHECK(ch != 'T'); parser->state = s_res_HTT; break; case s_res_HTT: STRICT_CHECK(ch != 'P'); parser->state = s_res_HTTP; break; case s_res_HTTP: STRICT_CHECK(ch != '/'); parser->state = s_res_first_http_major; break; case s_res_first_http_major: if (ch < '0' || ch > '9') { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; parser->state = s_res_http_major; break; /* major HTTP version or dot */ case s_res_http_major: { if (ch == '.') { parser->state = s_res_first_http_minor; break; } if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major *= 10; parser->http_major += ch - '0'; if (parser->http_major > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } /* first digit of minor HTTP version */ case s_res_first_http_minor: if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; parser->state = s_res_http_minor; break; /* minor HTTP version or end of request line */ case s_res_http_minor: { if (ch == ' ') { parser->state = s_res_first_status_code; break; } if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor *= 10; parser->http_minor += ch - '0'; if (parser->http_minor > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } case s_res_first_status_code: { if (!IS_NUM(ch)) { if (ch == ' ') { break; } SET_ERRNO(HPE_INVALID_STATUS); goto error; } parser->status_code = ch - '0'; parser->state = s_res_status_code; break; } case s_res_status_code: { if (!IS_NUM(ch)) { switch (ch) { case ' ': parser->state = s_res_status; break; case CR: parser->state = s_res_line_almost_done; break; case LF: parser->state = s_header_field_start; break; default: SET_ERRNO(HPE_INVALID_STATUS); goto error; } break; } parser->status_code *= 10; parser->status_code += ch - '0'; if (parser->status_code > 999) { SET_ERRNO(HPE_INVALID_STATUS); goto error; } break; } case s_res_status: /* the human readable status. e.g. "NOT FOUND" * we are not humans so just ignore this */ if (ch == CR) { parser->state = s_res_line_almost_done; break; } if (ch == LF) { parser->state = s_header_field_start; break; } break; case s_res_line_almost_done: STRICT_CHECK(ch != LF); parser->state = s_header_field_start; break; case s_start_req: { if (ch == CR || ch == LF) break; parser->flags = 0; parser->content_length = ULLONG_MAX; if (!IS_ALPHA(ch)) { SET_ERRNO(HPE_INVALID_METHOD); goto error; } parser->method = (enum http_method) 0; parser->index = 1; switch (ch) { case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; case 'D': parser->method = HTTP_DELETE; break; case 'G': parser->method = HTTP_GET; break; case 'H': parser->method = HTTP_HEAD; break; case 'L': parser->method = HTTP_LOCK; break; case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; case 'N': parser->method = HTTP_NOTIFY; break; case 'O': parser->method = HTTP_OPTIONS; break; case 'P': parser->method = HTTP_POST; /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break; case 'R': parser->method = HTTP_REPORT; break; case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; case 'T': parser->method = HTTP_TRACE; break; case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; default: SET_ERRNO(HPE_INVALID_METHOD); goto error; } parser->state = s_req_method; CALLBACK_NOTIFY(message_begin); break; } case s_req_method: { const char *matcher; if (ch == '\0') { SET_ERRNO(HPE_INVALID_METHOD); goto error; } matcher = method_strings[parser->method]; if (ch == ' ' && matcher[parser->index] == '\0') { parser->state = s_req_spaces_before_url; } else if (ch == matcher[parser->index]) { ; /* nada */ } else if (parser->method == HTTP_CONNECT) { if (parser->index == 1 && ch == 'H') { parser->method = HTTP_CHECKOUT; } else if (parser->index == 2 && ch == 'P') { parser->method = HTTP_COPY; } else { goto error; } } else if (parser->method == HTTP_MKCOL) { if (parser->index == 1 && ch == 'O') { parser->method = HTTP_MOVE; } else if (parser->index == 1 && ch == 'E') { parser->method = HTTP_MERGE; } else if (parser->index == 1 && ch == '-') { parser->method = HTTP_MSEARCH; } else if (parser->index == 2 && ch == 'A') { parser->method = HTTP_MKACTIVITY; } else { goto error; } } else if (parser->method == HTTP_SUBSCRIBE) { if (parser->index == 1 && ch == 'E') { parser->method = HTTP_SEARCH; } else { goto error; } } else if (parser->index == 1 && parser->method == HTTP_POST) { if (ch == 'R') { parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ } else if (ch == 'U') { parser->method = HTTP_PUT; /* or HTTP_PURGE */ } else if (ch == 'A') { parser->method = HTTP_PATCH; } else { goto error; } } else if (parser->index == 2) { if (parser->method == HTTP_PUT) { if (ch == 'R') parser->method = HTTP_PURGE; } else if (parser->method == HTTP_UNLOCK) { if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE; } } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { parser->method = HTTP_PROPPATCH; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; } ++parser->index; break; } case s_req_spaces_before_url: { if (ch == ' ') break; MARK(url); if (parser->method == HTTP_CONNECT) { parser->state = s_req_server_start; } parser->state = parse_url_char((enum state)parser->state, ch); if (parser->state == s_dead) { SET_ERRNO(HPE_INVALID_URL); goto error; } break; } case s_req_schema: case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: { switch (ch) { /* No whitespace allowed here */ case ' ': case CR: case LF: SET_ERRNO(HPE_INVALID_URL); goto error; default: parser->state = parse_url_char((enum state)parser->state, ch); if (parser->state == s_dead) { SET_ERRNO(HPE_INVALID_URL); goto error; } } break; } case s_req_server: case s_req_server_with_at: case s_req_path: case s_req_query_string_start: case s_req_query_string: case s_req_fragment_start: case s_req_fragment: { switch (ch) { case ' ': parser->state = s_req_http_start; CALLBACK_DATA(url); break; case CR: case LF: parser->http_major = 0; parser->http_minor = 9; parser->state = (ch == CR) ? s_req_line_almost_done : s_header_field_start; CALLBACK_DATA(url); break; default: parser->state = parse_url_char((enum state)parser->state, ch); if (parser->state == s_dead) { SET_ERRNO(HPE_INVALID_URL); goto error; } } break; } case s_req_http_start: switch (ch) { case 'H': parser->state = s_req_http_H; break; case ' ': break; default: SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } break; case s_req_http_H: STRICT_CHECK(ch != 'T'); parser->state = s_req_http_HT; break; case s_req_http_HT: STRICT_CHECK(ch != 'T'); parser->state = s_req_http_HTT; break; case s_req_http_HTT: STRICT_CHECK(ch != 'P'); parser->state = s_req_http_HTTP; break; case s_req_http_HTTP: STRICT_CHECK(ch != '/'); parser->state = s_req_first_http_major; break; /* first digit of major HTTP version */ case s_req_first_http_major: if (ch < '1' || ch > '9') { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; parser->state = s_req_http_major; break; /* major HTTP version or dot */ case s_req_http_major: { if (ch == '.') { parser->state = s_req_first_http_minor; break; } if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major *= 10; parser->http_major += ch - '0'; if (parser->http_major > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } /* first digit of minor HTTP version */ case s_req_first_http_minor: if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; parser->state = s_req_http_minor; break; /* minor HTTP version or end of request line */ case s_req_http_minor: { if (ch == CR) { parser->state = s_req_line_almost_done; break; } if (ch == LF) { parser->state = s_header_field_start; break; } /* XXX allow spaces after digit? */ if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor *= 10; parser->http_minor += ch - '0'; if (parser->http_minor > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } /* end of request line */ case s_req_line_almost_done: { if (ch != LF) { SET_ERRNO(HPE_LF_EXPECTED); goto error; } parser->state = s_header_field_start; break; } case s_header_field_start: { if (ch == CR) { parser->state = s_headers_almost_done; break; } if (ch == LF) { /* they might be just sending \n instead of \r\n so this would be * the second \n to denote the end of headers*/ parser->state = s_headers_almost_done; goto reexecute_byte; } c = TOKEN(ch); if (!c) { SET_ERRNO(HPE_INVALID_HEADER_TOKEN); goto error; } MARK(header_field); parser->index = 0; parser->state = s_header_field; switch (c) { case 'c': parser->header_state = h_C; break; case 'p': parser->header_state = h_matching_proxy_connection; break; case 't': parser->header_state = h_matching_transfer_encoding; break; case 'u': parser->header_state = h_matching_upgrade; break; default: parser->header_state = h_general; break; } break; } case s_header_field: { c = TOKEN(ch); if (c) { switch (parser->header_state) { case h_general: break; case h_C: parser->index++; parser->header_state = (c == 'o' ? h_CO : h_general); break; case h_CO: parser->index++; parser->header_state = (c == 'n' ? h_CON : h_general); break; case h_CON: parser->index++; switch (c) { case 'n': parser->header_state = h_matching_connection; break; case 't': parser->header_state = h_matching_content_length; break; default: parser->header_state = h_general; break; } break; /* connection */ case h_matching_connection: parser->index++; if (parser->index > sizeof(CONNECTION)-1 || c != CONNECTION[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(CONNECTION)-2) { parser->header_state = h_connection; } break; /* proxy-connection */ case h_matching_proxy_connection: parser->index++; if (parser->index > sizeof(PROXY_CONNECTION)-1 || c != PROXY_CONNECTION[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { parser->header_state = h_connection; } break; /* content-length */ case h_matching_content_length: parser->index++; if (parser->index > sizeof(CONTENT_LENGTH)-1 || c != CONTENT_LENGTH[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { parser->header_state = h_content_length; } break; /* transfer-encoding */ case h_matching_transfer_encoding: parser->index++; if (parser->index > sizeof(TRANSFER_ENCODING)-1 || c != TRANSFER_ENCODING[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { parser->header_state = h_transfer_encoding; } break; /* upgrade */ case h_matching_upgrade: parser->index++; if (parser->index > sizeof(UPGRADE)-1 || c != UPGRADE[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(UPGRADE)-2) { parser->header_state = h_upgrade; } break; case h_connection: case h_content_length: case h_transfer_encoding: case h_upgrade: if (ch != ' ') parser->header_state = h_general; break; default: assert(0 && "Unknown header_state"); break; } break; } if (ch == ':') { parser->state = s_header_value_start; CALLBACK_DATA(header_field); break; } if (ch == CR) { parser->state = s_header_almost_done; CALLBACK_DATA(header_field); break; } if (ch == LF) { parser->state = s_header_field_start; CALLBACK_DATA(header_field); break; } SET_ERRNO(HPE_INVALID_HEADER_TOKEN); goto error; } case s_header_value_start: { if (ch == ' ' || ch == '\t') break; MARK(header_value); parser->state = s_header_value; parser->index = 0; if (ch == CR) { parser->header_state = h_general; parser->state = s_header_almost_done; CALLBACK_DATA(header_value); break; } if (ch == LF) { parser->state = s_header_field_start; CALLBACK_DATA(header_value); break; } c = LOWER(ch); switch (parser->header_state) { case h_upgrade: parser->flags |= F_UPGRADE; parser->header_state = h_general; break; case h_transfer_encoding: /* looking for 'Transfer-Encoding: chunked' */ if ('c' == c) { parser->header_state = h_matching_transfer_encoding_chunked; } else { parser->header_state = h_general; } break; case h_content_length: if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } parser->content_length = ch - '0'; break; case h_connection: /* looking for 'Connection: keep-alive' */ if (c == 'k') { parser->header_state = h_matching_connection_keep_alive; /* looking for 'Connection: close' */ } else if (c == 'c') { parser->header_state = h_matching_connection_close; } else { parser->header_state = h_general; } break; default: parser->header_state = h_general; break; } break; } case s_header_value: { if (ch == CR) { parser->state = s_header_almost_done; CALLBACK_DATA(header_value); break; } if (ch == LF) { parser->state = s_header_almost_done; CALLBACK_DATA_NOADVANCE(header_value); goto reexecute_byte; } c = LOWER(ch); switch (parser->header_state) { case h_general: break; case h_connection: case h_transfer_encoding: assert(0 && "Shouldn't get here."); break; case h_content_length: { uint64_t t; if (ch == ' ') break; if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } t = parser->content_length; t *= 10; t += ch - '0'; /* Overflow? */ if (t < parser->content_length || t == ULLONG_MAX) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } parser->content_length = t; break; } /* Transfer-Encoding: chunked */ case h_matching_transfer_encoding_chunked: parser->index++; if (parser->index > sizeof(CHUNKED)-1 || c != CHUNKED[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(CHUNKED)-2) { parser->header_state = h_transfer_encoding_chunked; } break; /* looking for 'Connection: keep-alive' */ case h_matching_connection_keep_alive: parser->index++; if (parser->index > sizeof(KEEP_ALIVE)-1 || c != KEEP_ALIVE[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(KEEP_ALIVE)-2) { parser->header_state = h_connection_keep_alive; } break; /* looking for 'Connection: close' */ case h_matching_connection_close: parser->index++; if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(CLOSE)-2) { parser->header_state = h_connection_close; } break; case h_transfer_encoding_chunked: case h_connection_keep_alive: case h_connection_close: if (ch != ' ') parser->header_state = h_general; break; default: parser->state = s_header_value; parser->header_state = h_general; break; } break; } case s_header_almost_done: { STRICT_CHECK(ch != LF); parser->state = s_header_value_lws; switch (parser->header_state) { case h_connection_keep_alive: parser->flags |= F_CONNECTION_KEEP_ALIVE; break; case h_connection_close: parser->flags |= F_CONNECTION_CLOSE; break; case h_transfer_encoding_chunked: parser->flags |= F_CHUNKED; break; default: break; } break; } case s_header_value_lws: { if (ch == ' ' || ch == '\t') parser->state = s_header_value_start; else { parser->state = s_header_field_start; goto reexecute_byte; } break; } case s_headers_almost_done: { STRICT_CHECK(ch != LF); if (parser->flags & F_TRAILING) { /* End of a chunked request */ parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); break; } parser->state = s_headers_done; /* Set this here so that on_headers_complete() callbacks can see it */ parser->upgrade = (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT); /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we * will interpret that as saying that this message has no body. This * is needed for the annoying case of recieving a response to a HEAD * request. * * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so * we have to simulate it by handling a change in errno below. */ if (settings->on_headers_complete) { switch (settings->on_headers_complete(parser)) { case 0: break; case 1: parser->flags |= F_SKIPBODY; break; default: SET_ERRNO(HPE_CB_headers_complete); return p - data; /* Error */ } } if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { return p - data; } goto reexecute_byte; } case s_headers_done: { STRICT_CHECK(ch != LF); parser->nread = 0; /* Exit, the rest of the connect is in a different protocol. */ if (parser->upgrade) { parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); return (p - data) + 1; } if (parser->flags & F_SKIPBODY) { parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); } else if (parser->flags & F_CHUNKED) { /* chunked encoding - ignore Content-Length header */ parser->state = s_chunk_size_start; } else { if (parser->content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); } else if (parser->content_length != ULLONG_MAX) { /* Content-Length header given and non-zero */ parser->state = s_body_identity; } else { if (parser->type == HTTP_REQUEST || !http_message_needs_eof(parser)) { /* Assume content-length 0 - read the next */ parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); } else { /* Read body until EOF */ parser->state = s_body_identity_eof; } } } break; } case s_body_identity: { uint64_t to_read = MIN(parser->content_length, (uint64_t) ((data + len) - p)); assert(parser->content_length != 0 && parser->content_length != ULLONG_MAX); /* The difference between advancing content_length and p is because * the latter will automaticaly advance on the next loop iteration. * Further, if content_length ends up at 0, we want to see the last * byte again for our message complete callback. */ MARK(body); parser->content_length -= to_read; p += to_read - 1; if (parser->content_length == 0) { parser->state = s_message_done; /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. * * The alternative to doing this is to wait for the next byte to * trigger the data callback, just as in every other case. The * problem with this is that this makes it difficult for the test * harness to distinguish between complete-on-EOF and * complete-on-length. It's not clear that this distinction is * important for applications, but let's keep it for now. */ CALLBACK_DATA_(body, p - body_mark + 1, p - data); goto reexecute_byte; } break; } /* read until EOF */ case s_body_identity_eof: MARK(body); p = data + len - 1; break; case s_message_done: parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); break; case s_chunk_size_start: { assert(parser->nread == 1); assert(parser->flags & F_CHUNKED); unhex_val = unhex[(unsigned char)ch]; if (unhex_val == -1) { SET_ERRNO(HPE_INVALID_CHUNK_SIZE); goto error; } parser->content_length = unhex_val; parser->state = s_chunk_size; break; } case s_chunk_size: { uint64_t t; assert(parser->flags & F_CHUNKED); if (ch == CR) { parser->state = s_chunk_size_almost_done; break; } unhex_val = unhex[(unsigned char)ch]; if (unhex_val == -1) { if (ch == ';' || ch == ' ') { parser->state = s_chunk_parameters; break; } SET_ERRNO(HPE_INVALID_CHUNK_SIZE); goto error; } t = parser->content_length; t *= 16; t += unhex_val; /* Overflow? */ if (t < parser->content_length || t == ULLONG_MAX) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } parser->content_length = t; break; } case s_chunk_parameters: { assert(parser->flags & F_CHUNKED); /* just ignore this. TODO check for overflow */ if (ch == CR) { parser->state = s_chunk_size_almost_done; break; } break; } case s_chunk_size_almost_done: { assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; if (parser->content_length == 0) { parser->flags |= F_TRAILING; parser->state = s_header_field_start; } else { parser->state = s_chunk_data; } break; } case s_chunk_data: { uint64_t to_read = MIN(parser->content_length, (uint64_t) ((data + len) - p)); assert(parser->flags & F_CHUNKED); assert(parser->content_length != 0 && parser->content_length != ULLONG_MAX); /* See the explanation in s_body_identity for why the content * length and data pointers are managed this way. */ MARK(body); parser->content_length -= to_read; p += to_read - 1; if (parser->content_length == 0) { parser->state = s_chunk_data_almost_done; } break; } case s_chunk_data_almost_done: assert(parser->flags & F_CHUNKED); assert(parser->content_length == 0); STRICT_CHECK(ch != CR); parser->state = s_chunk_data_done; CALLBACK_DATA(body); break; case s_chunk_data_done: assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; parser->state = s_chunk_size_start; break; default: assert(0 && "unhandled state"); SET_ERRNO(HPE_INVALID_INTERNAL_STATE); goto error; } } /* Run callbacks for any marks that we have leftover after we ran our of * bytes. There should be at most one of these set, so it's OK to invoke * them in series (unset marks will not result in callbacks). * * We use the NOADVANCE() variety of callbacks here because 'p' has already * overflowed 'data' and this allows us to correct for the off-by-one that * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' * value that's in-bounds). */ assert(((header_field_mark ? 1 : 0) + (header_value_mark ? 1 : 0) + (url_mark ? 1 : 0) + (body_mark ? 1 : 0)) <= 1); CALLBACK_DATA_NOADVANCE(header_field); CALLBACK_DATA_NOADVANCE(header_value); CALLBACK_DATA_NOADVANCE(url); CALLBACK_DATA_NOADVANCE(body); return len; error: if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { SET_ERRNO(HPE_UNKNOWN); } return (p - data); } /* Does the parser need to see an EOF to find the end of the message? */ int http_message_needs_eof (const http_parser *parser) { if (parser->type == HTTP_REQUEST) { return 0; } /* See RFC 2616 section 4.4 */ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ parser->status_code == 204 || /* No Content */ parser->status_code == 304 || /* Not Modified */ parser->flags & F_SKIPBODY) { /* response to a HEAD request */ return 0; } if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { return 0; } return 1; } int http_should_keep_alive (const http_parser *parser) { if (parser->http_major > 0 && parser->http_minor > 0) { /* HTTP/1.1 */ if (parser->flags & F_CONNECTION_CLOSE) { return 0; } } else { /* HTTP/1.0 or earlier */ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { return 0; } } return !http_message_needs_eof(parser); } const char * http_method_str (enum http_method m) { return ELEM_AT(method_strings, m, ""); } void http_parser_init (http_parser *parser, enum http_parser_type t) { void *data = parser->data; /* preserve application data */ memset(parser, 0, sizeof(*parser)); parser->data = data; parser->type = t; parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); parser->http_errno = HPE_OK; } const char * http_errno_name(enum http_errno err) { assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); return http_strerror_tab[err].name; } const char * http_errno_description(enum http_errno err) { assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); return http_strerror_tab[err].description; } static enum http_host_state http_parse_host_char(enum http_host_state s, const char ch) { switch(s) { case s_http_userinfo: case s_http_userinfo_start: if (ch == '@') { return s_http_host_start; } if (IS_USERINFO_CHAR(ch)) { return s_http_userinfo; } break; case s_http_host_start: if (ch == '[') { return s_http_host_v6_start; } if (IS_HOST_CHAR(ch)) { return s_http_host; } break; case s_http_host: if (IS_HOST_CHAR(ch)) { return s_http_host; } /* FALLTHROUGH */ case s_http_host_v6_end: if (ch == ':') { return s_http_host_port_start; } break; case s_http_host_v6: if (ch == ']') { return s_http_host_v6_end; } /* FALLTHROUGH */ case s_http_host_v6_start: if (IS_HEX(ch) || ch == ':') { return s_http_host_v6; } break; case s_http_host_port: case s_http_host_port_start: if (IS_NUM(ch)) { return s_http_host_port; } break; default: break; } return s_http_host_dead; } static int http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { enum http_host_state s; const char *p; size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; if (buflen > UINT16_MAX) return 1; u->field_data[UF_HOST].len = 0; s = found_at ? s_http_userinfo_start : s_http_host_start; for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { enum http_host_state new_s = http_parse_host_char(s, *p); if (new_s == s_http_host_dead) { return 1; } switch(new_s) { case s_http_host: if (s != s_http_host) { u->field_data[UF_HOST].off = (uint16_t)(p - buf); } u->field_data[UF_HOST].len++; break; case s_http_host_v6: if (s != s_http_host_v6) { u->field_data[UF_HOST].off = (uint16_t)(p - buf); } u->field_data[UF_HOST].len++; break; case s_http_host_port: if (s != s_http_host_port) { u->field_data[UF_PORT].off = (uint16_t)(p - buf); u->field_data[UF_PORT].len = 0; u->field_set |= (1 << UF_PORT); } u->field_data[UF_PORT].len++; break; case s_http_userinfo: if (s != s_http_userinfo) { u->field_data[UF_USERINFO].off = (uint16_t)(p - buf); u->field_data[UF_USERINFO].len = 0; u->field_set |= (1 << UF_USERINFO); } u->field_data[UF_USERINFO].len++; break; default: break; } s = new_s; } /* Make sure we don't end somewhere unexpected */ switch (s) { case s_http_host_start: case s_http_host_v6_start: case s_http_host_v6: case s_http_userinfo: case s_http_userinfo_start: return 1; default: break; } return 0; } int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u) { enum state s; const char *p; enum http_parser_url_fields uf, old_uf; int found_at = 0; if (buflen > UINT16_MAX) return 1; u->port = u->field_set = 0; s = is_connect ? s_req_server_start : s_req_spaces_before_url; uf = old_uf = UF_MAX; for (p = buf; p < buf + buflen; p++) { s = parse_url_char(s, *p); /* Figure out the next field that we're operating on */ switch (s) { case s_dead: return 1; /* Skip delimeters */ case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: case s_req_query_string_start: case s_req_fragment_start: continue; case s_req_schema: uf = UF_SCHEMA; break; case s_req_server_with_at: found_at = 1; /* FALLTROUGH */ case s_req_server: uf = UF_HOST; break; case s_req_path: uf = UF_PATH; break; case s_req_query_string: uf = UF_QUERY; break; case s_req_fragment: uf = UF_FRAGMENT; break; default: assert(!"Unexpected state"); return 1; } /* Nothing's changed; soldier on */ if (uf == old_uf) { u->field_data[uf].len++; continue; } u->field_data[uf].off = (uint16_t)(p - buf); u->field_data[uf].len = 1; u->field_set |= (1 << uf); old_uf = uf; } /* host must be present if there is a schema */ /* parsing http:///toto will fail */ if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { if (http_parse_host(buf, u, found_at) != 0) { return 1; } } /* CONNECT requests can only contain "hostname:port" */ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { return 1; } if (u->field_set & (1 << UF_PORT)) { /* Don't bother with endp; we've already validated the string */ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); /* Ports have a max value of 2^16 */ if (v > 0xffff) { return 1; } u->port = (uint16_t) v; } return 0; } void http_parser_pause(http_parser *parser, int paused) { /* Users should only be pausing/unpausing a parser that is not in an error * state. In non-debug builds, there's not much that we can do about this * other than ignore it. */ if (HTTP_PARSER_ERRNO(parser) == HPE_OK || HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); } else { assert(0 && "Attempting to pause parser in error state"); } } int http_body_is_final(const struct http_parser *parser) { return parser->state == s_message_done; } git2r/src/libgit2/deps/regex/0000755000175000017500000000000013765070734015637 5ustar nileshnileshgit2r/src/libgit2/deps/regex/regcomp.c0000644000175000017500000033214613765070734017450 0ustar nileshnilesh/* Extended regular expression matching and search library. Copyright (C) 2002-2007,2009,2010 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Isamu Hasegawa . The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** * Changed all 'abort' to 'Rf_error' to pass 'R CMD check git2r' * 2014-08-21: Stefan Widgren * * Replaced cast from int to void* via 'long' with 'intptr_t' in * 'parse_dup_op' and 'mark_opt_subexp'. * 2014-08-23: Stefan Widgren */ void Rf_error(const char*, ...); static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, size_t length, reg_syntax_t syntax); static void re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, char *fastmap); static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len); #ifdef RE_ENABLE_I18N static void free_charset (re_charset_t *cset); #endif /* RE_ENABLE_I18N */ static void free_workarea_compile (regex_t *preg); static reg_errcode_t create_initial_state (re_dfa_t *dfa); #ifdef RE_ENABLE_I18N static void optimize_utf8 (re_dfa_t *dfa); #endif static reg_errcode_t analyze (regex_t *preg); static reg_errcode_t preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra); static reg_errcode_t postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra); static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node); static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node); static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node); static reg_errcode_t calc_first (void *extra, bin_tree_t *node); static reg_errcode_t calc_next (void *extra, bin_tree_t *node); static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node); static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint); static int search_duplicated_node (const re_dfa_t *dfa, int org_node, unsigned int constraint); static reg_errcode_t calc_eclosure (re_dfa_t *dfa); static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root); static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); static int fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax); static int peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax) internal_function; static bin_tree_t *parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax, reg_errcode_t *err); static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err); static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err); static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err); static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err); static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err); static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err); static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp, re_token_t *token, int token_len, re_dfa_t *dfa, reg_syntax_t syntax, int accept_hyphen); static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp, re_token_t *token); #ifdef RE_ENABLE_I18N static reg_errcode_t build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, int *equiv_class_alloc, const unsigned char *name); static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, re_charset_t *mbcset, int *char_class_alloc, const char *class_name, reg_syntax_t syntax); #else /* not RE_ENABLE_I18N */ static reg_errcode_t build_equiv_class (bitset_t sbcset, const unsigned char *name); static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, const char *class_name, reg_syntax_t syntax); #endif /* not RE_ENABLE_I18N */ static bin_tree_t *build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, const char *class_name, const char *extra, int non_match, reg_errcode_t *err); static bin_tree_t *create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, re_token_type_t type); static bin_tree_t *create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, const re_token_t *token); static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); static void free_token (re_token_t *node); static reg_errcode_t free_tree (void *extra, bin_tree_t *node); static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node); /* This table gives an error message for each of the error codes listed in regex.h. Obviously the order here has to be same as there. POSIX doesn't require that we do anything for REG_NOERROR, but why not be nice? */ const char __re_error_msgid[] attribute_hidden = { #define REG_NOERROR_IDX 0 gettext_noop ("Success") /* REG_NOERROR */ "\0" #define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") gettext_noop ("No match") /* REG_NOMATCH */ "\0" #define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") gettext_noop ("Invalid regular expression") /* REG_BADPAT */ "\0" #define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ "\0" #define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") gettext_noop ("Invalid character class name") /* REG_ECTYPE */ "\0" #define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") gettext_noop ("Trailing backslash") /* REG_EESCAPE */ "\0" #define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") gettext_noop ("Invalid back reference") /* REG_ESUBREG */ "\0" #define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ "\0" #define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ "\0" #define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") gettext_noop ("Unmatched \\{") /* REG_EBRACE */ "\0" #define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ "\0" #define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") gettext_noop ("Invalid range end") /* REG_ERANGE */ "\0" #define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") gettext_noop ("Memory exhausted") /* REG_ESPACE */ "\0" #define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ "\0" #define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") gettext_noop ("Premature end of regular expression") /* REG_EEND */ "\0" #define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") gettext_noop ("Regular expression too big") /* REG_ESIZE */ "\0" #define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */ }; const size_t __re_error_msgid_idx[] attribute_hidden = { REG_NOERROR_IDX, REG_NOMATCH_IDX, REG_BADPAT_IDX, REG_ECOLLATE_IDX, REG_ECTYPE_IDX, REG_EESCAPE_IDX, REG_ESUBREG_IDX, REG_EBRACK_IDX, REG_EPAREN_IDX, REG_EBRACE_IDX, REG_BADBR_IDX, REG_ERANGE_IDX, REG_ESPACE_IDX, REG_BADRPT_IDX, REG_EEND_IDX, REG_ESIZE_IDX, REG_ERPAREN_IDX }; /* Entry points for GNU code. */ #ifdef ZOS_USS /* For ZOS USS we must define btowc */ wchar_t btowc (int c) { wchar_t wtmp[2]; char tmp[2]; tmp[0] = c; tmp[1] = 0; mbtowc (wtmp, tmp, 1); return wtmp[0]; } #endif /* re_compile_pattern is the GNU regular expression compiler: it compiles PATTERN (of length LENGTH) and puts the result in BUFP. Returns 0 if the pattern was valid, otherwise an error string. Assumes the `allocated' (and perhaps `buffer') and `translate' fields are set in BUFP on entry. */ const char * re_compile_pattern (const char *pattern, size_t length, struct re_pattern_buffer *bufp) { reg_errcode_t ret; /* And GNU code determines whether or not to get register information by passing null for the REGS argument to re_match, etc., not by setting no_sub, unless RE_NO_SUB is set. */ bufp->no_sub = !!(re_syntax_options & RE_NO_SUB); /* Match anchors at newline. */ bufp->newline_anchor = 1; ret = re_compile_internal (bufp, pattern, length, re_syntax_options); if (!ret) return NULL; return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); } #ifdef _LIBC weak_alias (__re_compile_pattern, re_compile_pattern) #endif /* Set by `re_set_syntax' to the current regexp syntax to recognize. Can also be assigned to arbitrarily: each pattern buffer stores its own syntax, so it can be changed between regex compilations. */ /* This has no initializer because initialized variables in Emacs become read-only after dumping. */ reg_syntax_t re_syntax_options; /* Specify the precise syntax of regexps for compilation. This provides for compatibility for various utilities which historically have different, incompatible syntaxes. The argument SYNTAX is a bit mask comprised of the various bits defined in regex.h. We return the old syntax. */ reg_syntax_t re_set_syntax (reg_syntax_t syntax) { reg_syntax_t ret = re_syntax_options; re_syntax_options = syntax; return ret; } #ifdef _LIBC weak_alias (__re_set_syntax, re_set_syntax) #endif int re_compile_fastmap (struct re_pattern_buffer *bufp) { re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; char *fastmap = bufp->fastmap; memset (fastmap, '\0', sizeof (char) * SBC_MAX); re_compile_fastmap_iter (bufp, dfa->init_state, fastmap); if (dfa->init_state != dfa->init_state_word) re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap); if (dfa->init_state != dfa->init_state_nl) re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap); if (dfa->init_state != dfa->init_state_begbuf) re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap); bufp->fastmap_accurate = 1; return 0; } #ifdef _LIBC weak_alias (__re_compile_fastmap, re_compile_fastmap) #endif static inline void __attribute ((always_inline)) re_set_fastmap (char *fastmap, int icase, int ch) { fastmap[ch] = 1; if (icase) fastmap[tolower (ch)] = 1; } /* Helper function for re_compile_fastmap. Compile fastmap for the initial_state INIT_STATE. */ static void re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, char *fastmap) { volatile re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; int node_cnt; int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE)); for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) { int node = init_state->nodes.elems[node_cnt]; re_token_type_t type = dfa->nodes[node].type; if (type == CHARACTER) { re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c); #ifdef RE_ENABLE_I18N if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) { unsigned char *buf = re_malloc (unsigned char, dfa->mb_cur_max), *p; wchar_t wc; mbstate_t state; p = buf; *p++ = dfa->nodes[node].opr.c; while (++node < dfa->nodes_len && dfa->nodes[node].type == CHARACTER && dfa->nodes[node].mb_partial) *p++ = dfa->nodes[node].opr.c; memset (&state, '\0', sizeof (state)); if (__mbrtowc (&wc, (const char *) buf, p - buf, &state) == p - buf && (__wcrtomb ((char *) buf, towlower (wc), &state) != (size_t) -1)) re_set_fastmap (fastmap, 0, buf[0]); re_free (buf); } #endif } else if (type == SIMPLE_BRACKET) { int i, ch; for (i = 0, ch = 0; i < BITSET_WORDS; ++i) { int j; bitset_word_t w = dfa->nodes[node].opr.sbcset[i]; for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) if (w & ((bitset_word_t) 1 << j)) re_set_fastmap (fastmap, icase, ch); } } #ifdef RE_ENABLE_I18N else if (type == COMPLEX_BRACKET) { re_charset_t *cset = dfa->nodes[node].opr.mbcset; int i; # ifdef _LIBC /* See if we have to try all bytes which start multiple collation elements. e.g. In da_DK, we want to catch 'a' since "aa" is a valid collation element, and don't catch 'b' since 'b' is the only collation element which starts from 'b' (and it is caught by SIMPLE_BRACKET). */ if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0 && (cset->ncoll_syms || cset->nranges)) { const int32_t *table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); for (i = 0; i < SBC_MAX; ++i) if (table[i] < 0) re_set_fastmap (fastmap, icase, i); } # endif /* _LIBC */ /* See if we have to start the match at all multibyte characters, i.e. where we would not find an invalid sequence. This only applies to multibyte character sets; for single byte character sets, the SIMPLE_BRACKET again suffices. */ if (dfa->mb_cur_max > 1 && (cset->nchar_classes || cset->non_match || cset->nranges # ifdef _LIBC || cset->nequiv_classes # endif /* _LIBC */ )) { unsigned char c = 0; do { mbstate_t mbs; memset (&mbs, 0, sizeof (mbs)); if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2) re_set_fastmap (fastmap, false, (int) c); } while (++c != 0); } else { /* ... Else catch all bytes which can start the mbchars. */ for (i = 0; i < cset->nmbchars; ++i) { char buf[256]; mbstate_t state; memset (&state, '\0', sizeof (state)); if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1) re_set_fastmap (fastmap, icase, *(unsigned char *) buf); if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) { if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state) != (size_t) -1) re_set_fastmap (fastmap, false, *(unsigned char *) buf); } } } } #endif /* RE_ENABLE_I18N */ else if (type == OP_PERIOD #ifdef RE_ENABLE_I18N || type == OP_UTF8_PERIOD #endif /* RE_ENABLE_I18N */ || type == END_OF_RE) { memset (fastmap, '\1', sizeof (char) * SBC_MAX); if (type == END_OF_RE) bufp->can_be_null = 1; return; } } } /* Entry point for POSIX code. */ /* regcomp takes a regular expression as a string and compiles it. PREG is a regex_t *. We do not expect any fields to be initialized, since POSIX says we shouldn't. Thus, we set `buffer' to the compiled pattern; `used' to the length of the compiled pattern; `syntax' to RE_SYNTAX_POSIX_EXTENDED if the REG_EXTENDED bit in CFLAGS is set; otherwise, to RE_SYNTAX_POSIX_BASIC; `newline_anchor' to REG_NEWLINE being set in CFLAGS; `fastmap' to an allocated space for the fastmap; `fastmap_accurate' to zero; `re_nsub' to the number of subexpressions in PATTERN. PATTERN is the address of the pattern string. CFLAGS is a series of bits which affect compilation. If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we use POSIX basic syntax. If REG_NEWLINE is set, then . and [^...] don't match newline. Also, regexec will try a match beginning after every newline. If REG_ICASE is set, then we considers upper- and lowercase versions of letters to be equivalent when matching. If REG_NOSUB is set, then when PREG is passed to regexec, that routine will report only success or failure, and nothing about the registers. It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for the return codes and their meanings.) */ int regcomp (regex_t *__restrict preg, const char *__restrict pattern, int cflags) { reg_errcode_t ret; reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC); preg->buffer = NULL; preg->allocated = 0; preg->used = 0; /* Try to allocate space for the fastmap. */ preg->fastmap = re_malloc (char, SBC_MAX); if (BE (preg->fastmap == NULL, 0)) return REG_ESPACE; syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0; /* If REG_NEWLINE is set, newlines are treated differently. */ if (cflags & REG_NEWLINE) { /* REG_NEWLINE implies neither . nor [^...] match newline. */ syntax &= ~RE_DOT_NEWLINE; syntax |= RE_HAT_LISTS_NOT_NEWLINE; /* It also changes the matching behavior. */ preg->newline_anchor = 1; } else preg->newline_anchor = 0; preg->no_sub = !!(cflags & REG_NOSUB); preg->translate = NULL; ret = re_compile_internal (preg, pattern, strlen (pattern), syntax); /* POSIX doesn't distinguish between an unmatched open-group and an unmatched close-group: both are REG_EPAREN. */ if (ret == REG_ERPAREN) ret = REG_EPAREN; /* We have already checked preg->fastmap != NULL. */ if (BE (ret == REG_NOERROR, 1)) /* Compute the fastmap now, since regexec cannot modify the pattern buffer. This function never fails in this implementation. */ (void) re_compile_fastmap (preg); else { /* Some error occurred while compiling the expression. */ re_free (preg->fastmap); preg->fastmap = NULL; } return (int) ret; } #ifdef _LIBC weak_alias (__regcomp, regcomp) #endif /* Returns a message corresponding to an error code, ERRCODE, returned from either regcomp or regexec. We don't use PREG here. */ size_t regerror(int errcode, UNUSED const regex_t *__restrict preg, char *__restrict errbuf, size_t errbuf_size) { const char *msg; size_t msg_size; if (BE (errcode < 0 || errcode >= (int) (sizeof (__re_error_msgid_idx) / sizeof (__re_error_msgid_idx[0])), 0)) /* Only error codes returned by the rest of the code should be passed to this routine. If we are given anything else, or if other regex code generates an invalid error code, then the program has a bug. Dump core so we can fix it. */ Rf_error( "Error in 'regerror': Unexpected error. Please report at" " https://github.com/ropensci/git2r/issues"); msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); msg_size = strlen (msg) + 1; /* Includes the null. */ if (BE (errbuf_size != 0, 1)) { if (BE (msg_size > errbuf_size, 0)) { memcpy (errbuf, msg, errbuf_size - 1); errbuf[errbuf_size - 1] = 0; } else memcpy (errbuf, msg, msg_size); } return msg_size; } #ifdef _LIBC weak_alias (__regerror, regerror) #endif #ifdef RE_ENABLE_I18N /* This static array is used for the map to single-byte characters when UTF-8 is used. Otherwise we would allocate memory just to initialize it the same all the time. UTF-8 is the preferred encoding so this is a worthwhile optimization. */ #if __GNUC__ >= 3 static const bitset_t utf8_sb_map = { /* Set the first 128 bits. */ [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX }; #else /* ! (__GNUC__ >= 3) */ static bitset_t utf8_sb_map; #endif /* __GNUC__ >= 3 */ #endif /* RE_ENABLE_I18N */ static void free_dfa_content (re_dfa_t *dfa) { unsigned int i; int j; if (dfa->nodes) for (i = 0; i < dfa->nodes_len; ++i) free_token (dfa->nodes + i); re_free (dfa->nexts); for (i = 0; i < dfa->nodes_len; ++i) { if (dfa->eclosures != NULL) re_node_set_free (dfa->eclosures + i); if (dfa->inveclosures != NULL) re_node_set_free (dfa->inveclosures + i); if (dfa->edests != NULL) re_node_set_free (dfa->edests + i); } re_free (dfa->edests); re_free (dfa->eclosures); re_free (dfa->inveclosures); re_free (dfa->nodes); if (dfa->state_table) for (i = 0; i <= dfa->state_hash_mask; ++i) { struct re_state_table_entry *entry = dfa->state_table + i; for (j = 0; j < entry->num; ++j) { re_dfastate_t *state = entry->array[j]; free_state (state); } re_free (entry->array); } re_free (dfa->state_table); #ifdef RE_ENABLE_I18N if (dfa->sb_char != utf8_sb_map) re_free (dfa->sb_char); #endif re_free (dfa->subexp_map); #ifdef DEBUG re_free (dfa->re_str); #endif re_free (dfa); } /* Free dynamically allocated space used by PREG. */ void regfree (regex_t *preg) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; if (BE (dfa != NULL, 1)) free_dfa_content (dfa); preg->buffer = NULL; preg->allocated = 0; re_free (preg->fastmap); preg->fastmap = NULL; re_free (preg->translate); preg->translate = NULL; } #ifdef _LIBC weak_alias (__regfree, regfree) #endif /* Entry points compatible with 4.2 BSD regex library. We don't define them unless specifically requested. */ #if defined _REGEX_RE_COMP || defined _LIBC /* BSD has one and only one pattern buffer. */ static struct re_pattern_buffer re_comp_buf; char * # ifdef _LIBC /* Make these definitions weak in libc, so POSIX programs can redefine these names if they don't use our functions, and still use regcomp/regexec above without link errors. */ weak_function # endif re_comp (s) const char *s; { reg_errcode_t ret; char *fastmap; if (!s) { if (!re_comp_buf.buffer) return gettext ("No previous regular expression"); return 0; } if (re_comp_buf.buffer) { fastmap = re_comp_buf.fastmap; re_comp_buf.fastmap = NULL; __regfree (&re_comp_buf); memset (&re_comp_buf, '\0', sizeof (re_comp_buf)); re_comp_buf.fastmap = fastmap; } if (re_comp_buf.fastmap == NULL) { re_comp_buf.fastmap = (char *) malloc (SBC_MAX); if (re_comp_buf.fastmap == NULL) return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) REG_ESPACE]); } /* Since `re_exec' always passes NULL for the `regs' argument, we don't need to initialize the pattern buffer fields which affect it. */ /* Match anchors at newlines. */ re_comp_buf.newline_anchor = 1; ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options); if (!ret) return NULL; /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); } #ifdef _LIBC libc_freeres_fn (free_mem) { __regfree (&re_comp_buf); } #endif #endif /* _REGEX_RE_COMP */ /* Internal entry point. Compile the regular expression PATTERN, whose length is LENGTH. SYNTAX indicate regular expression's syntax. */ static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, size_t length, reg_syntax_t syntax) { reg_errcode_t err = REG_NOERROR; re_dfa_t *dfa; re_string_t regexp; /* Initialize the pattern buffer. */ preg->fastmap_accurate = 0; preg->syntax = syntax; preg->not_bol = preg->not_eol = 0; preg->used = 0; preg->re_nsub = 0; preg->can_be_null = 0; preg->regs_allocated = REGS_UNALLOCATED; /* Initialize the dfa. */ dfa = (re_dfa_t *) preg->buffer; if (BE (preg->allocated < sizeof (re_dfa_t), 0)) { /* If zero allocated, but buffer is non-null, try to realloc enough space. This loses if buffer's address is bogus, but that is the user's responsibility. If ->buffer is NULL this is a simple allocation. */ dfa = re_realloc (preg->buffer, re_dfa_t, 1); if (dfa == NULL) return REG_ESPACE; preg->allocated = sizeof (re_dfa_t); preg->buffer = (unsigned char *) dfa; } preg->used = sizeof (re_dfa_t); err = init_dfa (dfa, length); if (BE (err != REG_NOERROR, 0)) { free_dfa_content (dfa); preg->buffer = NULL; preg->allocated = 0; return err; } #ifdef DEBUG /* Note: length+1 will not overflow since it is checked in init_dfa. */ dfa->re_str = re_malloc (char, length + 1); strncpy (dfa->re_str, pattern, length + 1); #endif __libc_lock_init (dfa->lock); err = re_string_construct (®exp, pattern, length, preg->translate, syntax & RE_ICASE, dfa); if (BE (err != REG_NOERROR, 0)) { re_compile_internal_free_return: free_workarea_compile (preg); re_string_destruct (®exp); free_dfa_content (dfa); preg->buffer = NULL; preg->allocated = 0; return err; } /* Parse the regular expression, and build a structure tree. */ preg->re_nsub = 0; dfa->str_tree = parse (®exp, preg, syntax, &err); if (BE (dfa->str_tree == NULL, 0)) goto re_compile_internal_free_return; /* Analyze the tree and create the nfa. */ err = analyze (preg); if (BE (err != REG_NOERROR, 0)) goto re_compile_internal_free_return; #ifdef RE_ENABLE_I18N /* If possible, do searching in single byte encoding to speed things up. */ if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL) optimize_utf8 (dfa); #endif /* Then create the initial state of the dfa. */ err = create_initial_state (dfa); /* Release work areas. */ free_workarea_compile (preg); re_string_destruct (®exp); if (BE (err != REG_NOERROR, 0)) { free_dfa_content (dfa); preg->buffer = NULL; preg->allocated = 0; } return err; } /* Initialize DFA. We use the length of the regular expression PAT_LEN as the initial length of some arrays. */ static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len) { unsigned int table_size; memset (dfa, '\0', sizeof (re_dfa_t)); /* Force allocation of str_tree_storage the first time. */ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; /* Avoid overflows. */ if (pat_len == SIZE_MAX) return REG_ESPACE; dfa->nodes_alloc = pat_len + 1; dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); /* table_size = 2 ^ ceil(log pat_len) */ for (table_size = 1; ; table_size <<= 1) if (table_size > pat_len) break; dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size); dfa->state_hash_mask = table_size - 1; dfa->mb_cur_max = MB_CUR_MAX; #ifdef _LIBC if (dfa->mb_cur_max == 6 && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0) dfa->is_utf8 = 1; dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII) != 0); #else dfa->is_utf8 = 1; /* We check exhaustively in the loop below if this charset is a superset of ASCII. */ dfa->map_notascii = 0; #endif #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { if (dfa->is_utf8) { #if !defined(__GNUC__) || __GNUC__ < 3 static short utf8_sb_map_inited = 0; if (! utf8_sb_map_inited) { int i; utf8_sb_map_inited = 0; for (i = 0; i <= 0x80 / BITSET_WORD_BITS - 1; i++) utf8_sb_map[i] = BITSET_WORD_MAX; } #endif dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map; } else { int i, j, ch; dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); if (BE (dfa->sb_char == NULL, 0)) return REG_ESPACE; /* Set the bits corresponding to single byte chars. */ for (i = 0, ch = 0; i < BITSET_WORDS; ++i) for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) { wint_t wch = __btowc (ch); if (wch != WEOF) dfa->sb_char[i] |= (bitset_word_t) 1 << j; # ifndef _LIBC if (isascii (ch) && wch != ch) dfa->map_notascii = 1; # endif } } } #endif if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0)) return REG_ESPACE; return REG_NOERROR; } /* Initialize WORD_CHAR table, which indicate which character is "word". In this case "word" means that it is the word construction character used by some operators like "\<", "\>", etc. */ static void internal_function init_word_char (re_dfa_t *dfa) { int i, j, ch; dfa->word_ops_used = 1; for (i = 0, ch = 0; i < BITSET_WORDS; ++i) for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) if (isalnum (ch) || ch == '_') dfa->word_char[i] |= (bitset_word_t) 1 << j; } /* Free the work area which are only used while compiling. */ static void free_workarea_compile (regex_t *preg) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; bin_tree_storage_t *storage, *next; for (storage = dfa->str_tree_storage; storage; storage = next) { next = storage->next; re_free (storage); } dfa->str_tree_storage = NULL; dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; dfa->str_tree = NULL; re_free (dfa->org_indices); dfa->org_indices = NULL; } /* Create initial states for all contexts. */ static reg_errcode_t create_initial_state (re_dfa_t *dfa) { int first, i; reg_errcode_t err; re_node_set init_nodes; /* Initial states have the epsilon closure of the node which is the first node of the regular expression. */ first = dfa->str_tree->first->node_idx; dfa->init_node = first; err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first); if (BE (err != REG_NOERROR, 0)) return err; /* The back-references which are in initial states can epsilon transit, since in this case all of the subexpressions can be null. Then we add epsilon closures of the nodes which are the next nodes of the back-references. */ if (dfa->nbackref > 0) for (i = 0; i < init_nodes.nelem; ++i) { int node_idx = init_nodes.elems[i]; re_token_type_t type = dfa->nodes[node_idx].type; int clexp_idx; if (type != OP_BACK_REF) continue; for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx) { re_token_t *clexp_node; clexp_node = dfa->nodes + init_nodes.elems[clexp_idx]; if (clexp_node->type == OP_CLOSE_SUBEXP && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx) break; } if (clexp_idx == init_nodes.nelem) continue; if (type == OP_BACK_REF) { int dest_idx = dfa->edests[node_idx].elems[0]; if (!re_node_set_contains (&init_nodes, dest_idx)) { reg_errcode_t err = re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx); if (err != REG_NOERROR) return err; i = 0; } } } /* It must be the first time to invoke acquire_state. */ dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0); /* We don't check ERR here, since the initial state must not be NULL. */ if (BE (dfa->init_state == NULL, 0)) return err; if (dfa->init_state->has_constraint) { dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes, CONTEXT_WORD); dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes, CONTEXT_NEWLINE); dfa->init_state_begbuf = re_acquire_state_context (&err, dfa, &init_nodes, CONTEXT_NEWLINE | CONTEXT_BEGBUF); if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL || dfa->init_state_begbuf == NULL, 0)) return err; } else dfa->init_state_word = dfa->init_state_nl = dfa->init_state_begbuf = dfa->init_state; re_node_set_free (&init_nodes); return REG_NOERROR; } #ifdef RE_ENABLE_I18N /* If it is possible to do searching in single byte encoding instead of UTF-8 to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change DFA nodes where needed. */ static void optimize_utf8 (re_dfa_t *dfa) { int node, i, mb_chars = 0, has_period = 0; for (node = 0; node < dfa->nodes_len; ++node) switch (dfa->nodes[node].type) { case CHARACTER: if (dfa->nodes[node].opr.c >= 0x80) mb_chars = 1; break; case ANCHOR: switch (dfa->nodes[node].opr.ctx_type) { case LINE_FIRST: case LINE_LAST: case BUF_FIRST: case BUF_LAST: break; default: /* Word anchors etc. cannot be handled. It's okay to test opr.ctx_type since constraints (for all DFA nodes) are created by ORing one or more opr.ctx_type values. */ return; } break; case OP_PERIOD: has_period = 1; break; case OP_BACK_REF: case OP_ALT: case END_OF_RE: case OP_DUP_ASTERISK: case OP_OPEN_SUBEXP: case OP_CLOSE_SUBEXP: break; case COMPLEX_BRACKET: return; case SIMPLE_BRACKET: /* Just double check. The non-ASCII range starts at 0x80. */ assert (0x80 % BITSET_WORD_BITS == 0); for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i) if (dfa->nodes[node].opr.sbcset[i]) return; break; default: Rf_error( "Error in 'optimize_utf8': Unexpected error. Please report at" " https://github.com/ropensci/git2r/issues"); } if (mb_chars || has_period) for (node = 0; node < dfa->nodes_len; ++node) { if (dfa->nodes[node].type == CHARACTER && dfa->nodes[node].opr.c >= 0x80) dfa->nodes[node].mb_partial = 0; else if (dfa->nodes[node].type == OP_PERIOD) dfa->nodes[node].type = OP_UTF8_PERIOD; } /* The search can be in single byte locale. */ dfa->mb_cur_max = 1; dfa->is_utf8 = 0; dfa->has_mb_node = dfa->nbackref > 0 || has_period; } #endif /* Analyze the structure tree, and calculate "first", "next", "edest", "eclosure", and "inveclosure". */ static reg_errcode_t analyze (regex_t *preg) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; reg_errcode_t ret; /* Allocate arrays. */ dfa->nexts = re_malloc (int, dfa->nodes_alloc); dfa->org_indices = re_malloc (int, dfa->nodes_alloc); dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL || dfa->eclosures == NULL, 0)) return REG_ESPACE; dfa->subexp_map = re_malloc (int, preg->re_nsub); if (dfa->subexp_map != NULL) { unsigned int i; for (i = 0; i < preg->re_nsub; i++) dfa->subexp_map[i] = i; preorder (dfa->str_tree, optimize_subexps, dfa); for (i = 0; i < preg->re_nsub; i++) if (dfa->subexp_map[i] != (int)i) break; if (i == preg->re_nsub) { free (dfa->subexp_map); dfa->subexp_map = NULL; } } ret = postorder (dfa->str_tree, lower_subexps, preg); if (BE (ret != REG_NOERROR, 0)) return ret; ret = postorder (dfa->str_tree, calc_first, dfa); if (BE (ret != REG_NOERROR, 0)) return ret; preorder (dfa->str_tree, calc_next, dfa); ret = preorder (dfa->str_tree, link_nfa_nodes, dfa); if (BE (ret != REG_NOERROR, 0)) return ret; ret = calc_eclosure (dfa); if (BE (ret != REG_NOERROR, 0)) return ret; /* We only need this during the prune_impossible_nodes pass in regexec.c; skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */ if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match) || dfa->nbackref) { dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len); if (BE (dfa->inveclosures == NULL, 0)) return REG_ESPACE; ret = calc_inveclosure (dfa); } return ret; } /* Our parse trees are very unbalanced, so we cannot use a stack to implement parse tree visits. Instead, we use parent pointers and some hairy code in these two functions. */ static reg_errcode_t postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra) { bin_tree_t *node, *prev; for (node = root; ; ) { /* Descend down the tree, preferably to the left (or to the right if that's the only child). */ while (node->left || node->right) if (node->left) node = node->left; else node = node->right; do { reg_errcode_t err = fn (extra, node); if (BE (err != REG_NOERROR, 0)) return err; if (node->parent == NULL) return REG_NOERROR; prev = node; node = node->parent; } /* Go up while we have a node that is reached from the right. */ while (node->right == prev || node->right == NULL); node = node->right; } } static reg_errcode_t preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra) { bin_tree_t *node; for (node = root; ; ) { reg_errcode_t err = fn (extra, node); if (BE (err != REG_NOERROR, 0)) return err; /* Go to the left node, or up and to the right. */ if (node->left) node = node->left; else { bin_tree_t *prev = NULL; while (node->right == prev || node->right == NULL) { prev = node; node = node->parent; if (!node) return REG_NOERROR; } node = node->right; } } } /* Optimization pass: if a SUBEXP is entirely contained, strip it and tell re_search_internal to map the inner one's opr.idx to this one's. Adjust backreferences as well. Requires a preorder visit. */ static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node) { re_dfa_t *dfa = (re_dfa_t *) extra; if (node->token.type == OP_BACK_REF && dfa->subexp_map) { int idx = node->token.opr.idx; node->token.opr.idx = dfa->subexp_map[idx]; dfa->used_bkref_map |= 1 << node->token.opr.idx; } else if (node->token.type == SUBEXP && node->left && node->left->token.type == SUBEXP) { int other_idx = node->left->token.opr.idx; node->left = node->left->left; if (node->left) node->left->parent = node; dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx]; if (other_idx < BITSET_WORD_BITS) dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx); } return REG_NOERROR; } /* Lowering pass: Turn each SUBEXP node into the appropriate concatenation of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */ static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node) { regex_t *preg = (regex_t *) extra; reg_errcode_t err = REG_NOERROR; if (node->left && node->left->token.type == SUBEXP) { node->left = lower_subexp (&err, preg, node->left); if (node->left) node->left->parent = node; } if (node->right && node->right->token.type == SUBEXP) { node->right = lower_subexp (&err, preg, node->right); if (node->right) node->right->parent = node; } return err; } static bin_tree_t * lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; bin_tree_t *body = node->left; bin_tree_t *op, *cls, *tree1, *tree; if (preg->no_sub /* We do not optimize empty subexpressions, because otherwise we may have bad CONCAT nodes with NULL children. This is obviously not very common, so we do not lose much. An example that triggers this case is the sed "script" /\(\)/x. */ && node->left != NULL && (node->token.opr.idx >= BITSET_WORD_BITS || !(dfa->used_bkref_map & ((bitset_word_t) 1 << node->token.opr.idx)))) return node->left; /* Convert the SUBEXP node to the concatenation of an OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */ op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP); cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP); tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls; tree = create_tree (dfa, op, tree1, CONCAT); if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0)) { *err = REG_ESPACE; return NULL; } op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx; op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp; return tree; } /* Pass 1 in building the NFA: compute FIRST and create unlinked automaton nodes. Requires a postorder visit. */ static reg_errcode_t calc_first (void *extra, bin_tree_t *node) { re_dfa_t *dfa = (re_dfa_t *) extra; if (node->token.type == CONCAT) { node->first = node->left->first; node->node_idx = node->left->node_idx; } else { node->first = node; node->node_idx = re_dfa_add_node (dfa, node->token); if (BE (node->node_idx == -1, 0)) return REG_ESPACE; if (node->token.type == ANCHOR) dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type; } return REG_NOERROR; } /* Pass 2: compute NEXT on the tree. Preorder visit. */ static reg_errcode_t calc_next (UNUSED void *extra, bin_tree_t *node) { switch (node->token.type) { case OP_DUP_ASTERISK: node->left->next = node; break; case CONCAT: node->left->next = node->right->first; node->right->next = node->next; break; default: if (node->left) node->left->next = node->next; if (node->right) node->right->next = node->next; break; } return REG_NOERROR; } /* Pass 3: link all DFA nodes to their NEXT node (any order will do). */ static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node) { re_dfa_t *dfa = (re_dfa_t *) extra; int idx = node->node_idx; reg_errcode_t err = REG_NOERROR; switch (node->token.type) { case CONCAT: break; case END_OF_RE: assert (node->next == NULL); break; case OP_DUP_ASTERISK: case OP_ALT: { int left, right; dfa->has_plural_match = 1; if (node->left != NULL) left = node->left->first->node_idx; else left = node->next->node_idx; if (node->right != NULL) right = node->right->first->node_idx; else right = node->next->node_idx; assert (left > -1); assert (right > -1); err = re_node_set_init_2 (dfa->edests + idx, left, right); } break; case ANCHOR: case OP_OPEN_SUBEXP: case OP_CLOSE_SUBEXP: err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx); break; case OP_BACK_REF: dfa->nexts[idx] = node->next->node_idx; if (node->token.type == OP_BACK_REF) err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]); break; default: assert (!IS_EPSILON_NODE (node->token.type)); dfa->nexts[idx] = node->next->node_idx; break; } return err; } /* Duplicate the epsilon closure of the node ROOT_NODE. Note that duplicated nodes have constraint INIT_CONSTRAINT in addition to their own constraint. */ static reg_errcode_t internal_function duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node, int root_node, unsigned int init_constraint) { int org_node, clone_node, ret; unsigned int constraint = init_constraint; for (org_node = top_org_node, clone_node = top_clone_node;;) { int org_dest, clone_dest; if (dfa->nodes[org_node].type == OP_BACK_REF) { /* If the back reference epsilon-transit, its destination must also have the constraint. Then duplicate the epsilon closure of the destination of the back reference, and store it in edests of the back reference. */ org_dest = dfa->nexts[org_node]; re_node_set_empty (dfa->edests + clone_node); clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; dfa->nexts[clone_node] = dfa->nexts[org_node]; ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (ret < 0, 0)) return REG_ESPACE; } else if (dfa->edests[org_node].nelem == 0) { /* In case of the node can't epsilon-transit, don't duplicate the destination and store the original destination as the destination of the node. */ dfa->nexts[clone_node] = dfa->nexts[org_node]; break; } else if (dfa->edests[org_node].nelem == 1) { /* In case of the node can epsilon-transit, and it has only one destination. */ org_dest = dfa->edests[org_node].elems[0]; re_node_set_empty (dfa->edests + clone_node); /* If the node is root_node itself, it means the epsilon clsoure has a loop. Then tie it to the destination of the root_node. */ if (org_node == root_node && clone_node != org_node) { ret = re_node_set_insert (dfa->edests + clone_node, org_dest); if (BE (ret < 0, 0)) return REG_ESPACE; break; } /* In case of the node has another constraint, add it. */ constraint |= dfa->nodes[org_node].constraint; clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (ret < 0, 0)) return REG_ESPACE; } else /* dfa->edests[org_node].nelem == 2 */ { /* In case of the node can epsilon-transit, and it has two destinations. In the bin_tree_t and DFA, that's '|' and '*'. */ org_dest = dfa->edests[org_node].elems[0]; re_node_set_empty (dfa->edests + clone_node); /* Search for a duplicated node which satisfies the constraint. */ clone_dest = search_duplicated_node (dfa, org_dest, constraint); if (clone_dest == -1) { /* There is no such duplicated node, create a new one. */ reg_errcode_t err; clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (ret < 0, 0)) return REG_ESPACE; err = duplicate_node_closure (dfa, org_dest, clone_dest, root_node, constraint); if (BE (err != REG_NOERROR, 0)) return err; } else { /* There is a duplicated node which satisfies the constraint, use it to avoid infinite loop. */ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (ret < 0, 0)) return REG_ESPACE; } org_dest = dfa->edests[org_node].elems[1]; clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (ret < 0, 0)) return REG_ESPACE; } org_node = org_dest; clone_node = clone_dest; } return REG_NOERROR; } /* Search for a node which is duplicated from the node ORG_NODE, and satisfies the constraint CONSTRAINT. */ static int search_duplicated_node (const re_dfa_t *dfa, int org_node, unsigned int constraint) { int idx; for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx) { if (org_node == dfa->org_indices[idx] && constraint == dfa->nodes[idx].constraint) return idx; /* Found. */ } return -1; /* Not found. */ } /* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT. Return the index of the new node, or -1 if insufficient storage is available. */ static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint) { int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]); if (BE (dup_idx != -1, 1)) { dfa->nodes[dup_idx].constraint = constraint; dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint; dfa->nodes[dup_idx].duplicated = 1; /* Store the index of the original node. */ dfa->org_indices[dup_idx] = org_idx; } return dup_idx; } static reg_errcode_t calc_inveclosure (re_dfa_t *dfa) { int ret; unsigned int src, idx; for (idx = 0; idx < dfa->nodes_len; ++idx) re_node_set_init_empty (dfa->inveclosures + idx); for (src = 0; src < dfa->nodes_len; ++src) { int *elems = dfa->eclosures[src].elems; int idx; for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) { ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src); if (BE (ret == -1, 0)) return REG_ESPACE; } } return REG_NOERROR; } /* Calculate "eclosure" for all the node in DFA. */ static reg_errcode_t calc_eclosure (re_dfa_t *dfa) { size_t node_idx; int incomplete; #ifdef DEBUG assert (dfa->nodes_len > 0); #endif incomplete = 0; /* For each nodes, calculate epsilon closure. */ for (node_idx = 0; ; ++node_idx) { reg_errcode_t err; re_node_set eclosure_elem; if (node_idx == dfa->nodes_len) { if (!incomplete) break; incomplete = 0; node_idx = 0; } #ifdef DEBUG assert (dfa->eclosures[node_idx].nelem != -1); #endif /* If we have already calculated, skip it. */ if (dfa->eclosures[node_idx].nelem != 0) continue; /* Calculate epsilon closure of `node_idx'. */ err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1); if (BE (err != REG_NOERROR, 0)) return err; if (dfa->eclosures[node_idx].nelem == 0) { incomplete = 1; re_node_set_free (&eclosure_elem); } } return REG_NOERROR; } /* Calculate epsilon closure of NODE. */ static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root) { reg_errcode_t err; int i; re_node_set eclosure; int ret; int incomplete = 0; err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); if (BE (err != REG_NOERROR, 0)) return err; /* This indicates that we are calculating this node now. We reference this value to avoid infinite loop. */ dfa->eclosures[node].nelem = -1; /* If the current node has constraints, duplicate all nodes since they must inherit the constraints. */ if (dfa->nodes[node].constraint && dfa->edests[node].nelem && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) { err = duplicate_node_closure (dfa, node, node, node, dfa->nodes[node].constraint); if (BE (err != REG_NOERROR, 0)) return err; } /* Expand each epsilon destination nodes. */ if (IS_EPSILON_NODE(dfa->nodes[node].type)) for (i = 0; i < dfa->edests[node].nelem; ++i) { re_node_set eclosure_elem; int edest = dfa->edests[node].elems[i]; /* If calculating the epsilon closure of `edest' is in progress, return intermediate result. */ if (dfa->eclosures[edest].nelem == -1) { incomplete = 1; continue; } /* If we haven't calculated the epsilon closure of `edest' yet, calculate now. Otherwise use calculated epsilon closure. */ if (dfa->eclosures[edest].nelem == 0) { err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0); if (BE (err != REG_NOERROR, 0)) return err; } else eclosure_elem = dfa->eclosures[edest]; /* Merge the epsilon closure of `edest'. */ err = re_node_set_merge (&eclosure, &eclosure_elem); if (BE (err != REG_NOERROR, 0)) return err; /* If the epsilon closure of `edest' is incomplete, the epsilon closure of this node is also incomplete. */ if (dfa->eclosures[edest].nelem == 0) { incomplete = 1; re_node_set_free (&eclosure_elem); } } /* An epsilon closure includes itself. */ ret = re_node_set_insert (&eclosure, node); if (BE (ret < 0, 0)) return REG_ESPACE; if (incomplete && !root) dfa->eclosures[node].nelem = 0; else dfa->eclosures[node] = eclosure; *new_set = eclosure; return REG_NOERROR; } /* Functions for token which are used in the parser. */ /* Fetch a token from INPUT. We must not use this function inside bracket expressions. */ static void internal_function fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax) { re_string_skip_bytes (input, peek_token (result, input, syntax)); } /* Peek a token from INPUT, and return the length of the token. We must not use this function inside bracket expressions. */ static int internal_function peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax) { unsigned char c; if (re_string_eoi (input)) { token->type = END_OF_RE; return 0; } c = re_string_peek_byte (input, 0); token->opr.c = c; token->word_char = 0; #ifdef RE_ENABLE_I18N token->mb_partial = 0; if (input->mb_cur_max > 1 && !re_string_first_byte (input, re_string_cur_idx (input))) { token->type = CHARACTER; token->mb_partial = 1; return 1; } #endif if (c == '\\') { unsigned char c2; if (re_string_cur_idx (input) + 1 >= re_string_length (input)) { token->type = BACK_SLASH; return 1; } c2 = re_string_peek_byte_case (input, 1); token->opr.c = c2; token->type = CHARACTER; #ifdef RE_ENABLE_I18N if (input->mb_cur_max > 1) { wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input) + 1); token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; } else #endif token->word_char = IS_WORD_CHAR (c2) != 0; switch (c2) { case '|': if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR)) token->type = OP_ALT; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (!(syntax & RE_NO_BK_REFS)) { token->type = OP_BACK_REF; token->opr.idx = c2 - '1'; } break; case '<': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = WORD_FIRST; } break; case '>': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = WORD_LAST; } break; case 'b': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = WORD_DELIM; } break; case 'B': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = NOT_WORD_DELIM; } break; case 'w': if (!(syntax & RE_NO_GNU_OPS)) token->type = OP_WORD; break; case 'W': if (!(syntax & RE_NO_GNU_OPS)) token->type = OP_NOTWORD; break; case 's': if (!(syntax & RE_NO_GNU_OPS)) token->type = OP_SPACE; break; case 'S': if (!(syntax & RE_NO_GNU_OPS)) token->type = OP_NOTSPACE; break; case '`': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = BUF_FIRST; } break; case '\'': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = BUF_LAST; } break; case '(': if (!(syntax & RE_NO_BK_PARENS)) token->type = OP_OPEN_SUBEXP; break; case ')': if (!(syntax & RE_NO_BK_PARENS)) token->type = OP_CLOSE_SUBEXP; break; case '+': if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) token->type = OP_DUP_PLUS; break; case '?': if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) token->type = OP_DUP_QUESTION; break; case '{': if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) token->type = OP_OPEN_DUP_NUM; break; case '}': if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) token->type = OP_CLOSE_DUP_NUM; break; default: break; } return 2; } token->type = CHARACTER; #ifdef RE_ENABLE_I18N if (input->mb_cur_max > 1) { wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input)); token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; } else #endif token->word_char = IS_WORD_CHAR (token->opr.c); switch (c) { case '\n': if (syntax & RE_NEWLINE_ALT) token->type = OP_ALT; break; case '|': if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR)) token->type = OP_ALT; break; case '*': token->type = OP_DUP_ASTERISK; break; case '+': if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) token->type = OP_DUP_PLUS; break; case '?': if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) token->type = OP_DUP_QUESTION; break; case '{': if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) token->type = OP_OPEN_DUP_NUM; break; case '}': if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) token->type = OP_CLOSE_DUP_NUM; break; case '(': if (syntax & RE_NO_BK_PARENS) token->type = OP_OPEN_SUBEXP; break; case ')': if (syntax & RE_NO_BK_PARENS) token->type = OP_CLOSE_SUBEXP; break; case '[': token->type = OP_OPEN_BRACKET; break; case '.': token->type = OP_PERIOD; break; case '^': if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) && re_string_cur_idx (input) != 0) { char prev = re_string_peek_byte (input, -1); if (!(syntax & RE_NEWLINE_ALT) || prev != '\n') break; } token->type = ANCHOR; token->opr.ctx_type = LINE_FIRST; break; case '$': if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && re_string_cur_idx (input) + 1 != re_string_length (input)) { re_token_t next; re_string_skip_bytes (input, 1); peek_token (&next, input, syntax); re_string_skip_bytes (input, -1); if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP) break; } token->type = ANCHOR; token->opr.ctx_type = LINE_LAST; break; default: break; } return 1; } /* Peek a token from INPUT, and return the length of the token. We must not use this function out of bracket expressions. */ static int internal_function peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax) { unsigned char c; if (re_string_eoi (input)) { token->type = END_OF_RE; return 0; } c = re_string_peek_byte (input, 0); token->opr.c = c; #ifdef RE_ENABLE_I18N if (input->mb_cur_max > 1 && !re_string_first_byte (input, re_string_cur_idx (input))) { token->type = CHARACTER; return 1; } #endif /* RE_ENABLE_I18N */ if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && re_string_cur_idx (input) + 1 < re_string_length (input)) { /* In this case, '\' escape a character. */ unsigned char c2; re_string_skip_bytes (input, 1); c2 = re_string_peek_byte (input, 0); token->opr.c = c2; token->type = CHARACTER; return 1; } if (c == '[') /* '[' is a special char in a bracket exps. */ { unsigned char c2; int token_len; if (re_string_cur_idx (input) + 1 < re_string_length (input)) c2 = re_string_peek_byte (input, 1); else c2 = 0; token->opr.c = c2; token_len = 2; switch (c2) { case '.': token->type = OP_OPEN_COLL_ELEM; break; case '=': token->type = OP_OPEN_EQUIV_CLASS; break; case ':': if (syntax & RE_CHAR_CLASSES) { token->type = OP_OPEN_CHAR_CLASS; break; } /* else fall through. */ default: token->type = CHARACTER; token->opr.c = c; token_len = 1; break; } return token_len; } switch (c) { case '-': token->type = OP_CHARSET_RANGE; break; case ']': token->type = OP_CLOSE_BRACKET; break; case '^': token->type = OP_NON_MATCH_LIST; break; default: token->type = CHARACTER; } return 1; } /* Functions for parser. */ /* Entry point of the parser. Parse the regular expression REGEXP and return the structure tree. If an error is occured, ERR is set by error code, and return NULL. This function build the following tree, from regular expression : CAT / \ / \ EOR CAT means concatenation. EOR means end of regular expression. */ static bin_tree_t * parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax, reg_errcode_t *err) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; bin_tree_t *tree, *eor, *root; re_token_t current_token; dfa->syntax = syntax; fetch_token (¤t_token, regexp, syntax | RE_CARET_ANCHORS_HERE); tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; eor = create_tree (dfa, NULL, NULL, END_OF_RE); if (tree != NULL) root = create_tree (dfa, tree, eor, CONCAT); else root = eor; if (BE (eor == NULL || root == NULL, 0)) { *err = REG_ESPACE; return NULL; } return root; } /* This function build the following tree, from regular expression |: ALT / \ / \ ALT means alternative, which represents the operator `|'. */ static bin_tree_t * parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; bin_tree_t *tree, *branch = NULL; tree = parse_branch (regexp, preg, token, syntax, nest, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; while (token->type == OP_ALT) { fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); if (token->type != OP_ALT && token->type != END_OF_RE && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) { branch = parse_branch (regexp, preg, token, syntax, nest, err); if (BE (*err != REG_NOERROR && branch == NULL, 0)) return NULL; } else branch = NULL; tree = create_tree (dfa, tree, branch, OP_ALT); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } } return tree; } /* This function build the following tree, from regular expression : CAT / \ / \ CAT means concatenation. */ static bin_tree_t * parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err) { bin_tree_t *tree, *exp; re_dfa_t *dfa = (re_dfa_t *) preg->buffer; tree = parse_expression (regexp, preg, token, syntax, nest, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; while (token->type != OP_ALT && token->type != END_OF_RE && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) { exp = parse_expression (regexp, preg, token, syntax, nest, err); if (BE (*err != REG_NOERROR && exp == NULL, 0)) { return NULL; } if (tree != NULL && exp != NULL) { tree = create_tree (dfa, tree, exp, CONCAT); if (tree == NULL) { *err = REG_ESPACE; return NULL; } } else if (tree == NULL) tree = exp; /* Otherwise exp == NULL, we don't need to create new tree. */ } return tree; } /* This function build the following tree, from regular expression a*: * | a */ static bin_tree_t * parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; bin_tree_t *tree; switch (token->type) { case CHARACTER: tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { while (!re_string_eoi (regexp) && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) { bin_tree_t *mbc_remain; fetch_token (token, regexp, syntax); mbc_remain = create_token_tree (dfa, NULL, NULL, token); tree = create_tree (dfa, tree, mbc_remain, CONCAT); if (BE (mbc_remain == NULL || tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } } } #endif break; case OP_OPEN_SUBEXP: tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; break; case OP_OPEN_BRACKET: tree = parse_bracket_exp (regexp, dfa, token, syntax, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; break; case OP_BACK_REF: if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1)) { *err = REG_ESUBREG; return NULL; } dfa->used_bkref_map |= 1 << token->opr.idx; tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } ++dfa->nbackref; dfa->has_mb_node = 1; break; case OP_OPEN_DUP_NUM: if (syntax & RE_CONTEXT_INVALID_DUP) { *err = REG_BADRPT; return NULL; } /* FALLTHROUGH */ case OP_DUP_ASTERISK: case OP_DUP_PLUS: case OP_DUP_QUESTION: if (syntax & RE_CONTEXT_INVALID_OPS) { *err = REG_BADRPT; return NULL; } else if (syntax & RE_CONTEXT_INDEP_OPS) { fetch_token (token, regexp, syntax); return parse_expression (regexp, preg, token, syntax, nest, err); } /* else fall through */ case OP_CLOSE_SUBEXP: if ((token->type == OP_CLOSE_SUBEXP) && !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) { *err = REG_ERPAREN; return NULL; } /* else fall through */ case OP_CLOSE_DUP_NUM: /* We treat it as a normal character. */ /* Then we can these characters as normal characters. */ token->type = CHARACTER; /* mb_partial and word_char bits should be initialized already by peek_token. */ tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } break; case ANCHOR: if ((token->opr.ctx_type & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST)) && dfa->word_ops_used == 0) init_word_char (dfa); if (token->opr.ctx_type == WORD_DELIM || token->opr.ctx_type == NOT_WORD_DELIM) { bin_tree_t *tree_first, *tree_last; if (token->opr.ctx_type == WORD_DELIM) { token->opr.ctx_type = WORD_FIRST; tree_first = create_token_tree (dfa, NULL, NULL, token); token->opr.ctx_type = WORD_LAST; } else { token->opr.ctx_type = INSIDE_WORD; tree_first = create_token_tree (dfa, NULL, NULL, token); token->opr.ctx_type = INSIDE_NOTWORD; } tree_last = create_token_tree (dfa, NULL, NULL, token); tree = create_tree (dfa, tree_first, tree_last, OP_ALT); if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } } else { tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } } /* We must return here, since ANCHORs can't be followed by repetition operators. eg. RE"^*" is invalid or "", it must not be "". */ fetch_token (token, regexp, syntax); return tree; case OP_PERIOD: tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } if (dfa->mb_cur_max > 1) dfa->has_mb_node = 1; break; case OP_WORD: case OP_NOTWORD: tree = build_charclass_op (dfa, regexp->trans, "alnum", "_", token->type == OP_NOTWORD, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; break; case OP_SPACE: case OP_NOTSPACE: tree = build_charclass_op (dfa, regexp->trans, "space", "", token->type == OP_NOTSPACE, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; break; case OP_ALT: case END_OF_RE: return NULL; case BACK_SLASH: *err = REG_EESCAPE; return NULL; default: /* Must not happen? */ #ifdef DEBUG assert (0); #endif return NULL; } fetch_token (token, regexp, syntax); while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM) { tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; /* In BRE consecutive duplications are not allowed. */ if ((syntax & RE_CONTEXT_INVALID_DUP) && (token->type == OP_DUP_ASTERISK || token->type == OP_OPEN_DUP_NUM)) { *err = REG_BADRPT; return NULL; } } return tree; } /* This function build the following tree, from regular expression (): SUBEXP | */ static bin_tree_t * parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, int nest, reg_errcode_t *err) { re_dfa_t *dfa = (re_dfa_t *) preg->buffer; bin_tree_t *tree; size_t cur_nsub; cur_nsub = preg->re_nsub++; fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); /* The subexpression may be a null string. */ if (token->type == OP_CLOSE_SUBEXP) tree = NULL; else { tree = parse_reg_exp (regexp, preg, token, syntax, nest, err); if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0)) *err = REG_EPAREN; if (BE (*err != REG_NOERROR, 0)) return NULL; } if (cur_nsub <= '9' - '1') dfa->completed_bkref_map |= 1 << cur_nsub; tree = create_tree (dfa, tree, NULL, SUBEXP); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } tree->token.opr.idx = cur_nsub; return tree; } /* This function parse repetition operators like "*", "+", "{1,3}" etc. */ static bin_tree_t * parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) { bin_tree_t *tree = NULL, *old_tree = NULL; int i, start, end, start_idx = re_string_cur_idx (regexp); #ifndef RE_TOKEN_INIT_BUG re_token_t start_token = *token; #else re_token_t start_token; memcpy ((void *) &start_token, (void *) token, sizeof start_token); #endif if (token->type == OP_OPEN_DUP_NUM) { end = 0; start = fetch_number (regexp, token, syntax); if (start == -1) { if (token->type == CHARACTER && token->opr.c == ',') start = 0; /* We treat "{,m}" as "{0,m}". */ else { *err = REG_BADBR; /* {} is invalid. */ return NULL; } } if (BE (start != -2, 1)) { /* We treat "{n}" as "{n,n}". */ end = ((token->type == OP_CLOSE_DUP_NUM) ? start : ((token->type == CHARACTER && token->opr.c == ',') ? fetch_number (regexp, token, syntax) : -2)); } if (BE (start == -2 || end == -2, 0)) { /* Invalid sequence. */ if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) { if (token->type == END_OF_RE) *err = REG_EBRACE; else *err = REG_BADBR; return NULL; } /* If the syntax bit is set, rollback. */ re_string_set_index (regexp, start_idx); *token = start_token; token->type = CHARACTER; /* mb_partial and word_char bits should be already initialized by peek_token. */ return elem; } if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0)) { /* First number greater than second. */ *err = REG_BADBR; return NULL; } } else { start = (token->type == OP_DUP_PLUS) ? 1 : 0; end = (token->type == OP_DUP_QUESTION) ? 1 : -1; } fetch_token (token, regexp, syntax); if (BE (elem == NULL, 0)) return NULL; if (BE (start == 0 && end == 0, 0)) { postorder (elem, free_tree, NULL); return NULL; } /* Extract "{n,m}" to "...{0,}". */ if (BE (start > 0, 0)) { tree = elem; for (i = 2; i <= start; ++i) { elem = duplicate_tree (elem, dfa); tree = create_tree (dfa, tree, elem, CONCAT); if (BE (elem == NULL || tree == NULL, 0)) goto parse_dup_op_espace; } if (start == end) return tree; /* Duplicate ELEM before it is marked optional. */ elem = duplicate_tree (elem, dfa); old_tree = tree; } else old_tree = NULL; if (elem->token.type == SUBEXP) postorder (elem, mark_opt_subexp, (void *) (intptr_t) elem->token.opr.idx); tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT)); if (BE (tree == NULL, 0)) goto parse_dup_op_espace; /* This loop is actually executed only when end != -1, to rewrite {0,n} as ((...?)?)?... We have already created the start+1-th copy. */ for (i = start + 2; i <= end; ++i) { elem = duplicate_tree (elem, dfa); tree = create_tree (dfa, tree, elem, CONCAT); if (BE (elem == NULL || tree == NULL, 0)) goto parse_dup_op_espace; tree = create_tree (dfa, tree, NULL, OP_ALT); if (BE (tree == NULL, 0)) goto parse_dup_op_espace; } if (old_tree) tree = create_tree (dfa, old_tree, tree, CONCAT); return tree; parse_dup_op_espace: *err = REG_ESPACE; return NULL; } /* Size of the names for collating symbol/equivalence_class/character_class. I'm not sure, but maybe enough. */ #define BRACKET_NAME_BUF_SIZE 32 #ifndef _LIBC /* Local function for parse_bracket_exp only used in case of NOT _LIBC. Build the range expression which starts from START_ELEM, and ends at END_ELEM. The result are written to MBCSET and SBCSET. RANGE_ALLOC is the allocated size of mbcset->range_starts, and mbcset->range_ends, is a pointer argument sinse we may update it. */ static reg_errcode_t internal_function # ifdef RE_ENABLE_I18N build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc, bracket_elem_t *start_elem, bracket_elem_t *end_elem) # else /* not RE_ENABLE_I18N */ build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem, bracket_elem_t *end_elem) # endif /* not RE_ENABLE_I18N */ { unsigned int start_ch, end_ch; /* Equivalence Classes and Character Classes can't be a range start/end. */ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, 0)) return REG_ERANGE; /* We can handle no multi character collating elements without libc support. */ if (BE ((start_elem->type == COLL_SYM && strlen ((char *) start_elem->opr.name) > 1) || (end_elem->type == COLL_SYM && strlen ((char *) end_elem->opr.name) > 1), 0)) return REG_ECOLLATE; # ifdef RE_ENABLE_I18N { wchar_t wc; wint_t start_wc; wint_t end_wc; wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] : 0)); end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] : 0)); #ifdef GAWK /* * Fedora Core 2, maybe others, have broken `btowc' that returns -1 * for any value > 127. Sigh. Note that `start_ch' and `end_ch' are * unsigned, so we don't have sign extension problems. */ start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) ? start_ch : start_elem->opr.wch); end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) ? end_ch : end_elem->opr.wch); #else start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) ? __btowc (start_ch) : start_elem->opr.wch); end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) ? __btowc (end_ch) : end_elem->opr.wch); #endif if (start_wc == WEOF || end_wc == WEOF) return REG_ECOLLATE; cmp_buf[0] = start_wc; cmp_buf[4] = end_wc; if (wcscoll (cmp_buf, cmp_buf + 4) > 0) return REG_ERANGE; /* Got valid collation sequence values, add them as a new entry. However, for !_LIBC we have no collation elements: if the character set is single byte, the single byte character set that we build below suffices. parse_bracket_exp passes no MBCSET if dfa->mb_cur_max == 1. */ if (mbcset) { /* Check the space of the arrays. */ if (BE (*range_alloc == mbcset->nranges, 0)) { /* There is not enough space, need realloc. */ wchar_t *new_array_start, *new_array_end; int new_nranges; /* +1 in case of mbcset->nranges is 0. */ new_nranges = 2 * mbcset->nranges + 1; /* Use realloc since mbcset->range_starts and mbcset->range_ends are NULL if *range_alloc == 0. */ new_array_start = re_realloc (mbcset->range_starts, wchar_t, new_nranges); new_array_end = re_realloc (mbcset->range_ends, wchar_t, new_nranges); if (BE (new_array_start == NULL || new_array_end == NULL, 0)) return REG_ESPACE; mbcset->range_starts = new_array_start; mbcset->range_ends = new_array_end; *range_alloc = new_nranges; } mbcset->range_starts[mbcset->nranges] = start_wc; mbcset->range_ends[mbcset->nranges++] = end_wc; } /* Build the table for single byte characters. */ for (wc = 0; wc < SBC_MAX; ++wc) { cmp_buf[2] = wc; if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) bitset_set (sbcset, wc); } } # else /* not RE_ENABLE_I18N */ { unsigned int ch; start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] : 0)); end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] : 0)); if (start_ch > end_ch) return REG_ERANGE; /* Build the table for single byte characters. */ for (ch = 0; ch < SBC_MAX; ++ch) if (start_ch <= ch && ch <= end_ch) bitset_set (sbcset, ch); } # endif /* not RE_ENABLE_I18N */ return REG_NOERROR; } #endif /* not _LIBC */ #ifndef _LIBC /* Helper function for parse_bracket_exp only used in case of NOT _LIBC.. Build the collating element which is represented by NAME. The result are written to MBCSET and SBCSET. COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a pointer argument since we may update it. */ static reg_errcode_t internal_function # ifdef RE_ENABLE_I18N build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset, int *coll_sym_alloc, const unsigned char *name) # else /* not RE_ENABLE_I18N */ build_collating_symbol (bitset_t sbcset, const unsigned char *name) # endif /* not RE_ENABLE_I18N */ { size_t name_len = strlen ((const char *) name); if (BE (name_len != 1, 0)) return REG_ECOLLATE; else { bitset_set (sbcset, name[0]); return REG_NOERROR; } } #endif /* not _LIBC */ /* This function parse bracket expression like "[abc]", "[a-c]", "[[.a-a.]]" etc. */ static bin_tree_t * parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) { #ifdef _LIBC const unsigned char *collseqmb; const char *collseqwc; uint32_t nrules; int32_t table_size; const int32_t *symb_table; const unsigned char *extra; /* Local function for parse_bracket_exp used in _LIBC environement. Seek the collating symbol entry correspondings to NAME. Return the index of the symbol in the SYMB_TABLE. */ auto inline int32_t __attribute ((always_inline)) seek_collating_symbol_entry (name, name_len) const unsigned char *name; size_t name_len; { int32_t hash = elem_hash ((const char *) name, name_len); int32_t elem = hash % table_size; if (symb_table[2 * elem] != 0) { int32_t second = hash % (table_size - 2) + 1; do { /* First compare the hashing value. */ if (symb_table[2 * elem] == hash /* Compare the length of the name. */ && name_len == extra[symb_table[2 * elem + 1]] /* Compare the name. */ && memcmp (name, &extra[symb_table[2 * elem + 1] + 1], name_len) == 0) { /* Yep, this is the entry. */ break; } /* Next entry. */ elem += second; } while (symb_table[2 * elem] != 0); } return elem; } /* Local function for parse_bracket_exp used in _LIBC environment. Look up the collation sequence value of BR_ELEM. Return the value if succeeded, UINT_MAX otherwise. */ auto inline unsigned int __attribute ((always_inline)) lookup_collation_sequence_value (br_elem) bracket_elem_t *br_elem; { if (br_elem->type == SB_CHAR) { /* if (MB_CUR_MAX == 1) */ if (nrules == 0) return collseqmb[br_elem->opr.ch]; else { wint_t wc = __btowc (br_elem->opr.ch); return __collseq_table_lookup (collseqwc, wc); } } else if (br_elem->type == MB_CHAR) { if (nrules != 0) return __collseq_table_lookup (collseqwc, br_elem->opr.wch); } else if (br_elem->type == COLL_SYM) { size_t sym_name_len = strlen ((char *) br_elem->opr.name); if (nrules != 0) { int32_t elem, idx; elem = seek_collating_symbol_entry (br_elem->opr.name, sym_name_len); if (symb_table[2 * elem] != 0) { /* We found the entry. */ idx = symb_table[2 * elem + 1]; /* Skip the name of collating element name. */ idx += 1 + extra[idx]; /* Skip the byte sequence of the collating element. */ idx += 1 + extra[idx]; /* Adjust for the alignment. */ idx = (idx + 3) & ~3; /* Skip the multibyte collation sequence value. */ idx += sizeof (unsigned int); /* Skip the wide char sequence of the collating element. */ idx += sizeof (unsigned int) * (1 + *(unsigned int *) (extra + idx)); /* Return the collation sequence value. */ return *(unsigned int *) (extra + idx); } else if (symb_table[2 * elem] == 0 && sym_name_len == 1) { /* No valid character. Match it as a single byte character. */ return collseqmb[br_elem->opr.name[0]]; } } else if (sym_name_len == 1) return collseqmb[br_elem->opr.name[0]]; } return UINT_MAX; } /* Local function for parse_bracket_exp used in _LIBC environement. Build the range expression which starts from START_ELEM, and ends at END_ELEM. The result are written to MBCSET and SBCSET. RANGE_ALLOC is the allocated size of mbcset->range_starts, and mbcset->range_ends, is a pointer argument sinse we may update it. */ auto inline reg_errcode_t __attribute ((always_inline)) build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) re_charset_t *mbcset; int *range_alloc; bitset_t sbcset; bracket_elem_t *start_elem, *end_elem; { unsigned int ch; uint32_t start_collseq; uint32_t end_collseq; /* Equivalence Classes and Character Classes can't be a range start/end. */ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, 0)) return REG_ERANGE; start_collseq = lookup_collation_sequence_value (start_elem); end_collseq = lookup_collation_sequence_value (end_elem); /* Check start/end collation sequence values. */ if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0)) return REG_ECOLLATE; if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0)) return REG_ERANGE; /* Got valid collation sequence values, add them as a new entry. However, if we have no collation elements, and the character set is single byte, the single byte character set that we build below suffices. */ if (nrules > 0 || dfa->mb_cur_max > 1) { /* Check the space of the arrays. */ if (BE (*range_alloc == mbcset->nranges, 0)) { /* There is not enough space, need realloc. */ uint32_t *new_array_start; uint32_t *new_array_end; int new_nranges; /* +1 in case of mbcset->nranges is 0. */ new_nranges = 2 * mbcset->nranges + 1; new_array_start = re_realloc (mbcset->range_starts, uint32_t, new_nranges); new_array_end = re_realloc (mbcset->range_ends, uint32_t, new_nranges); if (BE (new_array_start == NULL || new_array_end == NULL, 0)) return REG_ESPACE; mbcset->range_starts = new_array_start; mbcset->range_ends = new_array_end; *range_alloc = new_nranges; } mbcset->range_starts[mbcset->nranges] = start_collseq; mbcset->range_ends[mbcset->nranges++] = end_collseq; } /* Build the table for single byte characters. */ for (ch = 0; ch < SBC_MAX; ch++) { uint32_t ch_collseq; /* if (MB_CUR_MAX == 1) */ if (nrules == 0) ch_collseq = collseqmb[ch]; else ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch)); if (start_collseq <= ch_collseq && ch_collseq <= end_collseq) bitset_set (sbcset, ch); } return REG_NOERROR; } /* Local function for parse_bracket_exp used in _LIBC environement. Build the collating element which is represented by NAME. The result are written to MBCSET and SBCSET. COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a pointer argument sinse we may update it. */ auto inline reg_errcode_t __attribute ((always_inline)) build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) re_charset_t *mbcset; int *coll_sym_alloc; bitset_t sbcset; const unsigned char *name; { int32_t elem, idx; size_t name_len = strlen ((const char *) name); if (nrules != 0) { elem = seek_collating_symbol_entry (name, name_len); if (symb_table[2 * elem] != 0) { /* We found the entry. */ idx = symb_table[2 * elem + 1]; /* Skip the name of collating element name. */ idx += 1 + extra[idx]; } else if (symb_table[2 * elem] == 0 && name_len == 1) { /* No valid character, treat it as a normal character. */ bitset_set (sbcset, name[0]); return REG_NOERROR; } else return REG_ECOLLATE; /* Got valid collation sequence, add it as a new entry. */ /* Check the space of the arrays. */ if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0)) { /* Not enough, realloc it. */ /* +1 in case of mbcset->ncoll_syms is 0. */ int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; /* Use realloc since mbcset->coll_syms is NULL if *alloc == 0. */ int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t, new_coll_sym_alloc); if (BE (new_coll_syms == NULL, 0)) return REG_ESPACE; mbcset->coll_syms = new_coll_syms; *coll_sym_alloc = new_coll_sym_alloc; } mbcset->coll_syms[mbcset->ncoll_syms++] = idx; return REG_NOERROR; } else { if (BE (name_len != 1, 0)) return REG_ECOLLATE; else { bitset_set (sbcset, name[0]); return REG_NOERROR; } } } #endif re_token_t br_token; re_bitset_ptr_t sbcset; #ifdef RE_ENABLE_I18N re_charset_t *mbcset; int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; int equiv_class_alloc = 0, char_class_alloc = 0; #endif /* not RE_ENABLE_I18N */ int non_match = 0; bin_tree_t *work_tree; int token_len; int first_round = 1; #ifdef _LIBC collseqmb = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); if (nrules) { /* if (MB_CUR_MAX > 1) */ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB); symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_TABLEMB); extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); } #endif sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); #ifdef RE_ENABLE_I18N mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); #endif /* RE_ENABLE_I18N */ #ifdef RE_ENABLE_I18N if (BE (sbcset == NULL || mbcset == NULL, 0)) #else if (BE (sbcset == NULL, 0)) #endif /* RE_ENABLE_I18N */ { *err = REG_ESPACE; return NULL; } token_len = peek_token_bracket (token, regexp, syntax); if (BE (token->type == END_OF_RE, 0)) { *err = REG_BADPAT; goto parse_bracket_exp_free_return; } if (token->type == OP_NON_MATCH_LIST) { #ifdef RE_ENABLE_I18N mbcset->non_match = 1; #endif /* not RE_ENABLE_I18N */ non_match = 1; if (syntax & RE_HAT_LISTS_NOT_NEWLINE) bitset_set (sbcset, '\n'); re_string_skip_bytes (regexp, token_len); /* Skip a token. */ token_len = peek_token_bracket (token, regexp, syntax); if (BE (token->type == END_OF_RE, 0)) { *err = REG_BADPAT; goto parse_bracket_exp_free_return; } } /* We treat the first ']' as a normal character. */ if (token->type == OP_CLOSE_BRACKET) token->type = CHARACTER; while (1) { bracket_elem_t start_elem, end_elem; unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE]; unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE]; reg_errcode_t ret; int token_len2 = 0, is_range_exp = 0; re_token_t token2; start_elem.opr.name = start_name_buf; ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, syntax, first_round); if (BE (ret != REG_NOERROR, 0)) { *err = ret; goto parse_bracket_exp_free_return; } first_round = 0; /* Get information about the next token. We need it in any case. */ token_len = peek_token_bracket (token, regexp, syntax); /* Do not check for ranges if we know they are not allowed. */ if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS) { if (BE (token->type == END_OF_RE, 0)) { *err = REG_EBRACK; goto parse_bracket_exp_free_return; } if (token->type == OP_CHARSET_RANGE) { re_string_skip_bytes (regexp, token_len); /* Skip '-'. */ token_len2 = peek_token_bracket (&token2, regexp, syntax); if (BE (token2.type == END_OF_RE, 0)) { *err = REG_EBRACK; goto parse_bracket_exp_free_return; } if (token2.type == OP_CLOSE_BRACKET) { /* We treat the last '-' as a normal character. */ re_string_skip_bytes (regexp, -token_len); token->type = CHARACTER; } else is_range_exp = 1; } } if (is_range_exp == 1) { end_elem.opr.name = end_name_buf; ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, dfa, syntax, 1); if (BE (ret != REG_NOERROR, 0)) { *err = ret; goto parse_bracket_exp_free_return; } token_len = peek_token_bracket (token, regexp, syntax); #ifdef _LIBC *err = build_range_exp (sbcset, mbcset, &range_alloc, &start_elem, &end_elem); #else # ifdef RE_ENABLE_I18N *err = build_range_exp (sbcset, dfa->mb_cur_max > 1 ? mbcset : NULL, &range_alloc, &start_elem, &end_elem); # else *err = build_range_exp (sbcset, &start_elem, &end_elem); # endif #endif /* RE_ENABLE_I18N */ if (BE (*err != REG_NOERROR, 0)) goto parse_bracket_exp_free_return; } else { switch (start_elem.type) { case SB_CHAR: bitset_set (sbcset, start_elem.opr.ch); break; #ifdef RE_ENABLE_I18N case MB_CHAR: /* Check whether the array has enough space. */ if (BE (mbchar_alloc == mbcset->nmbchars, 0)) { wchar_t *new_mbchars; /* Not enough, realloc it. */ /* +1 in case of mbcset->nmbchars is 0. */ mbchar_alloc = 2 * mbcset->nmbchars + 1; /* Use realloc since array is NULL if *alloc == 0. */ new_mbchars = re_realloc (mbcset->mbchars, wchar_t, mbchar_alloc); if (BE (new_mbchars == NULL, 0)) goto parse_bracket_exp_espace; mbcset->mbchars = new_mbchars; } mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch; break; #endif /* RE_ENABLE_I18N */ case EQUIV_CLASS: *err = build_equiv_class (sbcset, #ifdef RE_ENABLE_I18N mbcset, &equiv_class_alloc, #endif /* RE_ENABLE_I18N */ start_elem.opr.name); if (BE (*err != REG_NOERROR, 0)) goto parse_bracket_exp_free_return; break; case COLL_SYM: *err = build_collating_symbol (sbcset, #ifdef RE_ENABLE_I18N mbcset, &coll_sym_alloc, #endif /* RE_ENABLE_I18N */ start_elem.opr.name); if (BE (*err != REG_NOERROR, 0)) goto parse_bracket_exp_free_return; break; case CHAR_CLASS: *err = build_charclass (regexp->trans, sbcset, #ifdef RE_ENABLE_I18N mbcset, &char_class_alloc, #endif /* RE_ENABLE_I18N */ (const char *) start_elem.opr.name, syntax); if (BE (*err != REG_NOERROR, 0)) goto parse_bracket_exp_free_return; break; default: assert (0); break; } } if (BE (token->type == END_OF_RE, 0)) { *err = REG_EBRACK; goto parse_bracket_exp_free_return; } if (token->type == OP_CLOSE_BRACKET) break; } re_string_skip_bytes (regexp, token_len); /* Skip a token. */ /* If it is non-matching list. */ if (non_match) bitset_not (sbcset); #ifdef RE_ENABLE_I18N /* Ensure only single byte characters are set. */ if (dfa->mb_cur_max > 1) bitset_mask (sbcset, dfa->sb_char); if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes || mbcset->non_match))) { bin_tree_t *mbc_tree; int sbc_idx; /* Build a tree for complex bracket. */ dfa->has_mb_node = 1; br_token.type = COMPLEX_BRACKET; br_token.opr.mbcset = mbcset; mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (mbc_tree == NULL, 0)) goto parse_bracket_exp_espace; for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx) if (sbcset[sbc_idx]) break; /* If there are no bits set in sbcset, there is no point of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */ if (sbc_idx < BITSET_WORDS) { /* Build a tree for simple bracket. */ br_token.type = SIMPLE_BRACKET; br_token.opr.sbcset = sbcset; work_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (work_tree == NULL, 0)) goto parse_bracket_exp_espace; /* Then join them by ALT node. */ work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT); if (BE (work_tree == NULL, 0)) goto parse_bracket_exp_espace; } else { re_free (sbcset); work_tree = mbc_tree; } } else #endif /* not RE_ENABLE_I18N */ { #ifdef RE_ENABLE_I18N free_charset (mbcset); #endif /* Build a tree for simple bracket. */ br_token.type = SIMPLE_BRACKET; br_token.opr.sbcset = sbcset; work_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (work_tree == NULL, 0)) goto parse_bracket_exp_espace; } return work_tree; parse_bracket_exp_espace: *err = REG_ESPACE; parse_bracket_exp_free_return: re_free (sbcset); #ifdef RE_ENABLE_I18N free_charset (mbcset); #endif /* RE_ENABLE_I18N */ return NULL; } /* Parse an element in the bracket expression. */ static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp, re_token_t *token, int token_len, UNUSED re_dfa_t *dfa, reg_syntax_t syntax, int accept_hyphen) { #ifdef RE_ENABLE_I18N int cur_char_size; cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp)); if (cur_char_size > 1) { elem->type = MB_CHAR; elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp)); re_string_skip_bytes (regexp, cur_char_size); return REG_NOERROR; } #endif /* RE_ENABLE_I18N */ re_string_skip_bytes (regexp, token_len); /* Skip a token. */ if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS || token->type == OP_OPEN_EQUIV_CLASS) return parse_bracket_symbol (elem, regexp, token); if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen) { /* A '-' must only appear as anything but a range indicator before the closing bracket. Everything else is an error. */ re_token_t token2; (void) peek_token_bracket (&token2, regexp, syntax); if (token2.type != OP_CLOSE_BRACKET) /* The actual error value is not standardized since this whole case is undefined. But ERANGE makes good sense. */ return REG_ERANGE; } elem->type = SB_CHAR; elem->opr.ch = token->opr.c; return REG_NOERROR; } /* Parse a bracket symbol in the bracket expression. Bracket symbols are such as [::], [..], and [==]. */ static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp, re_token_t *token) { unsigned char ch, delim = token->opr.c; int i = 0; if (re_string_eoi(regexp)) return REG_EBRACK; for (;; ++i) { if (i >= BRACKET_NAME_BUF_SIZE) return REG_EBRACK; if (token->type == OP_OPEN_CHAR_CLASS) ch = re_string_fetch_byte_case (regexp); else ch = re_string_fetch_byte (regexp); if (re_string_eoi(regexp)) return REG_EBRACK; if (ch == delim && re_string_peek_byte (regexp, 0) == ']') break; elem->opr.name[i] = ch; } re_string_skip_bytes (regexp, 1); elem->opr.name[i] = '\0'; switch (token->type) { case OP_OPEN_COLL_ELEM: elem->type = COLL_SYM; break; case OP_OPEN_EQUIV_CLASS: elem->type = EQUIV_CLASS; break; case OP_OPEN_CHAR_CLASS: elem->type = CHAR_CLASS; break; default: break; } return REG_NOERROR; } /* Helper function for parse_bracket_exp. Build the equivalence class which is represented by NAME. The result are written to MBCSET and SBCSET. EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes, is a pointer argument sinse we may update it. */ static reg_errcode_t #ifdef RE_ENABLE_I18N build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, int *equiv_class_alloc, const unsigned char *name) #else /* not RE_ENABLE_I18N */ build_equiv_class (bitset_t sbcset, const unsigned char *name) #endif /* not RE_ENABLE_I18N */ { #ifdef _LIBC uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); if (nrules != 0) { const int32_t *table, *indirect; const unsigned char *weights, *extra, *cp; unsigned char char_buf[2]; int32_t idx1, idx2; unsigned int ch; size_t len; /* This #include defines a local function! */ # include /* Calculate the index for equivalence class. */ cp = name; table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); idx1 = findidx (&cp); if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0)) /* This isn't a valid character. */ return REG_ECOLLATE; /* Build single byte matcing table for this equivalence class. */ char_buf[1] = (unsigned char) '\0'; len = weights[idx1 & 0xffffff]; for (ch = 0; ch < SBC_MAX; ++ch) { char_buf[0] = ch; cp = char_buf; idx2 = findidx (&cp); /* idx2 = table[ch]; */ if (idx2 == 0) /* This isn't a valid character. */ continue; /* Compare only if the length matches and the collation rule index is the same. */ if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24)) { int cnt = 0; while (cnt <= len && weights[(idx1 & 0xffffff) + 1 + cnt] == weights[(idx2 & 0xffffff) + 1 + cnt]) ++cnt; if (cnt > len) bitset_set (sbcset, ch); } } /* Check whether the array has enough space. */ if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0)) { /* Not enough, realloc it. */ /* +1 in case of mbcset->nequiv_classes is 0. */ int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; /* Use realloc since the array is NULL if *alloc == 0. */ int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes, int32_t, new_equiv_class_alloc); if (BE (new_equiv_classes == NULL, 0)) return REG_ESPACE; mbcset->equiv_classes = new_equiv_classes; *equiv_class_alloc = new_equiv_class_alloc; } mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; } else #endif /* _LIBC */ { if (BE (strlen ((const char *) name) != 1, 0)) return REG_ECOLLATE; bitset_set (sbcset, *name); } return REG_NOERROR; } /* Helper function for parse_bracket_exp. Build the character class which is represented by NAME. The result are written to MBCSET and SBCSET. CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes, is a pointer argument sinse we may update it. */ static reg_errcode_t #ifdef RE_ENABLE_I18N build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, re_charset_t *mbcset, int *char_class_alloc, const char *class_name, reg_syntax_t syntax) #else /* not RE_ENABLE_I18N */ build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, const char *class_name, reg_syntax_t syntax) #endif /* not RE_ENABLE_I18N */ { int i; /* In case of REG_ICASE "upper" and "lower" match the both of upper and lower cases. */ if ((syntax & RE_ICASE) && (strcmp (class_name, "upper") == 0 || strcmp (class_name, "lower") == 0)) class_name = "alpha"; #ifdef RE_ENABLE_I18N /* Check the space of the arrays. */ if (BE (*char_class_alloc == mbcset->nchar_classes, 0)) { /* Not enough, realloc it. */ /* +1 in case of mbcset->nchar_classes is 0. */ int new_char_class_alloc = 2 * mbcset->nchar_classes + 1; /* Use realloc since array is NULL if *alloc == 0. */ wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t, new_char_class_alloc); if (BE (new_char_classes == NULL, 0)) return REG_ESPACE; mbcset->char_classes = new_char_classes; *char_class_alloc = new_char_class_alloc; } mbcset->char_classes[mbcset->nchar_classes++] = __wctype (class_name); #endif /* RE_ENABLE_I18N */ #define BUILD_CHARCLASS_LOOP(ctype_func) \ do { \ if (BE (trans != NULL, 0)) \ { \ for (i = 0; i < SBC_MAX; ++i) \ if (ctype_func (i)) \ bitset_set (sbcset, trans[i]); \ } \ else \ { \ for (i = 0; i < SBC_MAX; ++i) \ if (ctype_func (i)) \ bitset_set (sbcset, i); \ } \ } while (0) if (strcmp (class_name, "alnum") == 0) BUILD_CHARCLASS_LOOP (isalnum); else if (strcmp (class_name, "cntrl") == 0) BUILD_CHARCLASS_LOOP (iscntrl); else if (strcmp (class_name, "lower") == 0) BUILD_CHARCLASS_LOOP (islower); else if (strcmp (class_name, "space") == 0) BUILD_CHARCLASS_LOOP (isspace); else if (strcmp (class_name, "alpha") == 0) BUILD_CHARCLASS_LOOP (isalpha); else if (strcmp (class_name, "digit") == 0) BUILD_CHARCLASS_LOOP (isdigit); else if (strcmp (class_name, "print") == 0) BUILD_CHARCLASS_LOOP (isprint); else if (strcmp (class_name, "upper") == 0) BUILD_CHARCLASS_LOOP (isupper); else if (strcmp (class_name, "blank") == 0) #ifndef GAWK BUILD_CHARCLASS_LOOP (isblank); #else /* see comments above */ BUILD_CHARCLASS_LOOP (is_blank); #endif else if (strcmp (class_name, "graph") == 0) BUILD_CHARCLASS_LOOP (isgraph); else if (strcmp (class_name, "punct") == 0) BUILD_CHARCLASS_LOOP (ispunct); else if (strcmp (class_name, "xdigit") == 0) BUILD_CHARCLASS_LOOP (isxdigit); else return REG_ECTYPE; return REG_NOERROR; } static bin_tree_t * build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, const char *class_name, const char *extra, int non_match, reg_errcode_t *err) { re_bitset_ptr_t sbcset; #ifdef RE_ENABLE_I18N re_charset_t *mbcset; int alloc = 0; #endif /* not RE_ENABLE_I18N */ reg_errcode_t ret; re_token_t br_token; bin_tree_t *tree; sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); #ifdef RE_ENABLE_I18N mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); #endif /* RE_ENABLE_I18N */ #ifdef RE_ENABLE_I18N if (BE (sbcset == NULL || mbcset == NULL, 0)) #else /* not RE_ENABLE_I18N */ if (BE (sbcset == NULL, 0)) #endif /* not RE_ENABLE_I18N */ { *err = REG_ESPACE; return NULL; } if (non_match) { #ifdef RE_ENABLE_I18N mbcset->non_match = 1; #endif /* not RE_ENABLE_I18N */ } /* We don't care the syntax in this case. */ ret = build_charclass (trans, sbcset, #ifdef RE_ENABLE_I18N mbcset, &alloc, #endif /* RE_ENABLE_I18N */ class_name, 0); if (BE (ret != REG_NOERROR, 0)) { re_free (sbcset); #ifdef RE_ENABLE_I18N free_charset (mbcset); #endif /* RE_ENABLE_I18N */ *err = ret; return NULL; } /* \w match '_' also. */ for (; *extra; extra++) bitset_set (sbcset, *extra); /* If it is non-matching list. */ if (non_match) bitset_not (sbcset); #ifdef RE_ENABLE_I18N /* Ensure only single byte characters are set. */ if (dfa->mb_cur_max > 1) bitset_mask (sbcset, dfa->sb_char); #endif /* Build a tree for simple bracket. */ br_token.type = SIMPLE_BRACKET; br_token.opr.sbcset = sbcset; tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (tree == NULL, 0)) goto build_word_op_espace; #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { bin_tree_t *mbc_tree; /* Build a tree for complex bracket. */ br_token.type = COMPLEX_BRACKET; br_token.opr.mbcset = mbcset; dfa->has_mb_node = 1; mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (mbc_tree == NULL, 0)) goto build_word_op_espace; /* Then join them by ALT node. */ tree = create_tree (dfa, tree, mbc_tree, OP_ALT); if (BE (mbc_tree != NULL, 1)) return tree; } else { free_charset (mbcset); return tree; } #else /* not RE_ENABLE_I18N */ return tree; #endif /* not RE_ENABLE_I18N */ build_word_op_espace: re_free (sbcset); #ifdef RE_ENABLE_I18N free_charset (mbcset); #endif /* RE_ENABLE_I18N */ *err = REG_ESPACE; return NULL; } /* This is intended for the expressions like "a{1,3}". Fetch a number from `input', and return the number. Return -1, if the number field is empty like "{,1}". Return -2, If an error is occured. */ static int fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax) { int num = -1; unsigned char c; while (1) { fetch_token (token, input, syntax); c = token->opr.c; if (BE (token->type == END_OF_RE, 0)) return -2; if (token->type == OP_CLOSE_DUP_NUM || c == ',') break; num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2) ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0')); num = (num > RE_DUP_MAX) ? -2 : num; } return num; } #ifdef RE_ENABLE_I18N static void free_charset (re_charset_t *cset) { re_free (cset->mbchars); # ifdef _LIBC re_free (cset->coll_syms); re_free (cset->equiv_classes); re_free (cset->range_starts); re_free (cset->range_ends); # endif re_free (cset->char_classes); re_free (cset); } #endif /* RE_ENABLE_I18N */ /* Functions for binary tree operation. */ /* Create a tree node. */ static bin_tree_t * create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, re_token_type_t type) { re_token_t t; t.type = type; return create_token_tree (dfa, left, right, &t); } static bin_tree_t * create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, const re_token_t *token) { bin_tree_t *tree; if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0)) { bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1); if (storage == NULL) return NULL; storage->next = dfa->str_tree_storage; dfa->str_tree_storage = storage; dfa->str_tree_storage_idx = 0; } tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++]; tree->parent = NULL; tree->left = left; tree->right = right; tree->token = *token; tree->token.duplicated = 0; tree->token.opt_subexp = 0; tree->first = NULL; tree->next = NULL; tree->node_idx = -1; if (left != NULL) left->parent = tree; if (right != NULL) right->parent = tree; return tree; } /* Mark the tree SRC as an optional subexpression. To be called from preorder or postorder. */ static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node) { int idx = (intptr_t) extra; if (node->token.type == SUBEXP && node->token.opr.idx == idx) node->token.opt_subexp = 1; return REG_NOERROR; } /* Free the allocated memory inside NODE. */ static void free_token (re_token_t *node) { #ifdef RE_ENABLE_I18N if (node->type == COMPLEX_BRACKET && node->duplicated == 0) free_charset (node->opr.mbcset); else #endif /* RE_ENABLE_I18N */ if (node->type == SIMPLE_BRACKET && node->duplicated == 0) re_free (node->opr.sbcset); } /* Worker function for tree walking. Free the allocated memory inside NODE and its children. */ static reg_errcode_t free_tree (UNUSED void *extra, bin_tree_t *node) { free_token (&node->token); return REG_NOERROR; } /* Duplicate the node SRC, and return new node. This is a preorder visit similar to the one implemented by the generic visitor, but we need more infrastructure to maintain two parallel trees --- so, it's easier to duplicate. */ static bin_tree_t * duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa) { const bin_tree_t *node; bin_tree_t *dup_root; bin_tree_t **p_new = &dup_root, *dup_node = root->parent; for (node = root; ; ) { /* Create a new tree and link it back to the current parent. */ *p_new = create_token_tree (dfa, NULL, NULL, &node->token); if (*p_new == NULL) return NULL; (*p_new)->parent = dup_node; (*p_new)->token.duplicated = 1; dup_node = *p_new; /* Go to the left node, or up and to the right. */ if (node->left) { node = node->left; p_new = &dup_node->left; } else { const bin_tree_t *prev = NULL; while (node->right == prev || node->right == NULL) { prev = node; node = node->parent; dup_node = dup_node->parent; if (!node) return dup_root; } node = node->right; p_new = &dup_node->right; } } } git2r/src/libgit2/deps/regex/regex_internal.h0000644000175000017500000005411413765070734021023 0ustar nileshnilesh/* Extended regular expression matching and search library. Copyright (C) 2002-2005, 2007, 2008, 2010 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Isamu Hasegawa . The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef _REGEX_INTERNAL_H #define _REGEX_INTERNAL_H 1 #include #include #include #include #include #ifndef UNUSED # ifdef __GNUC__ # define UNUSED __attribute__((unused)) # else # define UNUSED # endif #endif #if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC # include #endif #if defined HAVE_LOCALE_H || defined _LIBC # include #endif #if defined HAVE_WCHAR_H || defined _LIBC # include #endif /* HAVE_WCHAR_H || _LIBC */ #if defined HAVE_WCTYPE_H || defined _LIBC # include #endif /* HAVE_WCTYPE_H || _LIBC */ #if defined HAVE_STDBOOL_H || defined _LIBC # include #endif /* HAVE_STDBOOL_H || _LIBC */ #if !defined(ZOS_USS) #if defined HAVE_STDINT_H || defined _LIBC # include #endif /* HAVE_STDINT_H || _LIBC */ #endif /* !ZOS_USS */ #if defined _LIBC # include #else # define __libc_lock_define(CLASS,NAME) # define __libc_lock_init(NAME) do { } while (0) # define __libc_lock_lock(NAME) do { } while (0) # define __libc_lock_unlock(NAME) do { } while (0) #endif #ifndef GAWK /* In case that the system doesn't have isblank(). */ #if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank # define isblank(ch) ((ch) == ' ' || (ch) == '\t') #endif #else /* GAWK */ /* * This is a mess. On glibc systems you have to define * a magic constant to get isblank() out of , since it's * a C99 function. To heck with all that and borrow a page from * dfa.c's book. */ static int is_blank (int c) { return (c == ' ' || c == '\t'); } #endif /* GAWK */ #ifdef _LIBC # ifndef _RE_DEFINE_LOCALE_FUNCTIONS # define _RE_DEFINE_LOCALE_FUNCTIONS 1 # include # include # include # endif #endif /* This is for other GNU distributions with internationalized messages. */ #if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC # include # ifdef _LIBC # undef gettext # define gettext(msgid) \ INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES) # endif #else # define gettext(msgid) (msgid) #endif #ifndef gettext_noop /* This define is so xgettext can find the internationalizable strings. */ # define gettext_noop(String) String #endif /* For loser systems without the definition. */ #ifndef SIZE_MAX # define SIZE_MAX ((size_t) -1) #endif #ifndef NO_MBSUPPORT #include "mbsupport.h" /* gawk */ #endif #ifndef MB_CUR_MAX #define MB_CUR_MAX 1 #endif #if (defined MBS_SUPPORT) || _LIBC # define RE_ENABLE_I18N #endif #if __GNUC__ >= 3 # define BE(expr, val) __builtin_expect (expr, val) #else # define BE(expr, val) (expr) # ifdef inline # undef inline # endif # define inline #endif /* Number of single byte character. */ #define SBC_MAX 256 #define COLL_ELEM_LEN_MAX 8 /* The character which represents newline. */ #define NEWLINE_CHAR '\n' #define WIDE_NEWLINE_CHAR L'\n' /* Rename to standard API for using out of glibc. */ #ifndef _LIBC # ifdef __wctype # undef __wctype # endif # define __wctype wctype # ifdef __iswctype # undef __iswctype # endif # define __iswctype iswctype # define __btowc btowc # define __mbrtowc mbrtowc #undef __mempcpy /* GAWK */ # define __mempcpy mempcpy # define __wcrtomb wcrtomb # define __regfree regfree # define attribute_hidden #endif /* not _LIBC */ #ifdef __GNUC__ # define __attribute(arg) __attribute__ (arg) #else # define __attribute(arg) #endif extern const char __re_error_msgid[] attribute_hidden; extern const size_t __re_error_msgid_idx[] attribute_hidden; /* An integer used to represent a set of bits. It must be unsigned, and must be at least as wide as unsigned int. */ typedef unsigned long int bitset_word_t; /* All bits set in a bitset_word_t. */ #define BITSET_WORD_MAX ULONG_MAX /* Number of bits in a bitset_word_t. Cast to int as most code use it * like that for counting */ #define BITSET_WORD_BITS ((int)(sizeof (bitset_word_t) * CHAR_BIT)) /* Number of bitset_word_t in a bit_set. */ #define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS) typedef bitset_word_t bitset_t[BITSET_WORDS]; typedef bitset_word_t *re_bitset_ptr_t; typedef const bitset_word_t *re_const_bitset_ptr_t; #define bitset_set(set,i) \ (set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS) #define bitset_clear(set,i) \ (set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS)) #define bitset_contain(set,i) \ (set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS)) #define bitset_empty(set) memset (set, '\0', sizeof (bitset_t)) #define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t)) #define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t)) #define PREV_WORD_CONSTRAINT 0x0001 #define PREV_NOTWORD_CONSTRAINT 0x0002 #define NEXT_WORD_CONSTRAINT 0x0004 #define NEXT_NOTWORD_CONSTRAINT 0x0008 #define PREV_NEWLINE_CONSTRAINT 0x0010 #define NEXT_NEWLINE_CONSTRAINT 0x0020 #define PREV_BEGBUF_CONSTRAINT 0x0040 #define NEXT_ENDBUF_CONSTRAINT 0x0080 #define WORD_DELIM_CONSTRAINT 0x0100 #define NOT_WORD_DELIM_CONSTRAINT 0x0200 typedef enum { INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, LINE_FIRST = PREV_NEWLINE_CONSTRAINT, LINE_LAST = NEXT_NEWLINE_CONSTRAINT, BUF_FIRST = PREV_BEGBUF_CONSTRAINT, BUF_LAST = NEXT_ENDBUF_CONSTRAINT, WORD_DELIM = WORD_DELIM_CONSTRAINT, NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT } re_context_type; typedef struct { int alloc; int nelem; int *elems; } re_node_set; typedef enum { NON_TYPE = 0, /* Node type, These are used by token, node, tree. */ CHARACTER = 1, END_OF_RE = 2, SIMPLE_BRACKET = 3, OP_BACK_REF = 4, OP_PERIOD = 5, #ifdef RE_ENABLE_I18N COMPLEX_BRACKET = 6, OP_UTF8_PERIOD = 7, #endif /* RE_ENABLE_I18N */ /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used when the debugger shows values of this enum type. */ #define EPSILON_BIT 8 OP_OPEN_SUBEXP = EPSILON_BIT | 0, OP_CLOSE_SUBEXP = EPSILON_BIT | 1, OP_ALT = EPSILON_BIT | 2, OP_DUP_ASTERISK = EPSILON_BIT | 3, ANCHOR = EPSILON_BIT | 4, /* Tree type, these are used only by tree. */ CONCAT = 16, SUBEXP = 17, /* Token type, these are used only by token. */ OP_DUP_PLUS = 18, OP_DUP_QUESTION, OP_OPEN_BRACKET, OP_CLOSE_BRACKET, OP_CHARSET_RANGE, OP_OPEN_DUP_NUM, OP_CLOSE_DUP_NUM, OP_NON_MATCH_LIST, OP_OPEN_COLL_ELEM, OP_CLOSE_COLL_ELEM, OP_OPEN_EQUIV_CLASS, OP_CLOSE_EQUIV_CLASS, OP_OPEN_CHAR_CLASS, OP_CLOSE_CHAR_CLASS, OP_WORD, OP_NOTWORD, OP_SPACE, OP_NOTSPACE, BACK_SLASH } re_token_type_t; #ifdef RE_ENABLE_I18N typedef struct { /* Multibyte characters. */ wchar_t *mbchars; /* Collating symbols. */ # ifdef _LIBC int32_t *coll_syms; # endif /* Equivalence classes. */ # ifdef _LIBC int32_t *equiv_classes; # endif /* Range expressions. */ # ifdef _LIBC uint32_t *range_starts; uint32_t *range_ends; # else /* not _LIBC */ wchar_t *range_starts; wchar_t *range_ends; # endif /* not _LIBC */ /* Character classes. */ wctype_t *char_classes; /* If this character set is the non-matching list. */ unsigned int non_match : 1; /* # of multibyte characters. */ int nmbchars; /* # of collating symbols. */ int ncoll_syms; /* # of equivalence classes. */ int nequiv_classes; /* # of range expressions. */ int nranges; /* # of character classes. */ int nchar_classes; } re_charset_t; #endif /* RE_ENABLE_I18N */ typedef struct { union { unsigned char c; /* for CHARACTER */ re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */ #ifdef RE_ENABLE_I18N re_charset_t *mbcset; /* for COMPLEX_BRACKET */ #endif /* RE_ENABLE_I18N */ int idx; /* for BACK_REF */ re_context_type ctx_type; /* for ANCHOR */ } opr; #if __GNUC__ >= 2 re_token_type_t type : 8; #else re_token_type_t type; #endif unsigned int constraint : 10; /* context constraint */ unsigned int duplicated : 1; unsigned int opt_subexp : 1; #ifdef RE_ENABLE_I18N unsigned int accept_mb : 1; /* These 2 bits can be moved into the union if needed (e.g. if running out of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */ unsigned int mb_partial : 1; #endif unsigned int word_char : 1; } re_token_t; #define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT) struct re_string_t { /* Indicate the raw buffer which is the original string passed as an argument of regexec(), re_search(), etc.. */ const unsigned char *raw_mbs; /* Store the multibyte string. In case of "case insensitive mode" like REG_ICASE, upper cases of the string are stored, otherwise MBS points the same address that RAW_MBS points. */ unsigned char *mbs; #ifdef RE_ENABLE_I18N /* Store the wide character string which is corresponding to MBS. */ wint_t *wcs; int *offsets; mbstate_t cur_state; #endif /* Index in RAW_MBS. Each character mbs[i] corresponds to raw_mbs[raw_mbs_idx + i]. */ int raw_mbs_idx; /* The length of the valid characters in the buffers. */ int valid_len; /* The corresponding number of bytes in raw_mbs array. */ int valid_raw_len; /* The length of the buffers MBS and WCS. */ int bufs_len; /* The index in MBS, which is updated by re_string_fetch_byte. */ int cur_idx; /* length of RAW_MBS array. */ int raw_len; /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */ int len; /* End of the buffer may be shorter than its length in the cases such as re_match_2, re_search_2. Then, we use STOP for end of the buffer instead of LEN. */ int raw_stop; /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */ int stop; /* The context of mbs[0]. We store the context independently, since the context of mbs[0] may be different from raw_mbs[0], which is the beginning of the input string. */ unsigned int tip_context; /* The translation passed as a part of an argument of re_compile_pattern. */ RE_TRANSLATE_TYPE trans; /* Copy of re_dfa_t's word_char. */ re_const_bitset_ptr_t word_char; /* 1 if REG_ICASE. */ unsigned char icase; unsigned char is_utf8; unsigned char map_notascii; unsigned char mbs_allocated; unsigned char offsets_needed; unsigned char newline_anchor; unsigned char word_ops_used; int mb_cur_max; }; typedef struct re_string_t re_string_t; struct re_dfa_t; typedef struct re_dfa_t re_dfa_t; #ifndef _LIBC # ifdef __i386__ # define internal_function __attribute ((regparm (3), stdcall)) # else # define internal_function # endif #endif #ifndef NOT_IN_libc static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, int new_buf_len) internal_function; # ifdef RE_ENABLE_I18N static void build_wcs_buffer (re_string_t *pstr) internal_function; static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr) internal_function; # endif /* RE_ENABLE_I18N */ static void build_upper_buffer (re_string_t *pstr) internal_function; static void re_string_translate_buffer (re_string_t *pstr) internal_function; static unsigned int re_string_context_at (const re_string_t *input, int idx, int eflags) internal_function __attribute ((pure)); #endif #define re_string_peek_byte(pstr, offset) \ ((pstr)->mbs[(pstr)->cur_idx + offset]) #define re_string_fetch_byte(pstr) \ ((pstr)->mbs[(pstr)->cur_idx++]) #define re_string_first_byte(pstr, idx) \ ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF) #define re_string_is_single_byte_char(pstr, idx) \ ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \ || (pstr)->wcs[(idx) + 1] != WEOF)) #define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx) #define re_string_cur_idx(pstr) ((pstr)->cur_idx) #define re_string_get_buffer(pstr) ((pstr)->mbs) #define re_string_length(pstr) ((pstr)->len) #define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx]) #define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx)) #define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx)) #ifndef _LIBC # if HAVE_ALLOCA # if (_MSC_VER) # include # define __libc_use_alloca(n) 0 # else # include /* The OS usually guarantees only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely allocate anything larger than 4096 bytes. Also care for the possibility of a few compiler-allocated temporary stack slots. */ # define __libc_use_alloca(n) ((n) < 4032) # endif # else /* alloca is implemented with malloc, so just use malloc. */ # define __libc_use_alloca(n) 0 # endif #endif #define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t))) /* SunOS 4.1.x realloc doesn't accept null pointers: pre-Standard C. Sigh. */ #define re_realloc(p,t,n) ((p != NULL) ? (t *) realloc (p,(n)*sizeof(t)) : (t *) calloc(n,sizeof(t))) #define re_free(p) free (p) struct bin_tree_t { struct bin_tree_t *parent; struct bin_tree_t *left; struct bin_tree_t *right; struct bin_tree_t *first; struct bin_tree_t *next; re_token_t token; /* `node_idx' is the index in dfa->nodes, if `type' == 0. Otherwise `type' indicate the type of this node. */ int node_idx; }; typedef struct bin_tree_t bin_tree_t; #define BIN_TREE_STORAGE_SIZE \ ((1024 - sizeof (void *)) / sizeof (bin_tree_t)) struct bin_tree_storage_t { struct bin_tree_storage_t *next; bin_tree_t data[BIN_TREE_STORAGE_SIZE]; }; typedef struct bin_tree_storage_t bin_tree_storage_t; #define CONTEXT_WORD 1 #define CONTEXT_NEWLINE (CONTEXT_WORD << 1) #define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1) #define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1) #define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD) #define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE) #define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF) #define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF) #define IS_ORDINARY_CONTEXT(c) ((c) == 0) #define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_') #define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR) #define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_') #define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR) #define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\ || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context))) #define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \ || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context))) struct re_dfastate_t { unsigned int hash; re_node_set nodes; re_node_set non_eps_nodes; re_node_set inveclosure; re_node_set *entrance_nodes; struct re_dfastate_t **trtable, **word_trtable; unsigned int context : 4; unsigned int halt : 1; /* If this state can accept `multi byte'. Note that we refer to multibyte characters, and multi character collating elements as `multi byte'. */ unsigned int accept_mb : 1; /* If this state has backreference node(s). */ unsigned int has_backref : 1; unsigned int has_constraint : 1; }; typedef struct re_dfastate_t re_dfastate_t; struct re_state_table_entry { int num; int alloc; re_dfastate_t **array; }; /* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ typedef struct { int next_idx; int alloc; re_dfastate_t **array; } state_array_t; /* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ typedef struct { int node; int str_idx; /* The position NODE match at. */ state_array_t path; } re_sub_match_last_t; /* Store information about the node NODE whose type is OP_OPEN_SUBEXP. And information about the node, whose type is OP_CLOSE_SUBEXP, corresponding to NODE is stored in LASTS. */ typedef struct { int str_idx; int node; state_array_t *path; int alasts; /* Allocation size of LASTS. */ int nlasts; /* The number of LASTS. */ re_sub_match_last_t **lasts; } re_sub_match_top_t; struct re_backref_cache_entry { int node; int str_idx; int subexp_from; int subexp_to; char more; char unused; unsigned short int eps_reachable_subexps_map; }; typedef struct { /* The string object corresponding to the input string. */ re_string_t input; #if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) const re_dfa_t *const dfa; #else const re_dfa_t *dfa; #endif /* EFLAGS of the argument of regexec. */ int eflags; /* Where the matching ends. */ int match_last; int last_node; /* The state log used by the matcher. */ re_dfastate_t **state_log; int state_log_top; /* Back reference cache. */ int nbkref_ents; int abkref_ents; struct re_backref_cache_entry *bkref_ents; int max_mb_elem_len; int nsub_tops; int asub_tops; re_sub_match_top_t **sub_tops; } re_match_context_t; typedef struct { re_dfastate_t **sifted_states; re_dfastate_t **limited_states; int last_node; int last_str_idx; re_node_set limits; } re_sift_context_t; struct re_fail_stack_ent_t { int idx; int node; regmatch_t *regs; re_node_set eps_via_nodes; }; struct re_fail_stack_t { int num; int alloc; struct re_fail_stack_ent_t *stack; }; struct re_dfa_t { re_token_t *nodes; size_t nodes_alloc; size_t nodes_len; int *nexts; int *org_indices; re_node_set *edests; re_node_set *eclosures; re_node_set *inveclosures; struct re_state_table_entry *state_table; re_dfastate_t *init_state; re_dfastate_t *init_state_word; re_dfastate_t *init_state_nl; re_dfastate_t *init_state_begbuf; bin_tree_t *str_tree; bin_tree_storage_t *str_tree_storage; re_bitset_ptr_t sb_char; int str_tree_storage_idx; /* number of subexpressions `re_nsub' is in regex_t. */ unsigned int state_hash_mask; int init_node; int nbackref; /* The number of backreference in this dfa. */ /* Bitmap expressing which backreference is used. */ bitset_word_t used_bkref_map; bitset_word_t completed_bkref_map; unsigned int has_plural_match : 1; /* If this dfa has "multibyte node", which is a backreference or a node which can accept multibyte character or multi character collating element. */ unsigned int has_mb_node : 1; unsigned int is_utf8 : 1; unsigned int map_notascii : 1; unsigned int word_ops_used : 1; int mb_cur_max; bitset_t word_char; reg_syntax_t syntax; int *subexp_map; #ifdef DEBUG char* re_str; #endif #if defined _LIBC __libc_lock_define (, lock) #endif }; #define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) #define re_node_set_remove(set,id) \ (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1)) #define re_node_set_empty(p) ((p)->nelem = 0) #define re_node_set_free(set) re_free ((set)->elems) typedef enum { SB_CHAR, MB_CHAR, EQUIV_CLASS, COLL_SYM, CHAR_CLASS } bracket_elem_type; typedef struct { bracket_elem_type type; union { unsigned char ch; unsigned char *name; wchar_t wch; } opr; } bracket_elem_t; /* Inline functions for bitset operation. */ static inline void bitset_not (bitset_t set) { int bitset_i; for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) set[bitset_i] = ~set[bitset_i]; } static inline void bitset_merge (bitset_t dest, const bitset_t src) { int bitset_i; for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) dest[bitset_i] |= src[bitset_i]; } static inline void bitset_mask (bitset_t dest, const bitset_t src) { int bitset_i; for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) dest[bitset_i] &= src[bitset_i]; } #ifdef RE_ENABLE_I18N /* Inline functions for re_string. */ static inline int internal_function __attribute ((pure)) re_string_char_size_at (const re_string_t *pstr, int idx) { int byte_idx; if (pstr->mb_cur_max == 1) return 1; for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx) if (pstr->wcs[idx + byte_idx] != WEOF) break; return byte_idx; } static inline wint_t internal_function __attribute ((pure)) re_string_wchar_at (const re_string_t *pstr, int idx) { if (pstr->mb_cur_max == 1) return (wint_t) pstr->mbs[idx]; return (wint_t) pstr->wcs[idx]; } # ifndef NOT_IN_libc static int internal_function __attribute ((pure)) re_string_elem_size_at (const re_string_t *pstr, int idx) { # ifdef _LIBC const unsigned char *p, *extra; const int32_t *table, *indirect; int32_t tmp; # include uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); if (nrules != 0) { table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); p = pstr->mbs + idx; tmp = findidx (&p); return p - pstr->mbs - idx; } else # endif /* _LIBC */ return 1; } # endif #endif /* RE_ENABLE_I18N */ #endif /* _REGEX_INTERNAL_H */ git2r/src/libgit2/deps/regex/regexec.c0000644000175000017500000037275613765070734017451 0ustar nileshnilesh/* Extended regular expression matching and search library. Copyright (C) 2002-2005, 2007, 2009, 2010 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Isamu Hasegawa . The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, int n) internal_function; static void match_ctx_clean (re_match_context_t *mctx) internal_function; static void match_ctx_free (re_match_context_t *cache) internal_function; static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node, int str_idx, int from, int to) internal_function; static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx) internal_function; static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx) internal_function; static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx) internal_function; static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, re_dfastate_t **limited_sts, int last_node, int last_str_idx) internal_function; static reg_errcode_t re_search_internal (const regex_t *preg, const char *string, int length, int start, int range, int stop, size_t nmatch, regmatch_t pmatch[], int eflags); static int re_search_2_stub (struct re_pattern_buffer *bufp, const char *string1, int length1, const char *string2, int length2, int start, int range, struct re_registers *regs, int stop, int ret_len); static int re_search_stub (struct re_pattern_buffer *bufp, const char *string, int length, int start, int range, int stop, struct re_registers *regs, int ret_len); static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, unsigned int nregs, int regs_allocated); static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx); static int check_matching (re_match_context_t *mctx, int fl_longest_match, int *p_match_first) internal_function; static int check_halt_state_context (const re_match_context_t *mctx, const re_dfastate_t *state, int idx) internal_function; static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch) internal_function; static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node, int nregs, regmatch_t *regs, re_node_set *eps_via_nodes) internal_function; static reg_errcode_t set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, regmatch_t *pmatch, int fl_backtrack) internal_function; static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) internal_function; #ifdef RE_ENABLE_I18N static int sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx, int node_idx, int str_idx, int max_str_idx) internal_function; #endif /* RE_ENABLE_I18N */ static reg_errcode_t sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) internal_function; static reg_errcode_t build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, int str_idx, re_node_set *cur_dest) internal_function; static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx, re_sift_context_t *sctx, int str_idx, re_node_set *dest_nodes) internal_function; static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, const re_node_set *candidates) internal_function; static int check_dst_limits (const re_match_context_t *mctx, re_node_set *limits, int dst_node, int dst_idx, int src_node, int src_idx) internal_function; static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, int subexp_idx, int from_node, int bkref_idx) internal_function; static int check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit, int subexp_idx, int node, int str_idx, int bkref_idx) internal_function; static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes, const re_node_set *candidates, re_node_set *limits, struct re_backref_cache_entry *bkref_ents, int str_idx) internal_function; static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, int str_idx, const re_node_set *candidates) internal_function; static reg_errcode_t merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, re_dfastate_t **src, int num) internal_function; static re_dfastate_t *find_recover_state (reg_errcode_t *err, re_match_context_t *mctx) internal_function; static re_dfastate_t *transit_state (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *state) internal_function; static re_dfastate_t *merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *next_state) internal_function; static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, int str_idx) internal_function; #if 0 static re_dfastate_t *transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *pstate) internal_function; #endif #ifdef RE_ENABLE_I18N static reg_errcode_t transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate) internal_function; #endif /* RE_ENABLE_I18N */ static reg_errcode_t transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes) internal_function; static reg_errcode_t get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx) internal_function; static reg_errcode_t get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, re_sub_match_last_t *sub_last, int bkref_node, int bkref_str) internal_function; static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, int subexp_idx, int type) internal_function; static reg_errcode_t check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node, int top_str, int last_node, int last_str, int type) internal_function; static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx, re_node_set *cur_nodes, re_node_set *next_nodes) internal_function; static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes, int ex_subexp, int type) internal_function; static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, int target, int ex_subexp, int type) internal_function; static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, int cur_str, int subexp_num, int type) internal_function; static int build_trtable (const re_dfa_t *dfa, re_dfastate_t *state) internal_function; #ifdef RE_ENABLE_I18N static int check_node_accept_bytes (const re_dfa_t *dfa, int node_idx, const re_string_t *input, int idx) internal_function; # ifdef _LIBC static unsigned int find_collation_sequence_value (const unsigned char *mbs, size_t name_len) internal_function; # endif /* _LIBC */ #endif /* RE_ENABLE_I18N */ static int group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, re_node_set *states_node, bitset_t *states_ch) internal_function; static int check_node_accept (const re_match_context_t *mctx, const re_token_t *node, int idx) internal_function; static reg_errcode_t extend_buffers (re_match_context_t *mctx) internal_function; /* Entry point for POSIX code. */ /* regexec searches for a given pattern, specified by PREG, in the string STRING. If NMATCH is zero or REG_NOSUB was set in the cflags argument to `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at least NMATCH elements, and we set them to the offsets of the corresponding matched substrings. EFLAGS specifies `execution flags' which affect matching: if REG_NOTBOL is set, then ^ does not match at the beginning of the string; if REG_NOTEOL is set, then $ does not match at the end. We return 0 if we find a match and REG_NOMATCH if not. */ int regexec ( const regex_t *__restrict preg, const char *__restrict string, size_t nmatch, regmatch_t pmatch[], int eflags) { reg_errcode_t err; int start, length; if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND)) return REG_BADPAT; if (eflags & REG_STARTEND) { start = pmatch[0].rm_so; length = pmatch[0].rm_eo; } else { start = 0; length = strlen (string); } __libc_lock_lock (dfa->lock); if (preg->no_sub) err = re_search_internal (preg, string, length, start, length - start, length, 0, NULL, eflags); else err = re_search_internal (preg, string, length, start, length - start, length, nmatch, pmatch, eflags); __libc_lock_unlock (dfa->lock); return err != REG_NOERROR; } #ifdef _LIBC # include versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4); # if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) __typeof__ (__regexec) __compat_regexec; int attribute_compat_text_section __compat_regexec (const regex_t *__restrict preg, const char *__restrict string, size_t nmatch, regmatch_t pmatch[], int eflags) { return regexec (preg, string, nmatch, pmatch, eflags & (REG_NOTBOL | REG_NOTEOL)); } compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0); # endif #endif /* Entry points for GNU code. */ /* re_match, re_search, re_match_2, re_search_2 The former two functions operate on STRING with length LENGTH, while the later two operate on concatenation of STRING1 and STRING2 with lengths LENGTH1 and LENGTH2, respectively. re_match() matches the compiled pattern in BUFP against the string, starting at index START. re_search() first tries matching at index START, then it tries to match starting from index START + 1, and so on. The last start position tried is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same way as re_match().) The parameter STOP of re_{match,search}_2 specifies that no match exceeding the first STOP characters of the concatenation of the strings should be concerned. If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match and all groups is stroed in REGS. (For the "_2" variants, the offsets are computed relative to the concatenation, not relative to the individual strings.) On success, re_match* functions return the length of the match, re_search* return the position of the start of the match. Return value -1 means no match was found and -2 indicates an internal error. */ int re_match (struct re_pattern_buffer *bufp, const char *string, int length, int start, struct re_registers *regs) { return re_search_stub (bufp, string, length, start, 0, length, regs, 1); } #ifdef _LIBC weak_alias (__re_match, re_match) #endif int re_search (struct re_pattern_buffer *bufp, const char *string, int length, int start, int range, struct re_registers *regs) { return re_search_stub (bufp, string, length, start, range, length, regs, 0); } #ifdef _LIBC weak_alias (__re_search, re_search) #endif int re_match_2 (struct re_pattern_buffer *bufp, const char *string1, int length1, const char *string2, int length2, int start, struct re_registers *regs, int stop) { return re_search_2_stub (bufp, string1, length1, string2, length2, start, 0, regs, stop, 1); } #ifdef _LIBC weak_alias (__re_match_2, re_match_2) #endif int re_search_2 (struct re_pattern_buffer *bufp, const char *string1, int length1, const char *string2, int length2, int start, int range, struct re_registers *regs, int stop) { return re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs, stop, 0); } #ifdef _LIBC weak_alias (__re_search_2, re_search_2) #endif static int re_search_2_stub (struct re_pattern_buffer *bufp, const char *string1, int length1, const char *string2, int length2, int start, int range, struct re_registers *regs, int stop, int ret_len) { const char *str; int rval; int len = length1 + length2; int free_str = 0; if (BE (length1 < 0 || length2 < 0 || stop < 0, 0)) return -2; /* Concatenate the strings. */ if (length2 > 0) if (length1 > 0) { char *s = re_malloc (char, len); if (BE (s == NULL, 0)) return -2; memcpy (s, string1, length1); memcpy (s + length1, string2, length2); str = s; free_str = 1; } else str = string2; else str = string1; rval = re_search_stub (bufp, str, len, start, range, stop, regs, ret_len); if (free_str) re_free ((char *) str); return rval; } /* The parameters have the same meaning as those of re_search. Additional parameters: If RET_LEN is nonzero the length of the match is returned (re_match style); otherwise the position of the match is returned. */ static int re_search_stub (struct re_pattern_buffer *bufp, const char *string, int length, int start, int range, int stop, struct re_registers *regs, int ret_len) { reg_errcode_t result; regmatch_t *pmatch; int nregs, rval; int eflags = 0; /* Check for out-of-range. */ if (BE (start < 0 || start > length, 0)) return -1; if (BE (start + range > length, 0)) range = length - start; else if (BE (start + range < 0, 0)) range = -start; __libc_lock_lock (dfa->lock); eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; /* Compile fastmap if we haven't yet. */ if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate) re_compile_fastmap (bufp); if (BE (bufp->no_sub, 0)) regs = NULL; /* We need at least 1 register. */ if (regs == NULL) nregs = 1; else if (BE (bufp->regs_allocated == REGS_FIXED && regs->num_regs < bufp->re_nsub + 1, 0)) { nregs = regs->num_regs; if (BE (nregs < 1, 0)) { /* Nothing can be copied to regs. */ regs = NULL; nregs = 1; } } else nregs = bufp->re_nsub + 1; pmatch = re_malloc (regmatch_t, nregs); if (BE (pmatch == NULL, 0)) { rval = -2; goto out; } result = re_search_internal (bufp, string, length, start, range, stop, nregs, pmatch, eflags); rval = 0; /* I hope we needn't fill ther regs with -1's when no match was found. */ if (result != REG_NOERROR) rval = -1; else if (regs != NULL) { /* If caller wants register contents data back, copy them. */ bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, bufp->regs_allocated); if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0)) rval = -2; } if (BE (rval == 0, 1)) { if (ret_len) { assert (pmatch[0].rm_so == start); rval = pmatch[0].rm_eo - start; } else rval = pmatch[0].rm_so; } re_free (pmatch); out: __libc_lock_unlock (dfa->lock); return rval; } static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, unsigned int nregs, int regs_allocated) { int rval = REGS_REALLOCATE; unsigned int i; unsigned int need_regs = nregs + 1; /* We need one extra element beyond `num_regs' for the `-1' marker GNU code uses. */ /* Have the register data arrays been allocated? */ if (regs_allocated == REGS_UNALLOCATED) { /* No. So allocate them with malloc. */ regs->start = re_malloc (regoff_t, need_regs); if (BE (regs->start == NULL, 0)) return REGS_UNALLOCATED; regs->end = re_malloc (regoff_t, need_regs); if (BE (regs->end == NULL, 0)) { re_free (regs->start); return REGS_UNALLOCATED; } regs->num_regs = need_regs; } else if (regs_allocated == REGS_REALLOCATE) { /* Yes. If we need more elements than were already allocated, reallocate them. If we need fewer, just leave it alone. */ if (BE (need_regs > regs->num_regs, 0)) { regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs); regoff_t *new_end; if (BE (new_start == NULL, 0)) return REGS_UNALLOCATED; new_end = re_realloc (regs->end, regoff_t, need_regs); if (BE (new_end == NULL, 0)) { re_free (new_start); return REGS_UNALLOCATED; } regs->start = new_start; regs->end = new_end; regs->num_regs = need_regs; } } else { assert (regs_allocated == REGS_FIXED); /* This function may not be called with REGS_FIXED and nregs too big. */ assert (regs->num_regs >= nregs); rval = REGS_FIXED; } /* Copy the regs. */ for (i = 0; i < nregs; ++i) { regs->start[i] = pmatch[i].rm_so; regs->end[i] = pmatch[i].rm_eo; } for ( ; i < regs->num_regs; ++i) regs->start[i] = regs->end[i] = -1; return rval; } /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use this memory for recording register information. STARTS and ENDS must be allocated using the malloc library routine, and must each be at least NUM_REGS * sizeof (regoff_t) bytes long. If NUM_REGS == 0, then subsequent matches should allocate their own register data. Unless this function is called, the first search or match using PATTERN_BUFFER will allocate its own register data, without freeing the old data. */ void re_set_registers (struct re_pattern_buffer *bufp, struct re_registers *regs, unsigned num_regs, regoff_t *starts, regoff_t *ends) { if (num_regs) { bufp->regs_allocated = REGS_REALLOCATE; regs->num_regs = num_regs; regs->start = starts; regs->end = ends; } else { bufp->regs_allocated = REGS_UNALLOCATED; regs->num_regs = 0; regs->start = regs->end = (regoff_t *) 0; } } #ifdef _LIBC weak_alias (__re_set_registers, re_set_registers) #endif /* Entry points compatible with 4.2 BSD regex library. We don't define them unless specifically requested. */ #if defined _REGEX_RE_COMP || defined _LIBC int # ifdef _LIBC weak_function # endif re_exec (s) const char *s; { return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); } #endif /* _REGEX_RE_COMP */ /* Internal entry point. */ /* Searches for a compiled pattern PREG in the string STRING, whose length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same mingings with regexec. START, and RANGE have the same meanings with re_search. Return REG_NOERROR if we find a match, and REG_NOMATCH if not, otherwise return the error code. Note: We assume front end functions already check ranges. (START + RANGE >= 0 && START + RANGE <= LENGTH) */ static reg_errcode_t re_search_internal (const regex_t *preg, const char *string, int length, int start, int range, int stop, size_t nmatch, regmatch_t pmatch[], int eflags) { reg_errcode_t err; const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; int left_lim, right_lim, incr; int fl_longest_match, match_first, match_kind, match_last = -1; unsigned int extra_nmatch; int sb, ch; #if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) re_match_context_t mctx = { .dfa = dfa }; #else re_match_context_t mctx; #endif char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate && range && !preg->can_be_null) ? preg->fastmap : NULL; RE_TRANSLATE_TYPE t = preg->translate; #if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)) memset (&mctx, '\0', sizeof (re_match_context_t)); mctx.dfa = dfa; #endif extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0; nmatch -= extra_nmatch; /* Check if the DFA haven't been compiled. */ if (BE (preg->used == 0 || dfa->init_state == NULL || dfa->init_state_word == NULL || dfa->init_state_nl == NULL || dfa->init_state_begbuf == NULL, 0)) return REG_NOMATCH; #ifdef DEBUG /* We assume front-end functions already check them. */ assert (start + range >= 0 && start + range <= length); #endif /* If initial states with non-begbuf contexts have no elements, the regex must be anchored. If preg->newline_anchor is set, we'll never use init_state_nl, so do not check it. */ if (dfa->init_state->nodes.nelem == 0 && dfa->init_state_word->nodes.nelem == 0 && (dfa->init_state_nl->nodes.nelem == 0 || !preg->newline_anchor)) { if (start != 0 && start + range != 0) return REG_NOMATCH; start = range = 0; } /* We must check the longest matching, if nmatch > 0. */ fl_longest_match = (nmatch != 0 || dfa->nbackref); err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1, preg->translate, preg->syntax & RE_ICASE, dfa); if (BE (err != REG_NOERROR, 0)) goto free_return; mctx.input.stop = stop; mctx.input.raw_stop = stop; mctx.input.newline_anchor = preg->newline_anchor; err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2); if (BE (err != REG_NOERROR, 0)) goto free_return; /* We will log all the DFA states through which the dfa pass, if nmatch > 1, or this dfa has "multibyte node", which is a back-reference or a node which can accept multibyte character or multi character collating element. */ if (nmatch > 1 || dfa->has_mb_node) { /* Avoid overflow. */ if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= (size_t)mctx.input.bufs_len, 0)) { err = REG_ESPACE; goto free_return; } mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1); if (BE (mctx.state_log == NULL, 0)) { err = REG_ESPACE; goto free_return; } } else mctx.state_log = NULL; match_first = start; mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF : CONTEXT_NEWLINE | CONTEXT_BEGBUF; /* Check incrementally whether of not the input string match. */ incr = (range < 0) ? -1 : 1; left_lim = (range < 0) ? start + range : start; right_lim = (range < 0) ? start : start + range; sb = dfa->mb_cur_max == 1; match_kind = (fastmap ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0) | (range >= 0 ? 2 : 0) | (t != NULL ? 1 : 0)) : 8); for (;; match_first += incr) { err = REG_NOMATCH; if (match_first < left_lim || right_lim < match_first) goto free_return; /* Advance as rapidly as possible through the string, until we find a plausible place to start matching. This may be done with varying efficiency, so there are various possibilities: only the most common of them are specialized, in order to save on code size. We use a switch statement for speed. */ switch (match_kind) { case 8: /* No fastmap. */ break; case 7: /* Fastmap with single-byte translation, match forward. */ while (BE (match_first < right_lim, 1) && !fastmap[t[(unsigned char) string[match_first]]]) ++match_first; goto forward_match_found_start_or_reached_end; case 6: /* Fastmap without translation, match forward. */ while (BE (match_first < right_lim, 1) && !fastmap[(unsigned char) string[match_first]]) ++match_first; forward_match_found_start_or_reached_end: if (BE (match_first == right_lim, 0)) { ch = match_first >= length ? 0 : (unsigned char) string[match_first]; if (!fastmap[t ? t[ch] : ch]) goto free_return; } break; case 4: case 5: /* Fastmap without multi-byte translation, match backwards. */ while (match_first >= left_lim) { ch = match_first >= length ? 0 : (unsigned char) string[match_first]; if (fastmap[t ? t[ch] : ch]) break; --match_first; } if (match_first < left_lim) goto free_return; break; default: /* In this case, we can't determine easily the current byte, since it might be a component byte of a multibyte character. Then we use the constructed buffer instead. */ for (;;) { /* If MATCH_FIRST is out of the valid range, reconstruct the buffers. */ unsigned int offset = match_first - mctx.input.raw_mbs_idx; if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0)) { err = re_string_reconstruct (&mctx.input, match_first, eflags); if (BE (err != REG_NOERROR, 0)) goto free_return; offset = match_first - mctx.input.raw_mbs_idx; } /* If MATCH_FIRST is out of the buffer, leave it as '\0'. Note that MATCH_FIRST must not be smaller than 0. */ ch = (match_first >= length ? 0 : re_string_byte_at (&mctx.input, offset)); if (fastmap[ch]) break; match_first += incr; if (match_first < left_lim || match_first > right_lim) { err = REG_NOMATCH; goto free_return; } } break; } /* Reconstruct the buffers so that the matcher can assume that the matching starts from the beginning of the buffer. */ err = re_string_reconstruct (&mctx.input, match_first, eflags); if (BE (err != REG_NOERROR, 0)) goto free_return; #ifdef RE_ENABLE_I18N /* Don't consider this char as a possible match start if it part, yet isn't the head, of a multibyte character. */ if (!sb && !re_string_first_byte (&mctx.input, 0)) continue; #endif /* It seems to be appropriate one, then use the matcher. */ /* We assume that the matching starts from 0. */ mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0; match_last = check_matching (&mctx, fl_longest_match, range >= 0 ? &match_first : NULL); if (match_last != -1) { if (BE (match_last == -2, 0)) { err = REG_ESPACE; goto free_return; } else { mctx.match_last = match_last; if ((!preg->no_sub && nmatch > 1) || dfa->nbackref) { re_dfastate_t *pstate = mctx.state_log[match_last]; mctx.last_node = check_halt_state_context (&mctx, pstate, match_last); } if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) || dfa->nbackref) { err = prune_impossible_nodes (&mctx); if (err == REG_NOERROR) break; if (BE (err != REG_NOMATCH, 0)) goto free_return; match_last = -1; } else break; /* We found a match. */ } } match_ctx_clean (&mctx); } #ifdef DEBUG assert (match_last != -1); assert (err == REG_NOERROR); #endif /* Set pmatch[] if we need. */ if (nmatch > 0) { unsigned int reg_idx; /* Initialize registers. */ for (reg_idx = 1; reg_idx < nmatch; ++reg_idx) pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1; /* Set the points where matching start/end. */ pmatch[0].rm_so = 0; pmatch[0].rm_eo = mctx.match_last; if (!preg->no_sub && nmatch > 1) { err = set_regs (preg, &mctx, nmatch, pmatch, dfa->has_plural_match && dfa->nbackref > 0); if (BE (err != REG_NOERROR, 0)) goto free_return; } /* At last, add the offset to the each registers, since we slided the buffers so that we could assume that the matching starts from 0. */ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) if (pmatch[reg_idx].rm_so != -1) { #ifdef RE_ENABLE_I18N if (BE (mctx.input.offsets_needed != 0, 0)) { pmatch[reg_idx].rm_so = (pmatch[reg_idx].rm_so == mctx.input.valid_len ? mctx.input.valid_raw_len : mctx.input.offsets[pmatch[reg_idx].rm_so]); pmatch[reg_idx].rm_eo = (pmatch[reg_idx].rm_eo == mctx.input.valid_len ? mctx.input.valid_raw_len : mctx.input.offsets[pmatch[reg_idx].rm_eo]); } #else assert (mctx.input.offsets_needed == 0); #endif pmatch[reg_idx].rm_so += match_first; pmatch[reg_idx].rm_eo += match_first; } for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx) { pmatch[nmatch + reg_idx].rm_so = -1; pmatch[nmatch + reg_idx].rm_eo = -1; } if (dfa->subexp_map) for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++) if (dfa->subexp_map[reg_idx] != (int)reg_idx) { pmatch[reg_idx + 1].rm_so = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so; pmatch[reg_idx + 1].rm_eo = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo; } } free_return: re_free (mctx.state_log); if (dfa->nbackref) match_ctx_free (&mctx); re_string_destruct (&mctx.input); return err; } static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx) { const re_dfa_t *const dfa = mctx->dfa; int halt_node, match_last; reg_errcode_t ret; re_dfastate_t **sifted_states; re_dfastate_t **lim_states = NULL; re_sift_context_t sctx; #ifdef DEBUG assert (mctx->state_log != NULL); #endif match_last = mctx->match_last; halt_node = mctx->last_node; /* Avoid overflow. */ if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= (size_t)match_last, 0)) return REG_ESPACE; sifted_states = re_malloc (re_dfastate_t *, match_last + 1); if (BE (sifted_states == NULL, 0)) { ret = REG_ESPACE; goto free_return; } if (dfa->nbackref) { lim_states = re_malloc (re_dfastate_t *, match_last + 1); if (BE (lim_states == NULL, 0)) { ret = REG_ESPACE; goto free_return; } while (1) { memset (lim_states, '\0', sizeof (re_dfastate_t *) * (match_last + 1)); sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); ret = sift_states_backward (mctx, &sctx); re_node_set_free (&sctx.limits); if (BE (ret != REG_NOERROR, 0)) goto free_return; if (sifted_states[0] != NULL || lim_states[0] != NULL) break; do { --match_last; if (match_last < 0) { ret = REG_NOMATCH; goto free_return; } } while (mctx->state_log[match_last] == NULL || !mctx->state_log[match_last]->halt); halt_node = check_halt_state_context (mctx, mctx->state_log[match_last], match_last); } ret = merge_state_array (dfa, sifted_states, lim_states, match_last + 1); re_free (lim_states); lim_states = NULL; if (BE (ret != REG_NOERROR, 0)) goto free_return; } else { sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); ret = sift_states_backward (mctx, &sctx); re_node_set_free (&sctx.limits); if (BE (ret != REG_NOERROR, 0)) goto free_return; if (sifted_states[0] == NULL) { ret = REG_NOMATCH; goto free_return; } } re_free (mctx->state_log); mctx->state_log = sifted_states; sifted_states = NULL; mctx->last_node = halt_node; mctx->match_last = match_last; ret = REG_NOERROR; free_return: re_free (sifted_states); re_free (lim_states); return ret; } /* Acquire an initial state and return it. We must select appropriate initial state depending on the context, since initial states may have constraints like "\<", "^", etc.. */ static inline re_dfastate_t * __attribute ((always_inline)) internal_function acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx, int idx) { const re_dfa_t *const dfa = mctx->dfa; if (dfa->init_state->has_constraint) { unsigned int context; context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags); if (IS_WORD_CONTEXT (context)) return dfa->init_state_word; else if (IS_ORDINARY_CONTEXT (context)) return dfa->init_state; else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context)) return dfa->init_state_begbuf; else if (IS_NEWLINE_CONTEXT (context)) return dfa->init_state_nl; else if (IS_BEGBUF_CONTEXT (context)) { /* It is relatively rare case, then calculate on demand. */ return re_acquire_state_context (err, dfa, dfa->init_state->entrance_nodes, context); } else /* Must not happen? */ return dfa->init_state; } else return dfa->init_state; } /* Check whether the regular expression match input string INPUT or not, and return the index where the matching end, return -1 if not match, or return -2 in case of an error. FL_LONGEST_MATCH means we want the POSIX longest matching. If P_MATCH_FIRST is not NULL, and the match fails, it is set to the next place where we may want to try matching. Note that the matcher assume that the maching starts from the current index of the buffer. */ static int internal_function check_matching (re_match_context_t *mctx, int fl_longest_match, int *p_match_first) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; int match = 0; int match_last = -1; int cur_str_idx = re_string_cur_idx (&mctx->input); re_dfastate_t *cur_state; int at_init_state = p_match_first != NULL; int next_start_idx = cur_str_idx; err = REG_NOERROR; cur_state = acquire_init_state_context (&err, mctx, cur_str_idx); /* An initial state must not be NULL (invalid). */ if (BE (cur_state == NULL, 0)) { assert (err == REG_ESPACE); return -2; } if (mctx->state_log != NULL) { mctx->state_log[cur_str_idx] = cur_state; /* Check OP_OPEN_SUBEXP in the initial state in case that we use them later. E.g. Processing back references. */ if (BE (dfa->nbackref, 0)) { at_init_state = 0; err = check_subexp_matching_top (mctx, &cur_state->nodes, 0); if (BE (err != REG_NOERROR, 0)) return err; if (cur_state->has_backref) { err = transit_state_bkref (mctx, &cur_state->nodes); if (BE (err != REG_NOERROR, 0)) return err; } } } /* If the RE accepts NULL string. */ if (BE (cur_state->halt, 0)) { if (!cur_state->has_constraint || check_halt_state_context (mctx, cur_state, cur_str_idx)) { if (!fl_longest_match) return cur_str_idx; else { match_last = cur_str_idx; match = 1; } } } while (!re_string_eoi (&mctx->input)) { re_dfastate_t *old_state = cur_state; int next_char_idx = re_string_cur_idx (&mctx->input) + 1; if (BE (next_char_idx >= mctx->input.bufs_len, 0) || (BE (next_char_idx >= mctx->input.valid_len, 0) && mctx->input.valid_len < mctx->input.len)) { err = extend_buffers (mctx); if (BE (err != REG_NOERROR, 0)) { assert (err == REG_ESPACE); return -2; } } cur_state = transit_state (&err, mctx, cur_state); if (mctx->state_log != NULL) cur_state = merge_state_with_log (&err, mctx, cur_state); if (cur_state == NULL) { /* Reached the invalid state or an error. Try to recover a valid state using the state log, if available and if we have not already found a valid (even if not the longest) match. */ if (BE (err != REG_NOERROR, 0)) return -2; if (mctx->state_log == NULL || (match && !fl_longest_match) || (cur_state = find_recover_state (&err, mctx)) == NULL) break; } if (BE (at_init_state, 0)) { if (old_state == cur_state) next_start_idx = next_char_idx; else at_init_state = 0; } if (cur_state->halt) { /* Reached a halt state. Check the halt state can satisfy the current context. */ if (!cur_state->has_constraint || check_halt_state_context (mctx, cur_state, re_string_cur_idx (&mctx->input))) { /* We found an appropriate halt state. */ match_last = re_string_cur_idx (&mctx->input); match = 1; /* We found a match, do not modify match_first below. */ p_match_first = NULL; if (!fl_longest_match) break; } } } if (p_match_first) *p_match_first += next_start_idx; return match_last; } /* Check NODE match the current context. */ static int internal_function check_halt_node_context (const re_dfa_t *dfa, int node, unsigned int context) { re_token_type_t type = dfa->nodes[node].type; unsigned int constraint = dfa->nodes[node].constraint; if (type != END_OF_RE) return 0; if (!constraint) return 1; if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) return 0; return 1; } /* Check the halt state STATE match the current context. Return 0 if not match, if the node, STATE has, is a halt node and match the context, return the node. */ static int internal_function check_halt_state_context (const re_match_context_t *mctx, const re_dfastate_t *state, int idx) { int i; unsigned int context; #ifdef DEBUG assert (state->halt); #endif context = re_string_context_at (&mctx->input, idx, mctx->eflags); for (i = 0; i < state->nodes.nelem; ++i) if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context)) return state->nodes.elems[i]; return 0; } /* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA corresponding to the DFA). Return the destination node, and update EPS_VIA_NODES, return -1 in case of errors. */ static int internal_function proceed_next_node (const re_match_context_t *mctx, int nregs, regmatch_t *regs, int *pidx, int node, re_node_set *eps_via_nodes, struct re_fail_stack_t *fs) { const re_dfa_t *const dfa = mctx->dfa; int i, err; if (IS_EPSILON_NODE (dfa->nodes[node].type)) { re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; re_node_set *edests = &dfa->edests[node]; int dest_node; err = re_node_set_insert (eps_via_nodes, node); if (BE (err < 0, 0)) return -2; /* Pick up a valid destination, or return -1 if none is found. */ for (dest_node = -1, i = 0; i < edests->nelem; ++i) { int candidate = edests->elems[i]; if (!re_node_set_contains (cur_nodes, candidate)) continue; if (dest_node == -1) dest_node = candidate; else { /* In order to avoid infinite loop like "(a*)*", return the second epsilon-transition if the first was already considered. */ if (re_node_set_contains (eps_via_nodes, dest_node)) return candidate; /* Otherwise, push the second epsilon-transition on the fail stack. */ else if (fs != NULL && push_fail_stack (fs, *pidx, candidate, nregs, regs, eps_via_nodes)) return -2; /* We know we are going to exit. */ break; } } return dest_node; } else { int naccepted = 0; re_token_type_t type = dfa->nodes[node].type; #ifdef RE_ENABLE_I18N if (dfa->nodes[node].accept_mb) naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx); else #endif /* RE_ENABLE_I18N */ if (type == OP_BACK_REF) { int subexp_idx = dfa->nodes[node].opr.idx + 1; naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so; if (fs != NULL) { if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1) return -1; else if (naccepted) { char *buf = (char *) re_string_get_buffer (&mctx->input); if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, naccepted) != 0) return -1; } } if (naccepted == 0) { int dest_node; err = re_node_set_insert (eps_via_nodes, node); if (BE (err < 0, 0)) return -2; dest_node = dfa->edests[node].elems[0]; if (re_node_set_contains (&mctx->state_log[*pidx]->nodes, dest_node)) return dest_node; } } if (naccepted != 0 || check_node_accept (mctx, dfa->nodes + node, *pidx)) { int dest_node = dfa->nexts[node]; *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted; if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL || !re_node_set_contains (&mctx->state_log[*pidx]->nodes, dest_node))) return -1; re_node_set_empty (eps_via_nodes); return dest_node; } } return -1; } static reg_errcode_t internal_function push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node, int nregs, regmatch_t *regs, re_node_set *eps_via_nodes) { reg_errcode_t err; int num = fs->num++; if (fs->num == fs->alloc) { struct re_fail_stack_ent_t *new_array; new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t) * fs->alloc * 2)); if (new_array == NULL) return REG_ESPACE; fs->alloc *= 2; fs->stack = new_array; } fs->stack[num].idx = str_idx; fs->stack[num].node = dest_node; fs->stack[num].regs = re_malloc (regmatch_t, nregs); if (fs->stack[num].regs == NULL) return REG_ESPACE; memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs); err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes); return err; } static int internal_function pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs, regmatch_t *regs, re_node_set *eps_via_nodes) { int num = --fs->num; assert (num >= 0); *pidx = fs->stack[num].idx; memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs); re_node_set_free (eps_via_nodes); re_free (fs->stack[num].regs); *eps_via_nodes = fs->stack[num].eps_via_nodes; return fs->stack[num].node; } /* Set the positions where the subexpressions are starts/ends to registers PMATCH. Note: We assume that pmatch[0] is already set, and pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */ static reg_errcode_t internal_function set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, regmatch_t *pmatch, int fl_backtrack) { const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; int idx, cur_node; re_node_set eps_via_nodes; struct re_fail_stack_t *fs; struct re_fail_stack_t fs_body = { 0, 2, NULL }; regmatch_t *prev_idx_match; int prev_idx_match_malloced = 0; #ifdef DEBUG assert (nmatch > 1); assert (mctx->state_log != NULL); #endif if (fl_backtrack) { fs = &fs_body; fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc); if (fs->stack == NULL) return REG_ESPACE; } else fs = NULL; cur_node = dfa->init_node; re_node_set_init_empty (&eps_via_nodes); #ifdef HAVE_ALLOCA if (__libc_use_alloca (nmatch * sizeof (regmatch_t))) prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t)); else #endif { prev_idx_match = re_malloc (regmatch_t, nmatch); if (prev_idx_match == NULL) { free_fail_stack_return (fs); return REG_ESPACE; } prev_idx_match_malloced = 1; } memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) { update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) { unsigned int reg_idx; if (fs) { for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1) break; if (reg_idx == nmatch) { re_node_set_free (&eps_via_nodes); if (prev_idx_match_malloced) re_free (prev_idx_match); return free_fail_stack_return (fs); } cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, &eps_via_nodes); } else { re_node_set_free (&eps_via_nodes); if (prev_idx_match_malloced) re_free (prev_idx_match); return REG_NOERROR; } } /* Proceed to next node. */ cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node, &eps_via_nodes, fs); if (BE (cur_node < 0, 0)) { if (BE (cur_node == -2, 0)) { re_node_set_free (&eps_via_nodes); if (prev_idx_match_malloced) re_free (prev_idx_match); free_fail_stack_return (fs); return REG_ESPACE; } if (fs) cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, &eps_via_nodes); else { re_node_set_free (&eps_via_nodes); if (prev_idx_match_malloced) re_free (prev_idx_match); return REG_NOMATCH; } } } re_node_set_free (&eps_via_nodes); if (prev_idx_match_malloced) re_free (prev_idx_match); return free_fail_stack_return (fs); } static reg_errcode_t internal_function free_fail_stack_return (struct re_fail_stack_t *fs) { if (fs) { int fs_idx; for (fs_idx = 0; fs_idx < fs->num; ++fs_idx) { re_node_set_free (&fs->stack[fs_idx].eps_via_nodes); re_free (fs->stack[fs_idx].regs); } re_free (fs->stack); } return REG_NOERROR; } static void internal_function update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch) { int type = dfa->nodes[cur_node].type; if (type == OP_OPEN_SUBEXP) { int reg_num = dfa->nodes[cur_node].opr.idx + 1; /* We are at the first node of this sub expression. */ if (reg_num < nmatch) { pmatch[reg_num].rm_so = cur_idx; pmatch[reg_num].rm_eo = -1; } } else if (type == OP_CLOSE_SUBEXP) { int reg_num = dfa->nodes[cur_node].opr.idx + 1; if (reg_num < nmatch) { /* We are at the last node of this sub expression. */ if (pmatch[reg_num].rm_so < cur_idx) { pmatch[reg_num].rm_eo = cur_idx; /* This is a non-empty match or we are not inside an optional subexpression. Accept this right away. */ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); } else { if (dfa->nodes[cur_node].opt_subexp && prev_idx_match[reg_num].rm_so != -1) /* We transited through an empty match for an optional subexpression, like (a?)*, and this is not the subexp's first match. Copy back the old content of the registers so that matches of an inner subexpression are undone as well, like in ((a?))*. */ memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch); else /* We completed a subexpression, but it may be part of an optional one, so do not update PREV_IDX_MATCH. */ pmatch[reg_num].rm_eo = cur_idx; } } } } /* This function checks the STATE_LOG from the SCTX->last_str_idx to 0 and sift the nodes in each states according to the following rules. Updated state_log will be wrote to STATE_LOG. Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if... 1. When STR_IDX == MATCH_LAST(the last index in the state_log): If `a' isn't the LAST_NODE and `a' can't epsilon transit to the LAST_NODE, we throw away the node `a'. 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts string `s' and transit to `b': i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw away the node `a'. ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is thrown away, we throw away the node `a'. 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b': i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the node `a'. ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away, we throw away the node `a'. */ #define STATE_NODE_CONTAINS(state,node) \ ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) static reg_errcode_t internal_function sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) { reg_errcode_t err; int null_cnt = 0; int str_idx = sctx->last_str_idx; re_node_set cur_dest; #ifdef DEBUG assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); #endif /* Build sifted state_log[str_idx]. It has the nodes which can epsilon transit to the last_node and the last_node itself. */ err = re_node_set_init_1 (&cur_dest, sctx->last_node); if (BE (err != REG_NOERROR, 0)) return err; err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); if (BE (err != REG_NOERROR, 0)) goto free_return; /* Then check each states in the state_log. */ while (str_idx > 0) { /* Update counters. */ null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0; if (null_cnt > mctx->max_mb_elem_len) { memset (sctx->sifted_states, '\0', sizeof (re_dfastate_t *) * str_idx); re_node_set_free (&cur_dest); return REG_NOERROR; } re_node_set_empty (&cur_dest); --str_idx; if (mctx->state_log[str_idx]) { err = build_sifted_states (mctx, sctx, str_idx, &cur_dest); if (BE (err != REG_NOERROR, 0)) goto free_return; } /* Add all the nodes which satisfy the following conditions: - It can epsilon transit to a node in CUR_DEST. - It is in CUR_SRC. And update state_log. */ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); if (BE (err != REG_NOERROR, 0)) goto free_return; } err = REG_NOERROR; free_return: re_node_set_free (&cur_dest); return err; } static reg_errcode_t internal_function build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, int str_idx, re_node_set *cur_dest) { const re_dfa_t *const dfa = mctx->dfa; const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes; int i; /* Then build the next sifted state. We build the next sifted state on `cur_dest', and update `sifted_states[str_idx]' with `cur_dest'. Note: `cur_dest' is the sifted state from `state_log[str_idx + 1]'. `cur_src' points the node_set of the old `state_log[str_idx]' (with the epsilon nodes pre-filtered out). */ for (i = 0; i < cur_src->nelem; i++) { int prev_node = cur_src->elems[i]; int naccepted = 0; int ret; #ifdef DEBUG re_token_type_t type = dfa->nodes[prev_node].type; assert (!IS_EPSILON_NODE (type)); #endif #ifdef RE_ENABLE_I18N /* If the node may accept `multi byte'. */ if (dfa->nodes[prev_node].accept_mb) naccepted = sift_states_iter_mb (mctx, sctx, prev_node, str_idx, sctx->last_str_idx); #endif /* RE_ENABLE_I18N */ /* We don't check backreferences here. See update_cur_sifted_state(). */ if (!naccepted && check_node_accept (mctx, dfa->nodes + prev_node, str_idx) && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1], dfa->nexts[prev_node])) naccepted = 1; if (naccepted == 0) continue; if (sctx->limits.nelem) { int to_idx = str_idx + naccepted; if (check_dst_limits (mctx, &sctx->limits, dfa->nexts[prev_node], to_idx, prev_node, str_idx)) continue; } ret = re_node_set_insert (cur_dest, prev_node); if (BE (ret == -1, 0)) return REG_ESPACE; } return REG_NOERROR; } /* Helper functions. */ static reg_errcode_t internal_function clean_state_log_if_needed (re_match_context_t *mctx, int next_state_log_idx) { int top = mctx->state_log_top; if (next_state_log_idx >= mctx->input.bufs_len || (next_state_log_idx >= mctx->input.valid_len && mctx->input.valid_len < mctx->input.len)) { reg_errcode_t err; err = extend_buffers (mctx); if (BE (err != REG_NOERROR, 0)) return err; } if (top < next_state_log_idx) { memset (mctx->state_log + top + 1, '\0', sizeof (re_dfastate_t *) * (next_state_log_idx - top)); mctx->state_log_top = next_state_log_idx; } return REG_NOERROR; } static reg_errcode_t internal_function merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, re_dfastate_t **src, int num) { int st_idx; reg_errcode_t err; for (st_idx = 0; st_idx < num; ++st_idx) { if (dst[st_idx] == NULL) dst[st_idx] = src[st_idx]; else if (src[st_idx] != NULL) { re_node_set merged_set; err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes, &src[st_idx]->nodes); if (BE (err != REG_NOERROR, 0)) return err; dst[st_idx] = re_acquire_state (&err, dfa, &merged_set); re_node_set_free (&merged_set); if (BE (err != REG_NOERROR, 0)) return err; } } return REG_NOERROR; } static reg_errcode_t internal_function update_cur_sifted_state (const re_match_context_t *mctx, re_sift_context_t *sctx, int str_idx, re_node_set *dest_nodes) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err = REG_NOERROR; const re_node_set *candidates; candidates = ((mctx->state_log[str_idx] == NULL) ? NULL : &mctx->state_log[str_idx]->nodes); if (dest_nodes->nelem == 0) sctx->sifted_states[str_idx] = NULL; else { if (candidates) { /* At first, add the nodes which can epsilon transit to a node in DEST_NODE. */ err = add_epsilon_src_nodes (dfa, dest_nodes, candidates); if (BE (err != REG_NOERROR, 0)) return err; /* Then, check the limitations in the current sift_context. */ if (sctx->limits.nelem) { err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits, mctx->bkref_ents, str_idx); if (BE (err != REG_NOERROR, 0)) return err; } } sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); if (BE (err != REG_NOERROR, 0)) return err; } if (candidates && mctx->state_log[str_idx]->has_backref) { err = sift_states_bkref (mctx, sctx, str_idx, candidates); if (BE (err != REG_NOERROR, 0)) return err; } return REG_NOERROR; } static reg_errcode_t internal_function add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, const re_node_set *candidates) { reg_errcode_t err = REG_NOERROR; int i; re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes); if (BE (err != REG_NOERROR, 0)) return err; if (!state->inveclosure.alloc) { err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem); if (BE (err != REG_NOERROR, 0)) return REG_ESPACE; for (i = 0; i < dest_nodes->nelem; i++) { err = re_node_set_merge (&state->inveclosure, dfa->inveclosures + dest_nodes->elems[i]); if (BE (err != REG_NOERROR, 0)) return REG_ESPACE; } } return re_node_set_add_intersect (dest_nodes, candidates, &state->inveclosure); } static reg_errcode_t internal_function sub_epsilon_src_nodes (const re_dfa_t *dfa, int node, re_node_set *dest_nodes, const re_node_set *candidates) { int ecl_idx; reg_errcode_t err; re_node_set *inv_eclosure = dfa->inveclosures + node; re_node_set except_nodes; re_node_set_init_empty (&except_nodes); for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) { int cur_node = inv_eclosure->elems[ecl_idx]; if (cur_node == node) continue; if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) { int edst1 = dfa->edests[cur_node].elems[0]; int edst2 = ((dfa->edests[cur_node].nelem > 1) ? dfa->edests[cur_node].elems[1] : -1); if ((!re_node_set_contains (inv_eclosure, edst1) && re_node_set_contains (dest_nodes, edst1)) || (edst2 > 0 && !re_node_set_contains (inv_eclosure, edst2) && re_node_set_contains (dest_nodes, edst2))) { err = re_node_set_add_intersect (&except_nodes, candidates, dfa->inveclosures + cur_node); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&except_nodes); return err; } } } } for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) { int cur_node = inv_eclosure->elems[ecl_idx]; if (!re_node_set_contains (&except_nodes, cur_node)) { int idx = re_node_set_contains (dest_nodes, cur_node) - 1; re_node_set_remove_at (dest_nodes, idx); } } re_node_set_free (&except_nodes); return REG_NOERROR; } static int internal_function check_dst_limits (const re_match_context_t *mctx, re_node_set *limits, int dst_node, int dst_idx, int src_node, int src_idx) { const re_dfa_t *const dfa = mctx->dfa; int lim_idx, src_pos, dst_pos; int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx); int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx); for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) { int subexp_idx; struct re_backref_cache_entry *ent; ent = mctx->bkref_ents + limits->elems[lim_idx]; subexp_idx = dfa->nodes[ent->node].opr.idx; dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], subexp_idx, dst_node, dst_idx, dst_bkref_idx); src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], subexp_idx, src_node, src_idx, src_bkref_idx); /* In case of: ( ) ( ) ( ) */ if (src_pos == dst_pos) continue; /* This is unrelated limitation. */ else return 1; } return 0; } static int internal_function check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, int subexp_idx, int from_node, int bkref_idx) { const re_dfa_t *const dfa = mctx->dfa; const re_node_set *eclosures = dfa->eclosures + from_node; int node_idx; /* Else, we are on the boundary: examine the nodes on the epsilon closure. */ for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) { int node = eclosures->elems[node_idx]; switch (dfa->nodes[node].type) { case OP_BACK_REF: if (bkref_idx != -1) { struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx; do { int dst, cpos; if (ent->node != node) continue; if (subexp_idx < BITSET_WORD_BITS && !(ent->eps_reachable_subexps_map & ((bitset_word_t) 1 << subexp_idx))) continue; /* Recurse trying to reach the OP_OPEN_SUBEXP and OP_CLOSE_SUBEXP cases below. But, if the destination node is the same node as the source node, don't recurse because it would cause an infinite loop: a regex that exhibits this behavior is ()\1*\1* */ dst = dfa->edests[node].elems[0]; if (dst == from_node) { if (boundaries & 1) return -1; else /* if (boundaries & 2) */ return 0; } cpos = check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, dst, bkref_idx); if (cpos == -1 /* && (boundaries & 1) */) return -1; if (cpos == 0 && (boundaries & 2)) return 0; if (subexp_idx < BITSET_WORD_BITS) ent->eps_reachable_subexps_map &= ~((bitset_word_t) 1 << subexp_idx); } while (ent++->more); } break; case OP_OPEN_SUBEXP: if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx) return -1; break; case OP_CLOSE_SUBEXP: if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx) return 0; break; default: break; } } return (boundaries & 2) ? 1 : 0; } static int internal_function check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit, int subexp_idx, int from_node, int str_idx, int bkref_idx) { struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; int boundaries; /* If we are outside the range of the subexpression, return -1 or 1. */ if (str_idx < lim->subexp_from) return -1; if (lim->subexp_to < str_idx) return 1; /* If we are within the subexpression, return 0. */ boundaries = (str_idx == lim->subexp_from); boundaries |= (str_idx == lim->subexp_to) << 1; if (boundaries == 0) return 0; /* Else, examine epsilon closure. */ return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, from_node, bkref_idx); } /* Check the limitations of sub expressions LIMITS, and remove the nodes which are against limitations from DEST_NODES. */ static reg_errcode_t internal_function check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes, const re_node_set *candidates, re_node_set *limits, struct re_backref_cache_entry *bkref_ents, int str_idx) { reg_errcode_t err; int node_idx, lim_idx; for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) { int subexp_idx; struct re_backref_cache_entry *ent; ent = bkref_ents + limits->elems[lim_idx]; if (str_idx <= ent->subexp_from || ent->str_idx < str_idx) continue; /* This is unrelated limitation. */ subexp_idx = dfa->nodes[ent->node].opr.idx; if (ent->subexp_to == str_idx) { int ops_node = -1; int cls_node = -1; for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) { int node = dest_nodes->elems[node_idx]; re_token_type_t type = dfa->nodes[node].type; if (type == OP_OPEN_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx) ops_node = node; else if (type == OP_CLOSE_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx) cls_node = node; } /* Check the limitation of the open subexpression. */ /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */ if (ops_node >= 0) { err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes, candidates); if (BE (err != REG_NOERROR, 0)) return err; } /* Check the limitation of the close subexpression. */ if (cls_node >= 0) for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) { int node = dest_nodes->elems[node_idx]; if (!re_node_set_contains (dfa->inveclosures + node, cls_node) && !re_node_set_contains (dfa->eclosures + node, cls_node)) { /* It is against this limitation. Remove it form the current sifted state. */ err = sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates); if (BE (err != REG_NOERROR, 0)) return err; --node_idx; } } } else /* (ent->subexp_to != str_idx) */ { for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) { int node = dest_nodes->elems[node_idx]; re_token_type_t type = dfa->nodes[node].type; if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP) { if (subexp_idx != dfa->nodes[node].opr.idx) continue; /* It is against this limitation. Remove it form the current sifted state. */ err = sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates); if (BE (err != REG_NOERROR, 0)) return err; } } } } return REG_NOERROR; } static reg_errcode_t internal_function sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, int str_idx, const re_node_set *candidates) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; int node_idx, node; re_sift_context_t local_sctx; int first_idx = search_cur_bkref_entry (mctx, str_idx); if (first_idx == -1) return REG_NOERROR; local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) { int enabled_idx; re_token_type_t type; struct re_backref_cache_entry *entry; node = candidates->elems[node_idx]; type = dfa->nodes[node].type; /* Avoid infinite loop for the REs like "()\1+". */ if (node == sctx->last_node && str_idx == sctx->last_str_idx) continue; if (type != OP_BACK_REF) continue; entry = mctx->bkref_ents + first_idx; enabled_idx = first_idx; do { int subexp_len; int to_idx; int dst_node; int ret; re_dfastate_t *cur_state; if (entry->node != node) continue; subexp_len = entry->subexp_to - entry->subexp_from; to_idx = str_idx + subexp_len; dst_node = (subexp_len ? dfa->nexts[node] : dfa->edests[node].elems[0]); if (to_idx > sctx->last_str_idx || sctx->sifted_states[to_idx] == NULL || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node) || check_dst_limits (mctx, &sctx->limits, node, str_idx, dst_node, to_idx)) continue; if (local_sctx.sifted_states == NULL) { local_sctx = *sctx; err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits); if (BE (err != REG_NOERROR, 0)) goto free_return; } local_sctx.last_node = node; local_sctx.last_str_idx = str_idx; ret = re_node_set_insert (&local_sctx.limits, enabled_idx); if (BE (ret < 0, 0)) { err = REG_ESPACE; goto free_return; } cur_state = local_sctx.sifted_states[str_idx]; err = sift_states_backward (mctx, &local_sctx); if (BE (err != REG_NOERROR, 0)) goto free_return; if (sctx->limited_states != NULL) { err = merge_state_array (dfa, sctx->limited_states, local_sctx.sifted_states, str_idx + 1); if (BE (err != REG_NOERROR, 0)) goto free_return; } local_sctx.sifted_states[str_idx] = cur_state; re_node_set_remove (&local_sctx.limits, enabled_idx); /* mctx->bkref_ents may have changed, reload the pointer. */ entry = mctx->bkref_ents + enabled_idx; } while (enabled_idx++, entry++->more); } err = REG_NOERROR; free_return: if (local_sctx.sifted_states != NULL) { re_node_set_free (&local_sctx.limits); } return err; } #ifdef RE_ENABLE_I18N static int internal_function sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx, int node_idx, int str_idx, int max_str_idx) { const re_dfa_t *const dfa = mctx->dfa; int naccepted; /* Check the node can accept `multi byte'. */ naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx); if (naccepted > 0 && str_idx + naccepted <= max_str_idx && !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted], dfa->nexts[node_idx])) /* The node can't accept the `multi byte', or the destination was already thrown away, then the node could't accept the current input `multi byte'. */ naccepted = 0; /* Otherwise, it is sure that the node could accept `naccepted' bytes input. */ return naccepted; } #endif /* RE_ENABLE_I18N */ /* Functions for state transition. */ /* Return the next state to which the current state STATE will transit by accepting the current input byte, and update STATE_LOG if necessary. If STATE can accept a multibyte char/collating element/back reference update the destination of STATE_LOG. */ static re_dfastate_t * internal_function transit_state (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *state) { re_dfastate_t **trtable; unsigned char ch; #ifdef RE_ENABLE_I18N /* If the current state can accept multibyte. */ if (BE (state->accept_mb, 0)) { *err = transit_state_mb (mctx, state); if (BE (*err != REG_NOERROR, 0)) return NULL; } #endif /* RE_ENABLE_I18N */ /* Then decide the next state with the single byte. */ #if 0 if (0) /* don't use transition table */ return transit_state_sb (err, mctx, state); #endif /* Use transition table */ ch = re_string_fetch_byte (&mctx->input); for (;;) { trtable = state->trtable; if (BE (trtable != NULL, 1)) return trtable[ch]; trtable = state->word_trtable; if (BE (trtable != NULL, 1)) { unsigned int context; context = re_string_context_at (&mctx->input, re_string_cur_idx (&mctx->input) - 1, mctx->eflags); if (IS_WORD_CONTEXT (context)) return trtable[ch + SBC_MAX]; else return trtable[ch]; } if (!build_trtable (mctx->dfa, state)) { *err = REG_ESPACE; return NULL; } /* Retry, we now have a transition table. */ } } /* Update the state_log if we need */ re_dfastate_t * internal_function merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *next_state) { const re_dfa_t *const dfa = mctx->dfa; int cur_idx = re_string_cur_idx (&mctx->input); if (cur_idx > mctx->state_log_top) { mctx->state_log[cur_idx] = next_state; mctx->state_log_top = cur_idx; } else if (mctx->state_log[cur_idx] == 0) { mctx->state_log[cur_idx] = next_state; } else { re_dfastate_t *pstate; unsigned int context; re_node_set next_nodes, *log_nodes, *table_nodes = NULL; /* If (state_log[cur_idx] != 0), it implies that cur_idx is the destination of a multibyte char/collating element/ back reference. Then the next state is the union set of these destinations and the results of the transition table. */ pstate = mctx->state_log[cur_idx]; log_nodes = pstate->entrance_nodes; if (next_state != NULL) { table_nodes = next_state->entrance_nodes; *err = re_node_set_init_union (&next_nodes, table_nodes, log_nodes); if (BE (*err != REG_NOERROR, 0)) return NULL; } else next_nodes = *log_nodes; /* Note: We already add the nodes of the initial state, then we don't need to add them here. */ context = re_string_context_at (&mctx->input, re_string_cur_idx (&mctx->input) - 1, mctx->eflags); next_state = mctx->state_log[cur_idx] = re_acquire_state_context (err, dfa, &next_nodes, context); /* We don't need to check errors here, since the return value of this function is next_state and ERR is already set. */ if (table_nodes != NULL) re_node_set_free (&next_nodes); } if (BE (dfa->nbackref, 0) && next_state != NULL) { /* Check OP_OPEN_SUBEXP in the current state in case that we use them later. We must check them here, since the back references in the next state might use them. */ *err = check_subexp_matching_top (mctx, &next_state->nodes, cur_idx); if (BE (*err != REG_NOERROR, 0)) return NULL; /* If the next state has back references. */ if (next_state->has_backref) { *err = transit_state_bkref (mctx, &next_state->nodes); if (BE (*err != REG_NOERROR, 0)) return NULL; next_state = mctx->state_log[cur_idx]; } } return next_state; } /* Skip bytes in the input that correspond to part of a multi-byte match, then look in the log for a state from which to restart matching. */ re_dfastate_t * internal_function find_recover_state (reg_errcode_t *err, re_match_context_t *mctx) { re_dfastate_t *cur_state; do { int max = mctx->state_log_top; int cur_str_idx = re_string_cur_idx (&mctx->input); do { if (++cur_str_idx > max) return NULL; re_string_skip_bytes (&mctx->input, 1); } while (mctx->state_log[cur_str_idx] == NULL); cur_state = merge_state_with_log (err, mctx, NULL); } while (*err == REG_NOERROR && cur_state == NULL); return cur_state; } /* Helper functions for transit_state. */ /* From the node set CUR_NODES, pick up the nodes whose types are OP_OPEN_SUBEXP and which have corresponding back references in the regular expression. And register them to use them later for evaluating the correspoding back references. */ static reg_errcode_t internal_function check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, int str_idx) { const re_dfa_t *const dfa = mctx->dfa; int node_idx; reg_errcode_t err; /* TODO: This isn't efficient. Because there might be more than one nodes whose types are OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all nodes. E.g. RE: (a){2} */ for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx) { int node = cur_nodes->elems[node_idx]; if (dfa->nodes[node].type == OP_OPEN_SUBEXP && dfa->nodes[node].opr.idx < BITSET_WORD_BITS && (dfa->used_bkref_map & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx))) { err = match_ctx_add_subtop (mctx, node, str_idx); if (BE (err != REG_NOERROR, 0)) return err; } } return REG_NOERROR; } #if 0 /* Return the next state to which the current state STATE will transit by accepting the current input byte. */ static re_dfastate_t * transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *state) { const re_dfa_t *const dfa = mctx->dfa; re_node_set next_nodes; re_dfastate_t *next_state; int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input); unsigned int context; *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1); if (BE (*err != REG_NOERROR, 0)) return NULL; for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt) { int cur_node = state->nodes.elems[node_cnt]; if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx)) { *err = re_node_set_merge (&next_nodes, dfa->eclosures + dfa->nexts[cur_node]); if (BE (*err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return NULL; } } } context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); next_state = re_acquire_state_context (err, dfa, &next_nodes, context); /* We don't need to check errors here, since the return value of this function is next_state and ERR is already set. */ re_node_set_free (&next_nodes); re_string_skip_bytes (&mctx->input, 1); return next_state; } #endif #ifdef RE_ENABLE_I18N static reg_errcode_t internal_function transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; int i; for (i = 0; i < pstate->nodes.nelem; ++i) { re_node_set dest_nodes, *new_nodes; int cur_node_idx = pstate->nodes.elems[i]; int naccepted, dest_idx; unsigned int context; re_dfastate_t *dest_state; if (!dfa->nodes[cur_node_idx].accept_mb) continue; if (dfa->nodes[cur_node_idx].constraint) { context = re_string_context_at (&mctx->input, re_string_cur_idx (&mctx->input), mctx->eflags); if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, context)) continue; } /* How many bytes the node can accept? */ naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input, re_string_cur_idx (&mctx->input)); if (naccepted == 0) continue; /* The node can accepts `naccepted' bytes. */ dest_idx = re_string_cur_idx (&mctx->input) + naccepted; mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted : mctx->max_mb_elem_len); err = clean_state_log_if_needed (mctx, dest_idx); if (BE (err != REG_NOERROR, 0)) return err; #ifdef DEBUG assert (dfa->nexts[cur_node_idx] != -1); #endif new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx]; dest_state = mctx->state_log[dest_idx]; if (dest_state == NULL) dest_nodes = *new_nodes; else { err = re_node_set_init_union (&dest_nodes, dest_state->entrance_nodes, new_nodes); if (BE (err != REG_NOERROR, 0)) return err; } context = re_string_context_at (&mctx->input, dest_idx - 1, mctx->eflags); mctx->state_log[dest_idx] = re_acquire_state_context (&err, dfa, &dest_nodes, context); if (dest_state != NULL) re_node_set_free (&dest_nodes); if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0)) return err; } return REG_NOERROR; } #endif /* RE_ENABLE_I18N */ static reg_errcode_t internal_function transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; int i; int cur_str_idx = re_string_cur_idx (&mctx->input); for (i = 0; i < nodes->nelem; ++i) { int dest_str_idx, prev_nelem, bkc_idx; int node_idx = nodes->elems[i]; unsigned int context; const re_token_t *node = dfa->nodes + node_idx; re_node_set *new_dest_nodes; /* Check whether `node' is a backreference or not. */ if (node->type != OP_BACK_REF) continue; if (node->constraint) { context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) continue; } /* `node' is a backreference. Check the substring which the substring matched. */ bkc_idx = mctx->nbkref_ents; err = get_subexp (mctx, node_idx, cur_str_idx); if (BE (err != REG_NOERROR, 0)) goto free_return; /* And add the epsilon closures (which is `new_dest_nodes') of the backreference to appropriate state_log. */ #ifdef DEBUG assert (dfa->nexts[node_idx] != -1); #endif for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx) { int subexp_len; re_dfastate_t *dest_state; struct re_backref_cache_entry *bkref_ent; bkref_ent = mctx->bkref_ents + bkc_idx; if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx) continue; subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from; new_dest_nodes = (subexp_len == 0 ? dfa->eclosures + dfa->edests[node_idx].elems[0] : dfa->eclosures + dfa->nexts[node_idx]); dest_str_idx = (cur_str_idx + bkref_ent->subexp_to - bkref_ent->subexp_from); context = re_string_context_at (&mctx->input, dest_str_idx - 1, mctx->eflags); dest_state = mctx->state_log[dest_str_idx]; prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0 : mctx->state_log[cur_str_idx]->nodes.nelem); /* Add `new_dest_node' to state_log. */ if (dest_state == NULL) { mctx->state_log[dest_str_idx] = re_acquire_state_context (&err, dfa, new_dest_nodes, context); if (BE (mctx->state_log[dest_str_idx] == NULL && err != REG_NOERROR, 0)) goto free_return; } else { re_node_set dest_nodes; err = re_node_set_init_union (&dest_nodes, dest_state->entrance_nodes, new_dest_nodes); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&dest_nodes); goto free_return; } mctx->state_log[dest_str_idx] = re_acquire_state_context (&err, dfa, &dest_nodes, context); re_node_set_free (&dest_nodes); if (BE (mctx->state_log[dest_str_idx] == NULL && err != REG_NOERROR, 0)) goto free_return; } /* We need to check recursively if the backreference can epsilon transit. */ if (subexp_len == 0 && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem) { err = check_subexp_matching_top (mctx, new_dest_nodes, cur_str_idx); if (BE (err != REG_NOERROR, 0)) goto free_return; err = transit_state_bkref (mctx, new_dest_nodes); if (BE (err != REG_NOERROR, 0)) goto free_return; } } } err = REG_NOERROR; free_return: return err; } /* Enumerate all the candidates which the backreference BKREF_NODE can match at BKREF_STR_IDX, and register them by match_ctx_add_entry(). Note that we might collect inappropriate candidates here. However, the cost of checking them strictly here is too high, then we delay these checking for prune_impossible_nodes(). */ static reg_errcode_t internal_function get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx) { const re_dfa_t *const dfa = mctx->dfa; int subexp_num, sub_top_idx; const char *buf = (const char *) re_string_get_buffer (&mctx->input); /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); if (cache_idx != -1) { const struct re_backref_cache_entry *entry = mctx->bkref_ents + cache_idx; do if (entry->node == bkref_node) return REG_NOERROR; /* We already checked it. */ while (entry++->more); } subexp_num = dfa->nodes[bkref_node].opr.idx; /* For each sub expression */ for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx) { reg_errcode_t err; re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx]; re_sub_match_last_t *sub_last; int sub_last_idx, sl_str, bkref_str_off; if (dfa->nodes[sub_top->node].opr.idx != subexp_num) continue; /* It isn't related. */ sl_str = sub_top->str_idx; bkref_str_off = bkref_str_idx; /* At first, check the last node of sub expressions we already evaluated. */ for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx) { int sl_str_diff; sub_last = sub_top->lasts[sub_last_idx]; sl_str_diff = sub_last->str_idx - sl_str; /* The matched string by the sub expression match with the substring at the back reference? */ if (sl_str_diff > 0) { if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0)) { /* Not enough chars for a successful match. */ if (bkref_str_off + sl_str_diff > mctx->input.len) break; err = clean_state_log_if_needed (mctx, bkref_str_off + sl_str_diff); if (BE (err != REG_NOERROR, 0)) return err; buf = (const char *) re_string_get_buffer (&mctx->input); } if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0) /* We don't need to search this sub expression any more. */ break; } bkref_str_off += sl_str_diff; sl_str += sl_str_diff; err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, bkref_str_idx); /* Reload buf, since the preceding call might have reallocated the buffer. */ buf = (const char *) re_string_get_buffer (&mctx->input); if (err == REG_NOMATCH) continue; if (BE (err != REG_NOERROR, 0)) return err; } if (sub_last_idx < sub_top->nlasts) continue; if (sub_last_idx > 0) ++sl_str; /* Then, search for the other last nodes of the sub expression. */ for (; sl_str <= bkref_str_idx; ++sl_str) { int cls_node, sl_str_off; const re_node_set *nodes; sl_str_off = sl_str - sub_top->str_idx; /* The matched string by the sub expression match with the substring at the back reference? */ if (sl_str_off > 0) { if (BE (bkref_str_off >= mctx->input.valid_len, 0)) { /* If we are at the end of the input, we cannot match. */ if (bkref_str_off >= mctx->input.len) break; err = extend_buffers (mctx); if (BE (err != REG_NOERROR, 0)) return err; buf = (const char *) re_string_get_buffer (&mctx->input); } if (buf [bkref_str_off++] != buf[sl_str - 1]) break; /* We don't need to search this sub expression any more. */ } if (mctx->state_log[sl_str] == NULL) continue; /* Does this state have a ')' of the sub expression? */ nodes = &mctx->state_log[sl_str]->nodes; cls_node = find_subexp_node (dfa, nodes, subexp_num, OP_CLOSE_SUBEXP); if (cls_node == -1) continue; /* No. */ if (sub_top->path == NULL) { sub_top->path = calloc (sizeof (state_array_t), sl_str - sub_top->str_idx + 1); if (sub_top->path == NULL) return REG_ESPACE; } /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node in the current context? */ err = check_arrival (mctx, sub_top->path, sub_top->node, sub_top->str_idx, cls_node, sl_str, OP_CLOSE_SUBEXP); if (err == REG_NOMATCH) continue; if (BE (err != REG_NOERROR, 0)) return err; sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str); if (BE (sub_last == NULL, 0)) return REG_ESPACE; err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, bkref_str_idx); if (err == REG_NOMATCH) continue; } } return REG_NOERROR; } /* Helper functions for get_subexp(). */ /* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR. If it can arrive, register the sub expression expressed with SUB_TOP and SUB_LAST. */ static reg_errcode_t internal_function get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, re_sub_match_last_t *sub_last, int bkref_node, int bkref_str) { reg_errcode_t err; int to_idx; /* Can the subexpression arrive the back reference? */ err = check_arrival (mctx, &sub_last->path, sub_last->node, sub_last->str_idx, bkref_node, bkref_str, OP_OPEN_SUBEXP); if (err != REG_NOERROR) return err; err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx, sub_last->str_idx); if (BE (err != REG_NOERROR, 0)) return err; to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx; return clean_state_log_if_needed (mctx, to_idx); } /* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX. Search '(' if FL_OPEN, or search ')' otherwise. TODO: This function isn't efficient... Because there might be more than one nodes whose types are OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all nodes. E.g. RE: (a){2} */ static int internal_function find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, int subexp_idx, int type) { int cls_idx; for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) { int cls_node = nodes->elems[cls_idx]; const re_token_t *node = dfa->nodes + cls_node; if (node->type == type && node->opr.idx == subexp_idx) return cls_node; } return -1; } /* Check whether the node TOP_NODE at TOP_STR can arrive to the node LAST_NODE at LAST_STR. We record the path onto PATH since it will be heavily reused. Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */ static reg_errcode_t internal_function check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node, int top_str, int last_node, int last_str, int type) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err = REG_NOERROR; int subexp_num, backup_cur_idx, str_idx, null_cnt; re_dfastate_t *cur_state = NULL; re_node_set *cur_nodes, next_nodes; re_dfastate_t **backup_state_log; unsigned int context; subexp_num = dfa->nodes[top_node].opr.idx; /* Extend the buffer if we need. */ if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0)) { re_dfastate_t **new_array; int old_alloc = path->alloc; path->alloc += last_str + mctx->max_mb_elem_len + 1; new_array = re_realloc (path->array, re_dfastate_t *, path->alloc); if (BE (new_array == NULL, 0)) { path->alloc = old_alloc; return REG_ESPACE; } path->array = new_array; memset (new_array + old_alloc, '\0', sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); } str_idx = path->next_idx ? path->next_idx : top_str; /* Temporary modify MCTX. */ backup_state_log = mctx->state_log; backup_cur_idx = mctx->input.cur_idx; mctx->state_log = path->array; mctx->input.cur_idx = str_idx; /* Setup initial node set. */ context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); if (str_idx == top_str) { err = re_node_set_init_1 (&next_nodes, top_node); if (BE (err != REG_NOERROR, 0)) return err; err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } } else { cur_state = mctx->state_log[str_idx]; if (cur_state && cur_state->has_backref) { err = re_node_set_init_copy (&next_nodes, &cur_state->nodes); if (BE (err != REG_NOERROR, 0)) return err; } else re_node_set_init_empty (&next_nodes); } if (str_idx == top_str || (cur_state && cur_state->has_backref)) { if (next_nodes.nelem) { err = expand_bkref_cache (mctx, &next_nodes, str_idx, subexp_num, type); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } } cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); if (BE (cur_state == NULL && err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } mctx->state_log[str_idx] = cur_state; } for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;) { re_node_set_empty (&next_nodes); if (mctx->state_log[str_idx + 1]) { err = re_node_set_merge (&next_nodes, &mctx->state_log[str_idx + 1]->nodes); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } } if (cur_state) { err = check_arrival_add_next_nodes (mctx, str_idx, &cur_state->non_eps_nodes, &next_nodes); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } } ++str_idx; if (next_nodes.nelem) { err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } err = expand_bkref_cache (mctx, &next_nodes, str_idx, subexp_num, type); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } } context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); if (BE (cur_state == NULL && err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } mctx->state_log[str_idx] = cur_state; null_cnt = cur_state == NULL ? null_cnt + 1 : 0; } re_node_set_free (&next_nodes); cur_nodes = (mctx->state_log[last_str] == NULL ? NULL : &mctx->state_log[last_str]->nodes); path->next_idx = str_idx; /* Fix MCTX. */ mctx->state_log = backup_state_log; mctx->input.cur_idx = backup_cur_idx; /* Then check the current node set has the node LAST_NODE. */ if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node)) return REG_NOERROR; return REG_NOMATCH; } /* Helper functions for check_arrival. */ /* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them to NEXT_NODES. TODO: This function is similar to the functions transit_state*(), however this function has many additional works. Can't we unify them? */ static reg_errcode_t internal_function check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx, re_node_set *cur_nodes, re_node_set *next_nodes) { const re_dfa_t *const dfa = mctx->dfa; int result; int cur_idx; #ifdef RE_ENABLE_I18N reg_errcode_t err = REG_NOERROR; #endif re_node_set union_set; re_node_set_init_empty (&union_set); for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx) { int naccepted = 0; int cur_node = cur_nodes->elems[cur_idx]; #ifdef DEBUG re_token_type_t type = dfa->nodes[cur_node].type; assert (!IS_EPSILON_NODE (type)); #endif #ifdef RE_ENABLE_I18N /* If the node may accept `multi byte'. */ if (dfa->nodes[cur_node].accept_mb) { naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input, str_idx); if (naccepted > 1) { re_dfastate_t *dest_state; int next_node = dfa->nexts[cur_node]; int next_idx = str_idx + naccepted; dest_state = mctx->state_log[next_idx]; re_node_set_empty (&union_set); if (dest_state) { err = re_node_set_merge (&union_set, &dest_state->nodes); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&union_set); return err; } } result = re_node_set_insert (&union_set, next_node); if (BE (result < 0, 0)) { re_node_set_free (&union_set); return REG_ESPACE; } mctx->state_log[next_idx] = re_acquire_state (&err, dfa, &union_set); if (BE (mctx->state_log[next_idx] == NULL && err != REG_NOERROR, 0)) { re_node_set_free (&union_set); return err; } } } #endif /* RE_ENABLE_I18N */ if (naccepted || check_node_accept (mctx, dfa->nodes + cur_node, str_idx)) { result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); if (BE (result < 0, 0)) { re_node_set_free (&union_set); return REG_ESPACE; } } } re_node_set_free (&union_set); return REG_NOERROR; } /* For all the nodes in CUR_NODES, add the epsilon closures of them to CUR_NODES, however exclude the nodes which are: - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN. - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN. */ static reg_errcode_t internal_function check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes, int ex_subexp, int type) { reg_errcode_t err; int idx, outside_node; re_node_set new_nodes; #ifdef DEBUG assert (cur_nodes->nelem); #endif err = re_node_set_alloc (&new_nodes, cur_nodes->nelem); if (BE (err != REG_NOERROR, 0)) return err; /* Create a new node set NEW_NODES with the nodes which are epsilon closures of the node in CUR_NODES. */ for (idx = 0; idx < cur_nodes->nelem; ++idx) { int cur_node = cur_nodes->elems[idx]; const re_node_set *eclosure = dfa->eclosures + cur_node; outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type); if (outside_node == -1) { /* There are no problematic nodes, just merge them. */ err = re_node_set_merge (&new_nodes, eclosure); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&new_nodes); return err; } } else { /* There are problematic nodes, re-calculate incrementally. */ err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node, ex_subexp, type); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&new_nodes); return err; } } } re_node_set_free (cur_nodes); *cur_nodes = new_nodes; return REG_NOERROR; } /* Helper function for check_arrival_expand_ecl. Check incrementally the epsilon closure of TARGET, and if it isn't problematic append it to DST_NODES. */ static reg_errcode_t internal_function check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, int target, int ex_subexp, int type) { int cur_node; for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) { int err; if (dfa->nodes[cur_node].type == type && dfa->nodes[cur_node].opr.idx == ex_subexp) { if (type == OP_CLOSE_SUBEXP) { err = re_node_set_insert (dst_nodes, cur_node); if (BE (err == -1, 0)) return REG_ESPACE; } break; } err = re_node_set_insert (dst_nodes, cur_node); if (BE (err == -1, 0)) return REG_ESPACE; if (dfa->edests[cur_node].nelem == 0) break; if (dfa->edests[cur_node].nelem == 2) { err = check_arrival_expand_ecl_sub (dfa, dst_nodes, dfa->edests[cur_node].elems[1], ex_subexp, type); if (BE (err != REG_NOERROR, 0)) return err; } cur_node = dfa->edests[cur_node].elems[0]; } return REG_NOERROR; } /* For all the back references in the current state, calculate the destination of the back references by the appropriate entry in MCTX->BKREF_ENTS. */ static reg_errcode_t internal_function expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, int cur_str, int subexp_num, int type) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; int cache_idx_start = search_cur_bkref_entry (mctx, cur_str); struct re_backref_cache_entry *ent; if (cache_idx_start == -1) return REG_NOERROR; restart: ent = mctx->bkref_ents + cache_idx_start; do { int to_idx, next_node; /* Is this entry ENT is appropriate? */ if (!re_node_set_contains (cur_nodes, ent->node)) continue; /* No. */ to_idx = cur_str + ent->subexp_to - ent->subexp_from; /* Calculate the destination of the back reference, and append it to MCTX->STATE_LOG. */ if (to_idx == cur_str) { /* The backreference did epsilon transit, we must re-check all the node in the current state. */ re_node_set new_dests; reg_errcode_t err2, err3; next_node = dfa->edests[ent->node].elems[0]; if (re_node_set_contains (cur_nodes, next_node)) continue; err = re_node_set_init_1 (&new_dests, next_node); err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type); err3 = re_node_set_merge (cur_nodes, &new_dests); re_node_set_free (&new_dests); if (BE (err != REG_NOERROR || err2 != REG_NOERROR || err3 != REG_NOERROR, 0)) { err = (err != REG_NOERROR ? err : (err2 != REG_NOERROR ? err2 : err3)); return err; } /* TODO: It is still inefficient... */ goto restart; } else { re_node_set union_set; next_node = dfa->nexts[ent->node]; if (mctx->state_log[to_idx]) { int ret; if (re_node_set_contains (&mctx->state_log[to_idx]->nodes, next_node)) continue; err = re_node_set_init_copy (&union_set, &mctx->state_log[to_idx]->nodes); ret = re_node_set_insert (&union_set, next_node); if (BE (err != REG_NOERROR || ret < 0, 0)) { re_node_set_free (&union_set); err = err != REG_NOERROR ? err : REG_ESPACE; return err; } } else { err = re_node_set_init_1 (&union_set, next_node); if (BE (err != REG_NOERROR, 0)) return err; } mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set); re_node_set_free (&union_set); if (BE (mctx->state_log[to_idx] == NULL && err != REG_NOERROR, 0)) return err; } } while (ent++->more); return REG_NOERROR; } /* Build transition table for the state. Return 1 if succeeded, otherwise return NULL. */ static int internal_function build_trtable (const re_dfa_t *dfa, re_dfastate_t *state) { reg_errcode_t err; int i, j, ch, need_word_trtable = 0; bitset_word_t elem, mask; bool dests_node_malloced = false; bool dest_states_malloced = false; int ndests; /* Number of the destination states from `state'. */ re_dfastate_t **trtable; re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl; re_node_set follows, *dests_node; bitset_t *dests_ch; bitset_t acceptable; struct dests_alloc { re_node_set dests_node[SBC_MAX]; bitset_t dests_ch[SBC_MAX]; } *dests_alloc; /* We build DFA states which corresponds to the destination nodes from `state'. `dests_node[i]' represents the nodes which i-th destination state contains, and `dests_ch[i]' represents the characters which i-th destination state accepts. */ #ifdef HAVE_ALLOCA if (__libc_use_alloca (sizeof (struct dests_alloc))) dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc)); else #endif { dests_alloc = re_malloc (struct dests_alloc, 1); if (BE (dests_alloc == NULL, 0)) return 0; dests_node_malloced = true; } dests_node = dests_alloc->dests_node; dests_ch = dests_alloc->dests_ch; /* Initialize transiton table. */ state->word_trtable = state->trtable = NULL; /* At first, group all nodes belonging to `state' into several destinations. */ ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch); if (BE (ndests <= 0, 0)) { if (dests_node_malloced) free (dests_alloc); /* Return 0 in case of an error, 1 otherwise. */ if (ndests == 0) { state->trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); return 1; } return 0; } err = re_node_set_alloc (&follows, ndests + 1); if (BE (err != REG_NOERROR, 0)) goto out_free; /* Avoid arithmetic overflow in size calculation. */ if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX) / (3 * sizeof (re_dfastate_t *))) < (size_t)ndests), 0)) goto out_free; #ifdef HAVE_ALLOCA if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX + ndests * 3 * sizeof (re_dfastate_t *))) dest_states = (re_dfastate_t **) alloca (ndests * 3 * sizeof (re_dfastate_t *)); else #endif { dest_states = (re_dfastate_t **) malloc (ndests * 3 * sizeof (re_dfastate_t *)); if (BE (dest_states == NULL, 0)) { out_free: if (dest_states_malloced) free (dest_states); re_node_set_free (&follows); for (i = 0; i < ndests; ++i) re_node_set_free (dests_node + i); if (dests_node_malloced) free (dests_alloc); return 0; } dest_states_malloced = true; } dest_states_word = dest_states + ndests; dest_states_nl = dest_states_word + ndests; bitset_empty (acceptable); /* Then build the states for all destinations. */ for (i = 0; i < ndests; ++i) { int next_node; re_node_set_empty (&follows); /* Merge the follows of this destination states. */ for (j = 0; j < dests_node[i].nelem; ++j) { next_node = dfa->nexts[dests_node[i].elems[j]]; if (next_node != -1) { err = re_node_set_merge (&follows, dfa->eclosures + next_node); if (BE (err != REG_NOERROR, 0)) goto out_free; } } dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0); if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0)) goto out_free; /* If the new state has context constraint, build appropriate states for these contexts. */ if (dest_states[i]->has_constraint) { dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows, CONTEXT_WORD); if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0)) goto out_free; if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1) need_word_trtable = 1; dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows, CONTEXT_NEWLINE); if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0)) goto out_free; } else { dest_states_word[i] = dest_states[i]; dest_states_nl[i] = dest_states[i]; } bitset_merge (acceptable, dests_ch[i]); } if (!BE (need_word_trtable, 0)) { /* We don't care about whether the following character is a word character, or we are in a single-byte character set so we can discern by looking at the character code: allocate a 256-entry transition table. */ trtable = state->trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); if (BE (trtable == NULL, 0)) goto out_free; /* For all characters ch...: */ for (i = 0; i < BITSET_WORDS; ++i) for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; elem; mask <<= 1, elem >>= 1, ++ch) if (BE (elem & 1, 0)) { /* There must be exactly one destination which accepts character ch. See group_nodes_into_DFAstates. */ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) ; /* j-th destination accepts the word character ch. */ if (dfa->word_char[i] & mask) trtable[ch] = dest_states_word[j]; else trtable[ch] = dest_states[j]; } } else { /* We care about whether the following character is a word character, and we are in a multi-byte character set: discern by looking at the character code: build two 256-entry transition tables, one starting at trtable[0] and one starting at trtable[SBC_MAX]. */ trtable = state->word_trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX); if (BE (trtable == NULL, 0)) goto out_free; /* For all characters ch...: */ for (i = 0; i < BITSET_WORDS; ++i) for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; elem; mask <<= 1, elem >>= 1, ++ch) if (BE (elem & 1, 0)) { /* There must be exactly one destination which accepts character ch. See group_nodes_into_DFAstates. */ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) ; /* j-th destination accepts the word character ch. */ trtable[ch] = dest_states[j]; trtable[ch + SBC_MAX] = dest_states_word[j]; } } /* new line */ if (bitset_contain (acceptable, NEWLINE_CHAR)) { /* The current state accepts newline character. */ for (j = 0; j < ndests; ++j) if (bitset_contain (dests_ch[j], NEWLINE_CHAR)) { /* k-th destination accepts newline character. */ trtable[NEWLINE_CHAR] = dest_states_nl[j]; if (need_word_trtable) trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j]; /* There must be only one destination which accepts newline. See group_nodes_into_DFAstates. */ break; } } if (dest_states_malloced) free (dest_states); re_node_set_free (&follows); for (i = 0; i < ndests; ++i) re_node_set_free (dests_node + i); if (dests_node_malloced) free (dests_alloc); return 1; } /* Group all nodes belonging to STATE into several destinations. Then for all destinations, set the nodes belonging to the destination to DESTS_NODE[i] and set the characters accepted by the destination to DEST_CH[i]. This function return the number of destinations. */ static int internal_function group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, re_node_set *dests_node, bitset_t *dests_ch) { reg_errcode_t err; int result; int i, j, k; int ndests; /* Number of the destinations from `state'. */ bitset_t accepts; /* Characters a node can accept. */ const re_node_set *cur_nodes = &state->nodes; bitset_empty (accepts); ndests = 0; /* For all the nodes belonging to `state', */ for (i = 0; i < cur_nodes->nelem; ++i) { re_token_t *node = &dfa->nodes[cur_nodes->elems[i]]; re_token_type_t type = node->type; unsigned int constraint = node->constraint; /* Enumerate all single byte character this node can accept. */ if (type == CHARACTER) bitset_set (accepts, node->opr.c); else if (type == SIMPLE_BRACKET) { bitset_merge (accepts, node->opr.sbcset); } else if (type == OP_PERIOD) { #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) bitset_merge (accepts, dfa->sb_char); else #endif bitset_set_all (accepts); if (!(dfa->syntax & RE_DOT_NEWLINE)) bitset_clear (accepts, '\n'); if (dfa->syntax & RE_DOT_NOT_NULL) bitset_clear (accepts, '\0'); } #ifdef RE_ENABLE_I18N else if (type == OP_UTF8_PERIOD) { memset (accepts, '\xff', sizeof (bitset_t) / 2); if (!(dfa->syntax & RE_DOT_NEWLINE)) bitset_clear (accepts, '\n'); if (dfa->syntax & RE_DOT_NOT_NULL) bitset_clear (accepts, '\0'); } #endif else continue; /* Check the `accepts' and sift the characters which are not match it the context. */ if (constraint) { if (constraint & NEXT_NEWLINE_CONSTRAINT) { bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); bitset_empty (accepts); if (accepts_newline) bitset_set (accepts, NEWLINE_CHAR); else continue; } if (constraint & NEXT_ENDBUF_CONSTRAINT) { bitset_empty (accepts); continue; } if (constraint & NEXT_WORD_CONSTRAINT) { bitset_word_t any_set = 0; if (type == CHARACTER && !node->word_char) { bitset_empty (accepts); continue; } #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j])); else #endif for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= dfa->word_char[j]); if (!any_set) continue; } if (constraint & NEXT_NOTWORD_CONSTRAINT) { bitset_word_t any_set = 0; if (type == CHARACTER && node->word_char) { bitset_empty (accepts); continue; } #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j])); else #endif for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= ~dfa->word_char[j]); if (!any_set) continue; } } /* Then divide `accepts' into DFA states, or create a new state. Above, we make sure that accepts is not empty. */ for (j = 0; j < ndests; ++j) { bitset_t intersec; /* Intersection sets, see below. */ bitset_t remains; /* Flags, see below. */ bitset_word_t has_intersec, not_subset, not_consumed; /* Optimization, skip if this state doesn't accept the character. */ if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c)) continue; /* Enumerate the intersection set of this state and `accepts'. */ has_intersec = 0; for (k = 0; k < BITSET_WORDS; ++k) has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k]; /* And skip if the intersection set is empty. */ if (!has_intersec) continue; /* Then check if this state is a subset of `accepts'. */ not_subset = not_consumed = 0; for (k = 0; k < BITSET_WORDS; ++k) { not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k]; not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k]; } /* If this state isn't a subset of `accepts', create a new group state, which has the `remains'. */ if (not_subset) { bitset_copy (dests_ch[ndests], remains); bitset_copy (dests_ch[j], intersec); err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]); if (BE (err != REG_NOERROR, 0)) goto error_return; ++ndests; } /* Put the position in the current group. */ result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); if (BE (result < 0, 0)) goto error_return; /* If all characters are consumed, go to next node. */ if (!not_consumed) break; } /* Some characters remain, create a new group. */ if (j == ndests) { bitset_copy (dests_ch[ndests], accepts); err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]); if (BE (err != REG_NOERROR, 0)) goto error_return; ++ndests; bitset_empty (accepts); } } return ndests; error_return: for (j = 0; j < ndests; ++j) re_node_set_free (dests_node + j); return -1; } #ifdef RE_ENABLE_I18N /* Check how many bytes the node `dfa->nodes[node_idx]' accepts. Return the number of the bytes the node accepts. STR_IDX is the current index of the input string. This function handles the nodes which can accept one character, or one collating element like '.', '[a-z]', opposite to the other nodes can only accept one byte. */ static int internal_function check_node_accept_bytes (const re_dfa_t *dfa, int node_idx, const re_string_t *input, int str_idx) { const re_token_t *node = dfa->nodes + node_idx; int char_len, elem_len; int i; wint_t wc; if (BE (node->type == OP_UTF8_PERIOD, 0)) { unsigned char c = re_string_byte_at (input, str_idx), d; if (BE (c < 0xc2, 1)) return 0; if (str_idx + 2 > input->len) return 0; d = re_string_byte_at (input, str_idx + 1); if (c < 0xe0) return (d < 0x80 || d > 0xbf) ? 0 : 2; else if (c < 0xf0) { char_len = 3; if (c == 0xe0 && d < 0xa0) return 0; } else if (c < 0xf8) { char_len = 4; if (c == 0xf0 && d < 0x90) return 0; } else if (c < 0xfc) { char_len = 5; if (c == 0xf8 && d < 0x88) return 0; } else if (c < 0xfe) { char_len = 6; if (c == 0xfc && d < 0x84) return 0; } else return 0; if (str_idx + char_len > input->len) return 0; for (i = 1; i < char_len; ++i) { d = re_string_byte_at (input, str_idx + i); if (d < 0x80 || d > 0xbf) return 0; } return char_len; } char_len = re_string_char_size_at (input, str_idx); if (node->type == OP_PERIOD) { if (char_len <= 1) return 0; /* FIXME: I don't think this if is needed, as both '\n' and '\0' are char_len == 1. */ /* '.' accepts any one character except the following two cases. */ if ((!(dfa->syntax & RE_DOT_NEWLINE) && re_string_byte_at (input, str_idx) == '\n') || ((dfa->syntax & RE_DOT_NOT_NULL) && re_string_byte_at (input, str_idx) == '\0')) return 0; return char_len; } elem_len = re_string_elem_size_at (input, str_idx); wc = __btowc(*(input->mbs+str_idx)); if (((elem_len <= 1 && char_len <= 1) || char_len == 0) && (wc != WEOF && wc < SBC_MAX)) return 0; if (node->type == COMPLEX_BRACKET) { const re_charset_t *cset = node->opr.mbcset; # ifdef _LIBC const unsigned char *pin = ((const unsigned char *) re_string_get_buffer (input) + str_idx); int j; uint32_t nrules; # endif /* _LIBC */ int match_len = 0; wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars) ? re_string_wchar_at (input, str_idx) : 0); /* match with multibyte character? */ for (i = 0; i < cset->nmbchars; ++i) if (wc == cset->mbchars[i]) { match_len = char_len; goto check_node_accept_bytes_match; } /* match with character_class? */ for (i = 0; i < cset->nchar_classes; ++i) { wctype_t wt = cset->char_classes[i]; if (__iswctype (wc, wt)) { match_len = char_len; goto check_node_accept_bytes_match; } } # ifdef _LIBC nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); if (nrules != 0) { unsigned int in_collseq = 0; const int32_t *table, *indirect; const unsigned char *weights, *extra; const char *collseqwc; /* This #include defines a local function! */ # include /* match with collating_symbol? */ if (cset->ncoll_syms) extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); for (i = 0; i < cset->ncoll_syms; ++i) { const unsigned char *coll_sym = extra + cset->coll_syms[i]; /* Compare the length of input collating element and the length of current collating element. */ if (*coll_sym != elem_len) continue; /* Compare each bytes. */ for (j = 0; j < *coll_sym; j++) if (pin[j] != coll_sym[1 + j]) break; if (j == *coll_sym) { /* Match if every bytes is equal. */ match_len = j; goto check_node_accept_bytes_match; } } if (cset->nranges) { if (elem_len <= char_len) { collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); in_collseq = __collseq_table_lookup (collseqwc, wc); } else in_collseq = find_collation_sequence_value (pin, elem_len); } /* match with range expression? */ for (i = 0; i < cset->nranges; ++i) if (cset->range_starts[i] <= in_collseq && in_collseq <= cset->range_ends[i]) { match_len = elem_len; goto check_node_accept_bytes_match; } /* match with equivalence_class? */ if (cset->nequiv_classes) { const unsigned char *cp = pin; table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); int32_t idx = findidx (&cp); if (idx > 0) for (i = 0; i < cset->nequiv_classes; ++i) { int32_t equiv_class_idx = cset->equiv_classes[i]; size_t weight_len = weights[idx & 0xffffff]; if (weight_len == weights[equiv_class_idx & 0xffffff] && (idx >> 24) == (equiv_class_idx >> 24)) { int cnt = 0; idx &= 0xffffff; equiv_class_idx &= 0xffffff; while (cnt <= weight_len && (weights[equiv_class_idx + 1 + cnt] == weights[idx + 1 + cnt])) ++cnt; if (cnt > weight_len) { match_len = elem_len; goto check_node_accept_bytes_match; } } } } } else # endif /* _LIBC */ { /* match with range expression? */ #if __GNUC__ >= 2 wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'}; #else wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; cmp_buf[2] = wc; #endif for (i = 0; i < cset->nranges; ++i) { cmp_buf[0] = cset->range_starts[i]; cmp_buf[4] = cset->range_ends[i]; if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) { match_len = char_len; goto check_node_accept_bytes_match; } } } check_node_accept_bytes_match: if (!cset->non_match) return match_len; else { if (match_len > 0) return 0; else return (elem_len > char_len) ? elem_len : char_len; } } return 0; } # ifdef _LIBC static unsigned int internal_function find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len) { uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); if (nrules == 0) { if (mbs_len == 1) { /* No valid character. Match it as a single byte character. */ const unsigned char *collseq = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); return collseq[mbs[0]]; } return UINT_MAX; } else { int32_t idx; const unsigned char *extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); int32_t extrasize = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra; for (idx = 0; idx < extrasize;) { int mbs_cnt, found = 0; int32_t elem_mbs_len; /* Skip the name of collating element name. */ idx = idx + extra[idx] + 1; elem_mbs_len = extra[idx++]; if (mbs_len == elem_mbs_len) { for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt) if (extra[idx + mbs_cnt] != mbs[mbs_cnt]) break; if (mbs_cnt == elem_mbs_len) /* Found the entry. */ found = 1; } /* Skip the byte sequence of the collating element. */ idx += elem_mbs_len; /* Adjust for the alignment. */ idx = (idx + 3) & ~3; /* Skip the collation sequence value. */ idx += sizeof (uint32_t); /* Skip the wide char sequence of the collating element. */ idx = idx + sizeof (uint32_t) * (extra[idx] + 1); /* If we found the entry, return the sequence value. */ if (found) return *(uint32_t *) (extra + idx); /* Skip the collation sequence value. */ idx += sizeof (uint32_t); } return UINT_MAX; } } # endif /* _LIBC */ #endif /* RE_ENABLE_I18N */ /* Check whether the node accepts the byte which is IDX-th byte of the INPUT. */ static int internal_function check_node_accept (const re_match_context_t *mctx, const re_token_t *node, int idx) { unsigned char ch; ch = re_string_byte_at (&mctx->input, idx); switch (node->type) { case CHARACTER: if (node->opr.c != ch) return 0; break; case SIMPLE_BRACKET: if (!bitset_contain (node->opr.sbcset, ch)) return 0; break; #ifdef RE_ENABLE_I18N case OP_UTF8_PERIOD: if (ch >= 0x80) return 0; /* FALLTHROUGH */ #endif case OP_PERIOD: if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE)) || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL))) return 0; break; default: return 0; } if (node->constraint) { /* The node has constraints. Check whether the current context satisfies the constraints. */ unsigned int context = re_string_context_at (&mctx->input, idx, mctx->eflags); if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) return 0; } return 1; } /* Extend the buffers, if the buffers have run out. */ static reg_errcode_t internal_function extend_buffers (re_match_context_t *mctx) { reg_errcode_t ret; re_string_t *pstr = &mctx->input; /* Avoid overflow. */ if (BE (INT_MAX / 2 / sizeof (re_dfastate_t *) <= (size_t)pstr->bufs_len, 0)) return REG_ESPACE; /* Double the lengthes of the buffers. */ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); if (BE (ret != REG_NOERROR, 0)) return ret; if (mctx->state_log != NULL) { /* And double the length of state_log. */ /* XXX We have no indication of the size of this buffer. If this allocation fail we have no indication that the state_log array does not have the right size. */ re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *, pstr->bufs_len + 1); if (BE (new_array == NULL, 0)) return REG_ESPACE; mctx->state_log = new_array; } /* Then reconstruct the buffers. */ if (pstr->icase) { #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) { ret = build_wcs_upper_buffer (pstr); if (BE (ret != REG_NOERROR, 0)) return ret; } else #endif /* RE_ENABLE_I18N */ build_upper_buffer (pstr); } else { #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) build_wcs_buffer (pstr); else #endif /* RE_ENABLE_I18N */ { if (pstr->trans != NULL) re_string_translate_buffer (pstr); } } return REG_NOERROR; } /* Functions for matching context. */ /* Initialize MCTX. */ static reg_errcode_t internal_function match_ctx_init (re_match_context_t *mctx, int eflags, int n) { mctx->eflags = eflags; mctx->match_last = -1; if (n > 0) { mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n); mctx->sub_tops = re_malloc (re_sub_match_top_t *, n); if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0)) return REG_ESPACE; } /* Already zero-ed by the caller. else mctx->bkref_ents = NULL; mctx->nbkref_ents = 0; mctx->nsub_tops = 0; */ mctx->abkref_ents = n; mctx->max_mb_elem_len = 1; mctx->asub_tops = n; return REG_NOERROR; } /* Clean the entries which depend on the current input in MCTX. This function must be invoked when the matcher changes the start index of the input, or changes the input string. */ static void internal_function match_ctx_clean (re_match_context_t *mctx) { int st_idx; for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) { int sl_idx; re_sub_match_top_t *top = mctx->sub_tops[st_idx]; for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx) { re_sub_match_last_t *last = top->lasts[sl_idx]; re_free (last->path.array); re_free (last); } re_free (top->lasts); if (top->path) { re_free (top->path->array); re_free (top->path); } free (top); } mctx->nsub_tops = 0; mctx->nbkref_ents = 0; } /* Free all the memory associated with MCTX. */ static void internal_function match_ctx_free (re_match_context_t *mctx) { /* First, free all the memory associated with MCTX->SUB_TOPS. */ match_ctx_clean (mctx); re_free (mctx->sub_tops); re_free (mctx->bkref_ents); } /* Add a new backreference entry to MCTX. Note that we assume that caller never call this function with duplicate entry, and call with STR_IDX which isn't smaller than any existing entry. */ static reg_errcode_t internal_function match_ctx_add_entry (re_match_context_t *mctx, int node, int str_idx, int from, int to) { if (mctx->nbkref_ents >= mctx->abkref_ents) { struct re_backref_cache_entry* new_entry; new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry, mctx->abkref_ents * 2); if (BE (new_entry == NULL, 0)) { re_free (mctx->bkref_ents); return REG_ESPACE; } mctx->bkref_ents = new_entry; memset (mctx->bkref_ents + mctx->nbkref_ents, '\0', sizeof (struct re_backref_cache_entry) * mctx->abkref_ents); mctx->abkref_ents *= 2; } if (mctx->nbkref_ents > 0 && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx) mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1; mctx->bkref_ents[mctx->nbkref_ents].node = node; mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx; mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from; mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to; /* This is a cache that saves negative results of check_dst_limits_calc_pos. If bit N is clear, means that this entry won't epsilon-transition to an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If it is set, check_dst_limits_calc_pos_1 will recurse and try to find one such node. A backreference does not epsilon-transition unless it is empty, so set to all zeros if FROM != TO. */ mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map = (from == to ? ~0 : 0); mctx->bkref_ents[mctx->nbkref_ents++].more = 0; if (mctx->max_mb_elem_len < to - from) mctx->max_mb_elem_len = to - from; return REG_NOERROR; } /* Search for the first entry which has the same str_idx, or -1 if none is found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ static int internal_function search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx) { int left, right, mid, last; last = right = mctx->nbkref_ents; for (left = 0; left < right;) { mid = (left + right) / 2; if (mctx->bkref_ents[mid].str_idx < str_idx) left = mid + 1; else right = mid; } if (left < last && mctx->bkref_ents[left].str_idx == str_idx) return left; else return -1; } /* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches at STR_IDX. */ static reg_errcode_t internal_function match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx) { #ifdef DEBUG assert (mctx->sub_tops != NULL); assert (mctx->asub_tops > 0); #endif if (BE (mctx->nsub_tops == mctx->asub_tops, 0)) { int new_asub_tops = mctx->asub_tops * 2; re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops, re_sub_match_top_t *, new_asub_tops); if (BE (new_array == NULL, 0)) return REG_ESPACE; mctx->sub_tops = new_array; mctx->asub_tops = new_asub_tops; } mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0)) return REG_ESPACE; mctx->sub_tops[mctx->nsub_tops]->node = node; mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx; return REG_NOERROR; } /* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */ static re_sub_match_last_t * internal_function match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx) { re_sub_match_last_t *new_entry; if (BE (subtop->nlasts == subtop->alasts, 0)) { int new_alasts = 2 * subtop->alasts + 1; re_sub_match_last_t **new_array = re_realloc (subtop->lasts, re_sub_match_last_t *, new_alasts); if (BE (new_array == NULL, 0)) return NULL; subtop->lasts = new_array; subtop->alasts = new_alasts; } new_entry = calloc (1, sizeof (re_sub_match_last_t)); if (BE (new_entry != NULL, 1)) { subtop->lasts[subtop->nlasts] = new_entry; new_entry->node = node; new_entry->str_idx = str_idx; ++subtop->nlasts; } return new_entry; } static void internal_function sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, re_dfastate_t **limited_sts, int last_node, int last_str_idx) { sctx->sifted_states = sifted_sts; sctx->limited_states = limited_sts; sctx->last_node = last_node; sctx->last_str_idx = last_str_idx; re_node_set_init_empty (&sctx->limits); } git2r/src/libgit2/deps/regex/regex.h0000644000175000017500000005320713765070734017131 0ustar nileshnilesh#include #include /* Definitions for data structures and routines for the regular expression library. Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _REGEX_H #define _REGEX_H 1 #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifndef _LIBC #define __USE_GNU 1 #endif /** * Remapped the POSIX entry points with a 'gnu_' prefix to use the * bundled regex library on Solaris. * 2015-05-03: Stefan Widgren */ # define regfree(preg) gnu_regfree (preg) # define regexec(pr, st, nm, pm, ef) gnu_regexec (pr, st, nm, pm, ef) # define regcomp(preg, pattern, cflags) gnu_regcomp (preg, pattern, cflags) # define regerror(errcode, preg, errbuf, errbuf_size) \ gnu_regerror(errcode, preg, errbuf, errbuf_size) /* Allow the use in C++ code. */ #ifdef __cplusplus extern "C" { #endif /* The following two types have to be signed and unsigned integer type wide enough to hold a value of a pointer. For most ANSI compilers ptrdiff_t and size_t should be likely OK. Still size of these two types is 2 for Microsoft C. Ugh... */ typedef long int s_reg_t; typedef unsigned long int active_reg_t; /* The following bits are used to determine the regexp syntax we recognize. The set/not-set meanings are chosen so that Emacs syntax remains the value 0. The bits are given in alphabetical order, and the definitions shifted by one from the previous bit; thus, when we add or remove a bit, only one other definition need change. */ typedef unsigned long int reg_syntax_t; #ifdef __USE_GNU /* If this bit is not set, then \ inside a bracket expression is literal. If set, then such a \ quotes the following character. */ # define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) /* If this bit is not set, then + and ? are operators, and \+ and \? are literals. If set, then \+ and \? are operators and + and ? are literals. */ # define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) /* If this bit is set, then character classes are supported. They are: [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. If not set, then character classes are not supported. */ # define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) /* If this bit is set, then ^ and $ are always anchors (outside bracket expressions, of course). If this bit is not set, then it depends: ^ is an anchor if it is at the beginning of a regular expression or after an open-group or an alternation operator; $ is an anchor if it is at the end of a regular expression, or before a close-group or an alternation operator. This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because POSIX draft 11.2 says that * etc. in leading positions is undefined. We already implemented a previous draft which made those constructs invalid, though, so we haven't changed the code back. */ # define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) /* If this bit is set, then special characters are always special regardless of where they are in the pattern. If this bit is not set, then special characters are special only in some contexts; otherwise they are ordinary. Specifically, * + ? and intervals are only special when not after the beginning, open-group, or alternation operator. */ # define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) /* If this bit is set, then *, +, ?, and { cannot be first in an re or immediately after an alternation or begin-group operator. */ # define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) /* If this bit is set, then . matches newline. If not set, then it doesn't. */ # define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) /* If this bit is set, then . doesn't match NUL. If not set, then it does. */ # define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) /* If this bit is set, nonmatching lists [^...] do not match newline. If not set, they do. */ # define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) /* If this bit is set, either \{...\} or {...} defines an interval, depending on RE_NO_BK_BRACES. If not set, \{, \}, {, and } are literals. */ # define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) /* If this bit is set, +, ? and | aren't recognized as operators. If not set, they are. */ # define RE_LIMITED_OPS (RE_INTERVALS << 1) /* If this bit is set, newline is an alternation operator. If not set, newline is literal. */ # define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) /* If this bit is set, then `{...}' defines an interval, and \{ and \} are literals. If not set, then `\{...\}' defines an interval. */ # define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) /* If this bit is set, (...) defines a group, and \( and \) are literals. If not set, \(...\) defines a group, and ( and ) are literals. */ # define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) /* If this bit is set, then \ matches . If not set, then \ is a back-reference. */ # define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) /* If this bit is set, then | is an alternation operator, and \| is literal. If not set, then \| is an alternation operator, and | is literal. */ # define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) /* If this bit is set, then an ending range point collating higher than the starting range point, as in [z-a], is invalid. If not set, then when ending range point collates higher than the starting range point, the range is ignored. */ # define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) /* If this bit is set, then an unmatched ) is ordinary. If not set, then an unmatched ) is invalid. */ # define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) /* If this bit is set, succeed as soon as we match the whole pattern, without further backtracking. */ # define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) /* If this bit is set, do not process the GNU regex operators. If not set, then the GNU regex operators are recognized. */ # define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) /* If this bit is set, a syntactically invalid interval is treated as a string of ordinary characters. For example, the ERE 'a{1' is treated as 'a\{1'. */ # define RE_INVALID_INTERVAL_ORD (RE_NO_GNU_OPS << 1) /* If this bit is set, then ignore case when matching. If not set, then case is significant. */ # define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) /* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only for ^, because it is difficult to scan the regex backwards to find whether ^ should be special. */ # define RE_CARET_ANCHORS_HERE (RE_ICASE << 1) /* If this bit is set, then \{ cannot be first in an bre or immediately after an alternation or begin-group operator. */ # define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1) /* If this bit is set, then no_sub will be set to 1 during re_compile_pattern. */ #define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1) #endif /* This global variable defines the particular regexp syntax to use (for some interfaces). When a regexp is compiled, the syntax used is stored in the pattern buffer, so changing this does not affect already-compiled regexps. */ extern reg_syntax_t re_syntax_options; #ifdef __USE_GNU /* Define combinations of the above bits for the standard possibilities. (The [[[ comments delimit what gets put into the Texinfo file, so don't delete them!) */ /* [[[begin syntaxes]]] */ #define RE_SYNTAX_EMACS 0 #define RE_SYNTAX_AWK \ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) #define RE_SYNTAX_GNU_AWK \ ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ | RE_INVALID_INTERVAL_ORD) \ & ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS \ | RE_CONTEXT_INVALID_OPS )) #define RE_SYNTAX_POSIX_AWK \ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ | RE_INTERVALS | RE_NO_GNU_OPS \ | RE_INVALID_INTERVAL_ORD) #define RE_SYNTAX_GREP \ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ | RE_NEWLINE_ALT) #define RE_SYNTAX_EGREP \ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ | RE_NO_BK_VBAR) #define RE_SYNTAX_POSIX_EGREP \ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ | RE_INVALID_INTERVAL_ORD) /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC #define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC /* Syntax bits common to both basic and extended POSIX regex syntax. */ #define _RE_SYNTAX_POSIX_COMMON \ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ | RE_INTERVALS | RE_NO_EMPTY_RANGES) #define RE_SYNTAX_POSIX_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP) /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this isn't minimal, since other operators, such as \`, aren't disabled. */ #define RE_SYNTAX_POSIX_MINIMAL_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) #define RE_SYNTAX_POSIX_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) /* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is removed and RE_NO_BK_REFS is added. */ #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) /* [[[end syntaxes]]] */ /* Maximum number of duplicates an interval can allow. Some systems (erroneously) define this in other header files, but we want our value, so remove any previous define. */ # ifdef RE_DUP_MAX # undef RE_DUP_MAX # endif /* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ # define RE_DUP_MAX (0x7fff) #endif /* POSIX `cflags' bits (i.e., information for `regcomp'). */ /* If this bit is set, then use extended regular expression syntax. If not set, then use basic regular expression syntax. */ #define REG_EXTENDED 1 /* If this bit is set, then ignore case when matching. If not set, then case is significant. */ #define REG_ICASE (REG_EXTENDED << 1) /* If this bit is set, then anchors do not match at newline characters in the string. If not set, then anchors do match at newlines. */ #define REG_NEWLINE (REG_ICASE << 1) /* If this bit is set, then report only success or fail in regexec. If not set, then returns differ between not matching and errors. */ #define REG_NOSUB (REG_NEWLINE << 1) /* POSIX `eflags' bits (i.e., information for regexec). */ /* If this bit is set, then the beginning-of-line operator doesn't match the beginning of the string (presumably because it's not the beginning of a line). If not set, then the beginning-of-line operator does match the beginning of the string. */ #define REG_NOTBOL 1 /* Like REG_NOTBOL, except for the end-of-line. */ #define REG_NOTEOL (1 << 1) /* Use PMATCH[0] to delimit the start and end of the search in the buffer. */ #define REG_STARTEND (1 << 2) /* If any error codes are removed, changed, or added, update the `re_error_msg' table in regex.c. */ typedef enum { #if defined _XOPEN_SOURCE || defined __USE_XOPEN2K REG_ENOSYS = -1, /* This will never happen for this implementation. */ #endif REG_NOERROR = 0, /* Success. */ REG_NOMATCH, /* Didn't find a match (for regexec). */ /* POSIX regcomp return error codes. (In the order listed in the standard.) */ REG_BADPAT, /* Invalid pattern. */ REG_ECOLLATE, /* Inalid collating element. */ REG_ECTYPE, /* Invalid character class name. */ REG_EESCAPE, /* Trailing backslash. */ REG_ESUBREG, /* Invalid back reference. */ REG_EBRACK, /* Unmatched left bracket. */ REG_EPAREN, /* Parenthesis imbalance. */ REG_EBRACE, /* Unmatched \{. */ REG_BADBR, /* Invalid contents of \{\}. */ REG_ERANGE, /* Invalid range end. */ REG_ESPACE, /* Ran out of memory. */ REG_BADRPT, /* No preceding re for repetition op. */ /* Error codes we've added. */ REG_EEND, /* Premature end. */ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ } reg_errcode_t; /* This data structure represents a compiled pattern. Before calling the pattern compiler, the fields `buffer', `allocated', `fastmap', `translate', and `no_sub' can be set. After the pattern has been compiled, the `re_nsub' field is available. All other fields are private to the regex routines. */ #ifndef RE_TRANSLATE_TYPE # define __RE_TRANSLATE_TYPE unsigned char * # ifdef __USE_GNU # define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE # endif #endif #ifdef __USE_GNU # define __REPB_PREFIX(name) name #else # define __REPB_PREFIX(name) __##name #endif struct re_pattern_buffer { /* Space that holds the compiled pattern. It is declared as `unsigned char *' because its elements are sometimes used as array indexes. */ unsigned char *__REPB_PREFIX(buffer); /* Number of bytes to which `buffer' points. */ unsigned long int __REPB_PREFIX(allocated); /* Number of bytes actually used in `buffer'. */ unsigned long int __REPB_PREFIX(used); /* Syntax setting with which the pattern was compiled. */ reg_syntax_t __REPB_PREFIX(syntax); /* Pointer to a fastmap, if any, otherwise zero. re_search uses the fastmap, if there is one, to skip over impossible starting points for matches. */ char *__REPB_PREFIX(fastmap); /* Either a translate table to apply to all characters before comparing them, or zero for no translation. The translation is applied to a pattern when it is compiled and to a string when it is matched. */ __RE_TRANSLATE_TYPE __REPB_PREFIX(translate); /* Number of subexpressions found by the compiler. */ size_t re_nsub; /* Zero if this pattern cannot match the empty string, one else. Well, in truth it's used only in `re_search_2', to see whether or not we should use the fastmap, so we don't set this absolutely perfectly; see `re_compile_fastmap' (the `duplicate' case). */ unsigned __REPB_PREFIX(can_be_null) : 1; /* If REGS_UNALLOCATED, allocate space in the `regs' structure for `max (RE_NREGS, re_nsub + 1)' groups. If REGS_REALLOCATE, reallocate space if necessary. If REGS_FIXED, use what's there. */ #ifdef __USE_GNU # define REGS_UNALLOCATED 0 # define REGS_REALLOCATE 1 # define REGS_FIXED 2 #endif unsigned __REPB_PREFIX(regs_allocated) : 2; /* Set to zero when `regex_compile' compiles a pattern; set to one by `re_compile_fastmap' if it updates the fastmap. */ unsigned __REPB_PREFIX(fastmap_accurate) : 1; /* If set, `re_match_2' does not return information about subexpressions. */ unsigned __REPB_PREFIX(no_sub) : 1; /* If set, a beginning-of-line anchor doesn't match at the beginning of the string. */ unsigned __REPB_PREFIX(not_bol) : 1; /* Similarly for an end-of-line anchor. */ unsigned __REPB_PREFIX(not_eol) : 1; /* If true, an anchor at a newline matches. */ unsigned __REPB_PREFIX(newline_anchor) : 1; }; typedef struct re_pattern_buffer regex_t; /* Type for byte offsets within the string. POSIX mandates this. */ typedef int regoff_t; #ifdef __USE_GNU /* This is the structure we store register match data in. See regex.texinfo for a full description of what registers match. */ struct re_registers { unsigned num_regs; regoff_t *start; regoff_t *end; }; /* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, `re_match_2' returns information about at least this many registers the first time a `regs' structure is passed. */ # ifndef RE_NREGS # define RE_NREGS 30 # endif #endif /* POSIX specification for registers. Aside from the different names than `re_registers', POSIX uses an array of structures, instead of a structure of arrays. */ typedef struct { regoff_t rm_so; /* Byte offset from string's start to substring's start. */ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ } regmatch_t; /* Declarations for routines. */ #ifdef __USE_GNU /* Sets the current default syntax to SYNTAX, and return the old syntax. You can also simply assign to the `re_syntax_options' variable. */ extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax); /* Compile the regular expression PATTERN, with length LENGTH and syntax given by the global `re_syntax_options', into the buffer BUFFER. Return NULL if successful, and an error string if not. */ extern const char *re_compile_pattern (const char *__pattern, size_t __length, struct re_pattern_buffer *__buffer); /* Compile a fastmap for the compiled pattern in BUFFER; used to accelerate searches. Return 0 if successful and -2 if was an internal error. */ extern int re_compile_fastmap (struct re_pattern_buffer *__buffer); /* Search in the string STRING (with length LENGTH) for the pattern compiled into BUFFER. Start searching at position START, for RANGE characters. Return the starting position of the match, -1 for no match, or -2 for an internal error. Also return register information in REGS (if REGS and BUFFER->no_sub are nonzero). */ extern int re_search (struct re_pattern_buffer *__buffer, const char *__cstring, int __length, int __start, int __range, struct re_registers *__regs); /* Like `re_search', but search in the concatenation of STRING1 and STRING2. Also, stop searching at index START + STOP. */ extern int re_search_2 (struct re_pattern_buffer *__buffer, const char *__string1, int __length1, const char *__string2, int __length2, int __start, int __range, struct re_registers *__regs, int __stop); /* Like `re_search', but return how many characters in STRING the regexp in BUFFER matched, starting at position START. */ extern int re_match (struct re_pattern_buffer *__buffer, const char *__cstring, int __length, int __start, struct re_registers *__regs); /* Relates to `re_match' as `re_search_2' relates to `re_search'. */ extern int re_match_2 (struct re_pattern_buffer *__buffer, const char *__string1, int __length1, const char *__string2, int __length2, int __start, struct re_registers *__regs, int __stop); /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using BUFFER and REGS will use this memory for recording register information. STARTS and ENDS must be allocated with malloc, and must each be at least `NUM_REGS * sizeof (regoff_t)' bytes long. If NUM_REGS == 0, then subsequent matches should allocate their own register data. Unless this function is called, the first search or match using PATTERN_BUFFER will allocate its own register data, without freeing the old data. */ extern void re_set_registers (struct re_pattern_buffer *__buffer, struct re_registers *__regs, unsigned int __num_regs, regoff_t *__starts, regoff_t *__ends); #endif /* Use GNU */ #if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD) # ifndef _CRAY /* 4.2 bsd compatibility. */ extern char *re_comp (const char *); extern int re_exec (const char *); # endif #endif /* GCC 2.95 and later have "__restrict"; C99 compilers have "restrict", and "configure" may have defined "restrict". */ #ifndef __restrict # if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) # if defined restrict || 199901L <= __STDC_VERSION__ # define __restrict restrict # else # define __restrict # endif # endif #endif /* gcc 3.1 and up support the [restrict] syntax. */ #ifndef __restrict_arr # if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \ && !defined __GNUG__ # define __restrict_arr __restrict # else # define __restrict_arr # endif #endif /* POSIX compatibility. */ extern int regcomp (regex_t *__restrict __preg, const char *__restrict __pattern, int __cflags); extern int regexec (const regex_t *__restrict __preg, const char *__restrict __cstring, size_t __nmatch, regmatch_t __pmatch[__restrict_arr], int __eflags); extern size_t regerror (int __errcode, const regex_t *__restrict __preg, char *__restrict __errbuf, size_t __errbuf_size); extern void regfree (regex_t *__preg); #ifdef __cplusplus } #endif /* C++ */ #endif /* regex.h */ git2r/src/libgit2/deps/regex/COPYING0000644000175000017500000006364213765070734016705 0ustar nileshnilesh GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! git2r/src/libgit2/deps/regex/regex.c0000644000175000017500000000621113765070734017115 0ustar nileshnilesh/* Extended regular expression matching and search library. Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Isamu Hasegawa . The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" /* Make sure noone compiles this code with a C++ compiler. */ #ifdef __cplusplus # error "This is C code, use a C compiler" #endif #ifdef _LIBC /* We have to keep the namespace clean. */ # define regfree(preg) __regfree (preg) # define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) # define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) # define regerror(errcode, preg, errbuf, errbuf_size) \ __regerror(errcode, preg, errbuf, errbuf_size) # define re_set_registers(bu, re, nu, st, en) \ __re_set_registers (bu, re, nu, st, en) # define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) # define re_match(bufp, string, size, pos, regs) \ __re_match (bufp, string, size, pos, regs) # define re_search(bufp, string, size, startpos, range, regs) \ __re_search (bufp, string, size, startpos, range, regs) # define re_compile_pattern(pattern, length, bufp) \ __re_compile_pattern (pattern, length, bufp) # define re_set_syntax(syntax) __re_set_syntax (syntax) # define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) # define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) # include "../locale/localeinfo.h" #endif #if defined (_MSC_VER) #include /* for size_t */ #endif /* On some systems, limits.h sets RE_DUP_MAX to a lower value than GNU regex allows. Include it before , which correctly #undefs RE_DUP_MAX and sets it to the right value. */ #include #ifdef GAWK #undef alloca #define alloca alloca_is_bad_you_should_never_use_it #endif #include #include "regex_internal.h" #include "regex_internal.c" #ifdef GAWK # define bool int # ifndef true # define true (1) # endif # ifndef false # define false (0) # endif #endif #include "regcomp.c" #include "regexec.c" /* Binary backward compatibility. */ #if _LIBC # include # if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.") int re_max_failures = 2000; # endif #endif git2r/src/libgit2/deps/regex/regex_internal.c0000644000175000017500000013506313765070734021021 0ustar nileshnilesh/* Extended regular expression matching and search library. Copyright (C) 2002-2006, 2010 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Isamu Hasegawa . The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ static void re_string_construct_common (const char *str, int len, re_string_t *pstr, RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) internal_function; static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, unsigned int hash) internal_function; static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, unsigned int context, unsigned int hash) internal_function; #ifdef GAWK #undef MAX /* safety */ static size_t MAX(size_t a, size_t b) { return (a > b ? a : b); } #endif /* Functions for string operation. */ /* This function allocate the buffers. It is necessary to call re_string_reconstruct before using the object. */ static reg_errcode_t internal_function re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len, RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) { reg_errcode_t ret; int init_buf_len; /* Ensure at least one character fits into the buffers. */ if (init_len < dfa->mb_cur_max) init_len = dfa->mb_cur_max; init_buf_len = (len + 1 < init_len) ? len + 1: init_len; re_string_construct_common (str, len, pstr, trans, icase, dfa); ret = re_string_realloc_buffers (pstr, init_buf_len); if (BE (ret != REG_NOERROR, 0)) return ret; pstr->word_char = dfa->word_char; pstr->word_ops_used = dfa->word_ops_used; pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len; pstr->valid_raw_len = pstr->valid_len; return REG_NOERROR; } /* This function allocate the buffers, and initialize them. */ static reg_errcode_t internal_function re_string_construct (re_string_t *pstr, const char *str, int len, RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) { reg_errcode_t ret; memset (pstr, '\0', sizeof (re_string_t)); re_string_construct_common (str, len, pstr, trans, icase, dfa); if (len > 0) { ret = re_string_realloc_buffers (pstr, len + 1); if (BE (ret != REG_NOERROR, 0)) return ret; } pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; if (icase) { #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { while (1) { ret = build_wcs_upper_buffer (pstr); if (BE (ret != REG_NOERROR, 0)) return ret; if (pstr->valid_raw_len >= len) break; if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max) break; ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); if (BE (ret != REG_NOERROR, 0)) return ret; } } else #endif /* RE_ENABLE_I18N */ build_upper_buffer (pstr); } else { #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) build_wcs_buffer (pstr); else #endif /* RE_ENABLE_I18N */ { if (trans != NULL) re_string_translate_buffer (pstr); else { pstr->valid_len = pstr->bufs_len; pstr->valid_raw_len = pstr->bufs_len; } } } return REG_NOERROR; } /* Helper functions for re_string_allocate, and re_string_construct. */ static reg_errcode_t internal_function re_string_realloc_buffers (re_string_t *pstr, int new_buf_len) { #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) { wint_t *new_wcs; /* Avoid overflow in realloc. */ const size_t max_object_size = MAX (sizeof (wint_t), sizeof (int)); if (BE (SIZE_MAX / max_object_size < new_buf_len, 0)) return REG_ESPACE; new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len); if (BE (new_wcs == NULL, 0)) return REG_ESPACE; pstr->wcs = new_wcs; if (pstr->offsets != NULL) { int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len); if (BE (new_offsets == NULL, 0)) return REG_ESPACE; pstr->offsets = new_offsets; } } #endif /* RE_ENABLE_I18N */ if (pstr->mbs_allocated) { unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char, new_buf_len); if (BE (new_mbs == NULL, 0)) return REG_ESPACE; pstr->mbs = new_mbs; } pstr->bufs_len = new_buf_len; return REG_NOERROR; } static void internal_function re_string_construct_common (const char *str, int len, re_string_t *pstr, RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) { pstr->raw_mbs = (const unsigned char *) str; pstr->len = len; pstr->raw_len = len; pstr->trans = trans; pstr->icase = icase ? 1 : 0; pstr->mbs_allocated = (trans != NULL || icase); pstr->mb_cur_max = dfa->mb_cur_max; pstr->is_utf8 = dfa->is_utf8; pstr->map_notascii = dfa->map_notascii; pstr->stop = pstr->len; pstr->raw_stop = pstr->stop; } #ifdef RE_ENABLE_I18N /* Build wide character buffer PSTR->WCS. If the byte sequence of the string are: (0), (1), (0), (1), Then wide character buffer will be: , WEOF , , WEOF , We use WEOF for padding, they indicate that the position isn't a first byte of a multibyte character. Note that this function assumes PSTR->VALID_LEN elements are already built and starts from PSTR->VALID_LEN. */ static void internal_function build_wcs_buffer (re_string_t *pstr) { #ifdef _LIBC unsigned char buf[MB_LEN_MAX]; assert (MB_LEN_MAX >= pstr->mb_cur_max); #else unsigned char buf[64]; #endif mbstate_t prev_st; int byte_idx, end_idx, remain_len; size_t mbclen; /* Build the buffers from pstr->valid_len to either pstr->len or pstr->bufs_len. */ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; for (byte_idx = pstr->valid_len; byte_idx < end_idx;) { wchar_t wc; const char *p; remain_len = end_idx - byte_idx; prev_st = pstr->cur_state; /* Apply the translation if we need. */ if (BE (pstr->trans != NULL, 0)) { int i, ch; for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) { ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i]; buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch]; } p = (const char *) buf; } else p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx; mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); if (BE (mbclen == (size_t) -2, 0)) { /* The buffer doesn't have enough space, finish to build. */ pstr->cur_state = prev_st; break; } else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0)) { /* We treat these cases as a singlebyte character. */ mbclen = 1; wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; if (BE (pstr->trans != NULL, 0)) wc = pstr->trans[wc]; pstr->cur_state = prev_st; } /* Write wide character and padding. */ pstr->wcs[byte_idx++] = wc; /* Write paddings. */ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) pstr->wcs[byte_idx++] = WEOF; } pstr->valid_len = byte_idx; pstr->valid_raw_len = byte_idx; } /* Build wide character buffer PSTR->WCS like build_wcs_buffer, but for REG_ICASE. */ static reg_errcode_t internal_function build_wcs_upper_buffer (re_string_t *pstr) { mbstate_t prev_st; int src_idx, byte_idx, end_idx, remain_len; size_t mbclen; #ifdef _LIBC char buf[MB_LEN_MAX]; assert (MB_LEN_MAX >= pstr->mb_cur_max); #else char buf[64]; #endif byte_idx = pstr->valid_len; end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; /* The following optimization assumes that ASCII characters can be mapped to wide characters with a simple cast. */ if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed) { while (byte_idx < end_idx) { wchar_t wc; if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]) && mbsinit (&pstr->cur_state)) { /* In case of a singlebyte character. */ pstr->mbs[byte_idx] = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]); /* The next step uses the assumption that wchar_t is encoded ASCII-safe: all ASCII values can be converted like this. */ pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx]; ++byte_idx; continue; } remain_len = end_idx - byte_idx; prev_st = pstr->cur_state; mbclen = __mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx), remain_len, &pstr->cur_state); if (BE (mbclen + 2 > 2, 1)) { wchar_t wcu = wc; if (iswlower (wc)) { size_t mbcdlen; wcu = towupper (wc); mbcdlen = wcrtomb (buf, wcu, &prev_st); if (BE (mbclen == mbcdlen, 1)) memcpy (pstr->mbs + byte_idx, buf, mbclen); else { src_idx = byte_idx; goto offsets_needed; } } else memcpy (pstr->mbs + byte_idx, pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); pstr->wcs[byte_idx++] = wcu; /* Write paddings. */ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) pstr->wcs[byte_idx++] = WEOF; } else if (mbclen == (size_t) -1 || mbclen == 0) { /* It is an invalid character or '\0'. Just use the byte. */ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; pstr->mbs[byte_idx] = ch; /* And also cast it to wide char. */ pstr->wcs[byte_idx++] = (wchar_t) ch; if (BE (mbclen == (size_t) -1, 0)) pstr->cur_state = prev_st; } else { /* The buffer doesn't have enough space, finish to build. */ pstr->cur_state = prev_st; break; } } pstr->valid_len = byte_idx; pstr->valid_raw_len = byte_idx; return REG_NOERROR; } else for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;) { wchar_t wc; const char *p; offsets_needed: remain_len = end_idx - byte_idx; prev_st = pstr->cur_state; if (BE (pstr->trans != NULL, 0)) { int i, ch; for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) { ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i]; buf[i] = pstr->trans[ch]; } p = (const char *) buf; } else p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx; mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); if (BE (mbclen + 2 > 2, 1)) { wchar_t wcu = wc; if (iswlower (wc)) { size_t mbcdlen; wcu = towupper (wc); mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st); if (BE (mbclen == mbcdlen, 1)) memcpy (pstr->mbs + byte_idx, buf, mbclen); else if (mbcdlen != (size_t) -1) { size_t i; if (byte_idx + mbcdlen > pstr->bufs_len) { pstr->cur_state = prev_st; break; } if (pstr->offsets == NULL) { pstr->offsets = re_malloc (int, pstr->bufs_len); if (pstr->offsets == NULL) return REG_ESPACE; } if (!pstr->offsets_needed) { for (i = 0; i < (size_t) byte_idx; ++i) pstr->offsets[i] = i; pstr->offsets_needed = 1; } memcpy (pstr->mbs + byte_idx, buf, mbcdlen); pstr->wcs[byte_idx] = wcu; pstr->offsets[byte_idx] = src_idx; for (i = 1; i < mbcdlen; ++i) { pstr->offsets[byte_idx + i] = src_idx + (i < mbclen ? i : mbclen - 1); pstr->wcs[byte_idx + i] = WEOF; } pstr->len += mbcdlen - mbclen; if (pstr->raw_stop > src_idx) pstr->stop += mbcdlen - mbclen; end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; byte_idx += mbcdlen; src_idx += mbclen; continue; } else memcpy (pstr->mbs + byte_idx, p, mbclen); } else memcpy (pstr->mbs + byte_idx, p, mbclen); if (BE (pstr->offsets_needed != 0, 0)) { size_t i; for (i = 0; i < mbclen; ++i) pstr->offsets[byte_idx + i] = src_idx + i; } src_idx += mbclen; pstr->wcs[byte_idx++] = wcu; /* Write paddings. */ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) pstr->wcs[byte_idx++] = WEOF; } else if (mbclen == (size_t) -1 || mbclen == 0) { /* It is an invalid character or '\0'. Just use the byte. */ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx]; if (BE (pstr->trans != NULL, 0)) ch = pstr->trans [ch]; pstr->mbs[byte_idx] = ch; if (BE (pstr->offsets_needed != 0, 0)) pstr->offsets[byte_idx] = src_idx; ++src_idx; /* And also cast it to wide char. */ pstr->wcs[byte_idx++] = (wchar_t) ch; if (BE (mbclen == (size_t) -1, 0)) pstr->cur_state = prev_st; } else { /* The buffer doesn't have enough space, finish to build. */ pstr->cur_state = prev_st; break; } } pstr->valid_len = byte_idx; pstr->valid_raw_len = src_idx; return REG_NOERROR; } /* Skip characters until the index becomes greater than NEW_RAW_IDX. Return the index. */ static int internal_function re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc) { mbstate_t prev_st; int rawbuf_idx; size_t mbclen; wint_t wc = WEOF; /* Skip the characters which are not necessary to check. */ for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len; rawbuf_idx < new_raw_idx;) { wchar_t wc2; int remain_len = pstr->len - rawbuf_idx; prev_st = pstr->cur_state; mbclen = __mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx, remain_len, &pstr->cur_state); if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0)) { /* We treat these cases as a single byte character. */ if (mbclen == 0 || remain_len == 0) wc = L'\0'; else wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx); mbclen = 1; pstr->cur_state = prev_st; } else wc = (wint_t) wc2; /* Then proceed the next character. */ rawbuf_idx += mbclen; } *last_wc = (wint_t) wc; return rawbuf_idx; } #endif /* RE_ENABLE_I18N */ /* Build the buffer PSTR->MBS, and apply the translation if we need. This function is used in case of REG_ICASE. */ static void internal_function build_upper_buffer (re_string_t *pstr) { int char_idx, end_idx; end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx) { int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx]; if (BE (pstr->trans != NULL, 0)) ch = pstr->trans[ch]; if (islower (ch)) pstr->mbs[char_idx] = toupper (ch); else pstr->mbs[char_idx] = ch; } pstr->valid_len = char_idx; pstr->valid_raw_len = char_idx; } /* Apply TRANS to the buffer in PSTR. */ static void internal_function re_string_translate_buffer (re_string_t *pstr) { int buf_idx, end_idx; end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx) { int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx]; pstr->mbs[buf_idx] = pstr->trans[ch]; } pstr->valid_len = buf_idx; pstr->valid_raw_len = buf_idx; } /* This function re-construct the buffers. Concretely, convert to wide character in case of pstr->mb_cur_max > 1, convert to upper case in case of REG_ICASE, apply translation. */ static reg_errcode_t internal_function re_string_reconstruct (re_string_t *pstr, int idx, int eflags) { int offset = idx - pstr->raw_mbs_idx; if (BE (offset < 0, 0)) { /* Reset buffer. */ #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); #endif /* RE_ENABLE_I18N */ pstr->len = pstr->raw_len; pstr->stop = pstr->raw_stop; pstr->valid_len = 0; pstr->raw_mbs_idx = 0; pstr->valid_raw_len = 0; pstr->offsets_needed = 0; pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF : CONTEXT_NEWLINE | CONTEXT_BEGBUF); if (!pstr->mbs_allocated) pstr->mbs = (unsigned char *) pstr->raw_mbs; offset = idx; } if (BE (offset != 0, 1)) { /* Should the already checked characters be kept? */ if (BE (offset < pstr->valid_raw_len, 1)) { /* Yes, move them to the front of the buffer. */ #ifdef RE_ENABLE_I18N if (BE (pstr->offsets_needed, 0)) { int low = 0, high = pstr->valid_len, mid; do { mid = (high + low) / 2; if (pstr->offsets[mid] > offset) high = mid; else if (pstr->offsets[mid] < offset) low = mid + 1; else break; } while (low < high); if (pstr->offsets[mid] < offset) ++mid; pstr->tip_context = re_string_context_at (pstr, mid - 1, eflags); /* This can be quite complicated, so handle specially only the common and easy case where the character with different length representation of lower and upper case is present at or after offset. */ if (pstr->valid_len > offset && mid == offset && pstr->offsets[mid] == offset) { memmove (pstr->wcs, pstr->wcs + offset, (pstr->valid_len - offset) * sizeof (wint_t)); memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); pstr->valid_len -= offset; pstr->valid_raw_len -= offset; for (low = 0; low < pstr->valid_len; low++) pstr->offsets[low] = pstr->offsets[low + offset] - offset; } else { /* Otherwise, just find out how long the partial multibyte character at offset is and fill it with WEOF/255. */ pstr->len = pstr->raw_len - idx + offset; pstr->stop = pstr->raw_stop - idx + offset; pstr->offsets_needed = 0; while (mid > 0 && pstr->offsets[mid - 1] == offset) --mid; while (mid < pstr->valid_len) if (pstr->wcs[mid] != WEOF) break; else ++mid; if (mid == pstr->valid_len) pstr->valid_len = 0; else { pstr->valid_len = pstr->offsets[mid] - offset; if (pstr->valid_len) { for (low = 0; low < pstr->valid_len; ++low) pstr->wcs[low] = WEOF; memset (pstr->mbs, 255, pstr->valid_len); } } pstr->valid_raw_len = pstr->valid_len; } } else #endif { pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags); #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) memmove (pstr->wcs, pstr->wcs + offset, (pstr->valid_len - offset) * sizeof (wint_t)); #endif /* RE_ENABLE_I18N */ if (BE (pstr->mbs_allocated, 0)) memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); pstr->valid_len -= offset; pstr->valid_raw_len -= offset; #if DEBUG assert (pstr->valid_len > 0); #endif } } else { #ifdef RE_ENABLE_I18N /* No, skip all characters until IDX. */ int prev_valid_len = pstr->valid_len; if (BE (pstr->offsets_needed, 0)) { pstr->len = pstr->raw_len - idx + offset; pstr->stop = pstr->raw_stop - idx + offset; pstr->offsets_needed = 0; } #endif pstr->valid_len = 0; #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) { int wcs_idx; wint_t wc = WEOF; if (pstr->is_utf8) { const unsigned char *raw, *p, *end; /* Special case UTF-8. Multi-byte chars start with any byte other than 0x80 - 0xbf. */ raw = pstr->raw_mbs + pstr->raw_mbs_idx; end = raw + (offset - pstr->mb_cur_max); if (end < pstr->raw_mbs) end = pstr->raw_mbs; p = raw + offset - 1; #ifdef _LIBC /* We know the wchar_t encoding is UCS4, so for the simple case, ASCII characters, skip the conversion step. */ if (isascii (*p) && BE (pstr->trans == NULL, 1)) { memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); /* pstr->valid_len = 0; */ wc = (wchar_t) *p; } else #endif for (; p >= end; --p) if ((*p & 0xc0) != 0x80) { mbstate_t cur_state; wchar_t wc2; int mlen = raw + pstr->len - p; unsigned char buf[6]; size_t mbclen; if (BE (pstr->trans != NULL, 0)) { int i = mlen < 6 ? mlen : 6; while (--i >= 0) buf[i] = pstr->trans[p[i]]; } /* XXX Don't use mbrtowc, we know which conversion to use (UTF-8 -> UCS4). */ memset (&cur_state, 0, sizeof (cur_state)); mbclen = __mbrtowc (&wc2, (const char *) p, mlen, &cur_state); if (raw + offset - p <= mbclen && mbclen < (size_t) -2) { memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); pstr->valid_len = mbclen - (raw + offset - p); wc = wc2; } break; } } if (wc == WEOF) pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; if (wc == WEOF) pstr->tip_context = re_string_context_at (pstr, prev_valid_len - 1, eflags); else pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) ? CONTEXT_WORD : ((IS_WIDE_NEWLINE (wc) && pstr->newline_anchor) ? CONTEXT_NEWLINE : 0)); if (BE (pstr->valid_len, 0)) { for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) pstr->wcs[wcs_idx] = WEOF; if (pstr->mbs_allocated) memset (pstr->mbs, 255, pstr->valid_len); } pstr->valid_raw_len = pstr->valid_len; } else #endif /* RE_ENABLE_I18N */ { int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; pstr->valid_raw_len = 0; if (pstr->trans) c = pstr->trans[c]; pstr->tip_context = (bitset_contain (pstr->word_char, c) ? CONTEXT_WORD : ((IS_NEWLINE (c) && pstr->newline_anchor) ? CONTEXT_NEWLINE : 0)); } } if (!BE (pstr->mbs_allocated, 0)) pstr->mbs += offset; } pstr->raw_mbs_idx = idx; pstr->len -= offset; pstr->stop -= offset; /* Then build the buffers. */ #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) { if (pstr->icase) { reg_errcode_t ret = build_wcs_upper_buffer (pstr); if (BE (ret != REG_NOERROR, 0)) return ret; } else build_wcs_buffer (pstr); } else #endif /* RE_ENABLE_I18N */ if (BE (pstr->mbs_allocated, 0)) { if (pstr->icase) build_upper_buffer (pstr); else if (pstr->trans != NULL) re_string_translate_buffer (pstr); } else pstr->valid_len = pstr->len; pstr->cur_idx = 0; return REG_NOERROR; } static unsigned char internal_function __attribute ((pure)) re_string_peek_byte_case (const re_string_t *pstr, int idx) { int ch, off; /* Handle the common (easiest) cases first. */ if (BE (!pstr->mbs_allocated, 1)) return re_string_peek_byte (pstr, idx); #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1 && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx)) return re_string_peek_byte (pstr, idx); #endif off = pstr->cur_idx + idx; #ifdef RE_ENABLE_I18N if (pstr->offsets_needed) off = pstr->offsets[off]; #endif ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; #ifdef RE_ENABLE_I18N /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I this function returns CAPITAL LETTER I instead of first byte of DOTLESS SMALL LETTER I. The latter would confuse the parser, since peek_byte_case doesn't advance cur_idx in any way. */ if (pstr->offsets_needed && !isascii (ch)) return re_string_peek_byte (pstr, idx); #endif return ch; } static unsigned char internal_function __attribute ((pure)) re_string_fetch_byte_case (re_string_t *pstr) { if (BE (!pstr->mbs_allocated, 1)) return re_string_fetch_byte (pstr); #ifdef RE_ENABLE_I18N if (pstr->offsets_needed) { int off, ch; /* For tr_TR.UTF-8 [[:islower:]] there is [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip in that case the whole multi-byte character and return the original letter. On the other side, with [[: DOTLESS SMALL LETTER I return [[:I, as doing anything else would complicate things too much. */ if (!re_string_first_byte (pstr, pstr->cur_idx)) return re_string_fetch_byte (pstr); off = pstr->offsets[pstr->cur_idx]; ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; if (! isascii (ch)) return re_string_fetch_byte (pstr); re_string_skip_bytes (pstr, re_string_char_size_at (pstr, pstr->cur_idx)); return ch; } #endif return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++]; } static void internal_function re_string_destruct (re_string_t *pstr) { #ifdef RE_ENABLE_I18N re_free (pstr->wcs); re_free (pstr->offsets); #endif /* RE_ENABLE_I18N */ if (pstr->mbs_allocated) re_free (pstr->mbs); } /* Return the context at IDX in INPUT. */ static unsigned int internal_function re_string_context_at (const re_string_t *input, int idx, int eflags) { int c; if (BE (idx < 0, 0)) /* In this case, we use the value stored in input->tip_context, since we can't know the character in input->mbs[-1] here. */ return input->tip_context; if (BE (idx == input->len, 0)) return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF : CONTEXT_NEWLINE | CONTEXT_ENDBUF); #ifdef RE_ENABLE_I18N if (input->mb_cur_max > 1) { wint_t wc; int wc_idx = idx; while(input->wcs[wc_idx] == WEOF) { #ifdef DEBUG /* It must not happen. */ assert (wc_idx >= 0); #endif --wc_idx; if (wc_idx < 0) return input->tip_context; } wc = input->wcs[wc_idx]; if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) return CONTEXT_WORD; return (IS_WIDE_NEWLINE (wc) && input->newline_anchor ? CONTEXT_NEWLINE : 0); } else #endif { c = re_string_byte_at (input, idx); if (bitset_contain (input->word_char, c)) return CONTEXT_WORD; return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0; } } /* Functions for set operation. */ static reg_errcode_t internal_function re_node_set_alloc (re_node_set *set, int size) { /* * ADR: valgrind says size can be 0, which then doesn't * free the block of size 0. Harumph. This seems * to work ok, though. */ if (size == 0) { memset(set, 0, sizeof(*set)); return REG_NOERROR; } set->alloc = size; set->nelem = 0; set->elems = re_malloc (int, size); if (BE (set->elems == NULL, 0)) return REG_ESPACE; return REG_NOERROR; } static reg_errcode_t internal_function re_node_set_init_1 (re_node_set *set, int elem) { set->alloc = 1; set->nelem = 1; set->elems = re_malloc (int, 1); if (BE (set->elems == NULL, 0)) { set->alloc = set->nelem = 0; return REG_ESPACE; } set->elems[0] = elem; return REG_NOERROR; } static reg_errcode_t internal_function re_node_set_init_2 (re_node_set *set, int elem1, int elem2) { set->alloc = 2; set->elems = re_malloc (int, 2); if (BE (set->elems == NULL, 0)) return REG_ESPACE; if (elem1 == elem2) { set->nelem = 1; set->elems[0] = elem1; } else { set->nelem = 2; if (elem1 < elem2) { set->elems[0] = elem1; set->elems[1] = elem2; } else { set->elems[0] = elem2; set->elems[1] = elem1; } } return REG_NOERROR; } static reg_errcode_t internal_function re_node_set_init_copy (re_node_set *dest, const re_node_set *src) { dest->nelem = src->nelem; if (src->nelem > 0) { dest->alloc = dest->nelem; dest->elems = re_malloc (int, dest->alloc); if (BE (dest->elems == NULL, 0)) { dest->alloc = dest->nelem = 0; return REG_ESPACE; } memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); } else re_node_set_init_empty (dest); return REG_NOERROR; } /* Calculate the intersection of the sets SRC1 and SRC2. And merge it to DEST. Return value indicate the error code or REG_NOERROR if succeeded. Note: We assume dest->elems is NULL, when dest->alloc is 0. */ static reg_errcode_t internal_function re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1, const re_node_set *src2) { int i1, i2, is, id, delta, sbase; if (src1->nelem == 0 || src2->nelem == 0) return REG_NOERROR; /* We need dest->nelem + 2 * elems_in_intersection; this is a conservative estimate. */ if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) { int new_alloc = src1->nelem + src2->nelem + dest->alloc; int *new_elems = re_realloc (dest->elems, int, new_alloc); if (BE (new_elems == NULL, 0)) return REG_ESPACE; dest->elems = new_elems; dest->alloc = new_alloc; } /* Find the items in the intersection of SRC1 and SRC2, and copy into the top of DEST those that are not already in DEST itself. */ sbase = dest->nelem + src1->nelem + src2->nelem; i1 = src1->nelem - 1; i2 = src2->nelem - 1; id = dest->nelem - 1; for (;;) { if (src1->elems[i1] == src2->elems[i2]) { /* Try to find the item in DEST. Maybe we could binary search? */ while (id >= 0 && dest->elems[id] > src1->elems[i1]) --id; if (id < 0 || dest->elems[id] != src1->elems[i1]) dest->elems[--sbase] = src1->elems[i1]; if (--i1 < 0 || --i2 < 0) break; } /* Lower the highest of the two items. */ else if (src1->elems[i1] < src2->elems[i2]) { if (--i2 < 0) break; } else { if (--i1 < 0) break; } } id = dest->nelem - 1; is = dest->nelem + src1->nelem + src2->nelem - 1; delta = is - sbase + 1; /* Now copy. When DELTA becomes zero, the remaining DEST elements are already in place; this is more or less the same loop that is in re_node_set_merge. */ dest->nelem += delta; if (delta > 0 && id >= 0) for (;;) { if (dest->elems[is] > dest->elems[id]) { /* Copy from the top. */ dest->elems[id + delta--] = dest->elems[is--]; if (delta == 0) break; } else { /* Slide from the bottom. */ dest->elems[id + delta] = dest->elems[id]; if (--id < 0) break; } } /* Copy remaining SRC elements. */ memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int)); return REG_NOERROR; } /* Calculate the union set of the sets SRC1 and SRC2. And store it to DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ static reg_errcode_t internal_function re_node_set_init_union (re_node_set *dest, const re_node_set *src1, const re_node_set *src2) { int i1, i2, id; if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) { dest->alloc = src1->nelem + src2->nelem; dest->elems = re_malloc (int, dest->alloc); if (BE (dest->elems == NULL, 0)) return REG_ESPACE; } else { if (src1 != NULL && src1->nelem > 0) return re_node_set_init_copy (dest, src1); else if (src2 != NULL && src2->nelem > 0) return re_node_set_init_copy (dest, src2); else re_node_set_init_empty (dest); return REG_NOERROR; } for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) { if (src1->elems[i1] > src2->elems[i2]) { dest->elems[id++] = src2->elems[i2++]; continue; } if (src1->elems[i1] == src2->elems[i2]) ++i2; dest->elems[id++] = src1->elems[i1++]; } if (i1 < src1->nelem) { memcpy (dest->elems + id, src1->elems + i1, (src1->nelem - i1) * sizeof (int)); id += src1->nelem - i1; } else if (i2 < src2->nelem) { memcpy (dest->elems + id, src2->elems + i2, (src2->nelem - i2) * sizeof (int)); id += src2->nelem - i2; } dest->nelem = id; return REG_NOERROR; } /* Calculate the union set of the sets DEST and SRC. And store it to DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ static reg_errcode_t internal_function re_node_set_merge (re_node_set *dest, const re_node_set *src) { int is, id, sbase, delta; if (src == NULL || src->nelem == 0) return REG_NOERROR; if (dest->alloc < 2 * src->nelem + dest->nelem) { int new_alloc = 2 * (src->nelem + dest->alloc); int *new_buffer = re_realloc (dest->elems, int, new_alloc); if (BE (new_buffer == NULL, 0)) return REG_ESPACE; dest->elems = new_buffer; dest->alloc = new_alloc; } if (BE (dest->nelem == 0, 0)) { dest->nelem = src->nelem; memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); return REG_NOERROR; } /* Copy into the top of DEST the items of SRC that are not found in DEST. Maybe we could binary search in DEST? */ for (sbase = dest->nelem + 2 * src->nelem, is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; ) { if (dest->elems[id] == src->elems[is]) is--, id--; else if (dest->elems[id] < src->elems[is]) dest->elems[--sbase] = src->elems[is--]; else /* if (dest->elems[id] > src->elems[is]) */ --id; } if (is >= 0) { /* If DEST is exhausted, the remaining items of SRC must be unique. */ sbase -= is + 1; memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int)); } id = dest->nelem - 1; is = dest->nelem + 2 * src->nelem - 1; delta = is - sbase + 1; if (delta == 0) return REG_NOERROR; /* Now copy. When DELTA becomes zero, the remaining DEST elements are already in place. */ dest->nelem += delta; for (;;) { if (dest->elems[is] > dest->elems[id]) { /* Copy from the top. */ dest->elems[id + delta--] = dest->elems[is--]; if (delta == 0) break; } else { /* Slide from the bottom. */ dest->elems[id + delta] = dest->elems[id]; if (--id < 0) { /* Copy remaining SRC elements. */ memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int)); break; } } } return REG_NOERROR; } /* Insert the new element ELEM to the re_node_set* SET. SET should not already have ELEM. return -1 if an error is occured, return 1 otherwise. */ static int internal_function re_node_set_insert (re_node_set *set, int elem) { int idx; /* In case the set is empty. */ if (set->alloc == 0) { if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1)) return 1; else return -1; } if (BE (set->nelem, 0) == 0) { /* We already guaranteed above that set->alloc != 0. */ set->elems[0] = elem; ++set->nelem; return 1; } /* Realloc if we need. */ if (set->alloc == set->nelem) { int *new_elems; set->alloc = set->alloc * 2; new_elems = re_realloc (set->elems, int, set->alloc); if (BE (new_elems == NULL, 0)) return -1; set->elems = new_elems; } /* Move the elements which follows the new element. Test the first element separately to skip a check in the inner loop. */ if (elem < set->elems[0]) { idx = 0; for (idx = set->nelem; idx > 0; idx--) set->elems[idx] = set->elems[idx - 1]; } else { for (idx = set->nelem; set->elems[idx - 1] > elem; idx--) set->elems[idx] = set->elems[idx - 1]; } /* Insert the new element. */ set->elems[idx] = elem; ++set->nelem; return 1; } /* Insert the new element ELEM to the re_node_set* SET. SET should not already have any element greater than or equal to ELEM. Return -1 if an error is occured, return 1 otherwise. */ static int internal_function re_node_set_insert_last (re_node_set *set, int elem) { /* Realloc if we need. */ if (set->alloc == set->nelem) { int *new_elems; set->alloc = (set->alloc + 1) * 2; new_elems = re_realloc (set->elems, int, set->alloc); if (BE (new_elems == NULL, 0)) return -1; set->elems = new_elems; } /* Insert the new element. */ set->elems[set->nelem++] = elem; return 1; } /* Compare two node sets SET1 and SET2. return 1 if SET1 and SET2 are equivalent, return 0 otherwise. */ static int internal_function __attribute ((pure)) re_node_set_compare (const re_node_set *set1, const re_node_set *set2) { int i; if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) return 0; for (i = set1->nelem ; --i >= 0 ; ) if (set1->elems[i] != set2->elems[i]) return 0; return 1; } /* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ static int internal_function __attribute ((pure)) re_node_set_contains (const re_node_set *set, int elem) { unsigned int idx, right, mid; if (set->nelem <= 0) return 0; /* Binary search the element. */ idx = 0; right = set->nelem - 1; while (idx < right) { mid = (idx + right) / 2; if (set->elems[mid] < elem) idx = mid + 1; else right = mid; } return set->elems[idx] == elem ? idx + 1 : 0; } static void internal_function re_node_set_remove_at (re_node_set *set, int idx) { if (idx < 0 || idx >= set->nelem) return; --set->nelem; for (; idx < set->nelem; idx++) set->elems[idx] = set->elems[idx + 1]; } /* Add the token TOKEN to dfa->nodes, and return the index of the token. Or return -1, if an error will be occured. */ static int internal_function re_dfa_add_node (re_dfa_t *dfa, re_token_t token) { if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0)) { size_t new_nodes_alloc = dfa->nodes_alloc * 2; int *new_nexts, *new_indices; re_node_set *new_edests, *new_eclosures; re_token_t *new_nodes; /* Avoid overflows in realloc. */ const size_t max_object_size = MAX (sizeof (re_token_t), MAX (sizeof (re_node_set), sizeof (int))); if (BE (SIZE_MAX / max_object_size < new_nodes_alloc, 0)) return -1; new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc); if (BE (new_nodes == NULL, 0)) return -1; dfa->nodes = new_nodes; new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc); new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc); new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc); new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc); if (BE (new_nexts == NULL || new_indices == NULL || new_edests == NULL || new_eclosures == NULL, 0)) return -1; dfa->nexts = new_nexts; dfa->org_indices = new_indices; dfa->edests = new_edests; dfa->eclosures = new_eclosures; dfa->nodes_alloc = new_nodes_alloc; } dfa->nodes[dfa->nodes_len] = token; dfa->nodes[dfa->nodes_len].constraint = 0; #ifdef RE_ENABLE_I18N dfa->nodes[dfa->nodes_len].accept_mb = (token.type == OP_PERIOD && dfa->mb_cur_max > 1) || token.type == COMPLEX_BRACKET; #endif dfa->nexts[dfa->nodes_len] = -1; re_node_set_init_empty (dfa->edests + dfa->nodes_len); re_node_set_init_empty (dfa->eclosures + dfa->nodes_len); return dfa->nodes_len++; } static inline unsigned int internal_function calc_state_hash (const re_node_set *nodes, unsigned int context) { unsigned int hash = nodes->nelem + context; int i; for (i = 0 ; i < nodes->nelem ; i++) hash += nodes->elems[i]; return hash; } /* Search for the state whose node_set is equivalent to NODES. Return the pointer to the state, if we found it in the DFA. Otherwise create the new one and return it. In case of an error return NULL and set the error code in ERR. Note: - We assume NULL as the invalid state, then it is possible that return value is NULL and ERR is REG_NOERROR. - We never return non-NULL value in case of any errors, it is for optimization. */ static re_dfastate_t * internal_function re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa, const re_node_set *nodes) { unsigned int hash; re_dfastate_t *new_state; struct re_state_table_entry *spot; int i; if (BE (nodes->nelem == 0, 0)) { *err = REG_NOERROR; return NULL; } hash = calc_state_hash (nodes, 0); spot = dfa->state_table + (hash & dfa->state_hash_mask); for (i = 0 ; i < spot->num ; i++) { re_dfastate_t *state = spot->array[i]; if (hash != state->hash) continue; if (re_node_set_compare (&state->nodes, nodes)) return state; } /* There are no appropriate state in the dfa, create the new one. */ new_state = create_ci_newstate (dfa, nodes, hash); if (BE (new_state == NULL, 0)) *err = REG_ESPACE; return new_state; } /* Search for the state whose node_set is equivalent to NODES and whose context is equivalent to CONTEXT. Return the pointer to the state, if we found it in the DFA. Otherwise create the new one and return it. In case of an error return NULL and set the error code in ERR. Note: - We assume NULL as the invalid state, then it is possible that return value is NULL and ERR is REG_NOERROR. - We never return non-NULL value in case of any errors, it is for optimization. */ static re_dfastate_t * internal_function re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa, const re_node_set *nodes, unsigned int context) { unsigned int hash; re_dfastate_t *new_state; struct re_state_table_entry *spot; int i; if (nodes->nelem == 0) { *err = REG_NOERROR; return NULL; } hash = calc_state_hash (nodes, context); spot = dfa->state_table + (hash & dfa->state_hash_mask); for (i = 0 ; i < spot->num ; i++) { re_dfastate_t *state = spot->array[i]; if (state->hash == hash && state->context == context && re_node_set_compare (state->entrance_nodes, nodes)) return state; } /* There are no appropriate state in `dfa', create the new one. */ new_state = create_cd_newstate (dfa, nodes, context, hash); if (BE (new_state == NULL, 0)) *err = REG_ESPACE; return new_state; } /* Finish initialization of the new state NEWSTATE, and using its hash value HASH put in the appropriate bucket of DFA's state table. Return value indicates the error code if failed. */ static reg_errcode_t register_state (const re_dfa_t *dfa, re_dfastate_t *newstate, unsigned int hash) { struct re_state_table_entry *spot; reg_errcode_t err; int i; newstate->hash = hash; err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem); if (BE (err != REG_NOERROR, 0)) return REG_ESPACE; for (i = 0; i < newstate->nodes.nelem; i++) { int elem = newstate->nodes.elems[i]; if (!IS_EPSILON_NODE (dfa->nodes[elem].type)) if (re_node_set_insert_last (&newstate->non_eps_nodes, elem) < 0) return REG_ESPACE; } spot = dfa->state_table + (hash & dfa->state_hash_mask); if (BE (spot->alloc <= spot->num, 0)) { int new_alloc = 2 * spot->num + 2; re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *, new_alloc); if (BE (new_array == NULL, 0)) return REG_ESPACE; spot->array = new_array; spot->alloc = new_alloc; } spot->array[spot->num++] = newstate; return REG_NOERROR; } static void free_state (re_dfastate_t *state) { re_node_set_free (&state->non_eps_nodes); re_node_set_free (&state->inveclosure); if (state->entrance_nodes != &state->nodes) { re_node_set_free (state->entrance_nodes); re_free (state->entrance_nodes); } re_node_set_free (&state->nodes); re_free (state->word_trtable); re_free (state->trtable); re_free (state); } /* Create the new state which is independ of contexts. Return the new state if succeeded, otherwise return NULL. */ static re_dfastate_t * internal_function create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, unsigned int hash) { int i; reg_errcode_t err; re_dfastate_t *newstate; newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); if (BE (newstate == NULL, 0)) return NULL; err = re_node_set_init_copy (&newstate->nodes, nodes); if (BE (err != REG_NOERROR, 0)) { re_free (newstate); return NULL; } newstate->entrance_nodes = &newstate->nodes; for (i = 0 ; i < nodes->nelem ; i++) { re_token_t *node = dfa->nodes + nodes->elems[i]; re_token_type_t type = node->type; if (type == CHARACTER && !node->constraint) continue; #ifdef RE_ENABLE_I18N newstate->accept_mb |= node->accept_mb; #endif /* RE_ENABLE_I18N */ /* If the state has the halt node, the state is a halt state. */ if (type == END_OF_RE) newstate->halt = 1; else if (type == OP_BACK_REF) newstate->has_backref = 1; else if (type == ANCHOR || node->constraint) newstate->has_constraint = 1; } err = register_state (dfa, newstate, hash); if (BE (err != REG_NOERROR, 0)) { free_state (newstate); newstate = NULL; } return newstate; } /* Create the new state which is depend on the context CONTEXT. Return the new state if succeeded, otherwise return NULL. */ static re_dfastate_t * internal_function create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, unsigned int context, unsigned int hash) { int i, nctx_nodes = 0; reg_errcode_t err; re_dfastate_t *newstate; newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); if (BE (newstate == NULL, 0)) return NULL; err = re_node_set_init_copy (&newstate->nodes, nodes); if (BE (err != REG_NOERROR, 0)) { re_free (newstate); return NULL; } newstate->context = context; newstate->entrance_nodes = &newstate->nodes; for (i = 0 ; i < nodes->nelem ; i++) { re_token_t *node = dfa->nodes + nodes->elems[i]; re_token_type_t type = node->type; unsigned int constraint = node->constraint; if (type == CHARACTER && !constraint) continue; #ifdef RE_ENABLE_I18N newstate->accept_mb |= node->accept_mb; #endif /* RE_ENABLE_I18N */ /* If the state has the halt node, the state is a halt state. */ if (type == END_OF_RE) newstate->halt = 1; else if (type == OP_BACK_REF) newstate->has_backref = 1; if (constraint) { if (newstate->entrance_nodes == &newstate->nodes) { newstate->entrance_nodes = re_malloc (re_node_set, 1); if (BE (newstate->entrance_nodes == NULL, 0)) { free_state (newstate); return NULL; } if (re_node_set_init_copy (newstate->entrance_nodes, nodes) != REG_NOERROR) return NULL; nctx_nodes = 0; newstate->has_constraint = 1; } if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context)) { re_node_set_remove_at (&newstate->nodes, i - nctx_nodes); ++nctx_nodes; } } } err = register_state (dfa, newstate, hash); if (BE (err != REG_NOERROR, 0)) { free_state (newstate); newstate = NULL; } return newstate; } git2r/src/libgit2/deps/regex/config.h0000644000175000017500000000013713765070734017256 0ustar nileshnilesh#ifndef _REGEX_CONFIG_H_ #define _REGEX_CONFIG_H_ # define GAWK # define NO_MBSUPPORT #endif git2r/src/libgit2/include/0000755000175000017500000000000014125111754015202 5ustar nileshnileshgit2r/src/libgit2/include/git2.h0000644000175000017500000000344314125111754016224 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_git_h__ #define INCLUDE_git_git_h__ #include "git2/annotated_commit.h" #include "git2/apply.h" #include "git2/attr.h" #include "git2/blob.h" #include "git2/blame.h" #include "git2/branch.h" #include "git2/buffer.h" #include "git2/cert.h" #include "git2/checkout.h" #include "git2/cherrypick.h" #include "git2/clone.h" #include "git2/commit.h" #include "git2/common.h" #include "git2/config.h" #include "git2/credential.h" #include "git2/deprecated.h" #include "git2/describe.h" #include "git2/diff.h" #include "git2/email.h" #include "git2/errors.h" #include "git2/filter.h" #include "git2/global.h" #include "git2/graph.h" #include "git2/ignore.h" #include "git2/index.h" #include "git2/indexer.h" #include "git2/mailmap.h" #include "git2/merge.h" #include "git2/message.h" #include "git2/net.h" #include "git2/notes.h" #include "git2/object.h" #include "git2/odb.h" #include "git2/odb_backend.h" #include "git2/oid.h" #include "git2/pack.h" #include "git2/patch.h" #include "git2/pathspec.h" #include "git2/proxy.h" #include "git2/rebase.h" #include "git2/refdb.h" #include "git2/reflog.h" #include "git2/refs.h" #include "git2/refspec.h" #include "git2/remote.h" #include "git2/repository.h" #include "git2/reset.h" #include "git2/revert.h" #include "git2/revparse.h" #include "git2/revwalk.h" #include "git2/signature.h" #include "git2/stash.h" #include "git2/status.h" #include "git2/submodule.h" #include "git2/tag.h" #include "git2/transport.h" #include "git2/transaction.h" #include "git2/tree.h" #include "git2/types.h" #include "git2/version.h" #include "git2/worktree.h" #endif git2r/src/libgit2/include/git2/0000755000175000017500000000000014146643373016061 5ustar nileshnileshgit2r/src/libgit2/include/git2/commit.h0000644000175000017500000004335114125111754017516 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_commit_h__ #define INCLUDE_git_commit_h__ #include "common.h" #include "types.h" #include "oid.h" #include "object.h" /** * @file git2/commit.h * @brief Git commit parsing, formatting routines * @defgroup git_commit Git commit parsing, formatting routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Lookup a commit object from a repository. * * The returned object should be released with `git_commit_free` when no * longer needed. * * @param commit pointer to the looked up commit * @param repo the repo to use when locating the commit. * @param id identity of the commit to locate. If the object is * an annotated tag it will be peeled back to the commit. * @return 0 or an error code */ GIT_EXTERN(int) git_commit_lookup( git_commit **commit, git_repository *repo, const git_oid *id); /** * Lookup a commit object from a repository, given a prefix of its * identifier (short id). * * The returned object should be released with `git_commit_free` when no * longer needed. * * @see git_object_lookup_prefix * * @param commit pointer to the looked up commit * @param repo the repo to use when locating the commit. * @param id identity of the commit to locate. If the object is * an annotated tag it will be peeled back to the commit. * @param len the length of the short identifier * @return 0 or an error code */ GIT_EXTERN(int) git_commit_lookup_prefix( git_commit **commit, git_repository *repo, const git_oid *id, size_t len); /** * Close an open commit * * This is a wrapper around git_object_free() * * IMPORTANT: * It *is* necessary to call this method when you stop * using a commit. Failure to do so will cause a memory leak. * * @param commit the commit to close */ GIT_EXTERN(void) git_commit_free(git_commit *commit); /** * Get the id of a commit. * * @param commit a previously loaded commit. * @return object identity for the commit. */ GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *commit); /** * Get the repository that contains the commit. * * @param commit A previously loaded commit. * @return Repository that contains this commit. */ GIT_EXTERN(git_repository *) git_commit_owner(const git_commit *commit); /** * Get the encoding for the message of a commit, * as a string representing a standard encoding name. * * The encoding may be NULL if the `encoding` header * in the commit is missing; in that case UTF-8 is assumed. * * @param commit a previously loaded commit. * @return NULL, or the encoding */ GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit); /** * Get the full message of a commit. * * The returned message will be slightly prettified by removing any * potential leading newlines. * * @param commit a previously loaded commit. * @return the message of a commit */ GIT_EXTERN(const char *) git_commit_message(const git_commit *commit); /** * Get the full raw message of a commit. * * @param commit a previously loaded commit. * @return the raw message of a commit */ GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit); /** * Get the short "summary" of the git commit message. * * The returned message is the summary of the commit, comprising the * first paragraph of the message with whitespace trimmed and squashed. * * @param commit a previously loaded commit. * @return the summary of a commit or NULL on error */ GIT_EXTERN(const char *) git_commit_summary(git_commit *commit); /** * Get the long "body" of the git commit message. * * The returned message is the body of the commit, comprising * everything but the first paragraph of the message. Leading and * trailing whitespaces are trimmed. * * @param commit a previously loaded commit. * @return the body of a commit or NULL when no the message only * consists of a summary */ GIT_EXTERN(const char *) git_commit_body(git_commit *commit); /** * Get the commit time (i.e. committer time) of a commit. * * @param commit a previously loaded commit. * @return the time of a commit */ GIT_EXTERN(git_time_t) git_commit_time(const git_commit *commit); /** * Get the commit timezone offset (i.e. committer's preferred timezone) of a commit. * * @param commit a previously loaded commit. * @return positive or negative timezone offset, in minutes from UTC */ GIT_EXTERN(int) git_commit_time_offset(const git_commit *commit); /** * Get the committer of a commit. * * @param commit a previously loaded commit. * @return the committer of a commit */ GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit); /** * Get the author of a commit. * * @param commit a previously loaded commit. * @return the author of a commit */ GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit); /** * Get the committer of a commit, using the mailmap to map names and email * addresses to canonical real names and email addresses. * * Call `git_signature_free` to free the signature. * * @param out a pointer to store the resolved signature. * @param commit a previously loaded commit. * @param mailmap the mailmap to resolve with. (may be NULL) * @return 0 or an error code */ GIT_EXTERN(int) git_commit_committer_with_mailmap( git_signature **out, const git_commit *commit, const git_mailmap *mailmap); /** * Get the author of a commit, using the mailmap to map names and email * addresses to canonical real names and email addresses. * * Call `git_signature_free` to free the signature. * * @param out a pointer to store the resolved signature. * @param commit a previously loaded commit. * @param mailmap the mailmap to resolve with. (may be NULL) * @return 0 or an error code */ GIT_EXTERN(int) git_commit_author_with_mailmap( git_signature **out, const git_commit *commit, const git_mailmap *mailmap); /** * Get the full raw text of the commit header. * * @param commit a previously loaded commit * @return the header text of the commit */ GIT_EXTERN(const char *) git_commit_raw_header(const git_commit *commit); /** * Get the tree pointed to by a commit. * * @param tree_out pointer where to store the tree object * @param commit a previously loaded commit. * @return 0 or an error code */ GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, const git_commit *commit); /** * Get the id of the tree pointed to by a commit. This differs from * `git_commit_tree` in that no attempts are made to fetch an object * from the ODB. * * @param commit a previously loaded commit. * @return the id of tree pointed to by commit. */ GIT_EXTERN(const git_oid *) git_commit_tree_id(const git_commit *commit); /** * Get the number of parents of this commit * * @param commit a previously loaded commit. * @return integer of count of parents */ GIT_EXTERN(unsigned int) git_commit_parentcount(const git_commit *commit); /** * Get the specified parent of the commit. * * @param out Pointer where to store the parent commit * @param commit a previously loaded commit. * @param n the position of the parent (from 0 to `parentcount`) * @return 0 or an error code */ GIT_EXTERN(int) git_commit_parent( git_commit **out, const git_commit *commit, unsigned int n); /** * Get the oid of a specified parent for a commit. This is different from * `git_commit_parent`, which will attempt to load the parent commit from * the ODB. * * @param commit a previously loaded commit. * @param n the position of the parent (from 0 to `parentcount`) * @return the id of the parent, NULL on error. */ GIT_EXTERN(const git_oid *) git_commit_parent_id( const git_commit *commit, unsigned int n); /** * Get the commit object that is the th generation ancestor * of the named commit object, following only the first parents. * The returned commit has to be freed by the caller. * * Passing `0` as the generation number returns another instance of the * base commit itself. * * @param ancestor Pointer where to store the ancestor commit * @param commit a previously loaded commit. * @param n the requested generation * @return 0 on success; GIT_ENOTFOUND if no matching ancestor exists * or an error code */ GIT_EXTERN(int) git_commit_nth_gen_ancestor( git_commit **ancestor, const git_commit *commit, unsigned int n); /** * Get an arbitrary header field * * @param out the buffer to fill; existing content will be * overwritten * @param commit the commit to look in * @param field the header field to return * @return 0 on succeess, GIT_ENOTFOUND if the field does not exist, * or an error code */ GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field); /** * Extract the signature from a commit * * If the id is not for a commit, the error class will be * `GIT_ERROR_INVALID`. If the commit does not have a signature, the * error class will be `GIT_ERROR_OBJECT`. * * @param signature the signature block; existing content will be * overwritten * @param signed_data signed data; this is the commit contents minus the signature block; * existing content will be overwritten * @param repo the repository in which the commit exists * @param commit_id the commit from which to extract the data * @param field the name of the header field containing the signature * block; pass `NULL` to extract the default 'gpgsig' * @return 0 on success, GIT_ENOTFOUND if the id is not for a commit * or the commit does not have a signature. */ GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field); /** * Create new commit in the repository from a list of `git_object` pointers * * The message will **not** be cleaned up automatically. You can do that * with the `git_message_prettify()` function. * * @param id Pointer in which to store the OID of the newly created commit * * @param repo Repository where to store the commit * * @param update_ref If not NULL, name of the reference that * will be updated to point to this commit. If the reference * is not direct, it will be resolved to a direct reference. * Use "HEAD" to update the HEAD of the current branch and * make it point to this commit. If the reference doesn't * exist yet, it will be created. If it does exist, the first * parent must be the tip of this branch. * * @param author Signature with author and author time of commit * * @param committer Signature with committer and * commit time of commit * * @param message_encoding The encoding for the message in the * commit, represented with a standard encoding name. * E.g. "UTF-8". If NULL, no encoding header is written and * UTF-8 is assumed. * * @param message Full message for this commit * * @param tree An instance of a `git_tree` object that will * be used as the tree for the commit. This tree object must * also be owned by the given `repo`. * * @param parent_count Number of parents for this commit * * @param parents Array of `parent_count` pointers to `git_commit` * objects that will be used as the parents for this commit. This * array may be NULL if `parent_count` is 0 (root commit). All the * given commits must be owned by the `repo`. * * @return 0 or an error code * The created commit will be written to the Object Database and * the given reference will be updated to point to it */ GIT_EXTERN(int) git_commit_create( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, const git_commit *parents[]); /** * Create new commit in the repository using a variable argument list. * * The message will **not** be cleaned up automatically. You can do that * with the `git_message_prettify()` function. * * The parents for the commit are specified as a variable list of pointers * to `const git_commit *`. Note that this is a convenience method which may * not be safe to export for certain languages or compilers * * All other parameters remain the same as `git_commit_create()`. * * @see git_commit_create */ GIT_EXTERN(int) git_commit_create_v( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, ...); /** * Amend an existing commit by replacing only non-NULL values. * * This creates a new commit that is exactly the same as the old commit, * except that any non-NULL values will be updated. The new commit has * the same parents as the old commit. * * The `update_ref` value works as in the regular `git_commit_create()`, * updating the ref to point to the newly rewritten commit. If you want * to amend a commit that is not currently the tip of the branch and then * rewrite the following commits to reach a ref, pass this as NULL and * update the rest of the commit chain and ref separately. * * Unlike `git_commit_create()`, the `author`, `committer`, `message`, * `message_encoding`, and `tree` parameters can be NULL in which case this * will use the values from the original `commit_to_amend`. * * All parameters have the same meanings as in `git_commit_create()`. * * @see git_commit_create */ GIT_EXTERN(int) git_commit_amend( git_oid *id, const git_commit *commit_to_amend, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree); /** * Create a commit and write it into a buffer * * Create a commit as with `git_commit_create()` but instead of * writing it to the objectdb, write the contents of the object into a * buffer. * * @param out the buffer into which to write the commit object content * * @param repo Repository where the referenced tree and parents live * * @param author Signature with author and author time of commit * * @param committer Signature with committer and * commit time of commit * * @param message_encoding The encoding for the message in the * commit, represented with a standard encoding name. * E.g. "UTF-8". If NULL, no encoding header is written and * UTF-8 is assumed. * * @param message Full message for this commit * * @param tree An instance of a `git_tree` object that will * be used as the tree for the commit. This tree object must * also be owned by the given `repo`. * * @param parent_count Number of parents for this commit * * @param parents Array of `parent_count` pointers to `git_commit` * objects that will be used as the parents for this commit. This * array may be NULL if `parent_count` is 0 (root commit). All the * given commits must be owned by the `repo`. * * @return 0 or an error code */ GIT_EXTERN(int) git_commit_create_buffer( git_buf *out, git_repository *repo, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, const git_commit *parents[]); /** * Create a commit object from the given buffer and signature * * Given the unsigned commit object's contents, its signature and the * header field in which to store the signature, attach the signature * to the commit and write it into the given repository. * * @param out the resulting commit id * @param commit_content the content of the unsigned commit object * @param signature the signature to add to the commit. Leave `NULL` * to create a commit without adding a signature field. * @param signature_field which header field should contain this * signature. Leave `NULL` for the default of "gpgsig" * @return 0 or an error code */ GIT_EXTERN(int) git_commit_create_with_signature( git_oid *out, git_repository *repo, const char *commit_content, const char *signature, const char *signature_field); /** * Create an in-memory copy of a commit. The copy must be explicitly * free'd or it will leak. * * @param out Pointer to store the copy of the commit * @param source Original commit to copy */ GIT_EXTERN(int) git_commit_dup(git_commit **out, git_commit *source); /** * Commit creation callback: used when a function is going to create * commits (for example, in `git_rebase_commit`) to allow callers to * override the commit creation behavior. For example, users may * wish to sign commits by providing this information to * `git_commit_create_buffer`, signing that buffer, then calling * `git_commit_create_with_signature`. The resultant commit id * should be set in the `out` object id parameter. * * @param out pointer that this callback will populate with the object * id of the commit that is created * @param author the author name and time of the commit * @param committer the committer name and time of the commit * @param message_encoding the encoding of the given message, or NULL * to assume UTF8 * @param message the commit message * @param tree the tree to be committed * @param parent_count the number of parents for this commit * @param parents the commit parents * @param payload the payload pointer in the rebase options * @return 0 if this callback has created the commit and populated the out * parameter, GIT_PASSTHROUGH if the callback has not created a * commit and wants the calling function to create the commit as * if no callback had been specified, any other value to stop * and return a failure */ typedef int (*git_commit_create_cb)( git_oid *out, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, const git_commit *parents[], void *payload); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/tree.h0000644000175000017500000003427214125111754017167 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_tree_h__ #define INCLUDE_git_tree_h__ #include "common.h" #include "types.h" #include "oid.h" #include "object.h" /** * @file git2/tree.h * @brief Git tree parsing, loading routines * @defgroup git_tree Git tree parsing, loading routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Lookup a tree object from the repository. * * @param out Pointer to the looked up tree * @param repo The repo to use when locating the tree. * @param id Identity of the tree to locate. * @return 0 or an error code */ GIT_EXTERN(int) git_tree_lookup( git_tree **out, git_repository *repo, const git_oid *id); /** * Lookup a tree object from the repository, * given a prefix of its identifier (short id). * * @see git_object_lookup_prefix * * @param out pointer to the looked up tree * @param repo the repo to use when locating the tree. * @param id identity of the tree to locate. * @param len the length of the short identifier * @return 0 or an error code */ GIT_EXTERN(int) git_tree_lookup_prefix( git_tree **out, git_repository *repo, const git_oid *id, size_t len); /** * Close an open tree * * You can no longer use the git_tree pointer after this call. * * IMPORTANT: You MUST call this method when you stop using a tree to * release memory. Failure to do so will cause a memory leak. * * @param tree The tree to close */ GIT_EXTERN(void) git_tree_free(git_tree *tree); /** * Get the id of a tree. * * @param tree a previously loaded tree. * @return object identity for the tree. */ GIT_EXTERN(const git_oid *) git_tree_id(const git_tree *tree); /** * Get the repository that contains the tree. * * @param tree A previously loaded tree. * @return Repository that contains this tree. */ GIT_EXTERN(git_repository *) git_tree_owner(const git_tree *tree); /** * Get the number of entries listed in a tree * * @param tree a previously loaded tree. * @return the number of entries in the tree */ GIT_EXTERN(size_t) git_tree_entrycount(const git_tree *tree); /** * Lookup a tree entry by its filename * * This returns a git_tree_entry that is owned by the git_tree. You don't * have to free it, but you must not use it after the git_tree is released. * * @param tree a previously loaded tree. * @param filename the filename of the desired entry * @return the tree entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname( const git_tree *tree, const char *filename); /** * Lookup a tree entry by its position in the tree * * This returns a git_tree_entry that is owned by the git_tree. You don't * have to free it, but you must not use it after the git_tree is released. * * @param tree a previously loaded tree. * @param idx the position in the entry list * @return the tree entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex( const git_tree *tree, size_t idx); /** * Lookup a tree entry by SHA value. * * This returns a git_tree_entry that is owned by the git_tree. You don't * have to free it, but you must not use it after the git_tree is released. * * Warning: this must examine every entry in the tree, so it is not fast. * * @param tree a previously loaded tree. * @param id the sha being looked for * @return the tree entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byid( const git_tree *tree, const git_oid *id); /** * Retrieve a tree entry contained in a tree or in any of its subtrees, * given its relative path. * * Unlike the other lookup functions, the returned tree entry is owned by * the user and must be freed explicitly with `git_tree_entry_free()`. * * @param out Pointer where to store the tree entry * @param root Previously loaded tree which is the root of the relative path * @param path Path to the contained entry * @return 0 on success; GIT_ENOTFOUND if the path does not exist */ GIT_EXTERN(int) git_tree_entry_bypath( git_tree_entry **out, const git_tree *root, const char *path); /** * Duplicate a tree entry * * Create a copy of a tree entry. The returned copy is owned by the user, * and must be freed explicitly with `git_tree_entry_free()`. * * @param dest pointer where to store the copy * @param source tree entry to duplicate * @return 0 or an error code */ GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source); /** * Free a user-owned tree entry * * IMPORTANT: This function is only needed for tree entries owned by the * user, such as the ones returned by `git_tree_entry_dup()` or * `git_tree_entry_bypath()`. * * @param entry The entry to free */ GIT_EXTERN(void) git_tree_entry_free(git_tree_entry *entry); /** * Get the filename of a tree entry * * @param entry a tree entry * @return the name of the file */ GIT_EXTERN(const char *) git_tree_entry_name(const git_tree_entry *entry); /** * Get the id of the object pointed by the entry * * @param entry a tree entry * @return the oid of the object */ GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry); /** * Get the type of the object pointed by the entry * * @param entry a tree entry * @return the type of the pointed object */ GIT_EXTERN(git_object_t) git_tree_entry_type(const git_tree_entry *entry); /** * Get the UNIX file attributes of a tree entry * * @param entry a tree entry * @return filemode as an integer */ GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry); /** * Get the raw UNIX file attributes of a tree entry * * This function does not perform any normalization and is only useful * if you need to be able to recreate the original tree object. * * @param entry a tree entry * @return filemode as an integer */ GIT_EXTERN(git_filemode_t) git_tree_entry_filemode_raw(const git_tree_entry *entry); /** * Compare two tree entries * * @param e1 first tree entry * @param e2 second tree entry * @return <0 if e1 is before e2, 0 if e1 == e2, >0 if e1 is after e2 */ GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2); /** * Convert a tree entry to the git_object it points to. * * You must call `git_object_free()` on the object when you are done with it. * * @param object_out pointer to the converted object * @param repo repository where to lookup the pointed object * @param entry a tree entry * @return 0 or an error code */ GIT_EXTERN(int) git_tree_entry_to_object( git_object **object_out, git_repository *repo, const git_tree_entry *entry); /** * Create a new tree builder. * * The tree builder can be used to create or modify trees in memory and * write them as tree objects to the database. * * If the `source` parameter is not NULL, the tree builder will be * initialized with the entries of the given tree. * * If the `source` parameter is NULL, the tree builder will start with no * entries and will have to be filled manually. * * @param out Pointer where to store the tree builder * @param repo Repository in which to store the object * @param source Source tree to initialize the builder (optional) * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_treebuilder_new( git_treebuilder **out, git_repository *repo, const git_tree *source); /** * Clear all the entires in the builder * * @param bld Builder to clear * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_treebuilder_clear(git_treebuilder *bld); /** * Get the number of entries listed in a treebuilder * * @param bld a previously loaded treebuilder. * @return the number of entries in the treebuilder */ GIT_EXTERN(size_t) git_treebuilder_entrycount(git_treebuilder *bld); /** * Free a tree builder * * This will clear all the entries and free to builder. * Failing to free the builder after you're done using it * will result in a memory leak * * @param bld Builder to free */ GIT_EXTERN(void) git_treebuilder_free(git_treebuilder *bld); /** * Get an entry from the builder from its filename * * The returned entry is owned by the builder and should * not be freed manually. * * @param bld Tree builder * @param filename Name of the entry * @return pointer to the entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_treebuilder_get( git_treebuilder *bld, const char *filename); /** * Add or update an entry to the builder * * Insert a new entry for `filename` in the builder with the * given attributes. * * If an entry named `filename` already exists, its attributes * will be updated with the given ones. * * The optional pointer `out` can be used to retrieve a pointer to the * newly created/updated entry. Pass NULL if you do not need it. The * pointer may not be valid past the next operation in this * builder. Duplicate the entry if you want to keep it. * * By default the entry that you are inserting will be checked for * validity; that it exists in the object database and is of the * correct type. If you do not want this behavior, set the * `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` library option to false. * * @param out Pointer to store the entry (optional) * @param bld Tree builder * @param filename Filename of the entry * @param id SHA1 oid of the entry * @param filemode Folder attributes of the entry. This parameter must * be valued with one of the following entries: 0040000, 0100644, * 0100755, 0120000 or 0160000. * @return 0 or an error code */ GIT_EXTERN(int) git_treebuilder_insert( const git_tree_entry **out, git_treebuilder *bld, const char *filename, const git_oid *id, git_filemode_t filemode); /** * Remove an entry from the builder by its filename * * @param bld Tree builder * @param filename Filename of the entry to remove * @return 0 or an error code */ GIT_EXTERN(int) git_treebuilder_remove( git_treebuilder *bld, const char *filename); /** * Callback for git_treebuilder_filter * * The return value is treated as a boolean, with zero indicating that the * entry should be left alone and any non-zero value meaning that the * entry should be removed from the treebuilder list (i.e. filtered out). */ typedef int GIT_CALLBACK(git_treebuilder_filter_cb)( const git_tree_entry *entry, void *payload); /** * Selectively remove entries in the tree * * The `filter` callback will be called for each entry in the tree with a * pointer to the entry and the provided `payload`; if the callback returns * non-zero, the entry will be filtered (removed from the builder). * * @param bld Tree builder * @param filter Callback to filter entries * @param payload Extra data to pass to filter callback * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_treebuilder_filter( git_treebuilder *bld, git_treebuilder_filter_cb filter, void *payload); /** * Write the contents of the tree builder as a tree object * * The tree builder will be written to the given `repo`, and its * identifying SHA1 hash will be stored in the `id` pointer. * * @param id Pointer to store the OID of the newly written tree * @param bld Tree builder to write * @return 0 or an error code */ GIT_EXTERN(int) git_treebuilder_write( git_oid *id, git_treebuilder *bld); /** Callback for the tree traversal method */ typedef int GIT_CALLBACK(git_treewalk_cb)( const char *root, const git_tree_entry *entry, void *payload); /** Tree traversal modes */ typedef enum { GIT_TREEWALK_PRE = 0, /* Pre-order */ GIT_TREEWALK_POST = 1, /* Post-order */ } git_treewalk_mode; /** * Traverse the entries in a tree and its subtrees in post or pre order. * * The entries will be traversed in the specified order, children subtrees * will be automatically loaded as required, and the `callback` will be * called once per entry with the current (relative) root for the entry and * the entry data itself. * * If the callback returns a positive value, the passed entry will be * skipped on the traversal (in pre mode). A negative value stops the walk. * * @param tree The tree to walk * @param mode Traversal mode (pre or post-order) * @param callback Function to call on each tree entry * @param payload Opaque pointer to be passed on each callback * @return 0 or an error code */ GIT_EXTERN(int) git_tree_walk( const git_tree *tree, git_treewalk_mode mode, git_treewalk_cb callback, void *payload); /** * Create an in-memory copy of a tree. The copy must be explicitly * free'd or it will leak. * * @param out Pointer to store the copy of the tree * @param source Original tree to copy */ GIT_EXTERN(int) git_tree_dup(git_tree **out, git_tree *source); /** * The kind of update to perform */ typedef enum { /** Update or insert an entry at the specified path */ GIT_TREE_UPDATE_UPSERT, /** Remove an entry from the specified path */ GIT_TREE_UPDATE_REMOVE, } git_tree_update_t; /** * An action to perform during the update of a tree */ typedef struct { /** Update action. If it's an removal, only the path is looked at */ git_tree_update_t action; /** The entry's id */ git_oid id; /** The filemode/kind of object */ git_filemode_t filemode; /** The full path from the root tree */ const char *path; } git_tree_update; /** * Create a tree based on another one with the specified modifications * * Given the `baseline` perform the changes described in the list of * `updates` and create a new tree. * * This function is optimized for common file/directory addition, removal and * replacement in trees. It is much more efficient than reading the tree into a * `git_index` and modifying that, but in exchange it is not as flexible. * * Deleting and adding the same entry is undefined behaviour, changing * a tree to a blob or viceversa is not supported. * * @param out id of the new tree * @param repo the repository in which to create the tree, must be the * same as for `baseline` * @param baseline the tree to base these changes on * @param nupdates the number of elements in the update list * @param updates the list of updates to perform * @return 0 or an error code */ GIT_EXTERN(int) git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/cert.h0000644000175000017500000001027214125111754017157 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_cert_h__ #define INCLUDE_git_cert_h__ #include "common.h" #include "types.h" /** * @file git2/cert.h * @brief Git certificate objects * @defgroup git_cert Certificate objects * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Type of host certificate structure that is passed to the check callback */ typedef enum git_cert_t { /** * No information about the certificate is available. This may * happen when using curl. */ GIT_CERT_NONE, /** * The `data` argument to the callback will be a pointer to * the DER-encoded data. */ GIT_CERT_X509, /** * The `data` argument to the callback will be a pointer to a * `git_cert_hostkey` structure. */ GIT_CERT_HOSTKEY_LIBSSH2, /** * The `data` argument to the callback will be a pointer to a * `git_strarray` with `name:content` strings containing * information about the certificate. This is used when using * curl. */ GIT_CERT_STRARRAY, } git_cert_t; /** * Parent type for `git_cert_hostkey` and `git_cert_x509`. */ struct git_cert { /** * Type of certificate. A `GIT_CERT_` value. */ git_cert_t cert_type; }; /** * Callback for the user's custom certificate checks. * * @param cert The host certificate * @param valid Whether the libgit2 checks (OpenSSL or WinHTTP) think * this certificate is valid * @param host Hostname of the host libgit2 connected to * @param payload Payload provided by the caller * @return 0 to proceed with the connection, < 0 to fail the connection * or > 0 to indicate that the callback refused to act and that * the existing validity determination should be honored */ typedef int GIT_CALLBACK(git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload); /** * Type of SSH host fingerprint */ typedef enum { /** MD5 is available */ GIT_CERT_SSH_MD5 = (1 << 0), /** SHA-1 is available */ GIT_CERT_SSH_SHA1 = (1 << 1), /** SHA-256 is available */ GIT_CERT_SSH_SHA256 = (1 << 2), /** Raw hostkey is available */ GIT_CERT_SSH_RAW = (1 << 3), } git_cert_ssh_t; typedef enum { /** The raw key is of an unknown type. */ GIT_CERT_SSH_RAW_TYPE_UNKNOWN = 0, /** The raw key is an RSA key. */ GIT_CERT_SSH_RAW_TYPE_RSA = 1, /** The raw key is a DSS key. */ GIT_CERT_SSH_RAW_TYPE_DSS = 2, /** The raw key is a ECDSA 256 key. */ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256 = 3, /** The raw key is a ECDSA 384 key. */ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384 = 4, /** The raw key is a ECDSA 521 key. */ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521 = 5, /** The raw key is a ED25519 key. */ GIT_CERT_SSH_RAW_TYPE_KEY_ED25519 = 6 } git_cert_ssh_raw_type_t; /** * Hostkey information taken from libssh2 */ typedef struct { git_cert parent; /**< The parent cert */ /** * A bitmask containing the available fields. */ git_cert_ssh_t type; /** * Hostkey hash. If `type` has `GIT_CERT_SSH_MD5` set, this will * have the MD5 hash of the hostkey. */ unsigned char hash_md5[16]; /** * Hostkey hash. If `type` has `GIT_CERT_SSH_SHA1` set, this will * have the SHA-1 hash of the hostkey. */ unsigned char hash_sha1[20]; /** * Hostkey hash. If `type` has `GIT_CERT_SSH_SHA256` set, this will * have the SHA-256 hash of the hostkey. */ unsigned char hash_sha256[32]; /** * Raw hostkey type. If `type` has `GIT_CERT_SSH_RAW` set, this will * have the type of the raw hostkey. */ git_cert_ssh_raw_type_t raw_type; /** * Pointer to the raw hostkey. If `type` has `GIT_CERT_SSH_RAW` set, * this will have the raw contents of the hostkey. */ const char *hostkey; /** * Raw hostkey length. If `type` has `GIT_CERT_SSH_RAW` set, this will * have the length of the raw contents of the hostkey. */ size_t hostkey_len; } git_cert_hostkey; /** * X.509 certificate information */ typedef struct { git_cert parent; /**< The parent cert */ /** * Pointer to the X.509 certificate data */ void *data; /** * Length of the memory block pointed to by `data`. */ size_t len; } git_cert_x509; /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/trace.h0000644000175000017500000000335414125111754017323 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_trace_h__ #define INCLUDE_git_trace_h__ #include "common.h" #include "types.h" /** * @file git2/trace.h * @brief Git tracing configuration routines * @defgroup git_trace Git tracing configuration routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Available tracing levels. When tracing is set to a particular level, * callers will be provided tracing at the given level and all lower levels. */ typedef enum { /** No tracing will be performed. */ GIT_TRACE_NONE = 0, /** Severe errors that may impact the program's execution */ GIT_TRACE_FATAL = 1, /** Errors that do not impact the program's execution */ GIT_TRACE_ERROR = 2, /** Warnings that suggest abnormal data */ GIT_TRACE_WARN = 3, /** Informational messages about program execution */ GIT_TRACE_INFO = 4, /** Detailed data that allows for debugging */ GIT_TRACE_DEBUG = 5, /** Exceptionally detailed debugging data */ GIT_TRACE_TRACE = 6 } git_trace_level_t; /** * An instance for a tracing function */ typedef void GIT_CALLBACK(git_trace_cb)(git_trace_level_t level, const char *msg); /** * Sets the system tracing configuration to the specified level with the * specified callback. When system events occur at a level equal to, or * lower than, the given level they will be reported to the given callback. * * @param level Level to set tracing to * @param cb Function to call with trace data * @return 0 or an error code */ GIT_EXTERN(int) git_trace_set(git_trace_level_t level, git_trace_cb cb); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/pack.h0000644000175000017500000001627714125111754017153 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_pack_h__ #define INCLUDE_git_pack_h__ #include "common.h" #include "oid.h" #include "indexer.h" /** * @file git2/pack.h * @brief Git pack management routines * * Packing objects * --------------- * * Creation of packfiles requires two steps: * * - First, insert all the objects you want to put into the packfile * using `git_packbuilder_insert` and `git_packbuilder_insert_tree`. * It's important to add the objects in recency order ("in the order * that they are 'reachable' from head"). * * "ANY order will give you a working pack, ... [but it is] the thing * that gives packs good locality. It keeps the objects close to the * head (whether they are old or new, but they are _reachable_ from the * head) at the head of the pack. So packs actually have absolutely * _wonderful_ IO patterns." - Linus Torvalds * git.git/Documentation/technical/pack-heuristics.txt * * - Second, use `git_packbuilder_write` or `git_packbuilder_foreach` to * write the resulting packfile. * * libgit2 will take care of the delta ordering and generation. * `git_packbuilder_set_threads` can be used to adjust the number of * threads used for the process. * * See tests/pack/packbuilder.c for an example. * * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Stages that are reported by the packbuilder progress callback. */ typedef enum { GIT_PACKBUILDER_ADDING_OBJECTS = 0, GIT_PACKBUILDER_DELTAFICATION = 1, } git_packbuilder_stage_t; /** * Initialize a new packbuilder * * @param out The new packbuilder object * @param repo The repository * * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_new(git_packbuilder **out, git_repository *repo); /** * Set number of threads to spawn * * By default, libgit2 won't spawn any threads at all; * when set to 0, libgit2 will autodetect the number of * CPUs. * * @param pb The packbuilder * @param n Number of threads to spawn * @return number of actual threads to be used */ GIT_EXTERN(unsigned int) git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n); /** * Insert a single object * * For an optimal pack it's mandatory to insert objects in recency order, * commits followed by trees and blobs. * * @param pb The packbuilder * @param id The oid of the commit * @param name The name; might be NULL * * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, const char *name); /** * Insert a root tree object * * This will add the tree as well as all referenced trees and blobs. * * @param pb The packbuilder * @param id The oid of the root tree * * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id); /** * Insert a commit object * * This will add a commit as well as the completed referenced tree. * * @param pb The packbuilder * @param id The oid of the commit * * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id); /** * Insert objects as given by the walk * * Those commits and all objects they reference will be inserted into * the packbuilder. * * @param pb the packbuilder * @param walk the revwalk to use to fill the packbuilder * * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk); /** * Recursively insert an object and its referenced objects * * Insert the object as well as any object it references. * * @param pb the packbuilder * @param id the id of the root object to insert * @param name optional name for the object * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name); /** * Write the contents of the packfile to an in-memory buffer * * The contents of the buffer will become a valid packfile, even though there * will be no attached index * * @param buf Buffer where to write the packfile * @param pb The packbuilder */ GIT_EXTERN(int) git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb); /** * Write the new pack and corresponding index file to path. * * @param pb The packbuilder * @param path Path to the directory where the packfile and index should be stored, or NULL for default location * @param mode permissions to use creating a packfile or 0 for defaults * @param progress_cb function to call with progress information from the indexer (optional) * @param progress_cb_payload payload for the progress callback (optional) * * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_write( git_packbuilder *pb, const char *path, unsigned int mode, git_indexer_progress_cb progress_cb, void *progress_cb_payload); /** * Get the packfile's hash * * A packfile's name is derived from the sorted hashing of all object * names. This is only correct after the packfile has been written. * * @param pb The packbuilder object */ GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb); /** * Callback used to iterate over packed objects * * @see git_packbuilder_foreach * * @param buf A pointer to the object's data * @param size The size of the underlying object * @param payload Payload passed to git_packbuilder_foreach * @return non-zero to terminate the iteration */ typedef int GIT_CALLBACK(git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload); /** * Create the new pack and pass each object to the callback * * @param pb the packbuilder * @param cb the callback to call with each packed object's buffer * @param payload the callback's data * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_foreach(git_packbuilder *pb, git_packbuilder_foreach_cb cb, void *payload); /** * Get the total number of objects the packbuilder will write out * * @param pb the packbuilder * @return the number of objects in the packfile */ GIT_EXTERN(size_t) git_packbuilder_object_count(git_packbuilder *pb); /** * Get the number of objects the packbuilder has already written out * * @param pb the packbuilder * @return the number of objects which have already been written */ GIT_EXTERN(size_t) git_packbuilder_written(git_packbuilder *pb); /** Packbuilder progress notification function */ typedef int GIT_CALLBACK(git_packbuilder_progress)( int stage, uint32_t current, uint32_t total, void *payload); /** * Set the callbacks for a packbuilder * * @param pb The packbuilder object * @param progress_cb Function to call with progress information during * pack building. Be aware that this is called inline with pack building * operations, so performance may be affected. * @param progress_cb_payload Payload for progress callback. * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_set_callbacks( git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload); /** * Free the packbuilder and all associated data * * @param pb The packbuilder */ GIT_EXTERN(void) git_packbuilder_free(git_packbuilder *pb); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/revparse.h0000644000175000017500000000674614125111754020064 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_revparse_h__ #define INCLUDE_git_revparse_h__ #include "common.h" #include "types.h" /** * @file git2/revparse.h * @brief Git revision parsing routines * @defgroup git_revparse Git revision parsing routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Find a single object, as specified by a revision string. * * See `man gitrevisions`, or * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for * information on the syntax accepted. * * The returned object should be released with `git_object_free` when no * longer needed. * * @param out pointer to output object * @param repo the repository to search in * @param spec the textual specification for an object * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_revparse_single( git_object **out, git_repository *repo, const char *spec); /** * Find a single object and intermediate reference by a revision string. * * See `man gitrevisions`, or * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for * information on the syntax accepted. * * In some cases (`@{<-n>}` or `@{upstream}`), the expression may * point to an intermediate reference. When such expressions are being passed * in, `reference_out` will be valued as well. * * The returned object should be released with `git_object_free` and the * returned reference with `git_reference_free` when no longer needed. * * @param object_out pointer to output object * @param reference_out pointer to output reference or NULL * @param repo the repository to search in * @param spec the textual specification for an object * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC * or an error code */ GIT_EXTERN(int) git_revparse_ext( git_object **object_out, git_reference **reference_out, git_repository *repo, const char *spec); /** * Revparse flags. These indicate the intended behavior of the spec passed to * git_revparse. */ typedef enum { /** The spec targeted a single object. */ GIT_REVSPEC_SINGLE = 1 << 0, /** The spec targeted a range of commits. */ GIT_REVSPEC_RANGE = 1 << 1, /** The spec used the '...' operator, which invokes special semantics. */ GIT_REVSPEC_MERGE_BASE = 1 << 2, } git_revspec_t; /** * Git Revision Spec: output of a `git_revparse` operation */ typedef struct { /** The left element of the revspec; must be freed by the user */ git_object *from; /** The right element of the revspec; must be freed by the user */ git_object *to; /** The intent of the revspec (i.e. `git_revspec_mode_t` flags) */ unsigned int flags; } git_revspec; /** * Parse a revision string for `from`, `to`, and intent. * * See `man gitrevisions` or * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for * information on the syntax accepted. * * @param revspec Pointer to an user-allocated git_revspec struct where * the result of the rev-parse will be stored * @param repo the repository to search in * @param spec the rev-parse spec to parse * @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code */ GIT_EXTERN(int) git_revparse( git_revspec *revspec, git_repository *repo, const char *spec); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/tag.h0000644000175000017500000002503014125111754016773 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_tag_h__ #define INCLUDE_git_tag_h__ #include "common.h" #include "types.h" #include "oid.h" #include "object.h" #include "strarray.h" /** * @file git2/tag.h * @brief Git tag parsing routines * @defgroup git_tag Git tag management * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Lookup a tag object from the repository. * * @param out pointer to the looked up tag * @param repo the repo to use when locating the tag. * @param id identity of the tag to locate. * @return 0 or an error code */ GIT_EXTERN(int) git_tag_lookup( git_tag **out, git_repository *repo, const git_oid *id); /** * Lookup a tag object from the repository, * given a prefix of its identifier (short id). * * @see git_object_lookup_prefix * * @param out pointer to the looked up tag * @param repo the repo to use when locating the tag. * @param id identity of the tag to locate. * @param len the length of the short identifier * @return 0 or an error code */ GIT_EXTERN(int) git_tag_lookup_prefix( git_tag **out, git_repository *repo, const git_oid *id, size_t len); /** * Close an open tag * * You can no longer use the git_tag pointer after this call. * * IMPORTANT: You MUST call this method when you are through with a tag to * release memory. Failure to do so will cause a memory leak. * * @param tag the tag to close */ GIT_EXTERN(void) git_tag_free(git_tag *tag); /** * Get the id of a tag. * * @param tag a previously loaded tag. * @return object identity for the tag. */ GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag); /** * Get the repository that contains the tag. * * @param tag A previously loaded tag. * @return Repository that contains this tag. */ GIT_EXTERN(git_repository *) git_tag_owner(const git_tag *tag); /** * Get the tagged object of a tag * * This method performs a repository lookup for the * given object and returns it * * @param target_out pointer where to store the target * @param tag a previously loaded tag. * @return 0 or an error code */ GIT_EXTERN(int) git_tag_target(git_object **target_out, const git_tag *tag); /** * Get the OID of the tagged object of a tag * * @param tag a previously loaded tag. * @return pointer to the OID */ GIT_EXTERN(const git_oid *) git_tag_target_id(const git_tag *tag); /** * Get the type of a tag's tagged object * * @param tag a previously loaded tag. * @return type of the tagged object */ GIT_EXTERN(git_object_t) git_tag_target_type(const git_tag *tag); /** * Get the name of a tag * * @param tag a previously loaded tag. * @return name of the tag */ GIT_EXTERN(const char *) git_tag_name(const git_tag *tag); /** * Get the tagger (author) of a tag * * @param tag a previously loaded tag. * @return reference to the tag's author or NULL when unspecified */ GIT_EXTERN(const git_signature *) git_tag_tagger(const git_tag *tag); /** * Get the message of a tag * * @param tag a previously loaded tag. * @return message of the tag or NULL when unspecified */ GIT_EXTERN(const char *) git_tag_message(const git_tag *tag); /** * Create a new tag in the repository from an object * * A new reference will also be created pointing to * this tag object. If `force` is true and a reference * already exists with the given name, it'll be replaced. * * The message will not be cleaned up. This can be achieved * through `git_message_prettify()`. * * The tag name will be checked for validity. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * @param oid Pointer where to store the OID of the * newly created tag. If the tag already exists, this parameter * will be the oid of the existing tag, and the function will * return a GIT_EEXISTS error code. * * @param repo Repository where to store the tag * * @param tag_name Name for the tag; this name is validated * for consistency. It should also not conflict with an * already existing tag name * * @param target Object to which this tag points. This object * must belong to the given `repo`. * * @param tagger Signature of the tagger for this tag, and * of the tagging time * * @param message Full message for this tag * * @param force Overwrite existing references * * @return 0 on success, GIT_EINVALIDSPEC or an error code * A tag object is written to the ODB, and a proper reference * is written in the /refs/tags folder, pointing to it */ GIT_EXTERN(int) git_tag_create( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, const git_signature *tagger, const char *message, int force); /** * Create a new tag in the object database pointing to a git_object * * The message will not be cleaned up. This can be achieved * through `git_message_prettify()`. * * @param oid Pointer where to store the OID of the * newly created tag * * @param repo Repository where to store the tag * * @param tag_name Name for the tag * * @param target Object to which this tag points. This object * must belong to the given `repo`. * * @param tagger Signature of the tagger for this tag, and * of the tagging time * * @param message Full message for this tag * * @return 0 on success or an error code */ GIT_EXTERN(int) git_tag_annotation_create( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, const git_signature *tagger, const char *message); /** * Create a new tag in the repository from a buffer * * @param oid Pointer where to store the OID of the newly created tag * @param repo Repository where to store the tag * @param buffer Raw tag data * @param force Overwrite existing tags * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_tag_create_from_buffer( git_oid *oid, git_repository *repo, const char *buffer, int force); /** * Create a new lightweight tag pointing at a target object * * A new direct reference will be created pointing to * this target object. If `force` is true and a reference * already exists with the given name, it'll be replaced. * * The tag name will be checked for validity. * See `git_tag_create()` for rules about valid names. * * @param oid Pointer where to store the OID of the provided * target object. If the tag already exists, this parameter * will be filled with the oid of the existing pointed object * and the function will return a GIT_EEXISTS error code. * * @param repo Repository where to store the lightweight tag * * @param tag_name Name for the tag; this name is validated * for consistency. It should also not conflict with an * already existing tag name * * @param target Object to which this tag points. This object * must belong to the given `repo`. * * @param force Overwrite existing references * * @return 0 on success, GIT_EINVALIDSPEC or an error code * A proper reference is written in the /refs/tags folder, * pointing to the provided target object */ GIT_EXTERN(int) git_tag_create_lightweight( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, int force); /** * Delete an existing tag reference. * * The tag name will be checked for validity. * See `git_tag_create()` for rules about valid names. * * @param repo Repository where lives the tag * * @param tag_name Name of the tag to be deleted; * this name is validated for consistency. * * @return 0 on success, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_tag_delete( git_repository *repo, const char *tag_name); /** * Fill a list with all the tags in the Repository * * The string array will be filled with the names of the * matching tags; these values are owned by the user and * should be free'd manually when no longer needed, using * `git_strarray_free`. * * @param tag_names Pointer to a git_strarray structure where * the tag names will be stored * @param repo Repository where to find the tags * @return 0 or an error code */ GIT_EXTERN(int) git_tag_list( git_strarray *tag_names, git_repository *repo); /** * Fill a list with all the tags in the Repository * which name match a defined pattern * * If an empty pattern is provided, all the tags * will be returned. * * The string array will be filled with the names of the * matching tags; these values are owned by the user and * should be free'd manually when no longer needed, using * `git_strarray_free`. * * @param tag_names Pointer to a git_strarray structure where * the tag names will be stored * @param pattern Standard fnmatch pattern * @param repo Repository where to find the tags * @return 0 or an error code */ GIT_EXTERN(int) git_tag_list_match( git_strarray *tag_names, const char *pattern, git_repository *repo); /** * Callback used to iterate over tag names * * @see git_tag_foreach * * @param name The tag name * @param oid The tag's OID * @param payload Payload passed to git_tag_foreach * @return non-zero to terminate the iteration */ typedef int GIT_CALLBACK(git_tag_foreach_cb)(const char *name, git_oid *oid, void *payload); /** * Call callback `cb' for each tag in the repository * * @param repo Repository * @param callback Callback function * @param payload Pointer to callback data (optional) */ GIT_EXTERN(int) git_tag_foreach( git_repository *repo, git_tag_foreach_cb callback, void *payload); /** * Recursively peel a tag until a non tag git_object is found * * The retrieved `tag_target` object is owned by the repository * and should be closed with the `git_object_free` method. * * @param tag_target_out Pointer to the peeled git_object * @param tag The tag to be processed * @return 0 or an error code */ GIT_EXTERN(int) git_tag_peel( git_object **tag_target_out, const git_tag *tag); /** * Create an in-memory copy of a tag. The copy must be explicitly * free'd or it will leak. * * @param out Pointer to store the copy of the tag * @param source Original tag to copy */ GIT_EXTERN(int) git_tag_dup(git_tag **out, git_tag *source); /** * Determine whether a tag name is valid, meaning that (when prefixed * with `refs/tags/`) that it is a valid reference name, and that any * additional tag name restrictions are imposed (eg, it cannot start * with a `-`). * * @param valid output pointer to set with validity of given tag name * @param name a tag name to test * @return 0 on success or an error code */ GIT_EXTERN(int) git_tag_name_is_valid(int *valid, const char *name); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/refspec.h0000644000175000017500000000634614125111754017660 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_refspec_h__ #define INCLUDE_git_refspec_h__ #include "common.h" #include "types.h" #include "net.h" #include "buffer.h" /** * @file git2/refspec.h * @brief Git refspec attributes * @defgroup git_refspec Git refspec attributes * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Parse a given refspec string * * @param refspec a pointer to hold the refspec handle * @param input the refspec string * @param is_fetch is this a refspec for a fetch * @return 0 if the refspec string could be parsed, -1 otherwise */ GIT_EXTERN(int) git_refspec_parse(git_refspec **refspec, const char *input, int is_fetch); /** * Free a refspec object which has been created by git_refspec_parse * * @param refspec the refspec object */ GIT_EXTERN(void) git_refspec_free(git_refspec *refspec); /** * Get the source specifier * * @param refspec the refspec * @return the refspec's source specifier */ GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec); /** * Get the destination specifier * * @param refspec the refspec * @return the refspec's destination specifier */ GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec); /** * Get the refspec's string * * @param refspec the refspec * @returns the refspec's original string */ GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec); /** * Get the force update setting * * @param refspec the refspec * @return 1 if force update has been set, 0 otherwise */ GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec); /** * Get the refspec's direction. * * @param spec refspec * @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH */ GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec); /** * Check if a refspec's source descriptor matches a reference * * @param refspec the refspec * @param refname the name of the reference to check * @return 1 if the refspec matches, 0 otherwise */ GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname); /** * Check if a refspec's destination descriptor matches a reference * * @param refspec the refspec * @param refname the name of the reference to check * @return 1 if the refspec matches, 0 otherwise */ GIT_EXTERN(int) git_refspec_dst_matches(const git_refspec *refspec, const char *refname); /** * Transform a reference to its target following the refspec's rules * * @param out where to store the target name * @param spec the refspec * @param name the name of the reference to transform * @return 0, GIT_EBUFS or another error */ GIT_EXTERN(int) git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name); /** * Transform a target reference to its source reference following the refspec's rules * * @param out where to store the source reference name * @param spec the refspec * @param name the name of the reference to transform * @return 0, GIT_EBUFS or another error */ GIT_EXTERN(int) git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name); GIT_END_DECL #endif git2r/src/libgit2/include/git2/reset.h0000644000175000017500000000650314125111754017346 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_reset_h__ #define INCLUDE_git_reset_h__ #include "common.h" #include "types.h" #include "strarray.h" #include "checkout.h" /** * @file git2/reset.h * @brief Git reset management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Kinds of reset operation */ typedef enum { GIT_RESET_SOFT = 1, /**< Move the head to the given commit */ GIT_RESET_MIXED = 2, /**< SOFT plus reset index to the commit */ GIT_RESET_HARD = 3, /**< MIXED plus changes in working tree discarded */ } git_reset_t; /** * Sets the current head to the specified commit oid and optionally * resets the index and working tree to match. * * SOFT reset means the Head will be moved to the commit. * * MIXED reset will trigger a SOFT reset, plus the index will be replaced * with the content of the commit tree. * * HARD reset will trigger a MIXED reset and the working directory will be * replaced with the content of the index. (Untracked and ignored files * will be left alone, however.) * * TODO: Implement remaining kinds of resets. * * @param repo Repository where to perform the reset operation. * * @param target Committish to which the Head should be moved to. This object * must belong to the given `repo` and can either be a git_commit or a * git_tag. When a git_tag is being passed, it should be dereferencable * to a git_commit which oid will be used as the target of the branch. * * @param reset_type Kind of reset operation to perform. * * @param checkout_opts Optional checkout options to be used for a HARD reset. * The checkout_strategy field will be overridden (based on reset_type). * This parameter can be used to propagate notify and progress callbacks. * * @return 0 on success or an error code */ GIT_EXTERN(int) git_reset( git_repository *repo, const git_object *target, git_reset_t reset_type, const git_checkout_options *checkout_opts); /** * Sets the current head to the specified commit oid and optionally * resets the index and working tree to match. * * This behaves like `git_reset()` but takes an annotated commit, * which lets you specify which extended sha syntax string was * specified by a user, allowing for more exact reflog messages. * * See the documentation for `git_reset()`. * * @see git_reset */ GIT_EXTERN(int) git_reset_from_annotated( git_repository *repo, const git_annotated_commit *commit, git_reset_t reset_type, const git_checkout_options *checkout_opts); /** * Updates some entries in the index from the target commit tree. * * The scope of the updated entries is determined by the paths * being passed in the `pathspec` parameters. * * Passing a NULL `target` will result in removing * entries in the index matching the provided pathspecs. * * @param repo Repository where to perform the reset operation. * * @param target The committish which content will be used to reset the content * of the index. * * @param pathspecs List of pathspecs to operate on. * * @return 0 on success or an error code < 0 */ GIT_EXTERN(int) git_reset_default( git_repository *repo, const git_object *target, const git_strarray* pathspecs); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/odb_backend.h0000644000175000017500000000744114125111754020441 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_odb_backend_h__ #define INCLUDE_git_odb_backend_h__ #include "common.h" #include "types.h" #include "indexer.h" /** * @file git2/backend.h * @brief Git custom backend functions * @defgroup git_odb Git object database routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /* * Constructors for in-box ODB backends. */ /** * Create a backend for the packfiles. * * @param out location to store the odb backend pointer * @param objects_dir the Git repository's objects directory * * @return 0 or an error code */ GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir); /** * Create a backend for loose objects * * @param out location to store the odb backend pointer * @param objects_dir the Git repository's objects directory * @param compression_level zlib compression level to use * @param do_fsync whether to do an fsync() after writing * @param dir_mode permissions to use creating a directory or 0 for defaults * @param file_mode permissions to use creating a file or 0 for defaults * * @return 0 or an error code */ GIT_EXTERN(int) git_odb_backend_loose( git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync, unsigned int dir_mode, unsigned int file_mode); /** * Create a backend out of a single packfile * * This can be useful for inspecting the contents of a single * packfile. * * @param out location to store the odb backend pointer * @param index_file path to the packfile's .idx file * * @return 0 or an error code */ GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file); /** Streaming mode */ typedef enum { GIT_STREAM_RDONLY = (1 << 1), GIT_STREAM_WRONLY = (1 << 2), GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY), } git_odb_stream_t; /** * A stream to read/write from a backend. * * This represents a stream of data being written to or read from a * backend. When writing, the frontend functions take care of * calculating the object's id and all `finalize_write` needs to do is * store the object with the id it is passed. */ struct git_odb_stream { git_odb_backend *backend; unsigned int mode; void *hash_ctx; git_object_size_t declared_size; git_object_size_t received_bytes; /** * Write at most `len` bytes into `buffer` and advance the stream. */ int GIT_CALLBACK(read)(git_odb_stream *stream, char *buffer, size_t len); /** * Write `len` bytes from `buffer` into the stream. */ int GIT_CALLBACK(write)(git_odb_stream *stream, const char *buffer, size_t len); /** * Store the contents of the stream as an object with the id * specified in `oid`. * * This method might not be invoked if: * - an error occurs earlier with the `write` callback, * - the object referred to by `oid` already exists in any backend, or * - the final number of received bytes differs from the size declared * with `git_odb_open_wstream()` */ int GIT_CALLBACK(finalize_write)(git_odb_stream *stream, const git_oid *oid); /** * Free the stream's memory. * * This method might be called without a call to `finalize_write` if * an error occurs or if the object is already present in the ODB. */ void GIT_CALLBACK(free)(git_odb_stream *stream); }; /** A stream to write a pack file to the ODB */ struct git_odb_writepack { git_odb_backend *backend; int GIT_CALLBACK(append)(git_odb_writepack *writepack, const void *data, size_t size, git_indexer_progress *stats); int GIT_CALLBACK(commit)(git_odb_writepack *writepack, git_indexer_progress *stats); void GIT_CALLBACK(free)(git_odb_writepack *writepack); }; GIT_END_DECL #endif git2r/src/libgit2/include/git2/mailmap.h0000644000175000017500000000705114125111754017643 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_mailmap_h__ #define INCLUDE_git_mailmap_h__ #include "common.h" #include "types.h" #include "buffer.h" /** * @file git2/mailmap.h * @brief Mailmap parsing routines * @defgroup git_mailmap Git mailmap routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Allocate a new mailmap object. * * This object is empty, so you'll have to add a mailmap file before you can do * anything with it. The mailmap must be freed with 'git_mailmap_free'. * * @param out pointer to store the new mailmap * @return 0 on success, or an error code */ GIT_EXTERN(int) git_mailmap_new(git_mailmap **out); /** * Free the mailmap and its associated memory. * * @param mm the mailmap to free */ GIT_EXTERN(void) git_mailmap_free(git_mailmap *mm); /** * Add a single entry to the given mailmap object. If the entry already exists, * it will be replaced with the new entry. * * @param mm mailmap to add the entry to * @param real_name the real name to use, or NULL * @param real_email the real email to use, or NULL * @param replace_name the name to replace, or NULL * @param replace_email the email to replace * @return 0 on success, or an error code */ GIT_EXTERN(int) git_mailmap_add_entry( git_mailmap *mm, const char *real_name, const char *real_email, const char *replace_name, const char *replace_email); /** * Create a new mailmap instance containing a single mailmap file * * @param out pointer to store the new mailmap * @param buf buffer to parse the mailmap from * @param len the length of the input buffer * @return 0 on success, or an error code */ GIT_EXTERN(int) git_mailmap_from_buffer( git_mailmap **out, const char *buf, size_t len); /** * Create a new mailmap instance from a repository, loading mailmap files based * on the repository's configuration. * * Mailmaps are loaded in the following order: * 1. '.mailmap' in the root of the repository's working directory, if present. * 2. The blob object identified by the 'mailmap.blob' config entry, if set. * [NOTE: 'mailmap.blob' defaults to 'HEAD:.mailmap' in bare repositories] * 3. The path in the 'mailmap.file' config entry, if set. * * @param out pointer to store the new mailmap * @param repo repository to load mailmap information from * @return 0 on success, or an error code */ GIT_EXTERN(int) git_mailmap_from_repository( git_mailmap **out, git_repository *repo); /** * Resolve a name and email to the corresponding real name and email. * * The lifetime of the strings are tied to `mm`, `name`, and `email` parameters. * * @param real_name pointer to store the real name * @param real_email pointer to store the real email * @param mm the mailmap to perform a lookup with (may be NULL) * @param name the name to look up * @param email the email to look up * @return 0 on success, or an error code */ GIT_EXTERN(int) git_mailmap_resolve( const char **real_name, const char **real_email, const git_mailmap *mm, const char *name, const char *email); /** * Resolve a signature to use real names and emails with a mailmap. * * Call `git_signature_free()` to free the data. * * @param out new signature * @param mm mailmap to resolve with * @param sig signature to resolve * @return 0 or an error code */ GIT_EXTERN(int) git_mailmap_resolve_signature( git_signature **out, const git_mailmap *mm, const git_signature *sig); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/strarray.h0000644000175000017500000000261614125111754020074 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_strarray_h__ #define INCLUDE_git_strarray_h__ #include "common.h" /** * @file git2/strarray.h * @brief Git string array routines * @defgroup git_strarray Git string array routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** Array of strings */ typedef struct git_strarray { char **strings; size_t count; } git_strarray; /** * Free the strings contained in a string array. This method should * be called on `git_strarray` objects that were provided by the * library. Not doing so, will result in a memory leak. * * This does not free the `git_strarray` itself, since the library will * never allocate that object directly itself. * * @param array The git_strarray that contains strings to free */ GIT_EXTERN(void) git_strarray_dispose(git_strarray *array); /** * Copy a string array object from source to target. * * Note: target is overwritten and hence should be empty, otherwise its * contents are leaked. Call git_strarray_free() if necessary. * * @param tgt target * @param src source * @return 0 on success, < 0 on allocation failure */ GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/blame.h0000644000175000017500000001716314125111754017310 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_blame_h__ #define INCLUDE_git_blame_h__ #include "common.h" #include "oid.h" /** * @file git2/blame.h * @brief Git blame routines * @defgroup git_blame Git blame routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Flags for indicating option behavior for git_blame APIs. */ typedef enum { /** Normal blame, the default */ GIT_BLAME_NORMAL = 0, /** * Track lines that have moved within a file (like `git blame -M`). * * This is not yet implemented and reserved for future use. */ GIT_BLAME_TRACK_COPIES_SAME_FILE = (1<<0), /** * Track lines that have moved across files in the same commit * (like `git blame -C`). * * This is not yet implemented and reserved for future use. */ GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = (1<<1), /** * Track lines that have been copied from another file that exists * in the same commit (like `git blame -CC`). Implies SAME_FILE. * * This is not yet implemented and reserved for future use. */ GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = (1<<2), /** * Track lines that have been copied from another file that exists in * *any* commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES. * * This is not yet implemented and reserved for future use. */ GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3), /** * Restrict the search of commits to those reachable following only * the first parents. */ GIT_BLAME_FIRST_PARENT = (1<<4), /** * Use mailmap file to map author and committer names and email * addresses to canonical real names and email addresses. The * mailmap will be read from the working directory, or HEAD in a * bare repository. */ GIT_BLAME_USE_MAILMAP = (1<<5), /** Ignore whitespace differences */ GIT_BLAME_IGNORE_WHITESPACE = (1<<6), } git_blame_flag_t; /** * Blame options structure * * Initialize with `GIT_BLAME_OPTIONS_INIT`. Alternatively, you can * use `git_blame_options_init`. * */ typedef struct git_blame_options { unsigned int version; /** A combination of `git_blame_flag_t` */ uint32_t flags; /** * The lower bound on the number of alphanumeric characters that * must be detected as moving/copying within a file for it to * associate those lines with the parent commit. The default value * is 20. * * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*` * flags are specified. */ uint16_t min_match_characters; /** The id of the newest commit to consider. The default is HEAD. */ git_oid newest_commit; /** * The id of the oldest commit to consider. * The default is the first commit encountered with a NULL parent. */ git_oid oldest_commit; /** * The first line in the file to blame. * The default is 1 (line numbers start with 1). */ size_t min_line; /** * The last line in the file to blame. * The default is the last line of the file. */ size_t max_line; } git_blame_options; #define GIT_BLAME_OPTIONS_VERSION 1 #define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION} /** * Initialize git_blame_options structure * * Initializes a `git_blame_options` with default values. Equivalent to creating * an instance with GIT_BLAME_OPTIONS_INIT. * * @param opts The `git_blame_options` struct to initialize. * @param version The struct version; pass `GIT_BLAME_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_blame_options_init( git_blame_options *opts, unsigned int version); /** * Structure that represents a blame hunk. */ typedef struct git_blame_hunk { /** * The number of lines in this hunk. */ size_t lines_in_hunk; /** * The OID of the commit where this line was last changed. */ git_oid final_commit_id; /** * The 1-based line number where this hunk begins, in the final version * of the file. */ size_t final_start_line_number; /** * The author of `final_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been * specified, it will contain the canonical real name and email address. */ git_signature *final_signature; /** * The OID of the commit where this hunk was found. * This will usually be the same as `final_commit_id`, except when * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified. */ git_oid orig_commit_id; /** * The path to the file where this hunk originated, as of the commit * specified by `orig_commit_id`. */ const char *orig_path; /** * The 1-based line number where this hunk begins in the file named by * `orig_path` in the commit specified by `orig_commit_id`. */ size_t orig_start_line_number; /** * The author of `orig_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been * specified, it will contain the canonical real name and email address. */ git_signature *orig_signature; /** * The 1 iff the hunk has been tracked to a boundary commit (the root, * or the commit specified in git_blame_options.oldest_commit) */ char boundary; } git_blame_hunk; /** Opaque structure to hold blame results */ typedef struct git_blame git_blame; /** * Gets the number of hunks that exist in the blame structure. */ GIT_EXTERN(uint32_t) git_blame_get_hunk_count(git_blame *blame); /** * Gets the blame hunk at the given index. * * @param blame the blame structure to query * @param index index of the hunk to retrieve * @return the hunk at the given index, or NULL on error */ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex( git_blame *blame, uint32_t index); /** * Gets the hunk that relates to the given line number in the newest commit. * * @param blame the blame structure to query * @param lineno the (1-based) line number to find a hunk for * @return the hunk that contains the given line, or NULL on error */ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline( git_blame *blame, size_t lineno); /** * Get the blame for a single file. * * @param out pointer that will receive the blame object * @param repo repository whose history is to be walked * @param path path to file to consider * @param options options for the blame operation. If NULL, this is treated as * though GIT_BLAME_OPTIONS_INIT were passed. * @return 0 on success, or an error code. (use git_error_last for information * about the error.) */ GIT_EXTERN(int) git_blame_file( git_blame **out, git_repository *repo, const char *path, git_blame_options *options); /** * Get blame data for a file that has been modified in memory. The `reference` * parameter is a pre-calculated blame for the in-odb history of the file. This * means that once a file blame is completed (which can be expensive), updating * the buffer blame is very fast. * * Lines that differ between the buffer and the committed version are marked as * having a zero OID for their final_commit_id. * * @param out pointer that will receive the resulting blame data * @param reference cached blame from the history of the file (usually the output * from git_blame_file) * @param buffer the (possibly) modified contents of the file * @param buffer_len number of valid bytes in the buffer * @return 0 on success, or an error code. (use git_error_last for information * about the error) */ GIT_EXTERN(int) git_blame_buffer( git_blame **out, git_blame *reference, const char *buffer, size_t buffer_len); /** * Free memory allocated by git_blame_file or git_blame_buffer. * * @param blame the blame structure to free */ GIT_EXTERN(void) git_blame_free(git_blame *blame); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/cherrypick.h0000644000175000017500000000533514125111754020371 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_cherrypick_h__ #define INCLUDE_git_cherrypick_h__ #include "common.h" #include "types.h" #include "merge.h" /** * @file git2/cherrypick.h * @brief Git cherry-pick routines * @defgroup git_cherrypick Git cherry-pick routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Cherry-pick options */ typedef struct { unsigned int version; /** For merge commits, the "mainline" is treated as the parent. */ unsigned int mainline; git_merge_options merge_opts; /**< Options for the merging */ git_checkout_options checkout_opts; /**< Options for the checkout */ } git_cherrypick_options; #define GIT_CHERRYPICK_OPTIONS_VERSION 1 #define GIT_CHERRYPICK_OPTIONS_INIT {GIT_CHERRYPICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT} /** * Initialize git_cherrypick_options structure * * Initializes a `git_cherrypick_options` with default values. Equivalent to creating * an instance with GIT_CHERRYPICK_OPTIONS_INIT. * * @param opts The `git_cherrypick_options` struct to initialize. * @param version The struct version; pass `GIT_CHERRYPICK_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_cherrypick_options_init( git_cherrypick_options *opts, unsigned int version); /** * Cherry-picks the given commit against the given "our" commit, producing an * index that reflects the result of the cherry-pick. * * The returned index must be freed explicitly with `git_index_free`. * * @param out pointer to store the index result in * @param repo the repository that contains the given commits * @param cherrypick_commit the commit to cherry-pick * @param our_commit the commit to cherry-pick against (eg, HEAD) * @param mainline the parent of the `cherrypick_commit`, if it is a merge * @param merge_options the merge options (or null for defaults) * @return zero on success, -1 on failure. */ GIT_EXTERN(int) git_cherrypick_commit( git_index **out, git_repository *repo, git_commit *cherrypick_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_options); /** * Cherry-pick the given commit, producing changes in the index and working directory. * * @param repo the repository to cherry-pick * @param commit the commit to cherry-pick * @param cherrypick_options the cherry-pick options (or null for defaults) * @return zero on success, -1 on failure. */ GIT_EXTERN(int) git_cherrypick( git_repository *repo, git_commit *commit, const git_cherrypick_options *cherrypick_options); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/pathspec.h0000644000175000017500000002267114125111754020037 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_pathspec_h__ #define INCLUDE_git_pathspec_h__ #include "common.h" #include "types.h" #include "strarray.h" #include "diff.h" GIT_BEGIN_DECL /** * Compiled pathspec */ typedef struct git_pathspec git_pathspec; /** * List of filenames matching a pathspec */ typedef struct git_pathspec_match_list git_pathspec_match_list; /** * Options controlling how pathspec match should be executed */ typedef enum { GIT_PATHSPEC_DEFAULT = 0, /** * GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise * match will use native case sensitivity of platform filesystem */ GIT_PATHSPEC_IGNORE_CASE = (1u << 0), /** * GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise * match will use native case sensitivity of platform filesystem */ GIT_PATHSPEC_USE_CASE = (1u << 1), /** * GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple * string comparison for matching */ GIT_PATHSPEC_NO_GLOB = (1u << 2), /** * GIT_PATHSPEC_NO_MATCH_ERROR means the match functions return error * code GIT_ENOTFOUND if no matches are found; otherwise no matches is * still success (return 0) but `git_pathspec_match_list_entrycount` * will indicate 0 matches. */ GIT_PATHSPEC_NO_MATCH_ERROR = (1u << 3), /** * GIT_PATHSPEC_FIND_FAILURES means that the `git_pathspec_match_list` * should track which patterns matched which files so that at the end of * the match we can identify patterns that did not match any files. */ GIT_PATHSPEC_FIND_FAILURES = (1u << 4), /** * GIT_PATHSPEC_FAILURES_ONLY means that the `git_pathspec_match_list` * does not need to keep the actual matching filenames. Use this to * just test if there were any matches at all or in combination with * GIT_PATHSPEC_FIND_FAILURES to validate a pathspec. */ GIT_PATHSPEC_FAILURES_ONLY = (1u << 5), } git_pathspec_flag_t; /** * Compile a pathspec * * @param out Output of the compiled pathspec * @param pathspec A git_strarray of the paths to match * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_pathspec_new( git_pathspec **out, const git_strarray *pathspec); /** * Free a pathspec * * @param ps The compiled pathspec */ GIT_EXTERN(void) git_pathspec_free(git_pathspec *ps); /** * Try to match a path against a pathspec * * Unlike most of the other pathspec matching functions, this will not * fall back on the native case-sensitivity for your platform. You must * explicitly pass flags to control case sensitivity or else this will * fall back on being case sensitive. * * @param ps The compiled pathspec * @param flags Combination of git_pathspec_flag_t options to control match * @param path The pathname to attempt to match * @return 1 is path matches spec, 0 if it does not */ GIT_EXTERN(int) git_pathspec_matches_path( const git_pathspec *ps, uint32_t flags, const char *path); /** * Match a pathspec against the working directory of a repository. * * This matches the pathspec against the current files in the working * directory of the repository. It is an error to invoke this on a bare * repo. This handles git ignores (i.e. ignored files will not be * considered to match the `pathspec` unless the file is tracked in the * index). * * If `out` is not NULL, this returns a `git_patchspec_match_list`. That * contains the list of all matched filenames (unless you pass the * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES` * flag). You must call `git_pathspec_match_list_free()` on this object. * * @param out Output list of matches; pass NULL to just get return value * @param repo The repository in which to match; bare repo is an error * @param flags Combination of git_pathspec_flag_t options to control match * @param ps Pathspec to be matched * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and * the GIT_PATHSPEC_NO_MATCH_ERROR flag was given */ GIT_EXTERN(int) git_pathspec_match_workdir( git_pathspec_match_list **out, git_repository *repo, uint32_t flags, git_pathspec *ps); /** * Match a pathspec against entries in an index. * * This matches the pathspec against the files in the repository index. * * NOTE: At the moment, the case sensitivity of this match is controlled * by the current case-sensitivity of the index object itself and the * USE_CASE and IGNORE_CASE flags will have no effect. This behavior will * be corrected in a future release. * * If `out` is not NULL, this returns a `git_patchspec_match_list`. That * contains the list of all matched filenames (unless you pass the * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES` * flag). You must call `git_pathspec_match_list_free()` on this object. * * @param out Output list of matches; pass NULL to just get return value * @param index The index to match against * @param flags Combination of git_pathspec_flag_t options to control match * @param ps Pathspec to be matched * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used */ GIT_EXTERN(int) git_pathspec_match_index( git_pathspec_match_list **out, git_index *index, uint32_t flags, git_pathspec *ps); /** * Match a pathspec against files in a tree. * * This matches the pathspec against the files in the given tree. * * If `out` is not NULL, this returns a `git_patchspec_match_list`. That * contains the list of all matched filenames (unless you pass the * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES` * flag). You must call `git_pathspec_match_list_free()` on this object. * * @param out Output list of matches; pass NULL to just get return value * @param tree The root-level tree to match against * @param flags Combination of git_pathspec_flag_t options to control match * @param ps Pathspec to be matched * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used */ GIT_EXTERN(int) git_pathspec_match_tree( git_pathspec_match_list **out, git_tree *tree, uint32_t flags, git_pathspec *ps); /** * Match a pathspec against files in a diff list. * * This matches the pathspec against the files in the given diff list. * * If `out` is not NULL, this returns a `git_patchspec_match_list`. That * contains the list of all matched filenames (unless you pass the * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES` * flag). You must call `git_pathspec_match_list_free()` on this object. * * @param out Output list of matches; pass NULL to just get return value * @param diff A generated diff list * @param flags Combination of git_pathspec_flag_t options to control match * @param ps Pathspec to be matched * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used */ GIT_EXTERN(int) git_pathspec_match_diff( git_pathspec_match_list **out, git_diff *diff, uint32_t flags, git_pathspec *ps); /** * Free memory associates with a git_pathspec_match_list * * @param m The git_pathspec_match_list to be freed */ GIT_EXTERN(void) git_pathspec_match_list_free(git_pathspec_match_list *m); /** * Get the number of items in a match list. * * @param m The git_pathspec_match_list object * @return Number of items in match list */ GIT_EXTERN(size_t) git_pathspec_match_list_entrycount( const git_pathspec_match_list *m); /** * Get a matching filename by position. * * This routine cannot be used if the match list was generated by * `git_pathspec_match_diff`. If so, it will always return NULL. * * @param m The git_pathspec_match_list object * @param pos The index into the list * @return The filename of the match */ GIT_EXTERN(const char *) git_pathspec_match_list_entry( const git_pathspec_match_list *m, size_t pos); /** * Get a matching diff delta by position. * * This routine can only be used if the match list was generated by * `git_pathspec_match_diff`. Otherwise it will always return NULL. * * @param m The git_pathspec_match_list object * @param pos The index into the list * @return The filename of the match */ GIT_EXTERN(const git_diff_delta *) git_pathspec_match_list_diff_entry( const git_pathspec_match_list *m, size_t pos); /** * Get the number of pathspec items that did not match. * * This will be zero unless you passed GIT_PATHSPEC_FIND_FAILURES when * generating the git_pathspec_match_list. * * @param m The git_pathspec_match_list object * @return Number of items in original pathspec that had no matches */ GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount( const git_pathspec_match_list *m); /** * Get an original pathspec string that had no matches. * * This will be return NULL for positions out of range. * * @param m The git_pathspec_match_list object * @param pos The index into the failed items * @return The pathspec pattern that didn't match anything */ GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry( const git_pathspec_match_list *m, size_t pos); GIT_END_DECL #endif git2r/src/libgit2/include/git2/patch.h0000644000175000017500000002313714125111754017325 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_patch_h__ #define INCLUDE_git_patch_h__ #include "common.h" #include "types.h" #include "oid.h" #include "diff.h" /** * @file git2/patch.h * @brief Patch handling routines. * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * The diff patch is used to store all the text diffs for a delta. * * You can easily loop over the content of patches and get information about * them. */ typedef struct git_patch git_patch; /** * Get the repository associated with this patch. May be NULL. * * @param patch the patch * @return a pointer to the repository */ GIT_EXTERN(git_repository *) git_patch_owner(const git_patch *patch); /** * Return a patch for an entry in the diff list. * * The `git_patch` is a newly created object contains the text diffs * for the delta. You have to call `git_patch_free()` when you are * done with it. You can use the patch object to loop over all the hunks * and lines in the diff of the one delta. * * For an unchanged file or a binary file, no `git_patch` will be * created, the output will be set to NULL, and the `binary` flag will be * set true in the `git_diff_delta` structure. * * It is okay to pass NULL for either of the output parameters; if you pass * NULL for the `git_patch`, then the text diff will not be calculated. * * @param out Output parameter for the delta patch object * @param diff Diff list object * @param idx Index into diff list * @return 0 on success, other value < 0 on error */ GIT_EXTERN(int) git_patch_from_diff( git_patch **out, git_diff *diff, size_t idx); /** * Directly generate a patch from the difference between two blobs. * * This is just like `git_diff_blobs()` except it generates a patch object * for the difference instead of directly making callbacks. You can use the * standard `git_patch` accessor functions to read the patch data, and * you must call `git_patch_free()` on the patch when done. * * @param out The generated patch; NULL on error * @param old_blob Blob for old side of diff, or NULL for empty blob * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param new_blob Blob for new side of diff, or NULL for empty blob * @param new_as_path Treat new blob as if it had this filename; can be NULL * @param opts Options for diff, or NULL for default options * @return 0 on success or error code < 0 */ GIT_EXTERN(int) git_patch_from_blobs( git_patch **out, const git_blob *old_blob, const char *old_as_path, const git_blob *new_blob, const char *new_as_path, const git_diff_options *opts); /** * Directly generate a patch from the difference between a blob and a buffer. * * This is just like `git_diff_blob_to_buffer()` except it generates a patch * object for the difference instead of directly making callbacks. You can * use the standard `git_patch` accessor functions to read the patch * data, and you must call `git_patch_free()` on the patch when done. * * @param out The generated patch; NULL on error * @param old_blob Blob for old side of diff, or NULL for empty blob * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param buffer Raw data for new side of diff, or NULL for empty * @param buffer_len Length of raw data for new side of diff * @param buffer_as_path Treat buffer as if it had this filename; can be NULL * @param opts Options for diff, or NULL for default options * @return 0 on success or error code < 0 */ GIT_EXTERN(int) git_patch_from_blob_and_buffer( git_patch **out, const git_blob *old_blob, const char *old_as_path, const void *buffer, size_t buffer_len, const char *buffer_as_path, const git_diff_options *opts); /** * Directly generate a patch from the difference between two buffers. * * This is just like `git_diff_buffers()` except it generates a patch * object for the difference instead of directly making callbacks. You can * use the standard `git_patch` accessor functions to read the patch * data, and you must call `git_patch_free()` on the patch when done. * * @param out The generated patch; NULL on error * @param old_buffer Raw data for old side of diff, or NULL for empty * @param old_len Length of the raw data for old side of the diff * @param old_as_path Treat old buffer as if it had this filename; can be NULL * @param new_buffer Raw data for new side of diff, or NULL for empty * @param new_len Length of raw data for new side of diff * @param new_as_path Treat buffer as if it had this filename; can be NULL * @param opts Options for diff, or NULL for default options * @return 0 on success or error code < 0 */ GIT_EXTERN(int) git_patch_from_buffers( git_patch **out, const void *old_buffer, size_t old_len, const char *old_as_path, const void *new_buffer, size_t new_len, const char *new_as_path, const git_diff_options *opts); /** * Free a git_patch object. */ GIT_EXTERN(void) git_patch_free(git_patch *patch); /** * Get the delta associated with a patch. This delta points to internal * data and you do not have to release it when you are done with it. */ GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch); /** * Get the number of hunks in a patch */ GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch); /** * Get line counts of each type in a patch. * * This helps imitate a diff --numstat type of output. For that purpose, * you only need the `total_additions` and `total_deletions` values, but we * include the `total_context` line count in case you want the total number * of lines of diff output that will be generated. * * All outputs are optional. Pass NULL if you don't need a particular count. * * @param total_context Count of context lines in output, can be NULL. * @param total_additions Count of addition lines in output, can be NULL. * @param total_deletions Count of deletion lines in output, can be NULL. * @param patch The git_patch object * @return 0 on success, <0 on error */ GIT_EXTERN(int) git_patch_line_stats( size_t *total_context, size_t *total_additions, size_t *total_deletions, const git_patch *patch); /** * Get the information about a hunk in a patch * * Given a patch and a hunk index into the patch, this returns detailed * information about that hunk. Any of the output pointers can be passed * as NULL if you don't care about that particular piece of information. * * @param out Output pointer to git_diff_hunk of hunk * @param lines_in_hunk Output count of total lines in this hunk * @param patch Input pointer to patch object * @param hunk_idx Input index of hunk to get information about * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error */ GIT_EXTERN(int) git_patch_get_hunk( const git_diff_hunk **out, size_t *lines_in_hunk, git_patch *patch, size_t hunk_idx); /** * Get the number of lines in a hunk. * * @param patch The git_patch object * @param hunk_idx Index of the hunk * @return Number of lines in hunk or GIT_ENOTFOUND if invalid hunk index */ GIT_EXTERN(int) git_patch_num_lines_in_hunk( const git_patch *patch, size_t hunk_idx); /** * Get data about a line in a hunk of a patch. * * Given a patch, a hunk index, and a line index in the hunk, this * will return a lot of details about that line. If you pass a hunk * index larger than the number of hunks or a line index larger than * the number of lines in the hunk, this will return -1. * * @param out The git_diff_line data for this line * @param patch The patch to look in * @param hunk_idx The index of the hunk * @param line_of_hunk The index of the line in the hunk * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_patch_get_line_in_hunk( const git_diff_line **out, git_patch *patch, size_t hunk_idx, size_t line_of_hunk); /** * Look up size of patch diff data in bytes * * This returns the raw size of the patch data. This only includes the * actual data from the lines of the diff, not the file or hunk headers. * * If you pass `include_context` as true (non-zero), this will be the size * of all of the diff output; if you pass it as false (zero), this will * only include the actual changed lines (as if `context_lines` was 0). * * @param patch A git_patch representing changes to one file * @param include_context Include context lines in size if non-zero * @param include_hunk_headers Include hunk header lines if non-zero * @param include_file_headers Include file header lines if non-zero * @return The number of bytes of data */ GIT_EXTERN(size_t) git_patch_size( git_patch *patch, int include_context, int include_hunk_headers, int include_file_headers); /** * Serialize the patch to text via callback. * * Returning a non-zero value from the callback will terminate the iteration * and return that value to the caller. * * @param patch A git_patch representing changes to one file * @param print_cb Callback function to output lines of the patch. Will be * called for file headers, hunk headers, and diff lines. * @param payload Reference pointer that will be passed to your callbacks. * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_patch_print( git_patch *patch, git_diff_line_cb print_cb, void *payload); /** * Get the content of a patch as a single diff text. * * @param out The git_buf to be filled in * @param patch A git_patch representing changes to one file * @return 0 on success, <0 on failure. */ GIT_EXTERN(int) git_patch_to_buf( git_buf *out, git_patch *patch); GIT_END_DECL /**@}*/ #endif git2r/src/libgit2/include/git2/reflog.h0000644000175000017500000001101014125111754017467 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_reflog_h__ #define INCLUDE_git_reflog_h__ #include "common.h" #include "types.h" #include "oid.h" /** * @file git2/reflog.h * @brief Git reflog management routines * @defgroup git_reflog Git reflog management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Read the reflog for the given reference * * If there is no reflog file for the given * reference yet, an empty reflog object will * be returned. * * The reflog must be freed manually by using * git_reflog_free(). * * @param out pointer to reflog * @param repo the repostiory * @param name reference to look up * @return 0 or an error code */ GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const char *name); /** * Write an existing in-memory reflog object back to disk * using an atomic file lock. * * @param reflog an existing reflog object * @return 0 or an error code */ GIT_EXTERN(int) git_reflog_write(git_reflog *reflog); /** * Add a new entry to the in-memory reflog. * * `msg` is optional and can be NULL. * * @param reflog an existing reflog object * @param id the OID the reference is now pointing to * @param committer the signature of the committer * @param msg the reflog message * @return 0 or an error code */ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg); /** * Rename a reflog * * The reflog to be renamed is expected to already exist * * The new name will be checked for validity. * See `git_reference_create_symbolic()` for rules about valid names. * * @param repo the repository * @param old_name the old name of the reference * @param name the new name of the reference * @return 0 on success, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name); /** * Delete the reflog for the given reference * * @param repo the repository * @param name the reflog to delete * @return 0 or an error code */ GIT_EXTERN(int) git_reflog_delete(git_repository *repo, const char *name); /** * Get the number of log entries in a reflog * * @param reflog the previously loaded reflog * @return the number of log entries */ GIT_EXTERN(size_t) git_reflog_entrycount(git_reflog *reflog); /** * Lookup an entry by its index * * Requesting the reflog entry with an index of 0 (zero) will * return the most recently created entry. * * @param reflog a previously loaded reflog * @param idx the position of the entry to lookup. Should be greater than or * equal to 0 (zero) and less than `git_reflog_entrycount()`. * @return the entry; NULL if not found */ GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(const git_reflog *reflog, size_t idx); /** * Remove an entry from the reflog by its index * * To ensure there's no gap in the log history, set `rewrite_previous_entry` * param value to 1. When deleting entry `n`, member old_oid of entry `n-1` * (if any) will be updated with the value of member new_oid of entry `n+1`. * * @param reflog a previously loaded reflog. * * @param idx the position of the entry to remove. Should be greater than or * equal to 0 (zero) and less than `git_reflog_entrycount()`. * * @param rewrite_previous_entry 1 to rewrite the history; 0 otherwise. * * @return 0 on success, GIT_ENOTFOUND if the entry doesn't exist * or an error code. */ GIT_EXTERN(int) git_reflog_drop( git_reflog *reflog, size_t idx, int rewrite_previous_entry); /** * Get the old oid * * @param entry a reflog entry * @return the old oid */ GIT_EXTERN(const git_oid *) git_reflog_entry_id_old(const git_reflog_entry *entry); /** * Get the new oid * * @param entry a reflog entry * @return the new oid at this time */ GIT_EXTERN(const git_oid *) git_reflog_entry_id_new(const git_reflog_entry *entry); /** * Get the committer of this entry * * @param entry a reflog entry * @return the committer */ GIT_EXTERN(const git_signature *) git_reflog_entry_committer(const git_reflog_entry *entry); /** * Get the log message * * @param entry a reflog entry * @return the log msg */ GIT_EXTERN(const char *) git_reflog_entry_message(const git_reflog_entry *entry); /** * Free the reflog * * @param reflog reflog to free */ GIT_EXTERN(void) git_reflog_free(git_reflog *reflog); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/graph.h0000644000175000017500000000455314125111754017330 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_graph_h__ #define INCLUDE_git_graph_h__ #include "common.h" #include "types.h" #include "oid.h" /** * @file git2/graph.h * @brief Git graph traversal routines * @defgroup git_revwalk Git graph traversal routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Count the number of unique commits between two commit objects * * There is no need for branches containing the commits to have any * upstream relationship, but it helps to think of one as a branch and * the other as its upstream, the `ahead` and `behind` values will be * what git would report for the branches. * * @param ahead number of unique from commits in `upstream` * @param behind number of unique from commits in `local` * @param repo the repository where the commits exist * @param local the commit for local * @param upstream the commit for upstream */ GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream); /** * Determine if a commit is the descendant of another commit. * * Note that a commit is not considered a descendant of itself, in contrast * to `git merge-base --is-ancestor`. * * @param repo the repository where the commits exist * @param commit a previously loaded commit * @param ancestor a potential ancestor commit * @return 1 if the given commit is a descendant of the potential ancestor, * 0 if not, error code otherwise. */ GIT_EXTERN(int) git_graph_descendant_of( git_repository *repo, const git_oid *commit, const git_oid *ancestor); /** * Determine if a commit is reachable from any of a list of commits by * following parent edges. * * @param repo the repository where the commits exist * @param commit a previously loaded commit * @param length the number of commits in the provided `descendant_array` * @param descendant_array oids of the commits * @return 1 if the given commit is an ancestor of any of the given potential * descendants, 0 if not, error code otherwise. */ GIT_EXTERN(int) git_graph_reachable_from_any( git_repository *repo, const git_oid *commit, const git_oid descendant_array[], size_t length); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/worktree.h0000644000175000017500000001723714125111754020074 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_worktree_h__ #define INCLUDE_git_worktree_h__ #include "common.h" #include "buffer.h" #include "types.h" #include "strarray.h" /** * @file git2/worktrees.h * @brief Git worktree related functions * @defgroup git_commit Git worktree related functions * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * List names of linked working trees * * The returned list should be released with `git_strarray_free` * when no longer needed. * * @param out pointer to the array of working tree names * @param repo the repo to use when listing working trees * @return 0 or an error code */ GIT_EXTERN(int) git_worktree_list(git_strarray *out, git_repository *repo); /** * Lookup a working tree by its name for a given repository * * @param out Output pointer to looked up worktree or `NULL` * @param repo The repository containing worktrees * @param name Name of the working tree to look up * @return 0 or an error code */ GIT_EXTERN(int) git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name); /** * Open a worktree of a given repository * * If a repository is not the main tree but a worktree, this * function will look up the worktree inside the parent * repository and create a new `git_worktree` structure. * * @param out Out-pointer for the newly allocated worktree * @param repo Repository to look up worktree for */ GIT_EXTERN(int) git_worktree_open_from_repository(git_worktree **out, git_repository *repo); /** * Free a previously allocated worktree * * @param wt worktree handle to close. If NULL nothing occurs. */ GIT_EXTERN(void) git_worktree_free(git_worktree *wt); /** * Check if worktree is valid * * A valid worktree requires both the git data structures inside * the linked parent repository and the linked working copy to be * present. * * @param wt Worktree to check * @return 0 when worktree is valid, error-code otherwise */ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt); /** * Worktree add options structure * * Initialize with `GIT_WORKTREE_ADD_OPTIONS_INIT`. Alternatively, you can * use `git_worktree_add_options_init`. * */ typedef struct git_worktree_add_options { unsigned int version; int lock; /**< lock newly created worktree */ git_reference *ref; /**< reference to use for the new worktree HEAD */ } git_worktree_add_options; #define GIT_WORKTREE_ADD_OPTIONS_VERSION 1 #define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL} /** * Initialize git_worktree_add_options structure * * Initializes a `git_worktree_add_options` with default values. Equivalent to * creating an instance with `GIT_WORKTREE_ADD_OPTIONS_INIT`. * * @param opts The `git_worktree_add_options` struct to initialize. * @param version The struct version; pass `GIT_WORKTREE_ADD_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_worktree_add_options_init(git_worktree_add_options *opts, unsigned int version); /** * Add a new working tree * * Add a new working tree for the repository, that is create the * required data structures inside the repository and check out * the current HEAD at `path` * * @param out Output pointer containing new working tree * @param repo Repository to create working tree for * @param name Name of the working tree * @param path Path to create working tree at * @param opts Options to modify default behavior. May be NULL * @return 0 or an error code */ GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *path, const git_worktree_add_options *opts); /** * Lock worktree if not already locked * * Lock a worktree, optionally specifying a reason why the linked * working tree is being locked. * * @param wt Worktree to lock * @param reason Reason why the working tree is being locked * @return 0 on success, non-zero otherwise */ GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, const char *reason); /** * Unlock a locked worktree * * @param wt Worktree to unlock * @return 0 on success, 1 if worktree was not locked, error-code * otherwise */ GIT_EXTERN(int) git_worktree_unlock(git_worktree *wt); /** * Check if worktree is locked * * A worktree may be locked if the linked working tree is stored * on a portable device which is not available. * * @param reason Buffer to store reason in. If NULL no reason is stored. * @param wt Worktree to check * @return 0 when the working tree not locked, a value greater * than zero if it is locked, less than zero if there was an * error */ GIT_EXTERN(int) git_worktree_is_locked(git_buf *reason, const git_worktree *wt); /** * Retrieve the name of the worktree * * @param wt Worktree to get the name for * @return The worktree's name. The pointer returned is valid for the * lifetime of the git_worktree */ GIT_EXTERN(const char *) git_worktree_name(const git_worktree *wt); /** * Retrieve the filesystem path for the worktree * * @param wt Worktree to get the path for * @return The worktree's filesystem path. The pointer returned * is valid for the lifetime of the git_worktree. */ GIT_EXTERN(const char *) git_worktree_path(const git_worktree *wt); /** * Flags which can be passed to git_worktree_prune to alter its * behavior. */ typedef enum { /* Prune working tree even if working tree is valid */ GIT_WORKTREE_PRUNE_VALID = 1u << 0, /* Prune working tree even if it is locked */ GIT_WORKTREE_PRUNE_LOCKED = 1u << 1, /* Prune checked out working tree */ GIT_WORKTREE_PRUNE_WORKING_TREE = 1u << 2, } git_worktree_prune_t; /** * Worktree prune options structure * * Initialize with `GIT_WORKTREE_PRUNE_OPTIONS_INIT`. Alternatively, you can * use `git_worktree_prune_options_init`. * */ typedef struct git_worktree_prune_options { unsigned int version; /** A combination of `git_worktree_prune_t` */ uint32_t flags; } git_worktree_prune_options; #define GIT_WORKTREE_PRUNE_OPTIONS_VERSION 1 #define GIT_WORKTREE_PRUNE_OPTIONS_INIT {GIT_WORKTREE_PRUNE_OPTIONS_VERSION,0} /** * Initialize git_worktree_prune_options structure * * Initializes a `git_worktree_prune_options` with default values. Equivalent to * creating an instance with `GIT_WORKTREE_PRUNE_OPTIONS_INIT`. * * @param opts The `git_worktree_prune_options` struct to initialize. * @param version The struct version; pass `GIT_WORKTREE_PRUNE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_worktree_prune_options_init( git_worktree_prune_options *opts, unsigned int version); /** * Is the worktree prunable with the given options? * * A worktree is not prunable in the following scenarios: * * - the worktree is linking to a valid on-disk worktree. The * `valid` member will cause this check to be ignored. * - the worktree is locked. The `locked` flag will cause this * check to be ignored. * * If the worktree is not valid and not locked or if the above * flags have been passed in, this function will return a * positive value. */ GIT_EXTERN(int) git_worktree_is_prunable(git_worktree *wt, git_worktree_prune_options *opts); /** * Prune working tree * * Prune the working tree, that is remove the git data * structures on disk. The repository will only be pruned of * `git_worktree_is_prunable` succeeds. * * @param wt Worktree to prune * @param opts Specifies which checks to override. See * `git_worktree_is_prunable`. May be NULL * @return 0 or an error code */ GIT_EXTERN(int) git_worktree_prune(git_worktree *wt, git_worktree_prune_options *opts); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/branch.h0000644000175000017500000002450414125111754017462 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_branch_h__ #define INCLUDE_git_branch_h__ #include "common.h" #include "oid.h" #include "types.h" /** * @file git2/branch.h * @brief Git branch parsing routines * @defgroup git_branch Git branch management * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a new branch pointing at a target commit * * A new direct reference will be created pointing to * this target commit. If `force` is true and a reference * already exists with the given name, it'll be replaced. * * The returned reference must be freed by the user. * * The branch name will be checked for validity. * See `git_tag_create()` for rules about valid names. * * @param out Pointer where to store the underlying reference. * * @param branch_name Name for the branch; this name is * validated for consistency. It should also not conflict with * an already existing branch name. * * @param target Commit to which this branch should point. This object * must belong to the given `repo`. * * @param force Overwrite existing branch. * * @return 0, GIT_EINVALIDSPEC or an error code. * A proper reference is written in the refs/heads namespace * pointing to the provided target commit. */ GIT_EXTERN(int) git_branch_create( git_reference **out, git_repository *repo, const char *branch_name, const git_commit *target, int force); /** * Create a new branch pointing at a target commit * * This behaves like `git_branch_create()` but takes an annotated * commit, which lets you specify which extended sha syntax string was * specified by a user, allowing for more exact reflog messages. * * See the documentation for `git_branch_create()`. * * @see git_branch_create */ GIT_EXTERN(int) git_branch_create_from_annotated( git_reference **ref_out, git_repository *repository, const char *branch_name, const git_annotated_commit *commit, int force); /** * Delete an existing branch reference. * * Note that if the deletion succeeds, the reference object will not * be valid anymore, and should be freed immediately by the user using * `git_reference_free()`. * * @param branch A valid reference representing a branch * @return 0 on success, or an error code. */ GIT_EXTERN(int) git_branch_delete(git_reference *branch); /** Iterator type for branches */ typedef struct git_branch_iterator git_branch_iterator; /** * Create an iterator which loops over the requested branches. * * @param out the iterator * @param repo Repository where to find the branches. * @param list_flags Filtering flags for the branch * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE * or GIT_BRANCH_ALL. * * @return 0 on success or an error code */ GIT_EXTERN(int) git_branch_iterator_new( git_branch_iterator **out, git_repository *repo, git_branch_t list_flags); /** * Retrieve the next branch from the iterator * * @param out the reference * @param out_type the type of branch (local or remote-tracking) * @param iter the branch iterator * @return 0 on success, GIT_ITEROVER if there are no more branches or an error code. */ GIT_EXTERN(int) git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *iter); /** * Free a branch iterator * * @param iter the iterator to free */ GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter); /** * Move/rename an existing local branch reference. * * The new branch name will be checked for validity. * See `git_tag_create()` for rules about valid names. * * Note that if the move succeeds, the old reference object will not + be valid anymore, and should be freed immediately by the user using + `git_reference_free()`. * * @param out New reference object for the updated name. * * @param branch Current underlying reference of the branch. * * @param new_branch_name Target name of the branch once the move * is performed; this name is validated for consistency. * * @param force Overwrite existing branch. * * @return 0 on success, GIT_EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_branch_move( git_reference **out, git_reference *branch, const char *new_branch_name, int force); /** * Lookup a branch by its name in a repository. * * The generated reference must be freed by the user. * The branch name will be checked for validity. * * @see git_tag_create for rules about valid names. * * @param out pointer to the looked-up branch reference * @param repo the repository to look up the branch * @param branch_name Name of the branch to be looked-up; * this name is validated for consistency. * @param branch_type Type of the considered branch. This should * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE. * * @return 0 on success; GIT_ENOTFOUND when no matching branch * exists, GIT_EINVALIDSPEC, otherwise an error code. */ GIT_EXTERN(int) git_branch_lookup( git_reference **out, git_repository *repo, const char *branch_name, git_branch_t branch_type); /** * Get the branch name * * Given a reference object, this will check that it really is a branch (ie. * it lives under "refs/heads/" or "refs/remotes/"), and return the branch part * of it. * * @param out Pointer to the abbreviated reference name. * Owned by ref, do not free. * * @param ref A reference object, ideally pointing to a branch * * @return 0 on success; GIT_EINVALID if the reference isn't either a local or * remote branch, otherwise an error code. */ GIT_EXTERN(int) git_branch_name( const char **out, const git_reference *ref); /** * Get the upstream of a branch * * Given a reference, this will return a new reference object corresponding * to its remote tracking branch. The reference must be a local branch. * * @see git_branch_upstream_name for details on the resolution. * * @param out Pointer where to store the retrieved reference. * @param branch Current underlying reference of the branch. * * @return 0 on success; GIT_ENOTFOUND when no remote tracking * reference exists, otherwise an error code. */ GIT_EXTERN(int) git_branch_upstream( git_reference **out, const git_reference *branch); /** * Set a branch's upstream branch * * This will update the configuration to set the branch named `branch_name` as the upstream of `branch`. * Pass a NULL name to unset the upstream information. * * @note the actual tracking reference must have been already created for the * operation to succeed. * * @param branch the branch to configure * @param branch_name remote-tracking or local branch to set as upstream. * * @return 0 on success; GIT_ENOTFOUND if there's no branch named `branch_name` * or an error code */ GIT_EXTERN(int) git_branch_set_upstream( git_reference *branch, const char *branch_name); /** * Get the upstream name of a branch * * Given a local branch, this will return its remote-tracking branch information, * as a full reference name, ie. "feature/nice" would become * "refs/remote/origin/feature/nice", depending on that branch's configuration. * * @param out the buffer into which the name will be written. * @param repo the repository where the branches live. * @param refname reference name of the local branch. * * @return 0 on success, GIT_ENOTFOUND when no remote tracking reference exists, * or an error code. */ GIT_EXTERN(int) git_branch_upstream_name( git_buf *out, git_repository *repo, const char *refname); /** * Determine if HEAD points to the given branch * * @param branch A reference to a local branch. * * @return 1 if HEAD points at the branch, 0 if it isn't, or a negative value * as an error code. */ GIT_EXTERN(int) git_branch_is_head( const git_reference *branch); /** * Determine if any HEAD points to the current branch * * This will iterate over all known linked repositories (usually in the form of * worktrees) and report whether any HEAD is pointing at the current branch. * * @param branch A reference to a local branch. * * @return 1 if branch is checked out, 0 if it isn't, an error code otherwise. */ GIT_EXTERN(int) git_branch_is_checked_out( const git_reference *branch); /** * Find the remote name of a remote-tracking branch * * This will return the name of the remote whose fetch refspec is matching * the given branch. E.g. given a branch "refs/remotes/test/master", it will * extract the "test" part. If refspecs from multiple remotes match, * the function will return GIT_EAMBIGUOUS. * * @param out The buffer into which the name will be written. * @param repo The repository where the branch lives. * @param refname complete name of the remote tracking branch. * * @return 0 on success, GIT_ENOTFOUND when no matching remote was found, * GIT_EAMBIGUOUS when the branch maps to several remotes, * otherwise an error code. */ GIT_EXTERN(int) git_branch_remote_name( git_buf *out, git_repository *repo, const char *refname); /** * Retrieve the upstream remote of a local branch * * This will return the currently configured "branch.*.remote" for a given * branch. This branch must be local. * * @param buf the buffer into which to write the name * @param repo the repository in which to look * @param refname the full name of the branch * @return 0 or an error code */ GIT_EXTERN(int) git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname); /** * Retrieve the upstream merge of a local branch * * This will return the currently configured "branch.*.merge" for a given * branch. This branch must be local. * * @param buf the buffer into which to write the name * @param repo the repository in which to look * @param refname the full name of the branch * @return 0 or an error code */ GIT_EXTERN(int) git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname); /** * Determine whether a branch name is valid, meaning that (when prefixed * with `refs/heads/`) that it is a valid reference name, and that any * additional branch name restrictions are imposed (eg, it cannot start * with a `-`). * * @param valid output pointer to set with validity of given branch name * @param name a branch name to test * @return 0 on success or an error code */ GIT_EXTERN(int) git_branch_name_is_valid(int *valid, const char *name); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/types.h0000644000175000017500000002767214125111754017402 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_types_h__ #define INCLUDE_git_types_h__ #include "common.h" /** * @file git2/types.h * @brief libgit2 base & compatibility types * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Cross-platform compatibility types for off_t / time_t * * NOTE: This needs to be in a public header so that both the library * implementation and client applications both agree on the same types. * Otherwise we get undefined behavior. * * Use the "best" types that each platform provides. Currently we truncate * these intermediate representations for compatibility with the git ABI, but * if and when it changes to support 64 bit types, our code will naturally * adapt. * NOTE: These types should match those that are returned by our internal * stat() functions, for all platforms. */ #include #ifdef __amigaos4__ #include #endif #if defined(_MSC_VER) typedef __int64 git_off_t; typedef __time64_t git_time_t; #elif defined(__MINGW32__) typedef off64_t git_off_t; typedef __time64_t git_time_t; #elif defined(__HAIKU__) typedef __haiku_std_int64 git_off_t; typedef __haiku_std_int64 git_time_t; #else /* POSIX */ /* * Note: Can't use off_t since if a client program includes * before us (directly or indirectly), they'll get 32 bit off_t in their client * app, even though /we/ define _FILE_OFFSET_BITS=64. */ typedef int64_t git_off_t; typedef int64_t git_time_t; /**< time in seconds from epoch */ #endif /** The maximum size of an object */ typedef uint64_t git_object_size_t; #include "buffer.h" #include "oid.h" /** Basic type (loose or packed) of any Git object. */ typedef enum { GIT_OBJECT_ANY = -2, /**< Object can be any of the following */ GIT_OBJECT_INVALID = -1, /**< Object is invalid. */ GIT_OBJECT_COMMIT = 1, /**< A commit object. */ GIT_OBJECT_TREE = 2, /**< A tree (directory listing) object. */ GIT_OBJECT_BLOB = 3, /**< A file revision object. */ GIT_OBJECT_TAG = 4, /**< An annotated tag object. */ GIT_OBJECT_OFS_DELTA = 6, /**< A delta, base is given by an offset. */ GIT_OBJECT_REF_DELTA = 7, /**< A delta, base is given by object id. */ } git_object_t; /** An open object database handle. */ typedef struct git_odb git_odb; /** A custom backend in an ODB */ typedef struct git_odb_backend git_odb_backend; /** An object read from the ODB */ typedef struct git_odb_object git_odb_object; /** A stream to read/write from the ODB */ typedef struct git_odb_stream git_odb_stream; /** A stream to write a packfile to the ODB */ typedef struct git_odb_writepack git_odb_writepack; /** a writer for multi-pack-index files. */ typedef struct git_midx_writer git_midx_writer; /** An open refs database handle. */ typedef struct git_refdb git_refdb; /** A custom backend for refs */ typedef struct git_refdb_backend git_refdb_backend; /** A git commit-graph */ typedef struct git_commit_graph git_commit_graph; /** a writer for commit-graph files. */ typedef struct git_commit_graph_writer git_commit_graph_writer; /** * Representation of an existing git repository, * including all its object contents */ typedef struct git_repository git_repository; /** Representation of a working tree */ typedef struct git_worktree git_worktree; /** Representation of a generic object in a repository */ typedef struct git_object git_object; /** Representation of an in-progress walk through the commits in a repo */ typedef struct git_revwalk git_revwalk; /** Parsed representation of a tag object. */ typedef struct git_tag git_tag; /** In-memory representation of a blob object. */ typedef struct git_blob git_blob; /** Parsed representation of a commit object. */ typedef struct git_commit git_commit; /** Representation of each one of the entries in a tree object. */ typedef struct git_tree_entry git_tree_entry; /** Representation of a tree object. */ typedef struct git_tree git_tree; /** Constructor for in-memory trees */ typedef struct git_treebuilder git_treebuilder; /** Memory representation of an index file. */ typedef struct git_index git_index; /** An iterator for entries in the index. */ typedef struct git_index_iterator git_index_iterator; /** An iterator for conflicts in the index. */ typedef struct git_index_conflict_iterator git_index_conflict_iterator; /** Memory representation of a set of config files */ typedef struct git_config git_config; /** Interface to access a configuration file */ typedef struct git_config_backend git_config_backend; /** Representation of a reference log entry */ typedef struct git_reflog_entry git_reflog_entry; /** Representation of a reference log */ typedef struct git_reflog git_reflog; /** Representation of a git note */ typedef struct git_note git_note; /** Representation of a git packbuilder */ typedef struct git_packbuilder git_packbuilder; /** Time in a signature */ typedef struct git_time { git_time_t time; /**< time in seconds from epoch */ int offset; /**< timezone offset, in minutes */ char sign; /**< indicator for questionable '-0000' offsets in signature */ } git_time; /** An action signature (e.g. for committers, taggers, etc) */ typedef struct git_signature { char *name; /**< full name of the author */ char *email; /**< email of the author */ git_time when; /**< time when the action happened */ } git_signature; /** In-memory representation of a reference. */ typedef struct git_reference git_reference; /** Iterator for references */ typedef struct git_reference_iterator git_reference_iterator; /** Transactional interface to references */ typedef struct git_transaction git_transaction; /** Annotated commits, the input to merge and rebase. */ typedef struct git_annotated_commit git_annotated_commit; /** Representation of a status collection */ typedef struct git_status_list git_status_list; /** Representation of a rebase */ typedef struct git_rebase git_rebase; /** Basic type of any Git reference. */ typedef enum { GIT_REFERENCE_INVALID = 0, /**< Invalid reference */ GIT_REFERENCE_DIRECT = 1, /**< A reference that points at an object id */ GIT_REFERENCE_SYMBOLIC = 2, /**< A reference that points at another reference */ GIT_REFERENCE_ALL = GIT_REFERENCE_DIRECT | GIT_REFERENCE_SYMBOLIC, } git_reference_t; /** Basic type of any Git branch. */ typedef enum { GIT_BRANCH_LOCAL = 1, GIT_BRANCH_REMOTE = 2, GIT_BRANCH_ALL = GIT_BRANCH_LOCAL|GIT_BRANCH_REMOTE, } git_branch_t; /** Valid modes for index and tree entries. */ typedef enum { GIT_FILEMODE_UNREADABLE = 0000000, GIT_FILEMODE_TREE = 0040000, GIT_FILEMODE_BLOB = 0100644, GIT_FILEMODE_BLOB_EXECUTABLE = 0100755, GIT_FILEMODE_LINK = 0120000, GIT_FILEMODE_COMMIT = 0160000, } git_filemode_t; /** * A refspec specifies the mapping between remote and local reference * names when fetch or pushing. */ typedef struct git_refspec git_refspec; /** * Git's idea of a remote repository. A remote can be anonymous (in * which case it does not have backing configuration entires). */ typedef struct git_remote git_remote; /** * Interface which represents a transport to communicate with a * remote. */ typedef struct git_transport git_transport; /** * Preparation for a push operation. Can be used to configure what to * push and the level of parallelism of the packfile builder. */ typedef struct git_push git_push; /* documentation in the definition */ typedef struct git_remote_head git_remote_head; typedef struct git_remote_callbacks git_remote_callbacks; /** * Parent type for `git_cert_hostkey` and `git_cert_x509`. */ typedef struct git_cert git_cert; /** * Opaque structure representing a submodule. */ typedef struct git_submodule git_submodule; /** * Submodule update values * * These values represent settings for the `submodule.$name.update` * configuration value which says how to handle `git submodule update` for * this submodule. The value is usually set in the ".gitmodules" file and * copied to ".git/config" when the submodule is initialized. * * You can override this setting on a per-submodule basis with * `git_submodule_set_update()` and write the changed value to disk using * `git_submodule_save()`. If you have overwritten the value, you can * revert it by passing `GIT_SUBMODULE_UPDATE_RESET` to the set function. * * The values are: * * - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is * updated, checkout the new detached HEAD to the submodule directory. * - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked * out branch onto the commit from the superproject. * - GIT_SUBMODULE_UPDATE_MERGE: update by merging the commit in the * superproject into the current checkout out branch of the submodule. * - GIT_SUBMODULE_UPDATE_NONE: do not update this submodule even when * the commit in the superproject is updated. * - GIT_SUBMODULE_UPDATE_DEFAULT: not used except as static initializer * when we don't want any particular update rule to be specified. */ typedef enum { GIT_SUBMODULE_UPDATE_CHECKOUT = 1, GIT_SUBMODULE_UPDATE_REBASE = 2, GIT_SUBMODULE_UPDATE_MERGE = 3, GIT_SUBMODULE_UPDATE_NONE = 4, GIT_SUBMODULE_UPDATE_DEFAULT = 0 } git_submodule_update_t; /** * Submodule ignore values * * These values represent settings for the `submodule.$name.ignore` * configuration value which says how deeply to look at the working * directory when getting submodule status. * * You can override this value in memory on a per-submodule basis with * `git_submodule_set_ignore()` and can write the changed value to disk * with `git_submodule_save()`. If you have overwritten the value, you * can revert to the on disk value by using `GIT_SUBMODULE_IGNORE_RESET`. * * The values are: * * - GIT_SUBMODULE_IGNORE_UNSPECIFIED: use the submodule's configuration * - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an * untracked file, will mark the submodule as dirty. Ignored files are * still ignored, of course. * - GIT_SUBMODULE_IGNORE_UNTRACKED: ignore untracked files; only changes * to tracked files, or the index or the HEAD commit will matter. * - GIT_SUBMODULE_IGNORE_DIRTY: ignore changes in the working directory, * only considering changes if the HEAD of submodule has moved from the * value in the superproject. * - GIT_SUBMODULE_IGNORE_ALL: never check if the submodule is dirty * - GIT_SUBMODULE_IGNORE_DEFAULT: not used except as static initializer * when we don't want any particular ignore rule to be specified. */ typedef enum { GIT_SUBMODULE_IGNORE_UNSPECIFIED = -1, /**< use the submodule's configuration */ GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */ GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */ GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */ } git_submodule_ignore_t; /** * Options for submodule recurse. * * Represent the value of `submodule.$name.fetchRecurseSubmodules` * * * GIT_SUBMODULE_RECURSE_NO - do no recurse into submodules * * GIT_SUBMODULE_RECURSE_YES - recurse into submodules * * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when * commit not already in local clone */ typedef enum { GIT_SUBMODULE_RECURSE_NO = 0, GIT_SUBMODULE_RECURSE_YES = 1, GIT_SUBMODULE_RECURSE_ONDEMAND = 2, } git_submodule_recurse_t; typedef struct git_writestream git_writestream; /** A type to write in a streaming fashion, for example, for filters. */ struct git_writestream { int GIT_CALLBACK(write)(git_writestream *stream, const char *buffer, size_t len); int GIT_CALLBACK(close)(git_writestream *stream); void GIT_CALLBACK(free)(git_writestream *stream); }; /** Representation of .mailmap file state. */ typedef struct git_mailmap git_mailmap; /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/notes.h0000644000175000017500000002036414125111754017355 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_note_h__ #define INCLUDE_git_note_h__ #include "oid.h" /** * @file git2/notes.h * @brief Git notes management routines * @defgroup git_note Git notes management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Callback for git_note_foreach. * * Receives: * - blob_id: Oid of the blob containing the message * - annotated_object_id: Oid of the git object being annotated * - payload: Payload data passed to `git_note_foreach` */ typedef int GIT_CALLBACK(git_note_foreach_cb)( const git_oid *blob_id, const git_oid *annotated_object_id, void *payload); /** * note iterator */ typedef struct git_iterator git_note_iterator; /** * Creates a new iterator for notes * * The iterator must be freed manually by the user. * * @param out pointer to the iterator * @param repo repository where to look up the note * @param notes_ref canonical name of the reference to use (optional); defaults to * "refs/notes/commits" * * @return 0 or an error code */ GIT_EXTERN(int) git_note_iterator_new( git_note_iterator **out, git_repository *repo, const char *notes_ref); /** * Creates a new iterator for notes from a commit * * The iterator must be freed manually by the user. * * @param out pointer to the iterator * @param notes_commit a pointer to the notes commit object * * @return 0 or an error code */ GIT_EXTERN(int) git_note_commit_iterator_new( git_note_iterator **out, git_commit *notes_commit); /** * Frees an git_note_iterator * * @param it pointer to the iterator */ GIT_EXTERN(void) git_note_iterator_free(git_note_iterator *it); /** * Return the current item (note_id and annotated_id) and advance the iterator * internally to the next value * * @param note_id id of blob containing the message * @param annotated_id id of the git object being annotated * @param it pointer to the iterator * * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code * (negative value) */ GIT_EXTERN(int) git_note_next( git_oid *note_id, git_oid *annotated_id, git_note_iterator *it); /** * Read the note for an object * * The note must be freed manually by the user. * * @param out pointer to the read note; NULL in case of error * @param repo repository where to look up the note * @param notes_ref canonical name of the reference to use (optional); defaults to * "refs/notes/commits" * @param oid OID of the git object to read the note from * * @return 0 or an error code */ GIT_EXTERN(int) git_note_read( git_note **out, git_repository *repo, const char *notes_ref, const git_oid *oid); /** * Read the note for an object from a note commit * * The note must be freed manually by the user. * * @param out pointer to the read note; NULL in case of error * @param repo repository where to look up the note * @param notes_commit a pointer to the notes commit object * @param oid OID of the git object to read the note from * * @return 0 or an error code */ GIT_EXTERN(int) git_note_commit_read( git_note **out, git_repository *repo, git_commit *notes_commit, const git_oid *oid); /** * Get the note author * * @param note the note * @return the author */ GIT_EXTERN(const git_signature *) git_note_author(const git_note *note); /** * Get the note committer * * @param note the note * @return the committer */ GIT_EXTERN(const git_signature *) git_note_committer(const git_note *note); /** * Get the note message * * @param note the note * @return the note message */ GIT_EXTERN(const char *) git_note_message(const git_note *note); /** * Get the note object's id * * @param note the note * @return the note object's id */ GIT_EXTERN(const git_oid *) git_note_id(const git_note *note); /** * Add a note for an object * * @param out pointer to store the OID (optional); NULL in case of error * @param repo repository where to store the note * @param notes_ref canonical name of the reference to use (optional); * defaults to "refs/notes/commits" * @param author signature of the notes commit author * @param committer signature of the notes commit committer * @param oid OID of the git object to decorate * @param note Content of the note to add for object oid * @param force Overwrite existing note * * @return 0 or an error code */ GIT_EXTERN(int) git_note_create( git_oid *out, git_repository *repo, const char *notes_ref, const git_signature *author, const git_signature *committer, const git_oid *oid, const char *note, int force); /** * Add a note for an object from a commit * * This function will create a notes commit for a given object, * the commit is a dangling commit, no reference is created. * * @param notes_commit_out pointer to store the commit (optional); * NULL in case of error * @param notes_blob_out a point to the id of a note blob (optional) * @param repo repository where the note will live * @param parent Pointer to parent note * or NULL if this shall start a new notes tree * @param author signature of the notes commit author * @param committer signature of the notes commit committer * @param oid OID of the git object to decorate * @param note Content of the note to add for object oid * @param allow_note_overwrite Overwrite existing note * * @return 0 or an error code */ GIT_EXTERN(int) git_note_commit_create( git_oid *notes_commit_out, git_oid *notes_blob_out, git_repository *repo, git_commit *parent, const git_signature *author, const git_signature *committer, const git_oid *oid, const char *note, int allow_note_overwrite); /** * Remove the note for an object * * @param repo repository where the note lives * @param notes_ref canonical name of the reference to use (optional); * defaults to "refs/notes/commits" * @param author signature of the notes commit author * @param committer signature of the notes commit committer * @param oid OID of the git object to remove the note from * * @return 0 or an error code */ GIT_EXTERN(int) git_note_remove( git_repository *repo, const char *notes_ref, const git_signature *author, const git_signature *committer, const git_oid *oid); /** * Remove the note for an object * * @param notes_commit_out pointer to store the new notes commit (optional); * NULL in case of error. * When removing a note a new tree containing all notes * sans the note to be removed is created and a new commit * pointing to that tree is also created. * In the case where the resulting tree is an empty tree * a new commit pointing to this empty tree will be returned. * @param repo repository where the note lives * @param notes_commit a pointer to the notes commit object * @param author signature of the notes commit author * @param committer signature of the notes commit committer * @param oid OID of the git object to remove the note from * * @return 0 or an error code */ GIT_EXTERN(int) git_note_commit_remove( git_oid *notes_commit_out, git_repository *repo, git_commit *notes_commit, const git_signature *author, const git_signature *committer, const git_oid *oid); /** * Free a git_note object * * @param note git_note object */ GIT_EXTERN(void) git_note_free(git_note *note); /** * Get the default notes reference for a repository * * @param out buffer in which to store the name of the default notes reference * @param repo The Git repository * * @return 0 or an error code */ GIT_EXTERN(int) git_note_default_ref(git_buf *out, git_repository *repo); /** * Loop over all the notes within a specified namespace * and issue a callback for each one. * * @param repo Repository where to find the notes. * * @param notes_ref Reference to read from (optional); defaults to * "refs/notes/commits". * * @param note_cb Callback to invoke per found annotation. Return non-zero * to stop looping. * * @param payload Extra parameter to callback function. * * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_note_foreach( git_repository *repo, const char *notes_ref, git_note_foreach_cb note_cb, void *payload); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/ignore.h0000644000175000017500000000505214125111754017505 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_ignore_h__ #define INCLUDE_git_ignore_h__ #include "common.h" #include "types.h" GIT_BEGIN_DECL /** * Add ignore rules for a repository. * * Excludesfile rules (i.e. .gitignore rules) are generally read from * .gitignore files in the repository tree or from a shared system file * only if a "core.excludesfile" config value is set. The library also * keeps a set of per-repository internal ignores that can be configured * in-memory and will not persist. This function allows you to add to * that internal rules list. * * Example usage: * * error = git_ignore_add_rule(myrepo, "*.c\ndir/\nFile with space\n"); * * This would add three rules to the ignores. * * @param repo The repository to add ignore rules to. * @param rules Text of rules, a la the contents of a .gitignore file. * It is okay to have multiple rules in the text; if so, * each rule should be terminated with a newline. * @return 0 on success */ GIT_EXTERN(int) git_ignore_add_rule( git_repository *repo, const char *rules); /** * Clear ignore rules that were explicitly added. * * Resets to the default internal ignore rules. This will not turn off * rules in .gitignore files that actually exist in the filesystem. * * The default internal ignores ignore ".", ".." and ".git" entries. * * @param repo The repository to remove ignore rules from. * @return 0 on success */ GIT_EXTERN(int) git_ignore_clear_internal_rules( git_repository *repo); /** * Test if the ignore rules apply to a given path. * * This function checks the ignore rules to see if they would apply to the * given file. This indicates if the file would be ignored regardless of * whether the file is already in the index or committed to the repository. * * One way to think of this is if you were to do "git check-ignore --no-index" * on the given file, would it be shown or not? * * @param ignored boolean returning 0 if the file is not ignored, 1 if it is * @param repo a repository object * @param path the file to check ignores for, relative to the repo's workdir. * @return 0 if ignore rules could be processed for the file (regardless * of whether it exists or not), or an error < 0 if they could not. */ GIT_EXTERN(int) git_ignore_path_is_ignored( int *ignored, git_repository *repo, const char *path); GIT_END_DECL #endif git2r/src/libgit2/include/git2/describe.h0000644000175000017500000001227214125111754020004 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_describe_h__ #define INCLUDE_git_describe_h__ #include "common.h" #include "types.h" #include "buffer.h" /** * @file git2/describe.h * @brief Git describing routines * @defgroup git_describe Git describing routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Reference lookup strategy * * These behave like the --tags and --all options to git-describe, * namely they say to look for any reference in either refs/tags/ or * refs/ respectively. */ typedef enum { GIT_DESCRIBE_DEFAULT, GIT_DESCRIBE_TAGS, GIT_DESCRIBE_ALL, } git_describe_strategy_t; /** * Describe options structure * * Initialize with `GIT_DESCRIBE_OPTIONS_INIT`. Alternatively, you can * use `git_describe_options_init`. * */ typedef struct git_describe_options { unsigned int version; unsigned int max_candidates_tags; /**< default: 10 */ unsigned int describe_strategy; /**< default: GIT_DESCRIBE_DEFAULT */ const char *pattern; /** * When calculating the distance from the matching tag or * reference, only walk down the first-parent ancestry. */ int only_follow_first_parent; /** * If no matching tag or reference is found, the describe * operation would normally fail. If this option is set, it * will instead fall back to showing the full id of the * commit. */ int show_commit_oid_as_fallback; } git_describe_options; #define GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS 10 #define GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE 7 #define GIT_DESCRIBE_OPTIONS_VERSION 1 #define GIT_DESCRIBE_OPTIONS_INIT { \ GIT_DESCRIBE_OPTIONS_VERSION, \ GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \ } /** * Initialize git_describe_options structure * * Initializes a `git_describe_options` with default values. Equivalent to creating * an instance with GIT_DESCRIBE_OPTIONS_INIT. * * @param opts The `git_describe_options` struct to initialize. * @param version The struct version; pass `GIT_DESCRIBE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_describe_options_init(git_describe_options *opts, unsigned int version); /** * Describe format options structure * * Initialize with `GIT_DESCRIBE_FORMAT_OPTIONS_INIT`. Alternatively, you can * use `git_describe_format_options_init`. * */ typedef struct { unsigned int version; /** * Size of the abbreviated commit id to use. This value is the * lower bound for the length of the abbreviated string. The * default is 7. */ unsigned int abbreviated_size; /** * Set to use the long format even when a shorter name could be used. */ int always_use_long_format; /** * If the workdir is dirty and this is set, this string will * be appended to the description string. */ const char *dirty_suffix; } git_describe_format_options; #define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION 1 #define GIT_DESCRIBE_FORMAT_OPTIONS_INIT { \ GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, \ GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE, \ } /** * Initialize git_describe_format_options structure * * Initializes a `git_describe_format_options` with default values. Equivalent to creating * an instance with GIT_DESCRIBE_FORMAT_OPTIONS_INIT. * * @param opts The `git_describe_format_options` struct to initialize. * @param version The struct version; pass `GIT_DESCRIBE_FORMAT_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_describe_format_options_init(git_describe_format_options *opts, unsigned int version); /** * A struct that stores the result of a describe operation. */ typedef struct git_describe_result git_describe_result; /** * Describe a commit * * Perform the describe operation on the given committish object. * * @param result pointer to store the result. You must free this once * you're done with it. * @param committish a committish to describe * @param opts the lookup options (or NULL for defaults) */ GIT_EXTERN(int) git_describe_commit( git_describe_result **result, git_object *committish, git_describe_options *opts); /** * Describe a commit * * Perform the describe operation on the current commit and the * worktree. After peforming describe on HEAD, a status is run and the * description is considered to be dirty if there are. * * @param out pointer to store the result. You must free this once * you're done with it. * @param repo the repository in which to perform the describe * @param opts the lookup options (or NULL for defaults) */ GIT_EXTERN(int) git_describe_workdir( git_describe_result **out, git_repository *repo, git_describe_options *opts); /** * Print the describe result to a buffer * * @param out The buffer to store the result * @param result the result from `git_describe_commit()` or * `git_describe_workdir()`. * @param opts the formatting options (or NULL for defaults) */ GIT_EXTERN(int) git_describe_format( git_buf *out, const git_describe_result *result, const git_describe_format_options *opts); /** * Free the describe result. */ GIT_EXTERN(void) git_describe_result_free(git_describe_result *result); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/proxy.h0000644000175000017500000000445214125111754017406 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_proxy_h__ #define INCLUDE_git_proxy_h__ #include "common.h" #include "cert.h" #include "credential.h" GIT_BEGIN_DECL /** * The type of proxy to use. */ typedef enum { /** * Do not attempt to connect through a proxy * * If built against libcurl, it itself may attempt to connect * to a proxy if the environment variables specify it. */ GIT_PROXY_NONE, /** * Try to auto-detect the proxy from the git configuration. */ GIT_PROXY_AUTO, /** * Connect via the URL given in the options */ GIT_PROXY_SPECIFIED, } git_proxy_t; /** * Options for connecting through a proxy * * Note that not all types may be supported, depending on the platform * and compilation options. */ typedef struct { unsigned int version; /** * The type of proxy to use, by URL, auto-detect. */ git_proxy_t type; /** * The URL of the proxy. */ const char *url; /** * This will be called if the remote host requires * authentication in order to connect to it. * * Returning GIT_PASSTHROUGH will make libgit2 behave as * though this field isn't set. */ git_credential_acquire_cb credentials; /** * If cert verification fails, this will be called to let the * user make the final decision of whether to allow the * connection to proceed. Returns 0 to allow the connection * or a negative value to indicate an error. */ git_transport_certificate_check_cb certificate_check; /** * Payload to be provided to the credentials and certificate * check callbacks. */ void *payload; } git_proxy_options; #define GIT_PROXY_OPTIONS_VERSION 1 #define GIT_PROXY_OPTIONS_INIT {GIT_PROXY_OPTIONS_VERSION} /** * Initialize git_proxy_options structure * * Initializes a `git_proxy_options` with default values. Equivalent to * creating an instance with `GIT_PROXY_OPTIONS_INIT`. * * @param opts The `git_proxy_options` struct to initialize. * @param version The struct version; pass `GIT_PROXY_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_proxy_options_init(git_proxy_options *opts, unsigned int version); GIT_END_DECL #endif git2r/src/libgit2/include/git2/version.h0000644000175000017500000000072414125111754017710 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ #define LIBGIT2_VERSION "1.3.0" #define LIBGIT2_VER_MAJOR 1 #define LIBGIT2_VER_MINOR 3 #define LIBGIT2_VER_REVISION 0 #define LIBGIT2_VER_PATCH 0 #define LIBGIT2_SOVERSION "1.3" #endif git2r/src/libgit2/include/git2/buffer.h0000644000175000017500000001020614125111754017470 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_buf_h__ #define INCLUDE_git_buf_h__ #include "common.h" /** * @file git2/buffer.h * @brief Buffer export structure * * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * A data buffer for exporting data from libgit2 * * Sometimes libgit2 wants to return an allocated data buffer to the * caller and have the caller take responsibility for freeing that memory. * This can be awkward if the caller does not have easy access to the same * allocation functions that libgit2 is using. In those cases, libgit2 * will fill in a `git_buf` and the caller can use `git_buf_dispose()` to * release it when they are done. * * A `git_buf` may also be used for the caller to pass in a reference to * a block of memory they hold. In this case, libgit2 will not resize or * free the memory, but will read from it as needed. * * Some APIs may occasionally do something slightly unusual with a buffer, * such as setting `ptr` to a value that was passed in by the user. In * those cases, the behavior will be clearly documented by the API. */ typedef struct { /** * The buffer contents. * * `ptr` points to the start of the allocated memory. If it is NULL, * then the `git_buf` is considered empty and libgit2 will feel free * to overwrite it with new data. */ char *ptr; /** * `asize` holds the known total amount of allocated memory if the `ptr` * was allocated by libgit2. It may be larger than `size`. If `ptr` * was not allocated by libgit2 and should not be resized and/or freed, * then `asize` will be set to zero. */ size_t asize; /** * `size` holds the size (in bytes) of the data that is actually used. */ size_t size; } git_buf; /** * Static initializer for git_buf from static buffer */ #define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) } /** * Free the memory referred to by the git_buf. * * Note that this does not free the `git_buf` itself, just the memory * pointed to by `buffer->ptr`. This will not free the memory if it looks * like it was not allocated internally, but it will clear the buffer back * to the empty state. * * @param buffer The buffer to deallocate */ GIT_EXTERN(void) git_buf_dispose(git_buf *buffer); /** * Resize the buffer allocation to make more space. * * This will attempt to grow the buffer to accommodate the target size. * * If the buffer refers to memory that was not allocated by libgit2 (i.e. * the `asize` field is zero), then `ptr` will be replaced with a newly * allocated block of data. Be careful so that memory allocated by the * caller is not lost. As a special variant, if you pass `target_size` as * 0 and the memory is not allocated by libgit2, this will allocate a new * buffer of size `size` and copy the external data into it. * * Currently, this will never shrink a buffer, only expand it. * * If the allocation fails, this will return an error and the buffer will be * marked as invalid for future operations, invaliding the contents. * * @param buffer The buffer to be resized; may or may not be allocated yet * @param target_size The desired available size * @return 0 on success, -1 on allocation failure */ GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size); /** * Set buffer to a copy of some raw data. * * @param buffer The buffer to set * @param data The data to copy into the buffer * @param datalen The length of the data to copy into the buffer * @return 0 on success, -1 on allocation failure */ GIT_EXTERN(int) git_buf_set( git_buf *buffer, const void *data, size_t datalen); /** * Check quickly if buffer looks like it contains binary data * * @param buf Buffer to check * @return 1 if buffer looks like non-text data */ GIT_EXTERN(int) git_buf_is_binary(const git_buf *buf); /** * Check quickly if buffer contains a NUL byte * * @param buf Buffer to check * @return 1 if buffer contains a NUL byte */ GIT_EXTERN(int) git_buf_contains_nul(const git_buf *buf); GIT_END_DECL /** @} */ #endif git2r/src/libgit2/include/git2/rebase.h0000644000175000017500000003034614125111754017467 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_rebase_h__ #define INCLUDE_git_rebase_h__ #include "common.h" #include "types.h" #include "oid.h" #include "annotated_commit.h" #include "merge.h" #include "checkout.h" #include "commit.h" /** * @file git2/rebase.h * @brief Git rebase routines * @defgroup git_rebase Git merge routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Rebase options * * Use to tell the rebase machinery how to operate. */ typedef struct { unsigned int version; /** * Used by `git_rebase_init`, this will instruct other clients working * on this rebase that you want a quiet rebase experience, which they * may choose to provide in an application-specific manner. This has no * effect upon libgit2 directly, but is provided for interoperability * between Git tools. */ int quiet; /** * Used by `git_rebase_init`, this will begin an in-memory rebase, * which will allow callers to step through the rebase operations and * commit the rebased changes, but will not rewind HEAD or update the * repository to be in a rebasing state. This will not interfere with * the working directory (if there is one). */ int inmemory; /** * Used by `git_rebase_finish`, this is the name of the notes reference * used to rewrite notes for rebased commits when finishing the rebase; * if NULL, the contents of the configuration option `notes.rewriteRef` * is examined, unless the configuration option `notes.rewrite.rebase` * is set to false. If `notes.rewriteRef` is also NULL, notes will * not be rewritten. */ const char *rewrite_notes_ref; /** * Options to control how trees are merged during `git_rebase_next`. */ git_merge_options merge_options; /** * Options to control how files are written during `git_rebase_init`, * `git_rebase_next` and `git_rebase_abort`. Note that a minimum * strategy of `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`, * and a minimum strategy of `GIT_CHECKOUT_FORCE` is defaulted in * `abort` to match git semantics. */ git_checkout_options checkout_options; /** * Optional callback that allows users to override commit * creation in `git_rebase_commit`. If specified, users can * create their own commit and provide the commit ID, which * may be useful for signing commits or otherwise customizing * the commit creation. * * If this callback returns `GIT_PASSTHROUGH`, then * `git_rebase_commit` will continue to create the commit. */ git_commit_create_cb commit_create_cb; #ifdef GIT_DEPRECATE_HARD void *reserved; #else /** * If provided, this will be called with the commit content, allowing * a signature to be added to the rebase commit. Can be skipped with * GIT_PASSTHROUGH. If GIT_PASSTHROUGH is returned, a commit will be made * without a signature. * * This field is only used when performing git_rebase_commit. * * This callback is not invoked if a `git_commit_create_cb` is * specified. * * This callback is deprecated; users should provide a * creation callback as `commit_create_cb` that produces a * commit buffer, signs it, and commits it. */ int (*signing_cb)(git_buf *, git_buf *, const char *, void *); #endif /** * This will be passed to each of the callbacks in this struct * as the last parameter. */ void *payload; } git_rebase_options; /** * Type of rebase operation in-progress after calling `git_rebase_next`. */ typedef enum { /** * The given commit is to be cherry-picked. The client should commit * the changes and continue if there are no conflicts. */ GIT_REBASE_OPERATION_PICK = 0, /** * The given commit is to be cherry-picked, but the client should prompt * the user to provide an updated commit message. */ GIT_REBASE_OPERATION_REWORD, /** * The given commit is to be cherry-picked, but the client should stop * to allow the user to edit the changes before committing them. */ GIT_REBASE_OPERATION_EDIT, /** * The given commit is to be squashed into the previous commit. The * commit message will be merged with the previous message. */ GIT_REBASE_OPERATION_SQUASH, /** * The given commit is to be squashed into the previous commit. The * commit message from this commit will be discarded. */ GIT_REBASE_OPERATION_FIXUP, /** * No commit will be cherry-picked. The client should run the given * command and (if successful) continue. */ GIT_REBASE_OPERATION_EXEC, } git_rebase_operation_t; #define GIT_REBASE_OPTIONS_VERSION 1 #define GIT_REBASE_OPTIONS_INIT \ { GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \ GIT_CHECKOUT_OPTIONS_INIT, NULL, NULL } /** Indicates that a rebase operation is not (yet) in progress. */ #define GIT_REBASE_NO_OPERATION SIZE_MAX /** * A rebase operation * * Describes a single instruction/operation to be performed during the * rebase. */ typedef struct { /** The type of rebase operation. */ git_rebase_operation_t type; /** * The commit ID being cherry-picked. This will be populated for * all operations except those of type `GIT_REBASE_OPERATION_EXEC`. */ const git_oid id; /** * The executable the user has requested be run. This will only * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`. */ const char *exec; } git_rebase_operation; /** * Initialize git_rebase_options structure * * Initializes a `git_rebase_options` with default values. Equivalent to * creating an instance with `GIT_REBASE_OPTIONS_INIT`. * * @param opts The `git_rebase_options` struct to initialize. * @param version The struct version; pass `GIT_REBASE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_rebase_options_init( git_rebase_options *opts, unsigned int version); /** * Initializes a rebase operation to rebase the changes in `branch` * relative to `upstream` onto another branch. To begin the rebase * process, call `git_rebase_next`. When you have finished with this * object, call `git_rebase_free`. * * @param out Pointer to store the rebase object * @param repo The repository to perform the rebase * @param branch The terminal commit to rebase, or NULL to rebase the * current branch * @param upstream The commit to begin rebasing from, or NULL to rebase all * reachable commits * @param onto The branch to rebase onto, or NULL to rebase onto the given * upstream * @param opts Options to specify how rebase is performed, or NULL * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_rebase_init( git_rebase **out, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto, const git_rebase_options *opts); /** * Opens an existing rebase that was previously started by either an * invocation of `git_rebase_init` or by another client. * * @param out Pointer to store the rebase object * @param repo The repository that has a rebase in-progress * @param opts Options to specify how rebase is performed * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_rebase_open( git_rebase **out, git_repository *repo, const git_rebase_options *opts); /** * Gets the original `HEAD` ref name for merge rebases. * * @return The original `HEAD` ref name */ GIT_EXTERN(const char *) git_rebase_orig_head_name(git_rebase *rebase); /** * Gets the original `HEAD` id for merge rebases. * * @return The original `HEAD` id */ GIT_EXTERN(const git_oid *) git_rebase_orig_head_id(git_rebase *rebase); /** * Gets the `onto` ref name for merge rebases. * * @return The `onto` ref name */ GIT_EXTERN(const char *) git_rebase_onto_name(git_rebase *rebase); /** * Gets the `onto` id for merge rebases. * * @return The `onto` id */ GIT_EXTERN(const git_oid *) git_rebase_onto_id(git_rebase *rebase); /** * Gets the count of rebase operations that are to be applied. * * @param rebase The in-progress rebase * @return The number of rebase operations in total */ GIT_EXTERN(size_t) git_rebase_operation_entrycount(git_rebase *rebase); /** * Gets the index of the rebase operation that is currently being applied. * If the first operation has not yet been applied (because you have * called `init` but not yet `next`) then this returns * `GIT_REBASE_NO_OPERATION`. * * @param rebase The in-progress rebase * @return The index of the rebase operation currently being applied. */ GIT_EXTERN(size_t) git_rebase_operation_current(git_rebase *rebase); /** * Gets the rebase operation specified by the given index. * * @param rebase The in-progress rebase * @param idx The index of the rebase operation to retrieve * @return The rebase operation or NULL if `idx` was out of bounds */ GIT_EXTERN(git_rebase_operation *) git_rebase_operation_byindex( git_rebase *rebase, size_t idx); /** * Performs the next rebase operation and returns the information about it. * If the operation is one that applies a patch (which is any operation except * GIT_REBASE_OPERATION_EXEC) then the patch will be applied and the index and * working directory will be updated with the changes. If there are conflicts, * you will need to address those before committing the changes. * * @param operation Pointer to store the rebase operation that is to be performed next * @param rebase The rebase in progress * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_rebase_next( git_rebase_operation **operation, git_rebase *rebase); /** * Gets the index produced by the last operation, which is the result * of `git_rebase_next` and which will be committed by the next * invocation of `git_rebase_commit`. This is useful for resolving * conflicts in an in-memory rebase before committing them. You must * call `git_index_free` when you are finished with this. * * This is only applicable for in-memory rebases; for rebases within * a working directory, the changes were applied to the repository's * index. */ GIT_EXTERN(int) git_rebase_inmemory_index( git_index **index, git_rebase *rebase); /** * Commits the current patch. You must have resolved any conflicts that * were introduced during the patch application from the `git_rebase_next` * invocation. * * @param id Pointer in which to store the OID of the newly created commit * @param rebase The rebase that is in-progress * @param author The author of the updated commit, or NULL to keep the * author from the original commit * @param committer The committer of the rebase * @param message_encoding The encoding for the message in the commit, * represented with a standard encoding name. If message is NULL, * this should also be NULL, and the encoding from the original * commit will be maintained. If message is specified, this may be * NULL to indicate that "UTF-8" is to be used. * @param message The message for this commit, or NULL to use the message * from the original commit. * @return Zero on success, GIT_EUNMERGED if there are unmerged changes in * the index, GIT_EAPPLIED if the current commit has already * been applied to the upstream and there is nothing to commit, * -1 on failure. */ GIT_EXTERN(int) git_rebase_commit( git_oid *id, git_rebase *rebase, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message); /** * Aborts a rebase that is currently in progress, resetting the repository * and working directory to their state before rebase began. * * @param rebase The rebase that is in-progress * @return Zero on success; GIT_ENOTFOUND if a rebase is not in progress, * -1 on other errors. */ GIT_EXTERN(int) git_rebase_abort(git_rebase *rebase); /** * Finishes a rebase that is currently in progress once all patches have * been applied. * * @param rebase The rebase that is in-progress * @param signature The identity that is finishing the rebase (optional) * @return Zero on success; -1 on error */ GIT_EXTERN(int) git_rebase_finish( git_rebase *rebase, const git_signature *signature); /** * Frees the `git_rebase` object. * * @param rebase The rebase object */ GIT_EXTERN(void) git_rebase_free(git_rebase *rebase); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/refdb.h0000644000175000017500000000346314125111754017310 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_refdb_h__ #define INCLUDE_git_refdb_h__ #include "common.h" #include "types.h" #include "oid.h" #include "refs.h" /** * @file git2/refdb.h * @brief Git custom refs backend functions * @defgroup git_refdb Git custom refs backend API * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a new reference database with no backends. * * Before the Ref DB can be used for read/writing, a custom database * backend must be manually set using `git_refdb_set_backend()` * * @param out location to store the database pointer, if opened. * Set to NULL if the open failed. * @param repo the repository * @return 0 or an error code */ GIT_EXTERN(int) git_refdb_new(git_refdb **out, git_repository *repo); /** * Create a new reference database and automatically add * the default backends: * * - git_refdb_dir: read and write loose and packed refs * from disk, assuming the repository dir as the folder * * @param out location to store the database pointer, if opened. * Set to NULL if the open failed. * @param repo the repository * @return 0 or an error code */ GIT_EXTERN(int) git_refdb_open(git_refdb **out, git_repository *repo); /** * Suggests that the given refdb compress or optimize its references. * This mechanism is implementation specific. For on-disk reference * databases, for example, this may pack all loose references. */ GIT_EXTERN(int) git_refdb_compress(git_refdb *refdb); /** * Close an open reference database. * * @param refdb reference database pointer or NULL */ GIT_EXTERN(void) git_refdb_free(git_refdb *refdb); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/credential_helpers.h0000644000175000017500000000310514125111754022053 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_credential_helpers_h__ #define INCLUDE_git_credential_helpers_h__ #include "transport.h" /** * @file git2/credential_helpers.h * @brief Utility functions for credential management * @defgroup git_credential_helpers credential management helpers * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Payload for git_credential_userpass_plaintext. */ typedef struct git_credential_userpass_payload { const char *username; const char *password; } git_credential_userpass_payload; /** * Stock callback usable as a git_credential_acquire_cb. This calls * git_cred_userpass_plaintext_new unless the protocol has not specified * `GIT_CREDENTIAL_USERPASS_PLAINTEXT` as an allowed type. * * @param out The newly created credential object. * @param url The resource for which we are demanding a credential. * @param user_from_url The username that was embedded in a "user\@host" * remote url, or NULL if not included. * @param allowed_types A bitmask stating which credential types are OK to return. * @param payload The payload provided when specifying this callback. (This is * interpreted as a `git_credential_userpass_payload*`.) */ GIT_EXTERN(int) git_credential_userpass( git_credential **out, const char *url, const char *user_from_url, unsigned int allowed_types, void *payload); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/oidarray.h0000644000175000017500000000162214125111754020033 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_oidarray_h__ #define INCLUDE_git_oidarray_h__ #include "common.h" #include "oid.h" GIT_BEGIN_DECL /** Array of object ids */ typedef struct git_oidarray { git_oid *ids; size_t count; } git_oidarray; /** * Free the object IDs contained in an oid_array. This method should * be called on `git_oidarray` objects that were provided by the * library. Not doing so will result in a memory leak. * * This does not free the `git_oidarray` itself, since the library will * never allocate that object directly itself. * * @param array git_oidarray from which to free oid data */ GIT_EXTERN(void) git_oidarray_dispose(git_oidarray *array); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/deprecated.h0000644000175000017500000007046114125111754020330 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_deprecated_h__ #define INCLUDE_git_deprecated_h__ #include "attr.h" #include "config.h" #include "common.h" #include "blame.h" #include "buffer.h" #include "checkout.h" #include "cherrypick.h" #include "clone.h" #include "describe.h" #include "diff.h" #include "errors.h" #include "filter.h" #include "index.h" #include "indexer.h" #include "merge.h" #include "object.h" #include "proxy.h" #include "refs.h" #include "rebase.h" #include "remote.h" #include "trace.h" #include "repository.h" #include "revert.h" #include "revparse.h" #include "stash.h" #include "status.h" #include "submodule.h" #include "worktree.h" #include "credential.h" #include "credential_helpers.h" /* * Users can avoid deprecated functions by defining `GIT_DEPRECATE_HARD`. */ #ifndef GIT_DEPRECATE_HARD /* * The credential structures are now opaque by default, and their * definition has moved into the `sys/credential.h` header; include * them here for backward compatibility. */ #include "sys/credential.h" /** * @file git2/deprecated.h * @brief libgit2 deprecated functions and values * @ingroup Git * @{ */ GIT_BEGIN_DECL /** @name Deprecated Attribute Constants * * These enumeration values are retained for backward compatibility. * The newer versions of these functions should be preferred in all * new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ #define GIT_ATTR_UNSPECIFIED_T GIT_ATTR_VALUE_UNSPECIFIED #define GIT_ATTR_TRUE_T GIT_ATTR_VALUE_TRUE #define GIT_ATTR_FALSE_T GIT_ATTR_VALUE_FALSE #define GIT_ATTR_VALUE_T GIT_ATTR_VALUE_STRING #define GIT_ATTR_TRUE(attr) GIT_ATTR_IS_TRUE(attr) #define GIT_ATTR_FALSE(attr) GIT_ATTR_IS_FALSE(attr) #define GIT_ATTR_UNSPECIFIED(attr) GIT_ATTR_IS_UNSPECIFIED(attr) typedef git_attr_value_t git_attr_t; /**@}*/ /** @name Deprecated Blob Functions and Constants * * These functions and enumeration values are retained for backward * compatibility. The newer versions of these functions and values * should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ #define GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD GIT_EXTERN(int) git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path); GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path); GIT_EXTERN(int) git_blob_create_fromstream( git_writestream **out, git_repository *repo, const char *hintpath); GIT_EXTERN(int) git_blob_create_fromstream_commit( git_oid *out, git_writestream *stream); GIT_EXTERN(int) git_blob_create_frombuffer( git_oid *id, git_repository *repo, const void *buffer, size_t len); /** Deprecated in favor of `git_blob_filter`. * * @deprecated Use git_blob_filter * @see git_blob_filter */ GIT_EXTERN(int) git_blob_filtered_content( git_buf *out, git_blob *blob, const char *as_path, int check_for_binary_data); /**@}*/ /** @name Deprecated Filter Functions * * These functions are retained for backward compatibility. The * newer versions of these functions should be preferred in all * new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** Deprecated in favor of `git_filter_list_stream_buffer`. * * @deprecated Use git_filter_list_stream_buffer * @see Use git_filter_list_stream_buffer */ GIT_EXTERN(int) git_filter_list_stream_data( git_filter_list *filters, git_buf *data, git_writestream *target); /** Deprecated in favor of `git_filter_list_apply_to_buffer`. * * @deprecated Use git_filter_list_apply_to_buffer * @see Use git_filter_list_apply_to_buffer */ GIT_EXTERN(int) git_filter_list_apply_to_data( git_buf *out, git_filter_list *filters, git_buf *in); /**@}*/ /** @name Deprecated Tree Functions * * These functions are retained for backward compatibility. The * newer versions of these functions and values should be preferred * in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** * Write the contents of the tree builder as a tree object. * This is an alias of `git_treebuilder_write` and is preserved * for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_treebuilder_write * @see git_treebuilder_write */ GIT_EXTERN(int) git_treebuilder_write_with_buffer( git_oid *oid, git_treebuilder *bld, git_buf *tree); /**@}*/ /** @name Deprecated Buffer Functions * * These functions and enumeration values are retained for backward * compatibility. The newer versions of these functions should be * preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** * Static initializer for git_buf from static buffer */ #define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) } /** * Resize the buffer allocation to make more space. * * This will attempt to grow the buffer to accommodate the target size. * * If the buffer refers to memory that was not allocated by libgit2 (i.e. * the `asize` field is zero), then `ptr` will be replaced with a newly * allocated block of data. Be careful so that memory allocated by the * caller is not lost. As a special variant, if you pass `target_size` as * 0 and the memory is not allocated by libgit2, this will allocate a new * buffer of size `size` and copy the external data into it. * * Currently, this will never shrink a buffer, only expand it. * * If the allocation fails, this will return an error and the buffer will be * marked as invalid for future operations, invaliding the contents. * * @param buffer The buffer to be resized; may or may not be allocated yet * @param target_size The desired available size * @return 0 on success, -1 on allocation failure */ GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size); /** * Set buffer to a copy of some raw data. * * @param buffer The buffer to set * @param data The data to copy into the buffer * @param datalen The length of the data to copy into the buffer * @return 0 on success, -1 on allocation failure */ GIT_EXTERN(int) git_buf_set( git_buf *buffer, const void *data, size_t datalen); /** * Check quickly if buffer looks like it contains binary data * * @param buf Buffer to check * @return 1 if buffer looks like non-text data */ GIT_EXTERN(int) git_buf_is_binary(const git_buf *buf); /** * Check quickly if buffer contains a NUL byte * * @param buf Buffer to check * @return 1 if buffer contains a NUL byte */ GIT_EXTERN(int) git_buf_contains_nul(const git_buf *buf); /** * Free the memory referred to by the git_buf. This is an alias of * `git_buf_dispose` and is preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_buf_dispose * @see git_buf_dispose */ GIT_EXTERN(void) git_buf_free(git_buf *buffer); /**@}*/ /** @name Deprecated Commit Definitions */ /**@{*/ /** * Provide a commit signature during commit creation. * * Callers should instead define a `git_commit_create_cb` that * generates a commit buffer using `git_commit_create_buffer`, sign * that buffer and call `git_commit_create_with_signature`. * * @deprecated use a `git_commit_create_cb` instead */ typedef int (*git_commit_signing_cb)( git_buf *signature, git_buf *signature_field, const char *commit_content, void *payload); /**@}*/ /** @name Deprecated Config Functions and Constants */ /**@{*/ #define GIT_CVAR_FALSE GIT_CONFIGMAP_FALSE #define GIT_CVAR_TRUE GIT_CONFIGMAP_TRUE #define GIT_CVAR_INT32 GIT_CONFIGMAP_INT32 #define GIT_CVAR_STRING GIT_CONFIGMAP_STRING typedef git_configmap git_cvar_map; /**@}*/ /** @name Deprecated Diff Functions and Constants * * These functions and enumeration values are retained for backward * compatibility. The newer versions of these functions and values * should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** * Formatting options for diff e-mail generation */ typedef enum { /** Normal patch, the default */ GIT_DIFF_FORMAT_EMAIL_NONE = 0, /** Don't insert "[PATCH]" in the subject header*/ GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0), } git_diff_format_email_flags_t; /** * Options for controlling the formatting of the generated e-mail. */ typedef struct { unsigned int version; /** see `git_diff_format_email_flags_t` above */ uint32_t flags; /** This patch number */ size_t patch_no; /** Total number of patches in this series */ size_t total_patches; /** id to use for the commit */ const git_oid *id; /** Summary of the change */ const char *summary; /** Commit message's body */ const char *body; /** Author of the change */ const git_signature *author; } git_diff_format_email_options; #define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1 #define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL} /** * Create an e-mail ready patch from a diff. * * @deprecated git_email_create_from_diff * @see git_email_create_from_diff */ GIT_EXTERN(int) git_diff_format_email( git_buf *out, git_diff *diff, const git_diff_format_email_options *opts); /** * Create an e-mail ready patch for a commit. * * @deprecated git_email_create_from_commit * @see git_email_create_from_commit */ GIT_EXTERN(int) git_diff_commit_as_email( git_buf *out, git_repository *repo, git_commit *commit, size_t patch_no, size_t total_patches, uint32_t flags, const git_diff_options *diff_opts); /** * Initialize git_diff_format_email_options structure * * Initializes a `git_diff_format_email_options` with default values. Equivalent * to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT. * * @param opts The `git_blame_options` struct to initialize. * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_diff_format_email_options_init( git_diff_format_email_options *opts, unsigned int version); /**@}*/ /** @name Deprecated Error Functions and Constants * * These functions and enumeration values are retained for backward * compatibility. The newer versions of these functions and values * should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ #define GITERR_NONE GIT_ERROR_NONE #define GITERR_NOMEMORY GIT_ERROR_NOMEMORY #define GITERR_OS GIT_ERROR_OS #define GITERR_INVALID GIT_ERROR_INVALID #define GITERR_REFERENCE GIT_ERROR_REFERENCE #define GITERR_ZLIB GIT_ERROR_ZLIB #define GITERR_REPOSITORY GIT_ERROR_REPOSITORY #define GITERR_CONFIG GIT_ERROR_CONFIG #define GITERR_REGEX GIT_ERROR_REGEX #define GITERR_ODB GIT_ERROR_ODB #define GITERR_INDEX GIT_ERROR_INDEX #define GITERR_OBJECT GIT_ERROR_OBJECT #define GITERR_NET GIT_ERROR_NET #define GITERR_TAG GIT_ERROR_TAG #define GITERR_TREE GIT_ERROR_TREE #define GITERR_INDEXER GIT_ERROR_INDEXER #define GITERR_SSL GIT_ERROR_SSL #define GITERR_SUBMODULE GIT_ERROR_SUBMODULE #define GITERR_THREAD GIT_ERROR_THREAD #define GITERR_STASH GIT_ERROR_STASH #define GITERR_CHECKOUT GIT_ERROR_CHECKOUT #define GITERR_FETCHHEAD GIT_ERROR_FETCHHEAD #define GITERR_MERGE GIT_ERROR_MERGE #define GITERR_SSH GIT_ERROR_SSH #define GITERR_FILTER GIT_ERROR_FILTER #define GITERR_REVERT GIT_ERROR_REVERT #define GITERR_CALLBACK GIT_ERROR_CALLBACK #define GITERR_CHERRYPICK GIT_ERROR_CHERRYPICK #define GITERR_DESCRIBE GIT_ERROR_DESCRIBE #define GITERR_REBASE GIT_ERROR_REBASE #define GITERR_FILESYSTEM GIT_ERROR_FILESYSTEM #define GITERR_PATCH GIT_ERROR_PATCH #define GITERR_WORKTREE GIT_ERROR_WORKTREE #define GITERR_SHA1 GIT_ERROR_SHA1 /** * Return the last `git_error` object that was generated for the * current thread. This is an alias of `git_error_last` and is * preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_error_last * @see git_error_last */ GIT_EXTERN(const git_error *) giterr_last(void); /** * Clear the last error. This is an alias of `git_error_last` and is * preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_error_clear * @see git_error_clear */ GIT_EXTERN(void) giterr_clear(void); /** * Sets the error message to the given string. This is an alias of * `git_error_set_str` and is preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_error_set_str * @see git_error_set_str */ GIT_EXTERN(void) giterr_set_str(int error_class, const char *string); /** * Indicates that an out-of-memory situation occurred. This is an alias * of `git_error_set_oom` and is preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_error_set_oom * @see git_error_set_oom */ GIT_EXTERN(void) giterr_set_oom(void); /**@}*/ /** @name Deprecated Index Functions and Constants * * These functions and enumeration values are retained for backward * compatibility. The newer versions of these values should be * preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ #define GIT_IDXENTRY_NAMEMASK GIT_INDEX_ENTRY_NAMEMASK #define GIT_IDXENTRY_STAGEMASK GIT_INDEX_ENTRY_STAGEMASK #define GIT_IDXENTRY_STAGESHIFT GIT_INDEX_ENTRY_STAGESHIFT /* The git_indxentry_flag_t enum */ #define GIT_IDXENTRY_EXTENDED GIT_INDEX_ENTRY_EXTENDED #define GIT_IDXENTRY_VALID GIT_INDEX_ENTRY_VALID #define GIT_IDXENTRY_STAGE(E) GIT_INDEX_ENTRY_STAGE(E) #define GIT_IDXENTRY_STAGE_SET(E,S) GIT_INDEX_ENTRY_STAGE_SET(E,S) /* The git_idxentry_extended_flag_t enum */ #define GIT_IDXENTRY_INTENT_TO_ADD GIT_INDEX_ENTRY_INTENT_TO_ADD #define GIT_IDXENTRY_SKIP_WORKTREE GIT_INDEX_ENTRY_SKIP_WORKTREE #define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_INDEX_ENTRY_INTENT_TO_ADD | GIT_INDEX_ENTRY_SKIP_WORKTREE) #define GIT_IDXENTRY_EXTENDED2 (1 << 15) #define GIT_IDXENTRY_UPDATE (1 << 0) #define GIT_IDXENTRY_REMOVE (1 << 1) #define GIT_IDXENTRY_UPTODATE (1 << 2) #define GIT_IDXENTRY_ADDED (1 << 3) #define GIT_IDXENTRY_HASHED (1 << 4) #define GIT_IDXENTRY_UNHASHED (1 << 5) #define GIT_IDXENTRY_WT_REMOVE (1 << 6) #define GIT_IDXENTRY_CONFLICTED (1 << 7) #define GIT_IDXENTRY_UNPACKED (1 << 8) #define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) /* The git_index_capability_t enum */ #define GIT_INDEXCAP_IGNORE_CASE GIT_INDEX_CAPABILITY_IGNORE_CASE #define GIT_INDEXCAP_NO_FILEMODE GIT_INDEX_CAPABILITY_NO_FILEMODE #define GIT_INDEXCAP_NO_SYMLINKS GIT_INDEX_CAPABILITY_NO_SYMLINKS #define GIT_INDEXCAP_FROM_OWNER GIT_INDEX_CAPABILITY_FROM_OWNER GIT_EXTERN(int) git_index_add_frombuffer( git_index *index, const git_index_entry *entry, const void *buffer, size_t len); /**@}*/ /** @name Deprecated Object Constants * * These enumeration values are retained for backward compatibility. The * newer versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ #define git_otype git_object_t #define GIT_OBJ_ANY GIT_OBJECT_ANY #define GIT_OBJ_BAD GIT_OBJECT_INVALID #define GIT_OBJ__EXT1 0 #define GIT_OBJ_COMMIT GIT_OBJECT_COMMIT #define GIT_OBJ_TREE GIT_OBJECT_TREE #define GIT_OBJ_BLOB GIT_OBJECT_BLOB #define GIT_OBJ_TAG GIT_OBJECT_TAG #define GIT_OBJ__EXT2 5 #define GIT_OBJ_OFS_DELTA GIT_OBJECT_OFS_DELTA #define GIT_OBJ_REF_DELTA GIT_OBJECT_REF_DELTA /** * Get the size in bytes for the structure which * acts as an in-memory representation of any given * object type. * * For all the core types, this would the equivalent * of calling `sizeof(git_commit)` if the core types * were not opaque on the external API. * * @param type object type to get its size * @return size in bytes of the object */ GIT_EXTERN(size_t) git_object__size(git_object_t type); /**@}*/ /** @name Deprecated Remote Functions * * These functions are retained for backward compatibility. The newer * versions of these functions should be preferred in all new code. * * There is no plan to remove these backward compatibility functions at * this time. */ /**@{*/ /** * Ensure the remote name is well-formed. * * @deprecated Use git_remote_name_is_valid * @param remote_name name to be checked. * @return 1 if the reference name is acceptable; 0 if it isn't */ GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name); /**@}*/ /** @name Deprecated Reference Functions and Constants * * These functions and enumeration values are retained for backward * compatibility. The newer versions of these values should be * preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** Basic type of any Git reference. */ #define git_ref_t git_reference_t #define git_reference_normalize_t git_reference_format_t #define GIT_REF_INVALID GIT_REFERENCE_INVALID #define GIT_REF_OID GIT_REFERENCE_DIRECT #define GIT_REF_SYMBOLIC GIT_REFERENCE_SYMBOLIC #define GIT_REF_LISTALL GIT_REFERENCE_ALL #define GIT_REF_FORMAT_NORMAL GIT_REFERENCE_FORMAT_NORMAL #define GIT_REF_FORMAT_ALLOW_ONELEVEL GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL #define GIT_REF_FORMAT_REFSPEC_PATTERN GIT_REFERENCE_FORMAT_REFSPEC_PATTERN #define GIT_REF_FORMAT_REFSPEC_SHORTHAND GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND /** * Ensure the reference name is well-formed. * * Valid reference names must follow one of two patterns: * * 1. Top-level names must contain only capital letters and underscores, * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). * 2. Names prefixed with "refs/" can be almost anything. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * @deprecated Use git_reference_name_is_valid * @param refname name to be checked. * @return 1 if the reference name is acceptable; 0 if it isn't */ GIT_EXTERN(int) git_reference_is_valid_name(const char *refname); GIT_EXTERN(int) git_tag_create_frombuffer( git_oid *oid, git_repository *repo, const char *buffer, int force); /**@}*/ /** @name Deprecated Revspec Constants * * These enumeration values are retained for backward compatibility. * The newer versions of these values should be preferred in all new * code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ typedef git_revspec_t git_revparse_mode_t; #define GIT_REVPARSE_SINGLE GIT_REVSPEC_SINGLE #define GIT_REVPARSE_RANGE GIT_REVSPEC_RANGE #define GIT_REVPARSE_MERGE_BASE GIT_REVSPEC_MERGE_BASE /**@}*/ /** @name Deprecated Credential Types * * These types are retained for backward compatibility. The newer * versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ typedef git_credential git_cred; typedef git_credential_userpass_plaintext git_cred_userpass_plaintext; typedef git_credential_username git_cred_username; typedef git_credential_default git_cred_default; typedef git_credential_ssh_key git_cred_ssh_key; typedef git_credential_ssh_interactive git_cred_ssh_interactive; typedef git_credential_ssh_custom git_cred_ssh_custom; typedef git_credential_acquire_cb git_cred_acquire_cb; typedef git_credential_sign_cb git_cred_sign_callback; typedef git_credential_sign_cb git_cred_sign_cb; typedef git_credential_ssh_interactive_cb git_cred_ssh_interactive_callback; typedef git_credential_ssh_interactive_cb git_cred_ssh_interactive_cb; #define git_credtype_t git_credential_t #define GIT_CREDTYPE_USERPASS_PLAINTEXT GIT_CREDENTIAL_USERPASS_PLAINTEXT #define GIT_CREDTYPE_SSH_KEY GIT_CREDENTIAL_SSH_KEY #define GIT_CREDTYPE_SSH_CUSTOM GIT_CREDENTIAL_SSH_CUSTOM #define GIT_CREDTYPE_DEFAULT GIT_CREDENTIAL_DEFAULT #define GIT_CREDTYPE_SSH_INTERACTIVE GIT_CREDENTIAL_SSH_INTERACTIVE #define GIT_CREDTYPE_USERNAME GIT_CREDENTIAL_USERNAME #define GIT_CREDTYPE_SSH_MEMORY GIT_CREDENTIAL_SSH_MEMORY GIT_EXTERN(void) git_cred_free(git_credential *cred); GIT_EXTERN(int) git_cred_has_username(git_credential *cred); GIT_EXTERN(const char *) git_cred_get_username(git_credential *cred); GIT_EXTERN(int) git_cred_userpass_plaintext_new( git_credential **out, const char *username, const char *password); GIT_EXTERN(int) git_cred_default_new(git_credential **out); GIT_EXTERN(int) git_cred_username_new(git_credential **out, const char *username); GIT_EXTERN(int) git_cred_ssh_key_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase); GIT_EXTERN(int) git_cred_ssh_key_memory_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase); GIT_EXTERN(int) git_cred_ssh_interactive_new( git_credential **out, const char *username, git_credential_ssh_interactive_cb prompt_callback, void *payload); GIT_EXTERN(int) git_cred_ssh_key_from_agent( git_credential **out, const char *username); GIT_EXTERN(int) git_cred_ssh_custom_new( git_credential **out, const char *username, const char *publickey, size_t publickey_len, git_credential_sign_cb sign_callback, void *payload); /* Deprecated Credential Helper Types */ typedef git_credential_userpass_payload git_cred_userpass_payload; GIT_EXTERN(int) git_cred_userpass( git_credential **out, const char *url, const char *user_from_url, unsigned int allowed_types, void *payload); /**@}*/ /** @name Deprecated Trace Callback Types * * These types are retained for backward compatibility. The newer * versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ typedef git_trace_cb git_trace_callback; /**@}*/ /** @name Deprecated Object ID Types * * These types are retained for backward compatibility. The newer * versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ GIT_EXTERN(int) git_oid_iszero(const git_oid *id); /**@}*/ /** @name Deprecated OID Array Functions * * These types are retained for backward compatibility. The newer * versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** * Free the memory referred to by the git_oidarray. This is an alias of * `git_oidarray_dispose` and is preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_oidarray_dispose * @see git_oidarray_dispose */ GIT_EXTERN(void) git_oidarray_free(git_oidarray *array); /**@}*/ /** @name Deprecated Transfer Progress Types * * These types are retained for backward compatibility. The newer * versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** * This structure is used to provide callers information about the * progress of indexing a packfile. * * This type is deprecated, but there is no plan to remove this * type definition at this time. */ typedef git_indexer_progress git_transfer_progress; /** * Type definition for progress callbacks during indexing. * * This type is deprecated, but there is no plan to remove this * type definition at this time. */ typedef git_indexer_progress_cb git_transfer_progress_cb; /** * Type definition for push transfer progress callbacks. * * This type is deprecated, but there is no plan to remove this * type definition at this time. */ typedef git_push_transfer_progress_cb git_push_transfer_progress; /** The type of a remote completion event */ #define git_remote_completion_type git_remote_completion_t /** * Callback for listing the remote heads */ typedef int GIT_CALLBACK(git_headlist_cb)(git_remote_head *rhead, void *payload); /**@}*/ /** @name Deprecated String Array Functions * * These types are retained for backward compatibility. The newer * versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** * Copy a string array object from source to target. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @param tgt target * @param src source * @return 0 on success, < 0 on allocation failure */ GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src); /** * Free the memory referred to by the git_strarray. This is an alias of * `git_strarray_dispose` and is preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Use git_strarray_dispose * @see git_strarray_dispose */ GIT_EXTERN(void) git_strarray_free(git_strarray *array); /**@}*/ /** @name Deprecated Options Initialization Functions * * These functions are retained for backward compatibility. The newer * versions of these functions should be preferred in all new code. * * There is no plan to remove these backward compatibility functions at * this time. */ /**@{*/ GIT_EXTERN(int) git_blame_init_options(git_blame_options *opts, unsigned int version); GIT_EXTERN(int) git_checkout_init_options(git_checkout_options *opts, unsigned int version); GIT_EXTERN(int) git_cherrypick_init_options(git_cherrypick_options *opts, unsigned int version); GIT_EXTERN(int) git_clone_init_options(git_clone_options *opts, unsigned int version); GIT_EXTERN(int) git_describe_init_options(git_describe_options *opts, unsigned int version); GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version); GIT_EXTERN(int) git_diff_init_options(git_diff_options *opts, unsigned int version); GIT_EXTERN(int) git_diff_find_init_options(git_diff_find_options *opts, unsigned int version); GIT_EXTERN(int) git_diff_format_email_init_options(git_diff_format_email_options *opts, unsigned int version); GIT_EXTERN(int) git_diff_patchid_init_options(git_diff_patchid_options *opts, unsigned int version); GIT_EXTERN(int) git_fetch_init_options(git_fetch_options *opts, unsigned int version); GIT_EXTERN(int) git_indexer_init_options(git_indexer_options *opts, unsigned int version); GIT_EXTERN(int) git_merge_init_options(git_merge_options *opts, unsigned int version); GIT_EXTERN(int) git_merge_file_init_input(git_merge_file_input *input, unsigned int version); GIT_EXTERN(int) git_merge_file_init_options(git_merge_file_options *opts, unsigned int version); GIT_EXTERN(int) git_proxy_init_options(git_proxy_options *opts, unsigned int version); GIT_EXTERN(int) git_push_init_options(git_push_options *opts, unsigned int version); GIT_EXTERN(int) git_rebase_init_options(git_rebase_options *opts, unsigned int version); GIT_EXTERN(int) git_remote_create_init_options(git_remote_create_options *opts, unsigned int version); GIT_EXTERN(int) git_repository_init_init_options(git_repository_init_options *opts, unsigned int version); GIT_EXTERN(int) git_revert_init_options(git_revert_options *opts, unsigned int version); GIT_EXTERN(int) git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version); GIT_EXTERN(int) git_status_init_options(git_status_options *opts, unsigned int version); GIT_EXTERN(int) git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version); GIT_EXTERN(int) git_worktree_add_init_options(git_worktree_add_options *opts, unsigned int version); GIT_EXTERN(int) git_worktree_prune_init_options(git_worktree_prune_options *opts, unsigned int version); /**@}*/ /** @} */ GIT_END_DECL #endif #endif git2r/src/libgit2/include/git2/remote.h0000644000175000017500000007333514125111754017526 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_remote_h__ #define INCLUDE_git_remote_h__ #include "common.h" #include "repository.h" #include "refspec.h" #include "net.h" #include "indexer.h" #include "strarray.h" #include "transport.h" #include "pack.h" #include "proxy.h" /** * @file git2/remote.h * @brief Git remote management functions * @defgroup git_remote remote management functions * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Add a remote with the default fetch refspec to the repository's configuration. * * @param out the resulting remote * @param repo the repository in which to create the remote * @param name the remote's name * @param url the remote's url * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code */ GIT_EXTERN(int) git_remote_create( git_remote **out, git_repository *repo, const char *name, const char *url); /** * Remote creation options flags */ typedef enum { /** Ignore the repository apply.insteadOf configuration */ GIT_REMOTE_CREATE_SKIP_INSTEADOF = (1 << 0), /** Don't build a fetchspec from the name if none is set */ GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC = (1 << 1), } git_remote_create_flags; /** * Remote creation options structure * * Initialize with `GIT_REMOTE_CREATE_OPTIONS_INIT`. Alternatively, you can * use `git_remote_create_options_init`. * */ typedef struct git_remote_create_options { unsigned int version; /** * The repository that should own the remote. * Setting this to NULL results in a detached remote. */ git_repository *repository; /** * The remote's name. * Setting this to NULL results in an in-memory/anonymous remote. */ const char *name; /** The fetchspec the remote should use. */ const char *fetchspec; /** Additional flags for the remote. See git_remote_create_flags. */ unsigned int flags; } git_remote_create_options; #define GIT_REMOTE_CREATE_OPTIONS_VERSION 1 #define GIT_REMOTE_CREATE_OPTIONS_INIT {GIT_REMOTE_CREATE_OPTIONS_VERSION} /** * Initialize git_remote_create_options structure * * Initializes a `git_remote_create_options` with default values. Equivalent to * creating an instance with `GIT_REMOTE_CREATE_OPTIONS_INIT`. * * @param opts The `git_remote_create_options` struct to initialize. * @param version The struct version; pass `GIT_REMOTE_CREATE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_remote_create_options_init( git_remote_create_options *opts, unsigned int version); /** * Create a remote, with options. * * This function allows more fine-grained control over the remote creation. * * Passing NULL as the opts argument will result in a detached remote. * * @param out the resulting remote * @param url the remote's url * @param opts the remote creation options * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code */ GIT_EXTERN(int) git_remote_create_with_opts( git_remote **out, const char *url, const git_remote_create_options *opts); /** * Add a remote with the provided fetch refspec (or default if NULL) to the repository's * configuration. * * @param out the resulting remote * @param repo the repository in which to create the remote * @param name the remote's name * @param url the remote's url * @param fetch the remote fetch value * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code */ GIT_EXTERN(int) git_remote_create_with_fetchspec( git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch); /** * Create an anonymous remote * * Create a remote with the given url in-memory. You can use this when * you have a URL instead of a remote's name. * * @param out pointer to the new remote objects * @param repo the associated repository * @param url the remote repository's URL * @return 0 or an error code */ GIT_EXTERN(int) git_remote_create_anonymous( git_remote **out, git_repository *repo, const char *url); /** * Create a remote without a connected local repo * * Create a remote with the given url in-memory. You can use this when * you have a URL instead of a remote's name. * * Contrasted with git_remote_create_anonymous, a detached remote * will not consider any repo configuration values (such as insteadof url * substitutions). * * @param out pointer to the new remote objects * @param url the remote repository's URL * @return 0 or an error code */ GIT_EXTERN(int) git_remote_create_detached( git_remote **out, const char *url); /** * Get the information for a particular remote * * The name will be checked for validity. * See `git_tag_create()` for rules about valid names. * * @param out pointer to the new remote object * @param repo the associated repository * @param name the remote's name * @return 0, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_remote_lookup(git_remote **out, git_repository *repo, const char *name); /** * Create a copy of an existing remote. All internal strings are also * duplicated. Callbacks are not duplicated. * * Call `git_remote_free` to free the data. * * @param dest pointer where to store the copy * @param source object to copy * @return 0 or an error code */ GIT_EXTERN(int) git_remote_dup(git_remote **dest, git_remote *source); /** * Get the remote's repository * * @param remote the remote * @return a pointer to the repository */ GIT_EXTERN(git_repository *) git_remote_owner(const git_remote *remote); /** * Get the remote's name * * @param remote the remote * @return a pointer to the name or NULL for in-memory remotes */ GIT_EXTERN(const char *) git_remote_name(const git_remote *remote); /** * Get the remote's url * * If url.*.insteadOf has been configured for this URL, it will * return the modified URL. If `git_remote_set_instance_pushurl` * has been called for this remote, then that URL will be returned. * * @param remote the remote * @return a pointer to the url */ GIT_EXTERN(const char *) git_remote_url(const git_remote *remote); /** * Get the remote's url for pushing. * * If url.*.pushInsteadOf has been configured for this URL, it * will return the modified URL. If `git_remote_set_instance_pushurl` * has been called for this remote, then that URL will be returned. * * @param remote the remote * @return a pointer to the url or NULL if no special url for pushing is set */ GIT_EXTERN(const char *) git_remote_pushurl(const git_remote *remote); /** * Set the remote's url in the configuration * * Remote objects already in memory will not be affected. This assumes * the common case of a single-url remote and will otherwise return an error. * * @param repo the repository in which to perform the change * @param remote the remote's name * @param url the url to set * @return 0 or an error value */ GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, const char *url); /** * Set the remote's url for pushing in the configuration. * * Remote objects already in memory will not be affected. This assumes * the common case of a single-url remote and will otherwise return an error. * * * @param repo the repository in which to perform the change * @param remote the remote's name * @param url the url to set * @return 0, or an error code */ GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url); /** * Set the url for this particular url instance. The URL in the * configuration will be ignored, and will not be changed. * * @param remote the remote's name * @param url the url to set * @return 0 or an error value */ GIT_EXTERN(int) git_remote_set_instance_url(git_remote *remote, const char *url); /** * Set the push url for this particular url instance. The URL in the * configuration will be ignored, and will not be changed. * * @param remote the remote's name * @param url the url to set * @return 0 or an error value */ GIT_EXTERN(int) git_remote_set_instance_pushurl(git_remote *remote, const char *url); /** * Add a fetch refspec to the remote's configuration * * Add the given refspec to the fetch list in the configuration. No * loaded remote instances will be affected. * * @param repo the repository in which to change the configuration * @param remote the name of the remote to change * @param refspec the new fetch refspec * @return 0, GIT_EINVALIDSPEC if refspec is invalid or an error value */ GIT_EXTERN(int) git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec); /** * Get the remote's list of fetch refspecs * * The memory is owned by the user and should be freed with * `git_strarray_free`. * * @param array pointer to the array in which to store the strings * @param remote the remote to query */ GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote); /** * Add a push refspec to the remote's configuration * * Add the given refspec to the push list in the configuration. No * loaded remote instances will be affected. * * @param repo the repository in which to change the configuration * @param remote the name of the remote to change * @param refspec the new push refspec * @return 0, GIT_EINVALIDSPEC if refspec is invalid or an error value */ GIT_EXTERN(int) git_remote_add_push(git_repository *repo, const char *remote, const char *refspec); /** * Get the remote's list of push refspecs * * The memory is owned by the user and should be freed with * `git_strarray_free`. * * @param array pointer to the array in which to store the strings * @param remote the remote to query */ GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote); /** * Get the number of refspecs for a remote * * @param remote the remote * @return the amount of refspecs configured in this remote */ GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote); /** * Get a refspec from the remote * * @param remote the remote to query * @param n the refspec to get * @return the nth refspec */ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n); /** * Open a connection to a remote * * The transport is selected based on the URL. The direction argument * is due to a limitation of the git protocol (over TCP or SSH) which * starts up a specific binary which can only do the one or the other. * * @param remote the remote to connect to * @param direction GIT_DIRECTION_FETCH if you want to fetch or * GIT_DIRECTION_PUSH if you want to push * @param callbacks the callbacks to use for this connection * @param proxy_opts proxy settings * @param custom_headers extra HTTP headers to use in this connection * @return 0 or an error code */ GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy_opts, const git_strarray *custom_headers); /** * Get the remote repository's reference advertisement list * * Get the list of references with which the server responds to a new * connection. * * The remote (or more exactly its transport) must have connected to * the remote repository. This list is available as soon as the * connection to the remote is initiated and it remains available * after disconnecting. * * The memory belongs to the remote. The pointer will be valid as long * as a new connection is not initiated, but it is recommended that * you make a copy in order to make use of the data. * * @param out pointer to the array * @param size the number of remote heads * @param remote the remote * @return 0 on success, or an error code */ GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote); /** * Check whether the remote is connected * * Check whether the remote's underlying transport is connected to the * remote host. * * @param remote the remote * @return 1 if it's connected, 0 otherwise. */ GIT_EXTERN(int) git_remote_connected(const git_remote *remote); /** * Cancel the operation * * At certain points in its operation, the network code checks whether * the operation has been cancelled and if so stops the operation. * * @param remote the remote * @return 0 on success, or an error code */ GIT_EXTERN(int) git_remote_stop(git_remote *remote); /** * Disconnect from the remote * * Close the connection to the remote. * * @param remote the remote to disconnect from * @return 0 on success, or an error code */ GIT_EXTERN(int) git_remote_disconnect(git_remote *remote); /** * Free the memory associated with a remote * * This also disconnects from the remote, if the connection * has not been closed yet (using git_remote_disconnect). * * @param remote the remote to free */ GIT_EXTERN(void) git_remote_free(git_remote *remote); /** * Get a list of the configured remotes for a repo * * The string array must be freed by the user. * * @param out a string array which receives the names of the remotes * @param repo the repository to query * @return 0 or an error code */ GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo); /** * Argument to the completion callback which tells it which operation * finished. */ typedef enum git_remote_completion_t { GIT_REMOTE_COMPLETION_DOWNLOAD, GIT_REMOTE_COMPLETION_INDEXING, GIT_REMOTE_COMPLETION_ERROR, } git_remote_completion_t; /** Push network progress notification function */ typedef int GIT_CALLBACK(git_push_transfer_progress_cb)( unsigned int current, unsigned int total, size_t bytes, void *payload); /** * Represents an update which will be performed on the remote during push */ typedef struct { /** * The source name of the reference */ char *src_refname; /** * The name of the reference to update on the server */ char *dst_refname; /** * The current target of the reference */ git_oid src; /** * The new target for the reference */ git_oid dst; } git_push_update; /** * Callback used to inform of upcoming updates. * * @param updates an array containing the updates which will be sent * as commands to the destination. * @param len number of elements in `updates` * @param payload Payload provided by the caller */ typedef int GIT_CALLBACK(git_push_negotiation)(const git_push_update **updates, size_t len, void *payload); /** * Callback used to inform of the update status from the remote. * * Called for each updated reference on push. If `status` is * not `NULL`, the update was rejected by the remote server * and `status` contains the reason given. * * @param refname refname specifying to the remote ref * @param status status message sent from the remote * @param data data provided by the caller * @return 0 on success, otherwise an error */ typedef int GIT_CALLBACK(git_push_update_reference_cb)(const char *refname, const char *status, void *data); #ifndef GIT_DEPRECATE_HARD /** * Callback to resolve URLs before connecting to remote * * If you return GIT_PASSTHROUGH, you don't need to write anything to * url_resolved. * * @param url_resolved The buffer to write the resolved URL to * @param url The URL to resolve * @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH * @param payload Payload provided by the caller * @return 0 on success, GIT_PASSTHROUGH or an error * @deprecated Use `git_remote_set_instance_url` */ typedef int GIT_CALLBACK(git_url_resolve_cb)(git_buf *url_resolved, const char *url, int direction, void *payload); #endif /** * Callback invoked immediately before we attempt to connect to the * given url. Callers may change the URL before the connection by * calling `git_remote_set_instance_url` in the callback. * * @param remote The remote to be connected * @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH * @param payload Payload provided by the caller * @return 0 on success, or an error */ typedef int GIT_CALLBACK(git_remote_ready_cb)(git_remote *remote, int direction, void *payload); /** * The callback settings structure * * Set the callbacks to be called by the remote when informing the user * about the progress of the network operations. */ struct git_remote_callbacks { unsigned int version; /**< The version */ /** * Textual progress from the remote. Text send over the * progress side-band will be passed to this function (this is * the 'counting objects' output). */ git_transport_message_cb sideband_progress; /** * Completion is called when different parts of the download * process are done (currently unused). */ int GIT_CALLBACK(completion)(git_remote_completion_t type, void *data); /** * This will be called if the remote host requires * authentication in order to connect to it. * * Returning GIT_PASSTHROUGH will make libgit2 behave as * though this field isn't set. */ git_credential_acquire_cb credentials; /** * If cert verification fails, this will be called to let the * user make the final decision of whether to allow the * connection to proceed. Returns 0 to allow the connection * or a negative value to indicate an error. */ git_transport_certificate_check_cb certificate_check; /** * During the download of new data, this will be regularly * called with the current count of progress done by the * indexer. */ git_indexer_progress_cb transfer_progress; /** * Each time a reference is updated locally, this function * will be called with information about it. */ int GIT_CALLBACK(update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data); /** * Function to call with progress information during pack * building. Be aware that this is called inline with pack * building operations, so performance may be affected. */ git_packbuilder_progress pack_progress; /** * Function to call with progress information during the * upload portion of a push. Be aware that this is called * inline with pack building operations, so performance may be * affected. */ git_push_transfer_progress_cb push_transfer_progress; /** * See documentation of git_push_update_reference_cb */ git_push_update_reference_cb push_update_reference; /** * Called once between the negotiation step and the upload. It * provides information about what updates will be performed. */ git_push_negotiation push_negotiation; /** * Create the transport to use for this operation. Leave NULL * to auto-detect. */ git_transport_cb transport; /** * Callback when the remote is ready to connect. */ git_remote_ready_cb remote_ready; /** * This will be passed to each of the callbacks in this struct * as the last parameter. */ void *payload; #ifdef GIT_DEPRECATE_HARD void *reserved; #else /** * Resolve URL before connecting to remote. * The returned URL will be used to connect to the remote instead. * * This callback is deprecated; users should use * git_remote_ready_cb and configure the instance URL instead. */ git_url_resolve_cb resolve_url; #endif }; #define GIT_REMOTE_CALLBACKS_VERSION 1 #define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION} /** * Initializes a `git_remote_callbacks` with default values. Equivalent to * creating an instance with GIT_REMOTE_CALLBACKS_INIT. * * @param opts the `git_remote_callbacks` struct to initialize * @param version Version of struct; pass `GIT_REMOTE_CALLBACKS_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_remote_init_callbacks( git_remote_callbacks *opts, unsigned int version); /** Acceptable prune settings when fetching */ typedef enum { /** * Use the setting from the configuration */ GIT_FETCH_PRUNE_UNSPECIFIED, /** * Force pruning on */ GIT_FETCH_PRUNE, /** * Force pruning off */ GIT_FETCH_NO_PRUNE, } git_fetch_prune_t; /** * Automatic tag following option * * Lets us select the --tags option to use. */ typedef enum { /** * Use the setting from the configuration. */ GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = 0, /** * Ask the server for tags pointing to objects we're already * downloading. */ GIT_REMOTE_DOWNLOAD_TAGS_AUTO, /** * Don't ask for any tags beyond the refspecs. */ GIT_REMOTE_DOWNLOAD_TAGS_NONE, /** * Ask for the all the tags. */ GIT_REMOTE_DOWNLOAD_TAGS_ALL, } git_remote_autotag_option_t; /** * Fetch options structure. * * Zero out for defaults. Initialize with `GIT_FETCH_OPTIONS_INIT` macro to * correctly set the `version` field. E.g. * * git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; */ typedef struct { int version; /** * Callbacks to use for this fetch operation */ git_remote_callbacks callbacks; /** * Whether to perform a prune after the fetch */ git_fetch_prune_t prune; /** * Whether to write the results to FETCH_HEAD. Defaults to * on. Leave this default in order to behave like git. */ int update_fetchhead; /** * Determines how to behave regarding tags on the remote, such * as auto-downloading tags for objects we're downloading or * downloading all of them. * * The default is to auto-follow tags. */ git_remote_autotag_option_t download_tags; /** * Proxy options to use, by default no proxy is used. */ git_proxy_options proxy_opts; /** * Extra headers for this fetch operation */ git_strarray custom_headers; } git_fetch_options; #define GIT_FETCH_OPTIONS_VERSION 1 #define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1, \ GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT } /** * Initialize git_fetch_options structure * * Initializes a `git_fetch_options` with default values. Equivalent to * creating an instance with `GIT_FETCH_OPTIONS_INIT`. * * @param opts The `git_fetch_options` struct to initialize. * @param version The struct version; pass `GIT_FETCH_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_fetch_options_init( git_fetch_options *opts, unsigned int version); /** * Controls the behavior of a git_push object. */ typedef struct { unsigned int version; /** * If the transport being used to push to the remote requires the creation * of a pack file, this controls the number of worker threads used by * the packbuilder when creating that pack file to be sent to the remote. * * If set to 0, the packbuilder will auto-detect the number of threads * to create. The default value is 1. */ unsigned int pb_parallelism; /** * Callbacks to use for this push operation */ git_remote_callbacks callbacks; /** * Proxy options to use, by default no proxy is used. */ git_proxy_options proxy_opts; /** * Extra headers for this push operation */ git_strarray custom_headers; } git_push_options; #define GIT_PUSH_OPTIONS_VERSION 1 #define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION, 1, GIT_REMOTE_CALLBACKS_INIT, GIT_PROXY_OPTIONS_INIT } /** * Initialize git_push_options structure * * Initializes a `git_push_options` with default values. Equivalent to * creating an instance with `GIT_PUSH_OPTIONS_INIT`. * * @param opts The `git_push_options` struct to initialize. * @param version The struct version; pass `GIT_PUSH_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_push_options_init( git_push_options *opts, unsigned int version); /** * Download and index the packfile * * Connect to the remote if it hasn't been done yet, negotiate with * the remote git which objects are missing, download and index the * packfile. * * The .idx file will be created and both it and the packfile with be * renamed to their final name. * * @param remote the remote * @param refspecs the refspecs to use for this negotiation and * download. Use NULL or an empty array to use the base refspecs * @param opts the options to use for this fetch * @return 0 or an error code */ GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts); /** * Create a packfile and send it to the server * * Connect to the remote if it hasn't been done yet, negotiate with * the remote git which objects are missing, create a packfile with the missing objects and send it. * * @param remote the remote * @param refspecs the refspecs to use for this negotiation and * upload. Use NULL or an empty array to use the base refspecs * @param opts the options to use for this push * @return 0 or an error code */ GIT_EXTERN(int) git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts); /** * Update the tips to the new state * * @param remote the remote to update * @param reflog_message The message to insert into the reflogs. If * NULL and fetching, the default is "fetch ", where is * the name of the remote (or its url, for in-memory remotes). This * parameter is ignored when pushing. * @param callbacks pointer to the callback structure to use * @param update_fetchhead whether to write to FETCH_HEAD. Pass 1 to behave like git. * @param download_tags what the behaviour for downloading tags is for this fetch. This is * ignored for push. This must be the same value passed to `git_remote_download()`. * @return 0 or an error code */ GIT_EXTERN(int) git_remote_update_tips( git_remote *remote, const git_remote_callbacks *callbacks, int update_fetchhead, git_remote_autotag_option_t download_tags, const char *reflog_message); /** * Download new data and update tips * * Convenience function to connect to a remote, download the data, * disconnect and update the remote-tracking branches. * * @param remote the remote to fetch from * @param refspecs the refspecs to use for this fetch. Pass NULL or an * empty array to use the base refspecs. * @param opts options to use for this fetch * @param reflog_message The message to insert into the reflogs. If NULL, the * default is "fetch" * @return 0 or an error code */ GIT_EXTERN(int) git_remote_fetch( git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts, const char *reflog_message); /** * Prune tracking refs that are no longer present on remote * * @param remote the remote to prune * @param callbacks callbacks to use for this prune * @return 0 or an error code */ GIT_EXTERN(int) git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks); /** * Perform a push * * Peform all the steps from a push. * * @param remote the remote to push to * @param refspecs the refspecs to use for pushing. If NULL or an empty * array, the configured refspecs will be used * @param opts options to use for this push */ GIT_EXTERN(int) git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts); /** * Get the statistics structure that is filled in by the fetch operation. */ GIT_EXTERN(const git_indexer_progress *) git_remote_stats(git_remote *remote); /** * Retrieve the tag auto-follow setting * * @param remote the remote to query * @return the auto-follow setting */ GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(const git_remote *remote); /** * Set the remote's tag following setting. * * The change will be made in the configuration. No loaded remotes * will be affected. * * @param repo the repository in which to make the change * @param remote the name of the remote * @param value the new value to take. * @return 0, or an error code. */ GIT_EXTERN(int) git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value); /** * Retrieve the ref-prune setting * * @param remote the remote to query * @return the ref-prune setting */ GIT_EXTERN(int) git_remote_prune_refs(const git_remote *remote); /** * Give the remote a new name * * All remote-tracking branches and configuration settings * for the remote are updated. * * The new name will be checked for validity. * See `git_tag_create()` for rules about valid names. * * No loaded instances of a the remote with the old name will change * their name or their list of refspecs. * * @param problems non-default refspecs cannot be renamed and will be * stored here for further processing by the caller. Always free this * strarray on successful return. * @param repo the repository in which to rename * @param name the current name of the remote * @param new_name the new name the remote should bear * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code */ GIT_EXTERN(int) git_remote_rename( git_strarray *problems, git_repository *repo, const char *name, const char *new_name); /** * Ensure the remote name is well-formed. * * @param valid output pointer to set with validity of given remote name * @param remote_name name to be checked. * @return 0 on success or an error code */ GIT_EXTERN(int) git_remote_name_is_valid(int *valid, const char *remote_name); /** * Delete an existing persisted remote. * * All remote-tracking branches and configuration settings * for the remote will be removed. * * @param repo the repository in which to act * @param name the name of the remote to delete * @return 0 on success, or an error code. */ GIT_EXTERN(int) git_remote_delete(git_repository *repo, const char *name); /** * Retrieve the name of the remote's default branch * * The default branch of a repository is the branch which HEAD points * to. If the remote does not support reporting this information * directly, it performs the guess as git does; that is, if there are * multiple branches which point to the same commit, the first one is * chosen. If the master branch is a candidate, it wins. * * This function must only be called after connecting. * * @param out the buffer in which to store the reference name * @param remote the remote * @return 0, GIT_ENOTFOUND if the remote does not have any references * or none of them point to HEAD's commit, or an error message. */ GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/credential.h0000644000175000017500000002315314125111754020336 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_credential_h__ #define INCLUDE_git_credential_h__ #include "common.h" /** * @file git2/credential.h * @brief Git authentication & credential management * @defgroup git_credential Authentication & credential management * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Supported credential types * * This represents the various types of authentication methods supported by * the library. */ typedef enum { /** * A vanilla user/password request * @see git_credential_userpass_plaintext_new */ GIT_CREDENTIAL_USERPASS_PLAINTEXT = (1u << 0), /** * An SSH key-based authentication request * @see git_credential_ssh_key_new */ GIT_CREDENTIAL_SSH_KEY = (1u << 1), /** * An SSH key-based authentication request, with a custom signature * @see git_credential_ssh_custom_new */ GIT_CREDENTIAL_SSH_CUSTOM = (1u << 2), /** * An NTLM/Negotiate-based authentication request. * @see git_credential_default */ GIT_CREDENTIAL_DEFAULT = (1u << 3), /** * An SSH interactive authentication request * @see git_credential_ssh_interactive_new */ GIT_CREDENTIAL_SSH_INTERACTIVE = (1u << 4), /** * Username-only authentication request * * Used as a pre-authentication step if the underlying transport * (eg. SSH, with no username in its URL) does not know which username * to use. * * @see git_credential_username_new */ GIT_CREDENTIAL_USERNAME = (1u << 5), /** * An SSH key-based authentication request * * Allows credentials to be read from memory instead of files. * Note that because of differences in crypto backend support, it might * not be functional. * * @see git_credential_ssh_key_memory_new */ GIT_CREDENTIAL_SSH_MEMORY = (1u << 6), } git_credential_t; /** * The base structure for all credential types */ typedef struct git_credential git_credential; typedef struct git_credential_userpass_plaintext git_credential_userpass_plaintext; /** Username-only credential information */ typedef struct git_credential_username git_credential_username; /** A key for NTLM/Kerberos "default" credentials */ typedef struct git_credential git_credential_default; /** * A ssh key from disk */ typedef struct git_credential_ssh_key git_credential_ssh_key; /** * Keyboard-interactive based ssh authentication */ typedef struct git_credential_ssh_interactive git_credential_ssh_interactive; /** * A key with a custom signature function */ typedef struct git_credential_ssh_custom git_credential_ssh_custom; /** * Credential acquisition callback. * * This callback is usually involved any time another system might need * authentication. As such, you are expected to provide a valid * git_credential object back, depending on allowed_types (a * git_credential_t bitmask). * * Note that most authentication details are your responsibility - this * callback will be called until the authentication succeeds, or you report * an error. As such, it's easy to get in a loop if you fail to stop providing * the same incorrect credentials. * * @param out The newly created credential object. * @param url The resource for which we are demanding a credential. * @param username_from_url The username that was embedded in a "user\@host" * remote url, or NULL if not included. * @param allowed_types A bitmask stating which credential types are OK to return. * @param payload The payload provided when specifying this callback. * @return 0 for success, < 0 to indicate an error, > 0 to indicate * no credential was acquired */ typedef int GIT_CALLBACK(git_credential_acquire_cb)( git_credential **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload); /** * Free a credential. * * This is only necessary if you own the object; that is, if you are a * transport. * * @param cred the object to free */ GIT_EXTERN(void) git_credential_free(git_credential *cred); /** * Check whether a credential object contains username information. * * @param cred object to check * @return 1 if the credential object has non-NULL username, 0 otherwise */ GIT_EXTERN(int) git_credential_has_username(git_credential *cred); /** * Return the username associated with a credential object. * * @param cred object to check * @return the credential username, or NULL if not applicable */ GIT_EXTERN(const char *) git_credential_get_username(git_credential *cred); /** * Create a new plain-text username and password credential object. * The supplied credential parameter will be internally duplicated. * * @param out The newly created credential object. * @param username The username of the credential. * @param password The password of the credential. * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_credential_userpass_plaintext_new( git_credential **out, const char *username, const char *password); /** * Create a "default" credential usable for Negotiate mechanisms like NTLM * or Kerberos authentication. * * @param out The newly created credential object. * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_credential_default_new(git_credential **out); /** * Create a credential to specify a username. * * This is used with ssh authentication to query for the username if * none is specified in the url. * * @param out The newly created credential object. * @param username The username to authenticate with * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_credential_username_new(git_credential **out, const char *username); /** * Create a new passphrase-protected ssh key credential object. * The supplied credential parameter will be internally duplicated. * * @param out The newly created credential object. * @param username username to use to authenticate * @param publickey The path to the public key of the credential. * @param privatekey The path to the private key of the credential. * @param passphrase The passphrase of the credential. * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_credential_ssh_key_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase); /** * Create a new ssh key credential object reading the keys from memory. * * @param out The newly created credential object. * @param username username to use to authenticate. * @param publickey The public key of the credential. * @param privatekey The private key of the credential. * @param passphrase The passphrase of the credential. * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_credential_ssh_key_memory_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase); /* * If the user hasn't included libssh2.h before git2.h, we need to * define a few types for the callback signatures. */ #ifndef LIBSSH2_VERSION typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION; typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT LIBSSH2_USERAUTH_KBDINT_PROMPT; typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE LIBSSH2_USERAUTH_KBDINT_RESPONSE; #endif typedef void GIT_CALLBACK(git_credential_ssh_interactive_cb)( const char *name, int name_len, const char *instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, void **abstract); /** * Create a new ssh keyboard-interactive based credential object. * The supplied credential parameter will be internally duplicated. * * @param username Username to use to authenticate. * @param prompt_callback The callback method used for prompts. * @param payload Additional data to pass to the callback. * @return 0 for success or an error code for failure. */ GIT_EXTERN(int) git_credential_ssh_interactive_new( git_credential **out, const char *username, git_credential_ssh_interactive_cb prompt_callback, void *payload); /** * Create a new ssh key credential object used for querying an ssh-agent. * The supplied credential parameter will be internally duplicated. * * @param out The newly created credential object. * @param username username to use to authenticate * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_credential_ssh_key_from_agent( git_credential **out, const char *username); typedef int GIT_CALLBACK(git_credential_sign_cb)( LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, const unsigned char *data, size_t data_len, void **abstract); /** * Create an ssh key credential with a custom signing function. * * This lets you use your own function to sign the challenge. * * This function and its credential type is provided for completeness * and wraps `libssh2_userauth_publickey()`, which is undocumented. * * The supplied credential parameter will be internally duplicated. * * @param out The newly created credential object. * @param username username to use to authenticate * @param publickey The bytes of the public key. * @param publickey_len The length of the public key in bytes. * @param sign_callback The callback method to sign the data during the challenge. * @param payload Additional data to pass to the callback. * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_credential_ssh_custom_new( git_credential **out, const char *username, const char *publickey, size_t publickey_len, git_credential_sign_cb sign_callback, void *payload); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/index.h0000644000175000017500000006566414125111754017350 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_index_h__ #define INCLUDE_git_index_h__ #include "common.h" #include "indexer.h" #include "types.h" #include "oid.h" #include "strarray.h" /** * @file git2/index.h * @brief Git index parsing and manipulation routines * @defgroup git_index Git index parsing and manipulation routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** Time structure used in a git index entry */ typedef struct { int32_t seconds; /* nsec should not be stored as time_t compatible */ uint32_t nanoseconds; } git_index_time; /** * In-memory representation of a file entry in the index. * * This is a public structure that represents a file entry in the index. * The meaning of the fields corresponds to core Git's documentation (in * "Documentation/technical/index-format.txt"). * * The `flags` field consists of a number of bit fields which can be * accessed via the first set of `GIT_INDEX_ENTRY_...` bitmasks below. * These flags are all read from and persisted to disk. * * The `flags_extended` field also has a number of bit fields which can be * accessed via the later `GIT_INDEX_ENTRY_...` bitmasks below. Some of * these flags are read from and written to disk, but some are set aside * for in-memory only reference. * * Note that the time and size fields are truncated to 32 bits. This * is enough to detect changes, which is enough for the index to * function as a cache, but it should not be taken as an authoritative * source for that data. */ typedef struct git_index_entry { git_index_time ctime; git_index_time mtime; uint32_t dev; uint32_t ino; uint32_t mode; uint32_t uid; uint32_t gid; uint32_t file_size; git_oid id; uint16_t flags; uint16_t flags_extended; const char *path; } git_index_entry; /** * Bitmasks for on-disk fields of `git_index_entry`'s `flags` * * These bitmasks match the four fields in the `git_index_entry` `flags` * value both in memory and on disk. You can use them to interpret the * data in the `flags`. */ #define GIT_INDEX_ENTRY_NAMEMASK (0x0fff) #define GIT_INDEX_ENTRY_STAGEMASK (0x3000) #define GIT_INDEX_ENTRY_STAGESHIFT 12 /** * Flags for index entries */ typedef enum { GIT_INDEX_ENTRY_EXTENDED = (0x4000), GIT_INDEX_ENTRY_VALID = (0x8000), } git_index_entry_flag_t; #define GIT_INDEX_ENTRY_STAGE(E) \ (((E)->flags & GIT_INDEX_ENTRY_STAGEMASK) >> GIT_INDEX_ENTRY_STAGESHIFT) #define GIT_INDEX_ENTRY_STAGE_SET(E,S) do { \ (E)->flags = ((E)->flags & ~GIT_INDEX_ENTRY_STAGEMASK) | \ (((S) & 0x03) << GIT_INDEX_ENTRY_STAGESHIFT); } while (0) /** * Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended` * * In memory, the `flags_extended` fields are divided into two parts: the * fields that are read from and written to disk, and other fields that * in-memory only and used by libgit2. Only the flags in * `GIT_INDEX_ENTRY_EXTENDED_FLAGS` will get saved on-disk. * * Thee first three bitmasks match the three fields in the * `git_index_entry` `flags_extended` value that belong on disk. You * can use them to interpret the data in the `flags_extended`. * * The rest of the bitmasks match the other fields in the `git_index_entry` * `flags_extended` value that are only used in-memory by libgit2. * You can use them to interpret the data in the `flags_extended`. * */ typedef enum { GIT_INDEX_ENTRY_INTENT_TO_ADD = (1 << 13), GIT_INDEX_ENTRY_SKIP_WORKTREE = (1 << 14), GIT_INDEX_ENTRY_EXTENDED_FLAGS = (GIT_INDEX_ENTRY_INTENT_TO_ADD | GIT_INDEX_ENTRY_SKIP_WORKTREE), GIT_INDEX_ENTRY_UPTODATE = (1 << 2), } git_index_entry_extended_flag_t; /** Capabilities of system that affect index actions. */ typedef enum { GIT_INDEX_CAPABILITY_IGNORE_CASE = 1, GIT_INDEX_CAPABILITY_NO_FILEMODE = 2, GIT_INDEX_CAPABILITY_NO_SYMLINKS = 4, GIT_INDEX_CAPABILITY_FROM_OWNER = -1, } git_index_capability_t; /** Callback for APIs that add/remove/update files matching pathspec */ typedef int GIT_CALLBACK(git_index_matched_path_cb)( const char *path, const char *matched_pathspec, void *payload); /** Flags for APIs that add files matching pathspec */ typedef enum { GIT_INDEX_ADD_DEFAULT = 0, GIT_INDEX_ADD_FORCE = (1u << 0), GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH = (1u << 1), GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2), } git_index_add_option_t; /** Git index stage states */ typedef enum { /** * Match any index stage. * * Some index APIs take a stage to match; pass this value to match * any entry matching the path regardless of stage. */ GIT_INDEX_STAGE_ANY = -1, /** A normal staged file in the index. */ GIT_INDEX_STAGE_NORMAL = 0, /** The ancestor side of a conflict. */ GIT_INDEX_STAGE_ANCESTOR = 1, /** The "ours" side of a conflict. */ GIT_INDEX_STAGE_OURS = 2, /** The "theirs" side of a conflict. */ GIT_INDEX_STAGE_THEIRS = 3, } git_index_stage_t; /** * Create a new bare Git index object as a memory representation * of the Git index file in 'index_path', without a repository * to back it. * * Since there is no ODB or working directory behind this index, * any Index methods which rely on these (e.g. index_add_bypath) * will fail with the GIT_ERROR error code. * * If you need to access the index of an actual repository, * use the `git_repository_index` wrapper. * * The index must be freed once it's no longer in use. * * @param out the pointer for the new index * @param index_path the path to the index file in disk * @return 0 or an error code */ GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path); /** * Create an in-memory index object. * * This index object cannot be read/written to the filesystem, * but may be used to perform in-memory index operations. * * The index must be freed once it's no longer in use. * * @param out the pointer for the new index * @return 0 or an error code */ GIT_EXTERN(int) git_index_new(git_index **out); /** * Free an existing index object. * * @param index an existing index object */ GIT_EXTERN(void) git_index_free(git_index *index); /** * Get the repository this index relates to * * @param index The index * @return A pointer to the repository */ GIT_EXTERN(git_repository *) git_index_owner(const git_index *index); /** * Read index capabilities flags. * * @param index An existing index object * @return A combination of GIT_INDEX_CAPABILITY values */ GIT_EXTERN(int) git_index_caps(const git_index *index); /** * Set index capabilities flags. * * If you pass `GIT_INDEX_CAPABILITY_FROM_OWNER` for the caps, then * capabilities will be read from the config of the owner object, * looking at `core.ignorecase`, `core.filemode`, `core.symlinks`. * * @param index An existing index object * @param caps A combination of GIT_INDEX_CAPABILITY values * @return 0 on success, -1 on failure */ GIT_EXTERN(int) git_index_set_caps(git_index *index, int caps); /** * Get index on-disk version. * * Valid return values are 2, 3, or 4. If 3 is returned, an index * with version 2 may be written instead, if the extension data in * version 3 is not necessary. * * @param index An existing index object * @return the index version */ GIT_EXTERN(unsigned int) git_index_version(git_index *index); /** * Set index on-disk version. * * Valid values are 2, 3, or 4. If 2 is given, git_index_write may * write an index with version 3 instead, if necessary to accurately * represent the index. * * @param index An existing index object * @param version The new version number * @return 0 on success, -1 on failure */ GIT_EXTERN(int) git_index_set_version(git_index *index, unsigned int version); /** * Update the contents of an existing index object in memory by reading * from the hard disk. * * If `force` is true, this performs a "hard" read that discards in-memory * changes and always reloads the on-disk index data. If there is no * on-disk version, the index will be cleared. * * If `force` is false, this does a "soft" read that reloads the index * data from disk only if it has changed since the last time it was * loaded. Purely in-memory index data will be untouched. Be aware: if * there are changes on disk, unwritten in-memory changes are discarded. * * @param index an existing index object * @param force if true, always reload, vs. only read if file has changed * @return 0 or an error code */ GIT_EXTERN(int) git_index_read(git_index *index, int force); /** * Write an existing index object from memory back to disk * using an atomic file lock. * * @param index an existing index object * @return 0 or an error code */ GIT_EXTERN(int) git_index_write(git_index *index); /** * Get the full path to the index file on disk. * * @param index an existing index object * @return path to index file or NULL for in-memory index */ GIT_EXTERN(const char *) git_index_path(const git_index *index); /** * Get the checksum of the index * * This checksum is the SHA-1 hash over the index file (except the * last 20 bytes which are the checksum itself). In cases where the * index does not exist on-disk, it will be zeroed out. * * @param index an existing index object * @return a pointer to the checksum of the index */ GIT_EXTERN(const git_oid *) git_index_checksum(git_index *index); /** * Read a tree into the index file with stats * * The current index contents will be replaced by the specified tree. * * @param index an existing index object * @param tree tree to read * @return 0 or an error code */ GIT_EXTERN(int) git_index_read_tree(git_index *index, const git_tree *tree); /** * Write the index as a tree * * This method will scan the index and write a representation * of its current state back to disk; it recursively creates * tree objects for each of the subtrees stored in the index, * but only returns the OID of the root tree. This is the OID * that can be used e.g. to create a commit. * * The index instance cannot be bare, and needs to be associated * to an existing repository. * * The index must not contain any file in conflict. * * @param out Pointer where to store the OID of the written tree * @param index Index to write * @return 0 on success, GIT_EUNMERGED when the index is not clean * or an error code */ GIT_EXTERN(int) git_index_write_tree(git_oid *out, git_index *index); /** * Write the index as a tree to the given repository * * This method will do the same as `git_index_write_tree`, but * letting the user choose the repository where the tree will * be written. * * The index must not contain any file in conflict. * * @param out Pointer where to store OID of the written tree * @param index Index to write * @param repo Repository where to write the tree * @return 0 on success, GIT_EUNMERGED when the index is not clean * or an error code */ GIT_EXTERN(int) git_index_write_tree_to(git_oid *out, git_index *index, git_repository *repo); /**@}*/ /** @name Raw Index Entry Functions * * These functions work on index entries, and allow for raw manipulation * of the entries. */ /**@{*/ /* Index entry manipulation */ /** * Get the count of entries currently in the index * * @param index an existing index object * @return integer of count of current entries */ GIT_EXTERN(size_t) git_index_entrycount(const git_index *index); /** * Clear the contents (all the entries) of an index object. * * This clears the index object in memory; changes must be explicitly * written to disk for them to take effect persistently. * * @param index an existing index object * @return 0 on success, error code < 0 on failure */ GIT_EXTERN(int) git_index_clear(git_index *index); /** * Get a pointer to one of the entries in the index * * The entry is not modifiable and should not be freed. Because the * `git_index_entry` struct is a publicly defined struct, you should * be able to make your own permanent copy of the data if necessary. * * @param index an existing index object * @param n the position of the entry * @return a pointer to the entry; NULL if out of bounds */ GIT_EXTERN(const git_index_entry *) git_index_get_byindex( git_index *index, size_t n); /** * Get a pointer to one of the entries in the index * * The entry is not modifiable and should not be freed. Because the * `git_index_entry` struct is a publicly defined struct, you should * be able to make your own permanent copy of the data if necessary. * * @param index an existing index object * @param path path to search * @param stage stage to search * @return a pointer to the entry; NULL if it was not found */ GIT_EXTERN(const git_index_entry *) git_index_get_bypath( git_index *index, const char *path, int stage); /** * Remove an entry from the index * * @param index an existing index object * @param path path to search * @param stage stage to search * @return 0 or an error code */ GIT_EXTERN(int) git_index_remove(git_index *index, const char *path, int stage); /** * Remove all entries from the index under a given directory * * @param index an existing index object * @param dir container directory path * @param stage stage to search * @return 0 or an error code */ GIT_EXTERN(int) git_index_remove_directory( git_index *index, const char *dir, int stage); /** * Add or update an index entry from an in-memory struct * * If a previous index entry exists that has the same path and stage * as the given 'source_entry', it will be replaced. Otherwise, the * 'source_entry' will be added. * * A full copy (including the 'path' string) of the given * 'source_entry' will be inserted on the index. * * @param index an existing index object * @param source_entry new entry object * @return 0 or an error code */ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_entry); /** * Return the stage number from a git index entry * * This entry is calculated from the entry's flag attribute like this: * * (entry->flags & GIT_INDEX_ENTRY_STAGEMASK) >> GIT_INDEX_ENTRY_STAGESHIFT * * @param entry The entry * @return the stage number */ GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry); /** * Return whether the given index entry is a conflict (has a high stage * entry). This is simply shorthand for `git_index_entry_stage > 0`. * * @param entry The entry * @return 1 if the entry is a conflict entry, 0 otherwise */ GIT_EXTERN(int) git_index_entry_is_conflict(const git_index_entry *entry); /**@}*/ /** @name Index Entry Iteration Functions * * These functions provide an iterator for index entries. */ /**@{*/ /** * Create an iterator that will return every entry contained in the * index at the time of creation. Entries are returned in order, * sorted by path. This iterator is backed by a snapshot that allows * callers to modify the index while iterating without affecting the * iterator. * * @param iterator_out The newly created iterator * @param index The index to iterate */ GIT_EXTERN(int) git_index_iterator_new( git_index_iterator **iterator_out, git_index *index); /** * Return the next index entry in-order from the iterator. * * @param out Pointer to store the index entry in * @param iterator The iterator * @return 0, GIT_ITEROVER on iteration completion or an error code */ GIT_EXTERN(int) git_index_iterator_next( const git_index_entry **out, git_index_iterator *iterator); /** * Free the index iterator * * @param iterator The iterator to free */ GIT_EXTERN(void) git_index_iterator_free(git_index_iterator *iterator); /**@}*/ /** @name Workdir Index Entry Functions * * These functions work on index entries specifically in the working * directory (ie, stage 0). */ /**@{*/ /** * Add or update an index entry from a file on disk * * The file `path` must be relative to the repository's * working folder and must be readable. * * This method will fail in bare index instances. * * This forces the file to be added to the index, not looking * at gitignore rules. Those rules can be evaluated through * the git_status APIs (in status.h) before calling this. * * If this file currently is the result of a merge conflict, this * file will no longer be marked as conflicting. The data about * the conflict will be moved to the "resolve undo" (REUC) section. * * @param index an existing index object * @param path filename to add * @return 0 or an error code */ GIT_EXTERN(int) git_index_add_bypath(git_index *index, const char *path); /** * Add or update an index entry from a buffer in memory * * This method will create a blob in the repository that owns the * index and then add the index entry to the index. The `path` of the * entry represents the position of the blob relative to the * repository's root folder. * * If a previous index entry exists that has the same path as the * given 'entry', it will be replaced. Otherwise, the 'entry' will be * added. * * This forces the file to be added to the index, not looking * at gitignore rules. Those rules can be evaluated through * the git_status APIs (in status.h) before calling this. * * If this file currently is the result of a merge conflict, this * file will no longer be marked as conflicting. The data about * the conflict will be moved to the "resolve undo" (REUC) section. * * @param index an existing index object * @param entry filename to add * @param buffer data to be written into the blob * @param len length of the data * @return 0 or an error code */ GIT_EXTERN(int) git_index_add_from_buffer( git_index *index, const git_index_entry *entry, const void *buffer, size_t len); /** * Remove an index entry corresponding to a file on disk * * The file `path` must be relative to the repository's * working folder. It may exist. * * If this file currently is the result of a merge conflict, this * file will no longer be marked as conflicting. The data about * the conflict will be moved to the "resolve undo" (REUC) section. * * @param index an existing index object * @param path filename to remove * @return 0 or an error code */ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path); /** * Add or update index entries matching files in the working directory. * * This method will fail in bare index instances. * * The `pathspec` is a list of file names or shell glob patterns that will * be matched against files in the repository's working directory. Each * file that matches will be added to the index (either updating an * existing entry or adding a new entry). You can disable glob expansion * and force exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH` * flag. * * Files that are ignored will be skipped (unlike `git_index_add_bypath`). * If a file is already tracked in the index, then it *will* be updated * even if it is ignored. Pass the `GIT_INDEX_ADD_FORCE` flag to skip * the checking of ignore rules. * * To emulate `git add -A` and generate an error if the pathspec contains * the exact path of an ignored file (when not using FORCE), add the * `GIT_INDEX_ADD_CHECK_PATHSPEC` flag. This checks that each entry * in the `pathspec` that is an exact match to a filename on disk is * either not ignored or already in the index. If this check fails, the * function will return GIT_EINVALIDSPEC. * * To emulate `git add -A` with the "dry-run" option, just use a callback * function that always returns a positive value. See below for details. * * If any files are currently the result of a merge conflict, those files * will no longer be marked as conflicting. The data about the conflicts * will be moved to the "resolve undo" (REUC) section. * * If you provide a callback function, it will be invoked on each matching * item in the working directory immediately *before* it is added to / * updated in the index. Returning zero will add the item to the index, * greater than zero will skip the item, and less than zero will abort the * scan and return that value to the caller. * * @param index an existing index object * @param pathspec array of path patterns * @param flags combination of git_index_add_option_t flags * @param callback notification callback for each added/updated path (also * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_add_all( git_index *index, const git_strarray *pathspec, unsigned int flags, git_index_matched_path_cb callback, void *payload); /** * Remove all matching index entries. * * If you provide a callback function, it will be invoked on each matching * item in the index immediately *before* it is removed. Return 0 to * remove the item, > 0 to skip the item, and < 0 to abort the scan. * * @param index An existing index object * @param pathspec array of path patterns * @param callback notification callback for each removed path (also * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_remove_all( git_index *index, const git_strarray *pathspec, git_index_matched_path_cb callback, void *payload); /** * Update all index entries to match the working directory * * This method will fail in bare index instances. * * This scans the existing index entries and synchronizes them with the * working directory, deleting them if the corresponding working directory * file no longer exists otherwise updating the information (including * adding the latest version of file to the ODB if needed). * * If you provide a callback function, it will be invoked on each matching * item in the index immediately *before* it is updated (either refreshed * or removed depending on working directory state). Return 0 to proceed * with updating the item, > 0 to skip the item, and < 0 to abort the scan. * * @param index An existing index object * @param pathspec array of path patterns * @param callback notification callback for each updated path (also * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_update_all( git_index *index, const git_strarray *pathspec, git_index_matched_path_cb callback, void *payload); /** * Find the first position of any entries which point to given * path in the Git index. * * @param at_pos the address to which the position of the index entry is written (optional) * @param index an existing index object * @param path path to search * @return 0 or an error code */ GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path); /** * Find the first position of any entries matching a prefix. To find the first position * of a path inside a given folder, suffix the prefix with a '/'. * * @param at_pos the address to which the position of the index entry is written (optional) * @param index an existing index object * @param prefix the prefix to search for * @return 0 or an error code */ GIT_EXTERN(int) git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix); /**@}*/ /** @name Conflict Index Entry Functions * * These functions work on conflict index entries specifically (ie, stages 1-3) */ /**@{*/ /** * Add or update index entries to represent a conflict. Any staged * entries that exist at the given paths will be removed. * * The entries are the entries from the tree included in the merge. Any * entry may be null to indicate that that file was not present in the * trees during the merge. For example, ancestor_entry may be NULL to * indicate that a file was added in both branches and must be resolved. * * @param index an existing index object * @param ancestor_entry the entry data for the ancestor of the conflict * @param our_entry the entry data for our side of the merge conflict * @param their_entry the entry data for their side of the merge conflict * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_add( git_index *index, const git_index_entry *ancestor_entry, const git_index_entry *our_entry, const git_index_entry *their_entry); /** * Get the index entries that represent a conflict of a single file. * * The entries are not modifiable and should not be freed. Because the * `git_index_entry` struct is a publicly defined struct, you should * be able to make your own permanent copy of the data if necessary. * * @param ancestor_out Pointer to store the ancestor entry * @param our_out Pointer to store the our entry * @param their_out Pointer to store the their entry * @param index an existing index object * @param path path to search * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_get( const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index *index, const char *path); /** * Removes the index entries that represent a conflict of a single file. * * @param index an existing index object * @param path path to remove conflicts for * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path); /** * Remove all conflicts in the index (entries with a stage greater than 0). * * @param index an existing index object * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_cleanup(git_index *index); /** * Determine if the index contains entries representing file conflicts. * * @return 1 if at least one conflict is found, 0 otherwise. */ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index); /** * Create an iterator for the conflicts in the index. * * The index must not be modified while iterating; the results are undefined. * * @param iterator_out The newly created conflict iterator * @param index The index to scan * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_iterator_new( git_index_conflict_iterator **iterator_out, git_index *index); /** * Returns the current conflict (ancestor, ours and theirs entry) and * advance the iterator internally to the next value. * * @param ancestor_out Pointer to store the ancestor side of the conflict * @param our_out Pointer to store our side of the conflict * @param their_out Pointer to store their side of the conflict * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code * (negative value) */ GIT_EXTERN(int) git_index_conflict_next( const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index_conflict_iterator *iterator); /** * Frees a `git_index_conflict_iterator`. * * @param iterator pointer to the iterator */ GIT_EXTERN(void) git_index_conflict_iterator_free( git_index_conflict_iterator *iterator); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/odb.h0000644000175000017500000004502014125111754016765 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_odb_h__ #define INCLUDE_git_odb_h__ #include "common.h" #include "types.h" #include "oid.h" #include "oidarray.h" #include "indexer.h" /** * @file git2/odb.h * @brief Git object database routines * @defgroup git_odb Git object database routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Function type for callbacks from git_odb_foreach. */ typedef int GIT_CALLBACK(git_odb_foreach_cb)(const git_oid *id, void *payload); /** * Create a new object database with no backends. * * Before the ODB can be used for read/writing, a custom database * backend must be manually added using `git_odb_add_backend()` * * @param out location to store the database pointer, if opened. * Set to NULL if the open failed. * @return 0 or an error code */ GIT_EXTERN(int) git_odb_new(git_odb **out); /** * Create a new object database and automatically add * the two default backends: * * - git_odb_backend_loose: read and write loose object files * from disk, assuming `objects_dir` as the Objects folder * * - git_odb_backend_pack: read objects from packfiles, * assuming `objects_dir` as the Objects folder which * contains a 'pack/' folder with the corresponding data * * @param out location to store the database pointer, if opened. * Set to NULL if the open failed. * @param objects_dir path of the backends' "objects" directory. * @return 0 or an error code */ GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir); /** * Add an on-disk alternate to an existing Object DB. * * Note that the added path must point to an `objects`, not * to a full repository, to use it as an alternate store. * * Alternate backends are always checked for objects *after* * all the main backends have been exhausted. * * Writing is disabled on alternate backends. * * @param odb database to add the backend to * @param path path to the objects folder for the alternate * @return 0 on success, error code otherwise */ GIT_EXTERN(int) git_odb_add_disk_alternate(git_odb *odb, const char *path); /** * Close an open object database. * * @param db database pointer to close. If NULL no action is taken. */ GIT_EXTERN(void) git_odb_free(git_odb *db); /** * Read an object from the database. * * This method queries all available ODB backends * trying to read the given OID. * * The returned object is reference counted and * internally cached, so it should be closed * by the user once it's no longer in use. * * @param out pointer where to store the read object * @param db database to search for the object in. * @param id identity of the object to read. * @return 0 if the object was read, GIT_ENOTFOUND if the object is * not in the database. */ GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id); /** * Read an object from the database, given a prefix * of its identifier. * * This method queries all available ODB backends * trying to match the 'len' first hexadecimal * characters of the 'short_id'. * The remaining (GIT_OID_HEXSZ-len)*4 bits of * 'short_id' must be 0s. * 'len' must be at least GIT_OID_MINPREFIXLEN, * and the prefix must be long enough to identify * a unique object in all the backends; the * method will fail otherwise. * * The returned object is reference counted and * internally cached, so it should be closed * by the user once it's no longer in use. * * @param out pointer where to store the read object * @param db database to search for the object in. * @param short_id a prefix of the id of the object to read. * @param len the length of the prefix * @return 0 if the object was read, GIT_ENOTFOUND if the object is not in the * database. GIT_EAMBIGUOUS if the prefix is ambiguous * (several objects match the prefix) */ GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len); /** * Read the header of an object from the database, without * reading its full contents. * * The header includes the length and the type of an object. * * Note that most backends do not support reading only the header * of an object, so the whole object will be read and then the * header will be returned. * * @param len_out pointer where to store the length * @param type_out pointer where to store the type * @param db database to search for the object in. * @param id identity of the object to read. * @return 0 if the object was read, GIT_ENOTFOUND if the object is not * in the database. */ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_object_t *type_out, git_odb *db, const git_oid *id); /** * Determine if the given object can be found in the object database. * * @param db database to be searched for the given object. * @param id the object to search for. * @return 1 if the object was found, 0 otherwise */ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); /** * Determine if an object can be found in the object database by an * abbreviated object ID. * * @param out The full OID of the found object if just one is found. * @param db The database to be searched for the given object. * @param short_id A prefix of the id of the object to read. * @param len The length of the prefix. * @return 0 if found, GIT_ENOTFOUND if not found, GIT_EAMBIGUOUS if multiple * matches were found, other value < 0 if there was a read error. */ GIT_EXTERN(int) git_odb_exists_prefix( git_oid *out, git_odb *db, const git_oid *short_id, size_t len); /** * The information about object IDs to query in `git_odb_expand_ids`, * which will be populated upon return. */ typedef struct git_odb_expand_id { /** The object ID to expand */ git_oid id; /** * The length of the object ID (in nibbles, or packets of 4 bits; the * number of hex characters) * */ unsigned short length; /** * The (optional) type of the object to search for; leave as `0` or set * to `GIT_OBJECT_ANY` to query for any object matching the ID. */ git_object_t type; } git_odb_expand_id; /** * Determine if one or more objects can be found in the object database * by their abbreviated object ID and type. The given array will be * updated in place: for each abbreviated ID that is unique in the * database, and of the given type (if specified), the full object ID, * object ID length (`GIT_OID_HEXSZ`) and type will be written back to * the array. For IDs that are not found (or are ambiguous), the * array entry will be zeroed. * * Note that since this function operates on multiple objects, the * underlying database will not be asked to be reloaded if an object is * not found (which is unlike other object database operations.) * * @param db The database to be searched for the given objects. * @param ids An array of short object IDs to search for * @param count The length of the `ids` array * @return 0 on success or an error code on failure */ GIT_EXTERN(int) git_odb_expand_ids( git_odb *db, git_odb_expand_id *ids, size_t count); /** * Refresh the object database to load newly added files. * * If the object databases have changed on disk while the library * is running, this function will force a reload of the underlying * indexes. * * Use this function when you're confident that an external * application has tampered with the ODB. * * NOTE that it is not necessary to call this function at all. The * library will automatically attempt to refresh the ODB * when a lookup fails, to see if the looked up object exists * on disk but hasn't been loaded yet. * * @param db database to refresh * @return 0 on success, error code otherwise */ GIT_EXTERN(int) git_odb_refresh(struct git_odb *db); /** * List all objects available in the database * * The callback will be called for each object available in the * database. Note that the objects are likely to be returned in the index * order, which would make accessing the objects in that order inefficient. * Return a non-zero value from the callback to stop looping. * * @param db database to use * @param cb the callback to call for each object * @param payload data to pass to the callback * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload); /** * Write an object directly into the ODB * * This method writes a full object straight into the ODB. * For most cases, it is preferred to write objects through a write * stream, which is both faster and less memory intensive, specially * for big objects. * * This method is provided for compatibility with custom backends * which are not able to support streaming writes * * @param out pointer to store the OID result of the write * @param odb object database where to store the object * @param data buffer with the data to store * @param len size of the buffer * @param type type of the data to store * @return 0 or an error code */ GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size_t len, git_object_t type); /** * Open a stream to write an object into the ODB * * The type and final length of the object must be specified * when opening the stream. * * The returned stream will be of type `GIT_STREAM_WRONLY`, and it * won't be effective until `git_odb_stream_finalize_write` is called * and returns without an error * * The stream must always be freed when done with `git_odb_stream_free` or * will leak memory. * * @see git_odb_stream * * @param out pointer where to store the stream * @param db object database where the stream will write * @param size final size of the object that will be written * @param type type of the object that will be written * @return 0 if the stream was created; error code otherwise */ GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **out, git_odb *db, git_object_size_t size, git_object_t type); /** * Write to an odb stream * * This method will fail if the total number of received bytes exceeds the * size declared with `git_odb_open_wstream()` * * @param stream the stream * @param buffer the data to write * @param len the buffer's length * @return 0 if the write succeeded, error code otherwise */ GIT_EXTERN(int) git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len); /** * Finish writing to an odb stream * * The object will take its final name and will be available to the * odb. * * This method will fail if the total number of received bytes * differs from the size declared with `git_odb_open_wstream()` * * @param out pointer to store the resulting object's id * @param stream the stream * @return 0 on success, an error code otherwise */ GIT_EXTERN(int) git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream); /** * Read from an odb stream * * Most backends don't implement streaming reads */ GIT_EXTERN(int) git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len); /** * Free an odb stream * * @param stream the stream to free */ GIT_EXTERN(void) git_odb_stream_free(git_odb_stream *stream); /** * Open a stream to read an object from the ODB * * Note that most backends do *not* support streaming reads * because they store their objects as compressed/delta'ed blobs. * * It's recommended to use `git_odb_read` instead, which is * assured to work on all backends. * * The returned stream will be of type `GIT_STREAM_RDONLY` and * will have the following methods: * * - stream->read: read `n` bytes from the stream * - stream->free: free the stream * * The stream must always be free'd or will leak memory. * * @see git_odb_stream * * @param out pointer where to store the stream * @param len pointer where to store the length of the object * @param type pointer where to store the type of the object * @param db object database where the stream will read from * @param oid oid of the object the stream will read from * @return 0 if the stream was created, error code otherwise */ GIT_EXTERN(int) git_odb_open_rstream( git_odb_stream **out, size_t *len, git_object_t *type, git_odb *db, const git_oid *oid); /** * Open a stream for writing a pack file to the ODB. * * If the ODB layer understands pack files, then the given * packfile will likely be streamed directly to disk (and a * corresponding index created). If the ODB layer does not * understand pack files, the objects will be stored in whatever * format the ODB layer uses. * * @see git_odb_writepack * * @param out pointer to the writepack functions * @param db object database where the stream will read from * @param progress_cb function to call with progress information. * Be aware that this is called inline with network and indexing operations, * so performance may be affected. * @param progress_payload payload for the progress callback */ GIT_EXTERN(int) git_odb_write_pack( git_odb_writepack **out, git_odb *db, git_indexer_progress_cb progress_cb, void *progress_payload); /** * Write a `multi-pack-index` file from all the `.pack` files in the ODB. * * If the ODB layer understands pack files, then this will create a file called * `multi-pack-index` next to the `.pack` and `.idx` files, which will contain * an index of all objects stored in `.pack` files. This will allow for * O(log n) lookup for n objects (regardless of how many packfiles there * exist). * * @param db object database where the `multi-pack-index` file will be written. */ GIT_EXTERN(int) git_odb_write_multi_pack_index( git_odb *db); /** * Determine the object-ID (sha1 hash) of a data buffer * * The resulting SHA-1 OID will be the identifier for the data * buffer as if the data buffer it were to written to the ODB. * * @param out the resulting object-ID. * @param data data to hash * @param len size of the data * @param type of the data to hash * @return 0 or an error code */ GIT_EXTERN(int) git_odb_hash(git_oid *out, const void *data, size_t len, git_object_t type); /** * Read a file from disk and fill a git_oid with the object id * that the file would have if it were written to the Object * Database as an object of the given type (w/o applying filters). * Similar functionality to git.git's `git hash-object` without * the `-w` flag, however, with the --no-filters flag. * If you need filters, see git_repository_hashfile. * * @param out oid structure the result is written into. * @param path file to read and determine object id for * @param type the type of the object that will be hashed * @return 0 or an error code */ GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_object_t type); /** * Create a copy of an odb_object * * The returned copy must be manually freed with `git_odb_object_free`. * Note that because of an implementation detail, the returned copy will be * the same pointer as `source`: the object is internally refcounted, so the * copy still needs to be freed twice. * * @param dest pointer where to store the copy * @param source object to copy * @return 0 or an error code */ GIT_EXTERN(int) git_odb_object_dup(git_odb_object **dest, git_odb_object *source); /** * Close an ODB object * * This method must always be called once a `git_odb_object` is no * longer needed, otherwise memory will leak. * * @param object object to close */ GIT_EXTERN(void) git_odb_object_free(git_odb_object *object); /** * Return the OID of an ODB object * * This is the OID from which the object was read from * * @param object the object * @return a pointer to the OID */ GIT_EXTERN(const git_oid *) git_odb_object_id(git_odb_object *object); /** * Return the data of an ODB object * * This is the uncompressed, raw data as read from the ODB, * without the leading header. * * This pointer is owned by the object and shall not be free'd. * * @param object the object * @return a pointer to the data */ GIT_EXTERN(const void *) git_odb_object_data(git_odb_object *object); /** * Return the size of an ODB object * * This is the real size of the `data` buffer, not the * actual size of the object. * * @param object the object * @return the size */ GIT_EXTERN(size_t) git_odb_object_size(git_odb_object *object); /** * Return the type of an ODB object * * @param object the object * @return the type */ GIT_EXTERN(git_object_t) git_odb_object_type(git_odb_object *object); /** * Add a custom backend to an existing Object DB * * The backends are checked in relative ordering, based on the * value of the `priority` parameter. * * Read for more information. * * @param odb database to add the backend to * @param backend pointer to a git_odb_backend instance * @param priority Value for ordering the backends queue * @return 0 on success, error code otherwise */ GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority); /** * Add a custom backend to an existing Object DB; this * backend will work as an alternate. * * Alternate backends are always checked for objects *after* * all the main backends have been exhausted. * * The backends are checked in relative ordering, based on the * value of the `priority` parameter. * * Writing is disabled on alternate backends. * * Read for more information. * * @param odb database to add the backend to * @param backend pointer to a git_odb_backend instance * @param priority Value for ordering the backends queue * @return 0 on success, error code otherwise */ GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority); /** * Get the number of ODB backend objects * * @param odb object database * @return number of backends in the ODB */ GIT_EXTERN(size_t) git_odb_num_backends(git_odb *odb); /** * Lookup an ODB backend object by index * * @param out output pointer to ODB backend at pos * @param odb object database * @param pos index into object database backend list * @return 0 on success, GIT_ENOTFOUND if pos is invalid, other errors < 0 */ GIT_EXTERN(int) git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos); /** * Set the git commit-graph for the ODB. * * After a successfull call, the ownership of the cgraph parameter will be * transferred to libgit2, and the caller should not free it. * * The commit-graph can also be unset by explicitly passing NULL as the cgraph * parameter. * * @param odb object database * @param cgraph the git commit-graph * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/submodule.h0000644000175000017500000006006214125111754020223 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_submodule_h__ #define INCLUDE_git_submodule_h__ #include "common.h" #include "types.h" #include "oid.h" #include "remote.h" #include "checkout.h" /** * @file git2/submodule.h * @brief Git submodule management utilities * * Submodule support in libgit2 builds a list of known submodules and keeps * it in the repository. The list is built from the .gitmodules file, the * .git/config file, the index, and the HEAD tree. Items in the working * directory that look like submodules (i.e. a git repo) but are not * mentioned in those places won't be tracked. * * @defgroup git_submodule Git submodule management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Return codes for submodule status. * * A combination of these flags will be returned to describe the status of a * submodule. Depending on the "ignore" property of the submodule, some of * the flags may never be returned because they indicate changes that are * supposed to be ignored. * * Submodule info is contained in 4 places: the HEAD tree, the index, config * files (both .git/config and .gitmodules), and the working directory. Any * or all of those places might be missing information about the submodule * depending on what state the repo is in. We consider all four places to * build the combination of status flags. * * There are four values that are not really status, but give basic info * about what sources of submodule data are available. These will be * returned even if ignore is set to "ALL". * * * IN_HEAD - superproject head contains submodule * * IN_INDEX - superproject index contains submodule * * IN_CONFIG - superproject gitmodules has submodule * * IN_WD - superproject workdir has submodule * * The following values will be returned so long as ignore is not "ALL". * * * INDEX_ADDED - in index, not in head * * INDEX_DELETED - in head, not in index * * INDEX_MODIFIED - index and head don't match * * WD_UNINITIALIZED - workdir contains empty directory * * WD_ADDED - in workdir, not index * * WD_DELETED - in index, not workdir * * WD_MODIFIED - index and workdir head don't match * * The following can only be returned if ignore is "NONE" or "UNTRACKED". * * * WD_INDEX_MODIFIED - submodule workdir index is dirty * * WD_WD_MODIFIED - submodule workdir has modified files * * Lastly, the following will only be returned for ignore "NONE". * * * WD_UNTRACKED - wd contains untracked files */ typedef enum { GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0), GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1), GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2), GIT_SUBMODULE_STATUS_IN_WD = (1u << 3), GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4), GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5), GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6), GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7), GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8), GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9), GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10), GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11), GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12), GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13), } git_submodule_status_t; #define GIT_SUBMODULE_STATUS__IN_FLAGS 0x000Fu #define GIT_SUBMODULE_STATUS__INDEX_FLAGS 0x0070u #define GIT_SUBMODULE_STATUS__WD_FLAGS 0x3F80u #define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \ (((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) #define GIT_SUBMODULE_STATUS_IS_INDEX_UNMODIFIED(S) \ (((S) & GIT_SUBMODULE_STATUS__INDEX_FLAGS) == 0) #define GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(S) \ (((S) & (GIT_SUBMODULE_STATUS__WD_FLAGS & \ ~GIT_SUBMODULE_STATUS_WD_UNINITIALIZED)) == 0) #define GIT_SUBMODULE_STATUS_IS_WD_DIRTY(S) \ (((S) & (GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED | \ GIT_SUBMODULE_STATUS_WD_WD_MODIFIED | \ GIT_SUBMODULE_STATUS_WD_UNTRACKED)) != 0) /** * Function pointer to receive each submodule * * @param sm git_submodule currently being visited * @param name name of the submodule * @param payload value you passed to the foreach function as payload * @return 0 on success or error code */ typedef int GIT_CALLBACK(git_submodule_cb)( git_submodule *sm, const char *name, void *payload); /** * Submodule update options structure * * Initialize with `GIT_SUBMODULE_UPDATE_OPTIONS_INIT`. Alternatively, you can * use `git_submodule_update_options_init`. * */ typedef struct git_submodule_update_options { unsigned int version; /** * These options are passed to the checkout step. To disable * checkout, set the `checkout_strategy` to * `GIT_CHECKOUT_NONE`. Generally you will want the use * GIT_CHECKOUT_SAFE to update files in the working * directory. */ git_checkout_options checkout_opts; /** * Options which control the fetch, including callbacks. * * The callbacks to use for reporting fetch progress, and for acquiring * credentials in the event they are needed. */ git_fetch_options fetch_opts; /** * Allow fetching from the submodule's default remote if the target * commit isn't found. Enabled by default. */ int allow_fetch; } git_submodule_update_options; #define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION 1 #define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \ { GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \ { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \ GIT_FETCH_OPTIONS_INIT, 1 } /** * Initialize git_submodule_update_options structure * * Initializes a `git_submodule_update_options` with default values. Equivalent to * creating an instance with `GIT_SUBMODULE_UPDATE_OPTIONS_INIT`. * * @param opts The `git_submodule_update_options` struct to initialize. * @param version The struct version; pass `GIT_SUBMODULE_UPDATE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_submodule_update_options_init( git_submodule_update_options *opts, unsigned int version); /** * Update a submodule. This will clone a missing submodule and * checkout the subrepository to the commit specified in the index of * the containing repository. If the submodule repository doesn't contain * the target commit (e.g. because fetchRecurseSubmodules isn't set), then * the submodule is fetched using the fetch options supplied in options. * * @param submodule Submodule object * @param init If the submodule is not initialized, setting this flag to true * will initialize the submodule before updating. Otherwise, this will * return an error if attempting to update an uninitialzed repository. * but setting this to true forces them to be updated. * @param options configuration options for the update. If NULL, the * function works as though GIT_SUBMODULE_UPDATE_OPTIONS_INIT was passed. * @return 0 on success, any non-zero return value from a callback * function, or a negative value to indicate an error (use * `git_error_last` for a detailed error message). */ GIT_EXTERN(int) git_submodule_update(git_submodule *submodule, int init, git_submodule_update_options *options); /** * Lookup submodule information by name or path. * * Given either the submodule name or path (they are usually the same), this * returns a structure describing the submodule. * * There are two expected error scenarios: * * - The submodule is not mentioned in the HEAD, the index, and the config, * but does "exist" in the working directory (i.e. there is a subdirectory * that appears to be a Git repository). In this case, this function * returns GIT_EEXISTS to indicate a sub-repository exists but not in a * state where a git_submodule can be instantiated. * - The submodule is not mentioned in the HEAD, index, or config and the * working directory doesn't contain a value git repo at that path. * There may or may not be anything else at that path, but nothing that * looks like a submodule. In this case, this returns GIT_ENOTFOUND. * * You must call `git_submodule_free` when done with the submodule. * * @param out Output ptr to submodule; pass NULL to just get return code * @param repo The parent repository * @param name The name of or path to the submodule; trailing slashes okay * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, * GIT_EEXISTS if a repository is found in working directory only, * -1 on other errors. */ GIT_EXTERN(int) git_submodule_lookup( git_submodule **out, git_repository *repo, const char *name); /** * Create an in-memory copy of a submodule. The copy must be explicitly * free'd or it will leak. * * @param out Pointer to store the copy of the submodule. * @param source Original submodule to copy. */ GIT_EXTERN(int) git_submodule_dup(git_submodule **out, git_submodule *source); /** * Release a submodule * * @param submodule Submodule object */ GIT_EXTERN(void) git_submodule_free(git_submodule *submodule); /** * Iterate over all tracked submodules of a repository. * * See the note on `git_submodule` above. This iterates over the tracked * submodules as described therein. * * If you are concerned about items in the working directory that look like * submodules but are not tracked, the diff API will generate a diff record * for workdir items that look like submodules but are not tracked, showing * them as added in the workdir. Also, the status API will treat the entire * subdirectory of a contained git repo as a single GIT_STATUS_WT_NEW item. * * @param repo The repository * @param callback Function to be called with the name of each submodule. * Return a non-zero value to terminate the iteration. * @param payload Extra data to pass to callback * @return 0 on success, -1 on error, or non-zero return value of callback */ GIT_EXTERN(int) git_submodule_foreach( git_repository *repo, git_submodule_cb callback, void *payload); /** * Set up a new git submodule for checkout. * * This does "git submodule add" up to the fetch and checkout of the * submodule contents. It preps a new submodule, creates an entry in * .gitmodules and creates an empty initialized repository either at the * given path in the working directory or in .git/modules with a gitlink * from the working directory to the new repo. * * To fully emulate "git submodule add" call this function, then open the * submodule repo and perform the clone step as needed (if you don't need * anything custom see `git_submodule_add_clone()`). Lastly, call * `git_submodule_add_finalize()` to wrap up adding the new submodule and * .gitmodules to the index to be ready to commit. * * You must call `git_submodule_free` on the submodule object when done. * * @param out The newly created submodule ready to open for clone * @param repo The repository in which you want to create the submodule * @param url URL for the submodule's remote * @param path Path at which the submodule should be created * @param use_gitlink Should workdir contain a gitlink to the repo in * .git/modules vs. repo directly in workdir. * @return 0 on success, GIT_EEXISTS if submodule already exists, * -1 on other errors. */ GIT_EXTERN(int) git_submodule_add_setup( git_submodule **out, git_repository *repo, const char *url, const char *path, int use_gitlink); /** * Perform the clone step for a newly created submodule. * * This performs the necessary `git_clone` to setup a newly-created submodule. * * @param out The newly created repository object. Optional. * @param submodule The submodule currently waiting for its clone. * @param opts The options to use. * * @return 0 on success, -1 on other errors (see git_clone). */ GIT_EXTERN(int) git_submodule_clone( git_repository **out, git_submodule *submodule, const git_submodule_update_options *opts); /** * Resolve the setup of a new git submodule. * * This should be called on a submodule once you have called add setup * and done the clone of the submodule. This adds the .gitmodules file * and the newly cloned submodule to the index to be ready to be committed * (but doesn't actually do the commit). * * @param submodule The submodule to finish adding. */ GIT_EXTERN(int) git_submodule_add_finalize(git_submodule *submodule); /** * Add current submodule HEAD commit to index of superproject. * * @param submodule The submodule to add to the index * @param write_index Boolean if this should immediately write the index * file. If you pass this as false, you will have to get the * git_index and explicitly call `git_index_write()` on it to * save the change. * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_submodule_add_to_index( git_submodule *submodule, int write_index); /** * Get the containing repository for a submodule. * * This returns a pointer to the repository that contains the submodule. * This is a just a reference to the repository that was passed to the * original `git_submodule_lookup()` call, so if that repository has been * freed, then this may be a dangling reference. * * @param submodule Pointer to submodule object * @return Pointer to `git_repository` */ GIT_EXTERN(git_repository *) git_submodule_owner(git_submodule *submodule); /** * Get the name of submodule. * * @param submodule Pointer to submodule object * @return Pointer to the submodule name */ GIT_EXTERN(const char *) git_submodule_name(git_submodule *submodule); /** * Get the path to the submodule. * * The path is almost always the same as the submodule name, but the * two are actually not required to match. * * @param submodule Pointer to submodule object * @return Pointer to the submodule path */ GIT_EXTERN(const char *) git_submodule_path(git_submodule *submodule); /** * Get the URL for the submodule. * * @param submodule Pointer to submodule object * @return Pointer to the submodule url */ GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule); /** * Resolve a submodule url relative to the given repository. * * @param out buffer to store the absolute submodule url in * @param repo Pointer to repository object * @param url Relative url * @return 0 or an error code */ GIT_EXTERN(int) git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url); /** * Get the branch for the submodule. * * @param submodule Pointer to submodule object * @return Pointer to the submodule branch */ GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule); /** * Set the branch for the submodule in the configuration * * After calling this, you may wish to call `git_submodule_sync()` to * write the changes to the checked out submodule repository. * * @param repo the repository to affect * @param name the name of the submodule to configure * @param branch Branch that should be used for the submodule * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_submodule_set_branch(git_repository *repo, const char *name, const char *branch); /** * Set the URL for the submodule in the configuration * * * After calling this, you may wish to call `git_submodule_sync()` to * write the changes to the checked out submodule repository. * * @param repo the repository to affect * @param name the name of the submodule to configure * @param url URL that should be used for the submodule * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_submodule_set_url(git_repository *repo, const char *name, const char *url); /** * Get the OID for the submodule in the index. * * @param submodule Pointer to submodule object * @return Pointer to git_oid or NULL if submodule is not in index. */ GIT_EXTERN(const git_oid *) git_submodule_index_id(git_submodule *submodule); /** * Get the OID for the submodule in the current HEAD tree. * * @param submodule Pointer to submodule object * @return Pointer to git_oid or NULL if submodule is not in the HEAD. */ GIT_EXTERN(const git_oid *) git_submodule_head_id(git_submodule *submodule); /** * Get the OID for the submodule in the current working directory. * * This returns the OID that corresponds to looking up 'HEAD' in the checked * out submodule. If there are pending changes in the index or anything * else, this won't notice that. You should call `git_submodule_status()` * for a more complete picture about the state of the working directory. * * @param submodule Pointer to submodule object * @return Pointer to git_oid or NULL if submodule is not checked out. */ GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule); /** * Get the ignore rule that will be used for the submodule. * * These values control the behavior of `git_submodule_status()` for this * submodule. There are four ignore values: * * - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents * of the submodule from a clean checkout to be dirty, including the * addition of untracked files. This is the default if unspecified. * - **GIT_SUBMODULE_IGNORE_UNTRACKED** examines the contents of the * working tree (i.e. call `git_status_foreach()` on the submodule) but * UNTRACKED files will not count as making the submodule dirty. * - **GIT_SUBMODULE_IGNORE_DIRTY** means to only check if the HEAD of the * submodule has moved for status. This is fast since it does not need to * scan the working tree of the submodule at all. * - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo. * The working directory will be consider clean so long as there is a * checked out version present. * * @param submodule The submodule to check * @return The current git_submodule_ignore_t valyue what will be used for * this submodule. */ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore( git_submodule *submodule); /** * Set the ignore rule for the submodule in the configuration * * This does not affect any currently-loaded instances. * * @param repo the repository to affect * @param name the name of the submdule * @param ignore The new value for the ignore rule * @return 0 or an error code */ GIT_EXTERN(int) git_submodule_set_ignore( git_repository *repo, const char *name, git_submodule_ignore_t ignore); /** * Get the update rule that will be used for the submodule. * * This value controls the behavior of the `git submodule update` command. * There are four useful values documented with `git_submodule_update_t`. * * @param submodule The submodule to check * @return The current git_submodule_update_t value that will be used * for this submodule. */ GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy( git_submodule *submodule); /** * Set the update rule for the submodule in the configuration * * This setting won't affect any existing instances. * * @param repo the repository to affect * @param name the name of the submodule to configure * @param update The new value to use * @return 0 or an error code */ GIT_EXTERN(int) git_submodule_set_update( git_repository *repo, const char *name, git_submodule_update_t update); /** * Read the fetchRecurseSubmodules rule for a submodule. * * This accesses the submodule..fetchRecurseSubmodules value for * the submodule that controls fetching behavior for the submodule. * * Note that at this time, libgit2 does not honor this setting and the * fetch functionality current ignores submodules. * * @return 0 if fetchRecurseSubmodules is false, 1 if true */ GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules( git_submodule *submodule); /** * Set the fetchRecurseSubmodules rule for a submodule in the configuration * * This setting won't affect any existing instances. * * @param repo the repository to affect * @param name the submodule to configure * @param fetch_recurse_submodules Boolean value * @return old value for fetchRecurseSubmodules */ GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules( git_repository *repo, const char *name, git_submodule_recurse_t fetch_recurse_submodules); /** * Copy submodule info into ".git/config" file. * * Just like "git submodule init", this copies information about the * submodule into ".git/config". You can use the accessor functions * above to alter the in-memory git_submodule object and control what * is written to the config, overriding what is in .gitmodules. * * @param submodule The submodule to write into the superproject config * @param overwrite By default, existing entries will not be overwritten, * but setting this to true forces them to be updated. * @return 0 on success, <0 on failure. */ GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite); /** * Set up the subrepository for a submodule in preparation for clone. * * This function can be called to init and set up a submodule * repository from a submodule in preparation to clone it from * its remote. * * @param out Output pointer to the created git repository. * @param sm The submodule to create a new subrepository from. * @param use_gitlink Should the workdir contain a gitlink to * the repo in .git/modules vs. repo directly in workdir. * @return 0 on success, <0 on failure. */ GIT_EXTERN(int) git_submodule_repo_init( git_repository **out, const git_submodule *sm, int use_gitlink); /** * Copy submodule remote info into submodule repo. * * This copies the information about the submodules URL into the checked out * submodule config, acting like "git submodule sync". This is useful if * you have altered the URL for the submodule (or it has been altered by a * fetch of upstream changes) and you need to update your local repo. */ GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule); /** * Open the repository for a submodule. * * This is a newly opened repository object. The caller is responsible for * calling `git_repository_free()` on it when done. Multiple calls to this * function will return distinct `git_repository` objects. This will only * work if the submodule is checked out into the working directory. * * @param repo Pointer to the submodule repo which was opened * @param submodule Submodule to be opened * @return 0 on success, <0 if submodule repo could not be opened. */ GIT_EXTERN(int) git_submodule_open( git_repository **repo, git_submodule *submodule); /** * Reread submodule info from config, index, and HEAD. * * Call this to reread cached submodule information for this submodule if * you have reason to believe that it has changed. * * @param submodule The submodule to reload * @param force Force reload even if the data doesn't seem out of date * @return 0 on success, <0 on error */ GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force); /** * Get the status for a submodule. * * This looks at a submodule and tries to determine the status. It * will return a combination of the `GIT_SUBMODULE_STATUS` values above. * How deeply it examines the working directory to do this will depend * on the `git_submodule_ignore_t` value for the submodule. * * @param status Combination of `GIT_SUBMODULE_STATUS` flags * @param repo the repository in which to look * @param name name of the submodule * @param ignore the ignore rules to follow * @return 0 on success, <0 on error */ GIT_EXTERN(int) git_submodule_status( unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore); /** * Get the locations of submodule information. * * This is a bit like a very lightweight version of `git_submodule_status`. * It just returns a made of the first four submodule status values (i.e. * the ones like GIT_SUBMODULE_STATUS_IN_HEAD, etc) that tell you where the * submodule data comes from (i.e. the HEAD commit, gitmodules file, etc.). * This can be useful if you want to know if the submodule is present in the * working directory at this point in time, etc. * * @param location_status Combination of first four `GIT_SUBMODULE_STATUS` flags * @param submodule Submodule for which to get status * @return 0 on success, <0 on error */ GIT_EXTERN(int) git_submodule_location( unsigned int *location_status, git_submodule *submodule); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/revwalk.h0000644000175000017500000002113614125111754017676 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_revwalk_h__ #define INCLUDE_git_revwalk_h__ #include "common.h" #include "types.h" #include "oid.h" /** * @file git2/revwalk.h * @brief Git revision traversal routines * @defgroup git_revwalk Git revision traversal routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Flags to specify the sorting which a revwalk should perform. */ typedef enum { /** * Sort the output with the same default method from `git`: reverse * chronological order. This is the default sorting for new walkers. */ GIT_SORT_NONE = 0, /** * Sort the repository contents in topological order (no parents before * all of its children are shown); this sorting mode can be combined * with time sorting to produce `git`'s `--date-order``. */ GIT_SORT_TOPOLOGICAL = 1 << 0, /** * Sort the repository contents by commit time; * this sorting mode can be combined with * topological sorting. */ GIT_SORT_TIME = 1 << 1, /** * Iterate through the repository contents in reverse * order; this sorting mode can be combined with * any of the above. */ GIT_SORT_REVERSE = 1 << 2, } git_sort_t; /** * Allocate a new revision walker to iterate through a repo. * * This revision walker uses a custom memory pool and an internal * commit cache, so it is relatively expensive to allocate. * * For maximum performance, this revision walker should be * reused for different walks. * * This revision walker is *not* thread safe: it may only be * used to walk a repository on a single thread; however, * it is possible to have several revision walkers in * several different threads walking the same repository. * * @param out pointer to the new revision walker * @param repo the repo to walk through * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_new(git_revwalk **out, git_repository *repo); /** * Reset the revision walker for reuse. * * This will clear all the pushed and hidden commits, and * leave the walker in a blank state (just like at * creation) ready to receive new commit pushes and * start a new walk. * * The revision walk is automatically reset when a walk * is over. * * @param walker handle to reset. * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_reset(git_revwalk *walker); /** * Add a new root for the traversal * * The pushed commit will be marked as one of the roots from which to * start the walk. This commit may not be walked if it or a child is * hidden. * * At least one commit must be pushed onto the walker before a walk * can be started. * * The given id must belong to a committish on the walked * repository. * * @param walk the walker being used for the traversal. * @param id the oid of the commit to start from. * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *id); /** * Push matching references * * The OIDs pointed to by the references that match the given glob * pattern will be pushed to the revision walker. * * A leading 'refs/' is implied if not present as well as a trailing * '/\*' if the glob lacks '?', '\*' or '['. * * Any references matching this glob which do not point to a * committish will be ignored. * * @param walk the walker being used for the traversal * @param glob the glob pattern references should match * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_push_glob(git_revwalk *walk, const char *glob); /** * Push the repository's HEAD * * @param walk the walker being used for the traversal * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_push_head(git_revwalk *walk); /** * Mark a commit (and its ancestors) uninteresting for the output. * * The given id must belong to a committish on the walked * repository. * * The resolved commit and all its parents will be hidden from the * output on the revision walk. * * @param walk the walker being used for the traversal. * @param commit_id the oid of commit that will be ignored during the traversal * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *commit_id); /** * Hide matching references. * * The OIDs pointed to by the references that match the given glob * pattern and their ancestors will be hidden from the output on the * revision walk. * * A leading 'refs/' is implied if not present as well as a trailing * '/\*' if the glob lacks '?', '\*' or '['. * * Any references matching this glob which do not point to a * committish will be ignored. * * @param walk the walker being used for the traversal * @param glob the glob pattern references should match * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_hide_glob(git_revwalk *walk, const char *glob); /** * Hide the repository's HEAD * * @param walk the walker being used for the traversal * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_hide_head(git_revwalk *walk); /** * Push the OID pointed to by a reference * * The reference must point to a committish. * * @param walk the walker being used for the traversal * @param refname the reference to push * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_push_ref(git_revwalk *walk, const char *refname); /** * Hide the OID pointed to by a reference * * The reference must point to a committish. * * @param walk the walker being used for the traversal * @param refname the reference to hide * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname); /** * Get the next commit from the revision walk. * * The initial call to this method is *not* blocking when * iterating through a repo with a time-sorting mode. * * Iterating with Topological or inverted modes makes the initial * call blocking to preprocess the commit list, but this block should be * mostly unnoticeable on most repositories (topological preprocessing * times at 0.3s on the git.git repo). * * The revision walker is reset when the walk is over. * * @param out Pointer where to store the oid of the next commit * @param walk the walker to pop the commit from. * @return 0 if the next commit was found; * GIT_ITEROVER if there are no commits left to iterate */ GIT_EXTERN(int) git_revwalk_next(git_oid *out, git_revwalk *walk); /** * Change the sorting mode when iterating through the * repository's contents. * * Changing the sorting mode resets the walker. * * @param walk the walker being used for the traversal. * @param sort_mode combination of GIT_SORT_XXX flags * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode); /** * Push and hide the respective endpoints of the given range. * * The range should be of the form * .. * where each is in the form accepted by 'git_revparse_single'. * The left-hand commit will be hidden and the right-hand commit pushed. * * @param walk the walker being used for the traversal * @param range the range * @return 0 or an error code * */ GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range); /** * Simplify the history by first-parent * * No parents other than the first for each commit will be enqueued. * * @return 0 or an error code */ GIT_EXTERN(int) git_revwalk_simplify_first_parent(git_revwalk *walk); /** * Free a revision walker previously allocated. * * @param walk traversal handle to close. If NULL nothing occurs. */ GIT_EXTERN(void) git_revwalk_free(git_revwalk *walk); /** * Return the repository on which this walker * is operating. * * @param walk the revision walker * @return the repository being walked */ GIT_EXTERN(git_repository *) git_revwalk_repository(git_revwalk *walk); /** * This is a callback function that user can provide to hide a * commit and its parents. If the callback function returns non-zero value, * then this commit and its parents will be hidden. * * @param commit_id oid of Commit * @param payload User-specified pointer to data to be passed as data payload */ typedef int GIT_CALLBACK(git_revwalk_hide_cb)( const git_oid *commit_id, void *payload); /** * Adds, changes or removes a callback function to hide a commit and its parents * * @param walk the revision walker * @param hide_cb callback function to hide a commit and its parents * @param payload data payload to be passed to callback function */ GIT_EXTERN(int) git_revwalk_add_hide_cb( git_revwalk *walk, git_revwalk_hide_cb hide_cb, void *payload); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/global.h0000644000175000017500000000227714125111754017470 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_global_h__ #define INCLUDE_git_global_h__ #include "common.h" GIT_BEGIN_DECL /** * Init the global state * * This function must be called before any other libgit2 function in * order to set up global state and threading. * * This function may be called multiple times - it will return the number * of times the initialization has been called (including this one) that have * not subsequently been shutdown. * * @return the number of initializations of the library, or an error code. */ GIT_EXTERN(int) git_libgit2_init(void); /** * Shutdown the global state * * Clean up the global state and threading context after calling it as * many times as `git_libgit2_init()` was called - it will return the * number of remainining initializations that have not been shutdown * (after this one). * * @return the number of remaining initializations of the library, or an * error code. */ GIT_EXTERN(int) git_libgit2_shutdown(void); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/signature.h0000644000175000017500000000623414125111754020226 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_signature_h__ #define INCLUDE_git_signature_h__ #include "common.h" #include "types.h" /** * @file git2/signature.h * @brief Git signature creation * @defgroup git_signature Git signature creation * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a new action signature. * * Call `git_signature_free()` to free the data. * * Note: angle brackets ('<' and '>') characters are not allowed * to be used in either the `name` or the `email` parameter. * * @param out new signature, in case of error NULL * @param name name of the person * @param email email of the person * @param time time (in seconds from epoch) when the action happened * @param offset timezone offset (in minutes) for the time * @return 0 or an error code */ GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const char *email, git_time_t time, int offset); /** * Create a new action signature with a timestamp of 'now'. * * Call `git_signature_free()` to free the data. * * @param out new signature, in case of error NULL * @param name name of the person * @param email email of the person * @return 0 or an error code */ GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email); /** * Create a new action signature with default user and now timestamp. * * This looks up the user.name and user.email from the configuration and * uses the current time as the timestamp, and creates a new signature * based on that information. It will return GIT_ENOTFOUND if either the * user.name or user.email are not set. * * @param out new signature * @param repo repository pointer * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code */ GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo); /** * Create a new signature by parsing the given buffer, which is * expected to be in the format "Real Name timestamp tzoffset", * where `timestamp` is the number of seconds since the Unix epoch and * `tzoffset` is the timezone offset in `hhmm` format (note the lack * of a colon separator). * * @param out new signature * @param buf signature string * @return 0 on success, or an error code */ GIT_EXTERN(int) git_signature_from_buffer(git_signature **out, const char *buf); /** * Create a copy of an existing signature. All internal strings are also * duplicated. * * Call `git_signature_free()` to free the data. * * @param dest pointer where to store the copy * @param sig signature to duplicate * @return 0 or an error code */ GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig); /** * Free an existing signature. * * Because the signature is not an opaque structure, it is legal to free it * manually, but be sure to free the "name" and "email" strings in addition * to the structure itself. * * @param sig signature to free */ GIT_EXTERN(void) git_signature_free(git_signature *sig); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/annotated_commit.h0000644000175000017500000000744514125111754021557 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_annotated_commit_h__ #define INCLUDE_git_annotated_commit_h__ #include "common.h" #include "repository.h" #include "types.h" /** * @file git2/annotated_commit.h * @brief Git annotated commit routines * @defgroup git_annotated_commit Git annotated commit routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Creates a `git_annotated_commit` from the given reference. * The resulting git_annotated_commit must be freed with * `git_annotated_commit_free`. * * @param out pointer to store the git_annotated_commit result in * @param repo repository that contains the given reference * @param ref reference to use to lookup the git_annotated_commit * @return 0 on success or error code */ GIT_EXTERN(int) git_annotated_commit_from_ref( git_annotated_commit **out, git_repository *repo, const git_reference *ref); /** * Creates a `git_annotated_commit` from the given fetch head data. * The resulting git_annotated_commit must be freed with * `git_annotated_commit_free`. * * @param out pointer to store the git_annotated_commit result in * @param repo repository that contains the given commit * @param branch_name name of the (remote) branch * @param remote_url url of the remote * @param id the commit object id of the remote branch * @return 0 on success or error code */ GIT_EXTERN(int) git_annotated_commit_from_fetchhead( git_annotated_commit **out, git_repository *repo, const char *branch_name, const char *remote_url, const git_oid *id); /** * Creates a `git_annotated_commit` from the given commit id. * The resulting git_annotated_commit must be freed with * `git_annotated_commit_free`. * * An annotated commit contains information about how it was * looked up, which may be useful for functions like merge or * rebase to provide context to the operation. For example, * conflict files will include the name of the source or target * branches being merged. It is therefore preferable to use the * most specific function (eg `git_annotated_commit_from_ref`) * instead of this one when that data is known. * * @param out pointer to store the git_annotated_commit result in * @param repo repository that contains the given commit * @param id the commit object id to lookup * @return 0 on success or error code */ GIT_EXTERN(int) git_annotated_commit_lookup( git_annotated_commit **out, git_repository *repo, const git_oid *id); /** * Creates a `git_annotated_commit` from a revision string. * * See `man gitrevisions`, or * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for * information on the syntax accepted. * * @param out pointer to store the git_annotated_commit result in * @param repo repository that contains the given commit * @param revspec the extended sha syntax string to use to lookup the commit * @return 0 on success or error code */ GIT_EXTERN(int) git_annotated_commit_from_revspec( git_annotated_commit **out, git_repository *repo, const char *revspec); /** * Gets the commit ID that the given `git_annotated_commit` refers to. * * @param commit the given annotated commit * @return commit id */ GIT_EXTERN(const git_oid *) git_annotated_commit_id( const git_annotated_commit *commit); /** * Get the refname that the given `git_annotated_commit` refers to. * * @param commit the given annotated commit * @return ref name. */ GIT_EXTERN(const char *) git_annotated_commit_ref( const git_annotated_commit *commit); /** * Frees a `git_annotated_commit`. * * @param commit annotated commit to free */ GIT_EXTERN(void) git_annotated_commit_free( git_annotated_commit *commit); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/email.h0000644000175000017500000000654414125111754017320 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_email_h__ #define INCLUDE_git_email_h__ #include "common.h" /** * @file git2/email.h * @brief Git email formatting and application routines. * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Formatting options for diff e-mail generation */ typedef enum { /** Normal patch, the default */ GIT_EMAIL_CREATE_DEFAULT = 0, /** Do not include patch numbers in the subject prefix. */ GIT_EMAIL_CREATE_OMIT_NUMBERS = (1u << 0), /** * Include numbers in the subject prefix even when the * patch is for a single commit (1/1). */ GIT_EMAIL_CREATE_ALWAYS_NUMBER = (1u << 1), /** Do not perform rename or similarity detection. */ GIT_EMAIL_CREATE_NO_RENAMES = (1u << 2), } git_email_create_flags_t; /** * Options for controlling the formatting of the generated e-mail. */ typedef struct { unsigned int version; /** see `git_email_create_flags_t` above */ uint32_t flags; /** Options to use when creating diffs */ git_diff_options diff_opts; /** Options for finding similarities within diffs */ git_diff_find_options diff_find_opts; /** * The subject prefix, by default "PATCH". If set to an empty * string ("") then only the patch numbers will be shown in the * prefix. If the subject_prefix is empty and patch numbers * are not being shown, the prefix will be omitted entirely. */ const char *subject_prefix; /** * The starting patch number; this cannot be 0. By default, * this is 1. */ size_t start_number; /** The "re-roll" number. By default, there is no re-roll. */ size_t reroll_number; } git_email_create_options; /* * By default, our options include rename detection and binary * diffs to match `git format-patch`. */ #define GIT_EMAIL_CREATE_OPTIONS_VERSION 1 #define GIT_EMAIL_CREATE_OPTIONS_INIT \ { \ GIT_EMAIL_CREATE_OPTIONS_VERSION, \ GIT_EMAIL_CREATE_DEFAULT, \ { GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_SHOW_BINARY, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3 }, \ GIT_DIFF_FIND_OPTIONS_INIT \ } /** * Create a diff for a commit in mbox format for sending via email. * * @param out buffer to store the e-mail patch in * @param diff the changes to include in the email * @param patch_idx the patch index * @param patch_count the total number of patches that will be included * @param commit_id the commit id for this change * @param summary the commit message for this change * @param body optional text to include above the diffstat * @param author the person who authored this commit * @param opts email creation options */ GIT_EXTERN(int) git_email_create_from_diff( git_buf *out, git_diff *diff, size_t patch_idx, size_t patch_count, const git_oid *commit_id, const char *summary, const char *body, const git_signature *author, const git_email_create_options *opts); /** * Create a diff for a commit in mbox format for sending via email. * The commit must not be a merge commit. * * @param out buffer to store the e-mail patch in * @param commit commit to create a patch for * @param opts email creation options */ GIT_EXTERN(int) git_email_create_from_commit( git_buf *out, git_commit *commit, const git_email_create_options *opts); GIT_END_DECL /** @} */ #endif git2r/src/libgit2/include/git2/oid.h0000644000175000017500000002046314125111754017000 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_oid_h__ #define INCLUDE_git_oid_h__ #include "common.h" #include "types.h" /** * @file git2/oid.h * @brief Git object id routines * @defgroup git_oid Git object id routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** Size (in bytes) of a raw/binary oid */ #define GIT_OID_RAWSZ 20 /** Size (in bytes) of a hex formatted oid */ #define GIT_OID_HEXSZ (GIT_OID_RAWSZ * 2) /** Minimum length (in number of hex characters, * i.e. packets of 4 bits) of an oid prefix */ #define GIT_OID_MINPREFIXLEN 4 /** Unique identity of any object (commit, tree, blob, tag). */ typedef struct git_oid { /** raw binary formatted id */ unsigned char id[GIT_OID_RAWSZ]; } git_oid; /** * Parse a hex formatted object id into a git_oid. * * @param out oid structure the result is written into. * @param str input hex string; must be pointing at the start of * the hex sequence and have at least the number of bytes * needed for an oid encoded in hex (40 bytes). * @return 0 or an error code */ GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str); /** * Parse a hex formatted null-terminated string into a git_oid. * * @param out oid structure the result is written into. * @param str input hex string; must be null-terminated. * @return 0 or an error code */ GIT_EXTERN(int) git_oid_fromstrp(git_oid *out, const char *str); /** * Parse N characters of a hex formatted object id into a git_oid. * * If N is odd, the last byte's high nibble will be read in and the * low nibble set to zero. * * @param out oid structure the result is written into. * @param str input hex string of at least size `length` * @param length length of the input string * @return 0 or an error code */ GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length); /** * Copy an already raw oid into a git_oid structure. * * @param out oid structure the result is written into. * @param raw the raw input bytes to be copied. * @return 0 on success or error code */ GIT_EXTERN(int) git_oid_fromraw(git_oid *out, const unsigned char *raw); /** * Format a git_oid into a hex string. * * @param out output hex string; must be pointing at the start of * the hex sequence and have at least the number of bytes * needed for an oid encoded in hex (40 bytes). Only the * oid digits are written; a '\\0' terminator must be added * by the caller if it is required. * @param id oid structure to format. * @return 0 on success or error code */ GIT_EXTERN(int) git_oid_fmt(char *out, const git_oid *id); /** * Format a git_oid into a partial hex string. * * @param out output hex string; you say how many bytes to write. * If the number of bytes is > GIT_OID_HEXSZ, extra bytes * will be zeroed; if not, a '\0' terminator is NOT added. * @param n number of characters to write into out string * @param id oid structure to format. * @return 0 on success or error code */ GIT_EXTERN(int) git_oid_nfmt(char *out, size_t n, const git_oid *id); /** * Format a git_oid into a loose-object path string. * * The resulting string is "aa/...", where "aa" is the first two * hex digits of the oid and "..." is the remaining 38 digits. * * @param out output hex string; must be pointing at the start of * the hex sequence and have at least the number of bytes * needed for an oid encoded in hex (41 bytes). Only the * oid digits are written; a '\\0' terminator must be added * by the caller if it is required. * @param id oid structure to format. * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_oid_pathfmt(char *out, const git_oid *id); /** * Format a git_oid into a statically allocated c-string. * * The c-string is owned by the library and should not be freed * by the user. If libgit2 is built with thread support, the string * will be stored in TLS (i.e. one buffer per thread) to allow for * concurrent calls of the function. * * @param oid The oid structure to format * @return the c-string */ GIT_EXTERN(char *) git_oid_tostr_s(const git_oid *oid); /** * Format a git_oid into a buffer as a hex format c-string. * * If the buffer is smaller than GIT_OID_HEXSZ+1, then the resulting * oid c-string will be truncated to n-1 characters (but will still be * NUL-byte terminated). * * If there are any input parameter errors (out == NULL, n == 0, oid == * NULL), then a pointer to an empty string is returned, so that the * return value can always be printed. * * @param out the buffer into which the oid string is output. * @param n the size of the out buffer. * @param id the oid structure to format. * @return the out buffer pointer, assuming no input parameter * errors, otherwise a pointer to an empty string. */ GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *id); /** * Copy an oid from one structure to another. * * @param out oid structure the result is written into. * @param src oid structure to copy from. * @return 0 on success or error code */ GIT_EXTERN(int) git_oid_cpy(git_oid *out, const git_oid *src); /** * Compare two oid structures. * * @param a first oid structure. * @param b second oid structure. * @return <0, 0, >0 if a < b, a == b, a > b. */ GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b); /** * Compare two oid structures for equality * * @param a first oid structure. * @param b second oid structure. * @return true if equal, false otherwise */ GIT_EXTERN(int) git_oid_equal(const git_oid *a, const git_oid *b); /** * Compare the first 'len' hexadecimal characters (packets of 4 bits) * of two oid structures. * * @param a first oid structure. * @param b second oid structure. * @param len the number of hex chars to compare * @return 0 in case of a match */ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len); /** * Check if an oid equals an hex formatted object id. * * @param id oid structure. * @param str input hex string of an object id. * @return 0 in case of a match, -1 otherwise. */ GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str); /** * Compare an oid to an hex formatted object id. * * @param id oid structure. * @param str input hex string of an object id. * @return -1 if str is not valid, <0 if id sorts before str, * 0 if id matches str, >0 if id sorts after str. */ GIT_EXTERN(int) git_oid_strcmp(const git_oid *id, const char *str); /** * Check is an oid is all zeros. * * @return 1 if all zeros, 0 otherwise. */ GIT_EXTERN(int) git_oid_is_zero(const git_oid *id); /** * OID Shortener object */ typedef struct git_oid_shorten git_oid_shorten; /** * Create a new OID shortener. * * The OID shortener is used to process a list of OIDs * in text form and return the shortest length that would * uniquely identify all of them. * * E.g. look at the result of `git log --abbrev`. * * @param min_length The minimal length for all identifiers, * which will be used even if shorter OIDs would still * be unique. * @return a `git_oid_shorten` instance, NULL if OOM */ GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length); /** * Add a new OID to set of shortened OIDs and calculate * the minimal length to uniquely identify all the OIDs in * the set. * * The OID is expected to be a 40-char hexadecimal string. * The OID is owned by the user and will not be modified * or freed. * * For performance reasons, there is a hard-limit of how many * OIDs can be added to a single set (around ~32000, assuming * a mostly randomized distribution), which should be enough * for any kind of program, and keeps the algorithm fast and * memory-efficient. * * Attempting to add more than those OIDs will result in a * GIT_ERROR_INVALID error * * @param os a `git_oid_shorten` instance * @param text_id an OID in text form * @return the minimal length to uniquely identify all OIDs * added so far to the set; or an error code (<0) if an * error occurs. */ GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_id); /** * Free an OID shortener instance * * @param os a `git_oid_shorten` instance */ GIT_EXTERN(void) git_oid_shorten_free(git_oid_shorten *os); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/message.h0000644000175000017500000000452314125111754017650 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_message_h__ #define INCLUDE_git_message_h__ #include "common.h" #include "buffer.h" /** * @file git2/message.h * @brief Git message management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Clean up excess whitespace and make sure there is a trailing newline in the message. * * Optionally, it can remove lines which start with the comment character. * * @param out The user-allocated git_buf which will be filled with the * cleaned up message. * * @param message The message to be prettified. * * @param strip_comments Non-zero to remove comment lines, 0 to leave them in. * * @param comment_char Comment character. Lines starting with this character * are considered to be comments and removed if `strip_comments` is non-zero. * * @return 0 or an error code. */ GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char); /** * Represents a single git message trailer. */ typedef struct { const char *key; const char *value; } git_message_trailer; /** * Represents an array of git message trailers. * * Struct members under the private comment are private, subject to change * and should not be used by callers. */ typedef struct { git_message_trailer *trailers; size_t count; /* private */ char *_trailer_block; } git_message_trailer_array; /** * Parse trailers out of a message, filling the array pointed to by +arr+. * * Trailers are key/value pairs in the last paragraph of a message, not * including any patches or conflicts that may be present. * * @param arr A pre-allocated git_message_trailer_array struct to be filled in * with any trailers found during parsing. * @param message The message to be parsed * @return 0 on success, or non-zero on error. */ GIT_EXTERN(int) git_message_trailers(git_message_trailer_array *arr, const char *message); /** * Clean's up any allocated memory in the git_message_trailer_array filled by * a call to git_message_trailers. */ GIT_EXTERN(void) git_message_trailer_array_free(git_message_trailer_array *arr); /** @} */ GIT_END_DECL #endif /* INCLUDE_git_message_h__ */ git2r/src/libgit2/include/git2/transaction.h0000644000175000017500000000736014125111754020553 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_transaction_h__ #define INCLUDE_git_transaction_h__ #include "common.h" #include "types.h" /** * @file git2/transaction.h * @brief Git transactional reference routines * @defgroup git_transaction Git transactional reference routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a new transaction object * * This does not lock anything, but sets up the transaction object to * know from which repository to lock. * * @param out the resulting transaction * @param repo the repository in which to lock * @return 0 or an error code */ GIT_EXTERN(int) git_transaction_new(git_transaction **out, git_repository *repo); /** * Lock a reference * * Lock the specified reference. This is the first step to updating a * reference. * * @param tx the transaction * @param refname the reference to lock * @return 0 or an error message */ GIT_EXTERN(int) git_transaction_lock_ref(git_transaction *tx, const char *refname); /** * Set the target of a reference * * Set the target of the specified reference. This reference must be * locked. * * @param tx the transaction * @param refname reference to update * @param target target to set the reference to * @param sig signature to use in the reflog; pass NULL to read the identity from the config * @param msg message to use in the reflog * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code */ GIT_EXTERN(int) git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg); /** * Set the target of a reference * * Set the target of the specified reference. This reference must be * locked. * * @param tx the transaction * @param refname reference to update * @param target target to set the reference to * @param sig signature to use in the reflog; pass NULL to read the identity from the config * @param msg message to use in the reflog * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code */ GIT_EXTERN(int) git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg); /** * Set the reflog of a reference * * Set the specified reference's reflog. If this is combined with * setting the target, that update won't be written to the reflog. * * @param tx the transaction * @param refname the reference whose reflog to set * @param reflog the reflog as it should be written out * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code */ GIT_EXTERN(int) git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog); /** * Remove a reference * * @param tx the transaction * @param refname the reference to remove * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code */ GIT_EXTERN(int) git_transaction_remove(git_transaction *tx, const char *refname); /** * Commit the changes from the transaction * * Perform the changes that have been queued. The updates will be made * one by one, and the first failure will stop the processing. * * @param tx the transaction * @return 0 or an error code */ GIT_EXTERN(int) git_transaction_commit(git_transaction *tx); /** * Free the resources allocated by this transaction * * If any references remain locked, they will be unlocked without any * changes made to them. * * @param tx the transaction */ GIT_EXTERN(void) git_transaction_free(git_transaction *tx); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/status.h0000644000175000017500000003457314125111754017557 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_status_h__ #define INCLUDE_git_status_h__ #include "common.h" #include "types.h" #include "strarray.h" #include "diff.h" /** * @file git2/status.h * @brief Git file status routines * @defgroup git_status Git file status routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Status flags for a single file. * * A combination of these values will be returned to indicate the status of * a file. Status compares the working directory, the index, and the * current HEAD of the repository. The `GIT_STATUS_INDEX` set of flags * represents the status of file in the index relative to the HEAD, and the * `GIT_STATUS_WT` set of flags represent the status of the file in the * working directory relative to the index. */ typedef enum { GIT_STATUS_CURRENT = 0, GIT_STATUS_INDEX_NEW = (1u << 0), GIT_STATUS_INDEX_MODIFIED = (1u << 1), GIT_STATUS_INDEX_DELETED = (1u << 2), GIT_STATUS_INDEX_RENAMED = (1u << 3), GIT_STATUS_INDEX_TYPECHANGE = (1u << 4), GIT_STATUS_WT_NEW = (1u << 7), GIT_STATUS_WT_MODIFIED = (1u << 8), GIT_STATUS_WT_DELETED = (1u << 9), GIT_STATUS_WT_TYPECHANGE = (1u << 10), GIT_STATUS_WT_RENAMED = (1u << 11), GIT_STATUS_WT_UNREADABLE = (1u << 12), GIT_STATUS_IGNORED = (1u << 14), GIT_STATUS_CONFLICTED = (1u << 15), } git_status_t; /** * Function pointer to receive status on individual files * * `path` is the relative path to the file from the root of the repository. * * `status_flags` is a combination of `git_status_t` values that apply. * * `payload` is the value you passed to the foreach function as payload. */ typedef int GIT_CALLBACK(git_status_cb)( const char *path, unsigned int status_flags, void *payload); /** * Select the files on which to report status. * * With `git_status_foreach_ext`, this will control which changes get * callbacks. With `git_status_list_new`, these will control which * changes are included in the list. */ typedef enum { /** * The default. This roughly matches `git status --porcelain` regarding * which files are included and in what order. */ GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0, /** * Only gives status based on HEAD to index comparison, not looking at * working directory changes. */ GIT_STATUS_SHOW_INDEX_ONLY = 1, /** * Only gives status based on index to working directory comparison, * not comparing the index to the HEAD. */ GIT_STATUS_SHOW_WORKDIR_ONLY = 2, } git_status_show_t; /** * Flags to control status callbacks * * Calling `git_status_foreach()` is like calling the extended version * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED, * and GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS. Those options are bundled * together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline. */ typedef enum { /** * Says that callbacks should be made on untracked files. * These will only be made if the workdir files are included in the status * "show" option. */ GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0), /** * Says that ignored files get callbacks. * Again, these callbacks will only be made if the workdir files are * included in the status "show" option. */ GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1), /** * Indicates that callback should be made even on unmodified files. */ GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2), /** * Indicates that submodules should be skipped. * This only applies if there are no pending typechanges to the submodule * (either from or to another type). */ GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3), /** * Indicates that all files in untracked directories should be included. * Normally if an entire directory is new, then just the top-level * directory is included (with a trailing slash on the entry name). * This flag says to include all of the individual files in the directory * instead. */ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4), /** * Indicates that the given path should be treated as a literal path, * and not as a pathspec pattern. */ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5), /** * Indicates that the contents of ignored directories should be included * in the status. This is like doing `git ls-files -o -i --exclude-standard` * with core git. */ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6), /** * Indicates that rename detection should be processed between the head and * the index and enables the GIT_STATUS_INDEX_RENAMED as a possible status * flag. */ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7), /** * Indicates that rename detection should be run between the index and the * working directory and enabled GIT_STATUS_WT_RENAMED as a possible status * flag. */ GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8), /** * Overrides the native case sensitivity for the file system and forces * the output to be in case-sensitive order. */ GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9), /** * Overrides the native case sensitivity for the file system and forces * the output to be in case-insensitive order. */ GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10), /** * Iindicates that rename detection should include rewritten files. */ GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11), /** * Bypasses the default status behavior of doing a "soft" index reload * (i.e. reloading the index data if the file on disk has been modified * outside libgit2). */ GIT_STATUS_OPT_NO_REFRESH = (1u << 12), /** * Tells libgit2 to refresh the stat cache in the index for files that are * unchanged but have out of date stat einformation in the index. * It will result in less work being done on subsequent calls to get status. * This is mutually exclusive with the NO_REFRESH option. */ GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13), /** * Normally files that cannot be opened or read are ignored as * these are often transient files; this option will return * unreadable files as `GIT_STATUS_WT_UNREADABLE`. */ GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14), /** * Unreadable files will be detected and given the status * untracked instead of unreadable. */ GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15), } git_status_opt_t; #define GIT_STATUS_OPT_DEFAULTS \ (GIT_STATUS_OPT_INCLUDE_IGNORED | \ GIT_STATUS_OPT_INCLUDE_UNTRACKED | \ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) /** * Options to control how `git_status_foreach_ext()` will issue callbacks. * * Initialize with `GIT_STATUS_OPTIONS_INIT`. Alternatively, you can * use `git_status_options_init`. * */ typedef struct { /** * The struct version; pass `GIT_STATUS_OPTIONS_VERSION`. */ unsigned int version; /** * The `show` value is one of the `git_status_show_t` constants that * control which files to scan and in what order. */ git_status_show_t show; /** * The `flags` value is an OR'ed combination of the * `git_status_opt_t` values above. */ unsigned int flags; /** * The `pathspec` is an array of path patterns to match (using * fnmatch-style matching), or just an array of paths to match * exactly if `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified * in the flags. */ git_strarray pathspec; /** * The `baseline` is the tree to be used for comparison to the * working directory and index; defaults to HEAD. */ git_tree *baseline; } git_status_options; #define GIT_STATUS_OPTIONS_VERSION 1 #define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION} /** * Initialize git_status_options structure * * Initializes a `git_status_options` with default values. Equivalent to * creating an instance with `GIT_STATUS_OPTIONS_INIT`. * * @param opts The `git_status_options` struct to initialize. * @param version The struct version; pass `GIT_STATUS_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_status_options_init( git_status_options *opts, unsigned int version); /** * A status entry, providing the differences between the file as it exists * in HEAD and the index, and providing the differences between the index * and the working directory. * * The `status` value provides the status flags for this file. * * The `head_to_index` value provides detailed information about the * differences between the file in HEAD and the file in the index. * * The `index_to_workdir` value provides detailed information about the * differences between the file in the index and the file in the * working directory. */ typedef struct { git_status_t status; git_diff_delta *head_to_index; git_diff_delta *index_to_workdir; } git_status_entry; /** * Gather file statuses and run a callback for each one. * * The callback is passed the path of the file, the status (a combination of * the `git_status_t` values above) and the `payload` data pointer passed * into this function. * * If the callback returns a non-zero value, this function will stop looping * and return that value to caller. * * @param repo A repository object * @param callback The function to call on each file * @param payload Pointer to pass through to callback function * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach( git_repository *repo, git_status_cb callback, void *payload); /** * Gather file status information and run callbacks as requested. * * This is an extended version of the `git_status_foreach()` API that * allows for more granular control over which paths will be processed and * in what order. See the `git_status_options` structure for details * about the additional controls that this makes available. * * Note that if a `pathspec` is given in the `git_status_options` to filter * the status, then the results from rename detection (if you enable it) may * not be accurate. To do rename detection properly, this must be called * with no `pathspec` so that all files can be considered. * * @param repo Repository object * @param opts Status options structure * @param callback The function to call on each file * @param payload Pointer to pass through to callback function * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach_ext( git_repository *repo, const git_status_options *opts, git_status_cb callback, void *payload); /** * Get file status for a single file. * * This tries to get status for the filename that you give. If no files * match that name (in either the HEAD, index, or working directory), this * returns GIT_ENOTFOUND. * * If the name matches multiple files (for example, if the `path` names a * directory or if running on a case- insensitive filesystem and yet the * HEAD has two entries that both match the path), then this returns * GIT_EAMBIGUOUS because it cannot give correct results. * * This does not do any sort of rename detection. Renames require a set of * targets and because of the path filtering, there is not enough * information to check renames correctly. To check file status with rename * detection, there is no choice but to do a full `git_status_list_new` and * scan through looking for the path that you are interested in. * * @param status_flags Output combination of git_status_t values for file * @param repo A repository object * @param path The exact path to retrieve status for relative to the * repository working directory * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD, * index, and work tree, GIT_EAMBIGUOUS if `path` matches multiple files * or if it refers to a folder, and -1 on other errors. */ GIT_EXTERN(int) git_status_file( unsigned int *status_flags, git_repository *repo, const char *path); /** * Gather file status information and populate the `git_status_list`. * * Note that if a `pathspec` is given in the `git_status_options` to filter * the status, then the results from rename detection (if you enable it) may * not be accurate. To do rename detection properly, this must be called * with no `pathspec` so that all files can be considered. * * @param out Pointer to store the status results in * @param repo Repository object * @param opts Status options structure * @return 0 on success or error code */ GIT_EXTERN(int) git_status_list_new( git_status_list **out, git_repository *repo, const git_status_options *opts); /** * Gets the count of status entries in this list. * * If there are no changes in status (at least according the options given * when the status list was created), this can return 0. * * @param statuslist Existing status list object * @return the number of status entries */ GIT_EXTERN(size_t) git_status_list_entrycount( git_status_list *statuslist); /** * Get a pointer to one of the entries in the status list. * * The entry is not modifiable and should not be freed. * * @param statuslist Existing status list object * @param idx Position of the entry * @return Pointer to the entry; NULL if out of bounds */ GIT_EXTERN(const git_status_entry *) git_status_byindex( git_status_list *statuslist, size_t idx); /** * Free an existing status list * * @param statuslist Existing status list object */ GIT_EXTERN(void) git_status_list_free( git_status_list *statuslist); /** * Test if the ignore rules apply to a given file. * * This function checks the ignore rules to see if they would apply to the * given file. This indicates if the file would be ignored regardless of * whether the file is already in the index or committed to the repository. * * One way to think of this is if you were to do "git add ." on the * directory containing the file, would it be added or not? * * @param ignored Boolean returning 0 if the file is not ignored, 1 if it is * @param repo A repository object * @param path The file to check ignores for, rooted at the repo's workdir. * @return 0 if ignore rules could be processed for the file (regardless * of whether it exists or not), or an error < 0 if they could not. */ GIT_EXTERN(int) git_status_should_ignore( int *ignored, git_repository *repo, const char *path); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/cred_helpers.h0000644000175000017500000000062714125111754020664 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_cred_helpers_h__ #define INCLUDE_git_cred_helpers_h__ /* These declarations have moved. */ #ifndef GIT_DEPRECATE_HARD # include "git2/credential_helpers.h" #endif #endif git2r/src/libgit2/include/git2/refs.h0000644000175000017500000006646114125111754017174 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_refs_h__ #define INCLUDE_git_refs_h__ #include "common.h" #include "types.h" #include "oid.h" #include "strarray.h" /** * @file git2/refs.h * @brief Git reference management routines * @defgroup git_reference Git reference management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Lookup a reference by name in a repository. * * The returned reference must be freed by the user. * * The name will be checked for validity. * See `git_reference_symbolic_create()` for rules about valid names. * * @param out pointer to the looked-up reference * @param repo the repository to look up the reference * @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...) * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name); /** * Lookup a reference by name and resolve immediately to OID. * * This function provides a quick way to resolve a reference name straight * through to the object id that it refers to. This avoids having to * allocate or free any `git_reference` objects for simple situations. * * The name will be checked for validity. * See `git_reference_symbolic_create()` for rules about valid names. * * @param out Pointer to oid to be filled in * @param repo The repository in which to look up the reference * @param name The long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...) * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_reference_name_to_id( git_oid *out, git_repository *repo, const char *name); /** * Lookup a reference by DWIMing its short name * * Apply the git precendence rules to the given shorthand to determine * which reference the user is referring to. * * @param out pointer in which to store the reference * @param repo the repository in which to look * @param shorthand the short name for the reference * @return 0 or an error code */ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand); /** * Conditionally create a new symbolic reference. * * A symbolic reference is a reference name that refers to another * reference name. If the other name moves, the symbolic name will move, * too. As a simple example, the "HEAD" reference might refer to * "refs/heads/master" while on the "master" branch of a repository. * * The symbolic reference will be created in the repository and written to * the disk. The generated reference object must be freed by the user. * * Valid reference names must follow one of two patterns: * * 1. Top-level names must contain only capital letters and underscores, * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). * 2. Names prefixed with "refs/" can be almost anything. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * This function will return an error if a reference already exists with the * given name unless `force` is true, in which case it will be overwritten. * * The message for the reflog will be ignored if the reference does * not belong in the standard set (HEAD, branches and remote-tracking * branches) and it does not have a reflog. * * It will return GIT_EMODIFIED if the reference's value at the time * of updating does not match the one passed through `current_value` * (i.e. if the ref has changed since the user read it). * * If `current_value` is all zeros, this function will return GIT_EMODIFIED * if the ref already exists. * * @param out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param target The target of the reference * @param force Overwrite existing references * @param current_value The expected value of the reference when updating * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC, GIT_EMODIFIED or an error code */ GIT_EXTERN(int) git_reference_symbolic_create_matching(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *current_value, const char *log_message); /** * Create a new symbolic reference. * * A symbolic reference is a reference name that refers to another * reference name. If the other name moves, the symbolic name will move, * too. As a simple example, the "HEAD" reference might refer to * "refs/heads/master" while on the "master" branch of a repository. * * The symbolic reference will be created in the repository and written to * the disk. The generated reference object must be freed by the user. * * Valid reference names must follow one of two patterns: * * 1. Top-level names must contain only capital letters and underscores, * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). * 2. Names prefixed with "refs/" can be almost anything. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * This function will return an error if a reference already exists with the * given name unless `force` is true, in which case it will be overwritten. * * The message for the reflog will be ignored if the reference does * not belong in the standard set (HEAD, branches and remote-tracking * branches) and it does not have a reflog. * * @param out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param target The target of the reference * @param force Overwrite existing references * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *log_message); /** * Create a new direct reference. * * A direct reference (also called an object id reference) refers directly * to a specific object id (a.k.a. OID or SHA) in the repository. The id * permanently refers to the object (although the reference itself can be * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0" * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977. * * The direct reference will be created in the repository and written to * the disk. The generated reference object must be freed by the user. * * Valid reference names must follow one of two patterns: * * 1. Top-level names must contain only capital letters and underscores, * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). * 2. Names prefixed with "refs/" can be almost anything. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * This function will return an error if a reference already exists with the * given name unless `force` is true, in which case it will be overwritten. * * The message for the reflog will be ignored if the reference does * not belong in the standard set (HEAD, branches and remote-tracking * branches) and it does not have a reflog. * * @param out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param id The object id pointed to by the reference. * @param force Overwrite existing references * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const char *log_message); /** * Conditionally create new direct reference * * A direct reference (also called an object id reference) refers directly * to a specific object id (a.k.a. OID or SHA) in the repository. The id * permanently refers to the object (although the reference itself can be * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0" * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977. * * The direct reference will be created in the repository and written to * the disk. The generated reference object must be freed by the user. * * Valid reference names must follow one of two patterns: * * 1. Top-level names must contain only capital letters and underscores, * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). * 2. Names prefixed with "refs/" can be almost anything. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * This function will return an error if a reference already exists with the * given name unless `force` is true, in which case it will be overwritten. * * The message for the reflog will be ignored if the reference does * not belong in the standard set (HEAD, branches and remote-tracking * branches) and it does not have a reflog. * * It will return GIT_EMODIFIED if the reference's value at the time * of updating does not match the one passed through `current_id` * (i.e. if the ref has changed since the user read it). * * @param out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param id The object id pointed to by the reference. * @param force Overwrite existing references * @param current_id The expected value of the reference at the time of update * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EMODIFIED if the value of the reference * has changed, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_create_matching(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const git_oid *current_id, const char *log_message); /** * Get the OID pointed to by a direct reference. * * Only available if the reference is direct (i.e. an object id reference, * not a symbolic one). * * To find the OID of a symbolic ref, call `git_reference_resolve()` and * then this function (or maybe use `git_reference_name_to_id()` to * directly resolve a reference name all the way through to an OID). * * @param ref The reference * @return a pointer to the oid if available, NULL otherwise */ GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref); /** * Return the peeled OID target of this reference. * * This peeled OID only applies to direct references that point to * a hard Tag object: it is the result of peeling such Tag. * * @param ref The reference * @return a pointer to the oid if available, NULL otherwise */ GIT_EXTERN(const git_oid *) git_reference_target_peel(const git_reference *ref); /** * Get full name to the reference pointed to by a symbolic reference. * * Only available if the reference is symbolic. * * @param ref The reference * @return a pointer to the name if available, NULL otherwise */ GIT_EXTERN(const char *) git_reference_symbolic_target(const git_reference *ref); /** * Get the type of a reference. * * Either direct (GIT_REFERENCE_DIRECT) or symbolic (GIT_REFERENCE_SYMBOLIC) * * @param ref The reference * @return the type */ GIT_EXTERN(git_reference_t) git_reference_type(const git_reference *ref); /** * Get the full name of a reference. * * See `git_reference_symbolic_create()` for rules about valid names. * * @param ref The reference * @return the full name for the ref */ GIT_EXTERN(const char *) git_reference_name(const git_reference *ref); /** * Resolve a symbolic reference to a direct reference. * * This method iteratively peels a symbolic reference until it resolves to * a direct reference to an OID. * * The peeled reference is returned in the `resolved_ref` argument, and * must be freed manually once it's no longer needed. * * If a direct reference is passed as an argument, a copy of that * reference is returned. This copy must be manually freed too. * * @param out Pointer to the peeled reference * @param ref The reference * @return 0 or an error code */ GIT_EXTERN(int) git_reference_resolve(git_reference **out, const git_reference *ref); /** * Get the repository where a reference resides. * * @param ref The reference * @return a pointer to the repo */ GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref); /** * Create a new reference with the same name as the given reference but a * different symbolic target. The reference must be a symbolic reference, * otherwise this will fail. * * The new reference will be written to disk, overwriting the given reference. * * The target name will be checked for validity. * See `git_reference_symbolic_create()` for rules about valid names. * * The message for the reflog will be ignored if the reference does * not belong in the standard set (HEAD, branches and remote-tracking * branches) and it does not have a reflog. * * @param out Pointer to the newly created reference * @param ref The reference * @param target The new target for the reference * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_symbolic_set_target( git_reference **out, git_reference *ref, const char *target, const char *log_message); /** * Conditionally create a new reference with the same name as the given reference but a * different OID target. The reference must be a direct reference, otherwise * this will fail. * * The new reference will be written to disk, overwriting the given reference. * * @param out Pointer to the newly created reference * @param ref The reference * @param id The new target OID for the reference * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EMODIFIED if the value of the reference * has changed since it was read, or an error code */ GIT_EXTERN(int) git_reference_set_target( git_reference **out, git_reference *ref, const git_oid *id, const char *log_message); /** * Rename an existing reference. * * This method works for both direct and symbolic references. * * The new name will be checked for validity. * See `git_reference_symbolic_create()` for rules about valid names. * * If the `force` flag is not enabled, and there's already * a reference with the given name, the renaming will fail. * * IMPORTANT: * The user needs to write a proper reflog entry if the * reflog is enabled for the repository. We only rename * the reflog if it exists. * * @param ref The reference to rename * @param new_name The new name for the reference * @param force Overwrite an existing reference * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code * */ GIT_EXTERN(int) git_reference_rename( git_reference **new_ref, git_reference *ref, const char *new_name, int force, const char *log_message); /** * Delete an existing reference. * * This method works for both direct and symbolic references. The reference * will be immediately removed on disk but the memory will not be freed. * Callers must call `git_reference_free`. * * This function will return an error if the reference has changed * from the time it was looked up. * * @param ref The reference to remove * @return 0, GIT_EMODIFIED or an error code */ GIT_EXTERN(int) git_reference_delete(git_reference *ref); /** * Delete an existing reference by name * * This method removes the named reference from the repository without * looking at its old value. * * @param name The reference to remove * @return 0 or an error code */ GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name); /** * Fill a list with all the references that can be found in a repository. * * The string array will be filled with the names of all references; these * values are owned by the user and should be free'd manually when no * longer needed, using `git_strarray_free()`. * * @param array Pointer to a git_strarray structure where * the reference names will be stored * @param repo Repository where to find the refs * @return 0 or an error code */ GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo); /** * Callback used to iterate over references * * @see git_reference_foreach * * @param reference The reference object * @param payload Payload passed to git_reference_foreach * @return non-zero to terminate the iteration */ typedef int GIT_CALLBACK(git_reference_foreach_cb)(git_reference *reference, void *payload); /** * Callback used to iterate over reference names * * @see git_reference_foreach_name * * @param name The reference name * @param payload Payload passed to git_reference_foreach_name * @return non-zero to terminate the iteration */ typedef int GIT_CALLBACK(git_reference_foreach_name_cb)(const char *name, void *payload); /** * Perform a callback on each reference in the repository. * * The `callback` function will be called for each reference in the * repository, receiving the reference object and the `payload` value * passed to this method. Returning a non-zero value from the callback * will terminate the iteration. * * Note that the callback function is responsible to call `git_reference_free` * on each reference passed to it. * * @param repo Repository where to find the refs * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_reference_foreach( git_repository *repo, git_reference_foreach_cb callback, void *payload); /** * Perform a callback on the fully-qualified name of each reference. * * The `callback` function will be called for each reference in the * repository, receiving the name of the reference and the `payload` value * passed to this method. Returning a non-zero value from the callback * will terminate the iteration. * * @param repo Repository where to find the refs * @param callback Function which will be called for every listed ref name * @param payload Additional data to pass to the callback * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_reference_foreach_name( git_repository *repo, git_reference_foreach_name_cb callback, void *payload); /** * Create a copy of an existing reference. * * Call `git_reference_free` to free the data. * * @param dest pointer where to store the copy * @param source object to copy * @return 0 or an error code */ GIT_EXTERN(int) git_reference_dup(git_reference **dest, git_reference *source); /** * Free the given reference. * * @param ref git_reference */ GIT_EXTERN(void) git_reference_free(git_reference *ref); /** * Compare two references. * * @param ref1 The first git_reference * @param ref2 The second git_reference * @return 0 if the same, else a stable but meaningless ordering. */ GIT_EXTERN(int) git_reference_cmp( const git_reference *ref1, const git_reference *ref2); /** * Create an iterator for the repo's references * * @param out pointer in which to store the iterator * @param repo the repository * @return 0 or an error code */ GIT_EXTERN(int) git_reference_iterator_new( git_reference_iterator **out, git_repository *repo); /** * Create an iterator for the repo's references that match the * specified glob * * @param out pointer in which to store the iterator * @param repo the repository * @param glob the glob to match against the reference names * @return 0 or an error code */ GIT_EXTERN(int) git_reference_iterator_glob_new( git_reference_iterator **out, git_repository *repo, const char *glob); /** * Get the next reference * * @param out pointer in which to store the reference * @param iter the iterator * @return 0, GIT_ITEROVER if there are no more; or an error code */ GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter); /** * Get the next reference's name * * This function is provided for convenience in case only the names * are interesting as it avoids the allocation of the `git_reference` * object which `git_reference_next()` needs. * * @param out pointer in which to store the string * @param iter the iterator * @return 0, GIT_ITEROVER if there are no more; or an error code */ GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter); /** * Free the iterator and its associated resources * * @param iter the iterator to free */ GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); /** * Perform a callback on each reference in the repository whose name * matches the given pattern. * * This function acts like `git_reference_foreach()` with an additional * pattern match being applied to the reference name before issuing the * callback function. See that function for more information. * * The pattern is matched using fnmatch or "glob" style where a '*' matches * any sequence of letters, a '?' matches any letter, and square brackets * can be used to define character ranges (such as "[0-9]" for digits). * * @param repo Repository where to find the refs * @param glob Pattern to match (fnmatch-style) against reference name. * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_reference_foreach_glob( git_repository *repo, const char *glob, git_reference_foreach_name_cb callback, void *payload); /** * Check if a reflog exists for the specified reference. * * @param repo the repository * @param refname the reference's name * @return 0 when no reflog can be found, 1 when it exists; * otherwise an error code. */ GIT_EXTERN(int) git_reference_has_log(git_repository *repo, const char *refname); /** * Ensure there is a reflog for a particular reference. * * Make sure that successive updates to the reference will append to * its log. * * @param repo the repository * @param refname the reference's name * @return 0 or an error code. */ GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname); /** * Check if a reference is a local branch. * * @param ref A git reference * * @return 1 when the reference lives in the refs/heads * namespace; 0 otherwise. */ GIT_EXTERN(int) git_reference_is_branch(const git_reference *ref); /** * Check if a reference is a remote tracking branch * * @param ref A git reference * * @return 1 when the reference lives in the refs/remotes * namespace; 0 otherwise. */ GIT_EXTERN(int) git_reference_is_remote(const git_reference *ref); /** * Check if a reference is a tag * * @param ref A git reference * * @return 1 when the reference lives in the refs/tags * namespace; 0 otherwise. */ GIT_EXTERN(int) git_reference_is_tag(const git_reference *ref); /** * Check if a reference is a note * * @param ref A git reference * * @return 1 when the reference lives in the refs/notes * namespace; 0 otherwise. */ GIT_EXTERN(int) git_reference_is_note(const git_reference *ref); /** * Normalization options for reference lookup */ typedef enum { /** * No particular normalization. */ GIT_REFERENCE_FORMAT_NORMAL = 0u, /** * Control whether one-level refnames are accepted * (i.e., refnames that do not contain multiple /-separated * components). Those are expected to be written only using * uppercase letters and underscore (FETCH_HEAD, ...) */ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL = (1u << 0), /** * Interpret the provided name as a reference pattern for a * refspec (as used with remote repositories). If this option * is enabled, the name is allowed to contain a single * () * in place of a one full pathname component * (e.g., foo//bar but not foo/bar). */ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN = (1u << 1), /** * Interpret the name as part of a refspec in shorthand form * so the `ONELEVEL` naming rules aren't enforced and 'master' * becomes a valid name. */ GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND = (1u << 2), } git_reference_format_t; /** * Normalize reference name and check validity. * * This will normalize the reference name by removing any leading slash * '/' characters and collapsing runs of adjacent slashes between name * components into a single slash. * * Once normalized, if the reference name is valid, it will be returned in * the user allocated buffer. * * See `git_reference_symbolic_create()` for rules about valid names. * * @param buffer_out User allocated buffer to store normalized name * @param buffer_size Size of buffer_out * @param name Reference name to be checked. * @param flags Flags to constrain name validation rules - see the * GIT_REFERENCE_FORMAT constants above. * @return 0 on success, GIT_EBUFS if buffer is too small, GIT_EINVALIDSPEC * or an error code. */ GIT_EXTERN(int) git_reference_normalize_name( char *buffer_out, size_t buffer_size, const char *name, unsigned int flags); /** * Recursively peel reference until object of the specified type is found. * * The retrieved `peeled` object is owned by the repository * and should be closed with the `git_object_free` method. * * If you pass `GIT_OBJECT_ANY` as the target type, then the object * will be peeled until a non-tag object is met. * * @param out Pointer to the peeled git_object * @param ref The reference to be processed * @param type The type of the requested object (GIT_OBJECT_COMMIT, * GIT_OBJECT_TAG, GIT_OBJECT_TREE, GIT_OBJECT_BLOB or GIT_OBJECT_ANY). * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code */ GIT_EXTERN(int) git_reference_peel( git_object **out, const git_reference *ref, git_object_t type); /** * Ensure the reference name is well-formed. * * Valid reference names must follow one of two patterns: * * 1. Top-level names must contain only capital letters and underscores, * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). * 2. Names prefixed with "refs/" can be almost anything. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * @param valid output pointer to set with validity of given reference name * @param refname name to be checked. * @return 0 on success or an error code */ GIT_EXTERN(int) git_reference_name_is_valid(int *valid, const char *refname); /** * Get the reference's short name * * This will transform the reference name into a name "human-readable" * version. If no shortname is appropriate, it will return the full * name. * * The memory is owned by the reference and must not be freed. * * @param ref a reference * @return the human-readable version of the name */ GIT_EXTERN(const char *) git_reference_shorthand(const git_reference *ref); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/net.h0000644000175000017500000000211214125111754017002 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_net_h__ #define INCLUDE_git_net_h__ #include "common.h" #include "oid.h" #include "types.h" /** * @file git2/net.h * @brief Git networking declarations * @ingroup Git * @{ */ GIT_BEGIN_DECL #define GIT_DEFAULT_PORT "9418" /** * Direction of the connection. * * We need this because we need to know whether we should call * git-upload-pack or git-receive-pack on the remote end when get_refs * gets called. */ typedef enum { GIT_DIRECTION_FETCH = 0, GIT_DIRECTION_PUSH = 1 } git_direction; /** * Description of a reference advertised by a remote server, given out * on `ls` calls. */ struct git_remote_head { int local; /* available locally */ git_oid oid; git_oid loid; char *name; /** * If the server send a symref mapping for this ref, this will * point to the target. */ char *symref_target; }; /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/stash.h0000644000175000017500000001702414125111754017346 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_stash_h__ #define INCLUDE_git_stash_h__ #include "common.h" #include "types.h" #include "checkout.h" /** * @file git2/stash.h * @brief Git stash management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Stash flags */ typedef enum { /** * No option, default */ GIT_STASH_DEFAULT = 0, /** * All changes already added to the index are left intact in * the working directory */ GIT_STASH_KEEP_INDEX = (1 << 0), /** * All untracked files are also stashed and then cleaned up * from the working directory */ GIT_STASH_INCLUDE_UNTRACKED = (1 << 1), /** * All ignored files are also stashed and then cleaned up from * the working directory */ GIT_STASH_INCLUDE_IGNORED = (1 << 2), } git_stash_flags; /** * Save the local modifications to a new stash. * * @param out Object id of the commit containing the stashed state. * This commit is also the target of the direct reference refs/stash. * * @param repo The owning repository. * * @param stasher The identity of the person performing the stashing. * * @param message Optional description along with the stashed state. * * @param flags Flags to control the stashing process. (see GIT_STASH_* above) * * @return 0 on success, GIT_ENOTFOUND where there's nothing to stash, * or error code. */ GIT_EXTERN(int) git_stash_save( git_oid *out, git_repository *repo, const git_signature *stasher, const char *message, uint32_t flags); /** Stash application flags. */ typedef enum { GIT_STASH_APPLY_DEFAULT = 0, /* Try to reinstate not only the working tree's changes, * but also the index's changes. */ GIT_STASH_APPLY_REINSTATE_INDEX = (1 << 0), } git_stash_apply_flags; /** Stash apply progression states */ typedef enum { GIT_STASH_APPLY_PROGRESS_NONE = 0, /** Loading the stashed data from the object database. */ GIT_STASH_APPLY_PROGRESS_LOADING_STASH, /** The stored index is being analyzed. */ GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX, /** The modified files are being analyzed. */ GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED, /** The untracked and ignored files are being analyzed. */ GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED, /** The untracked files are being written to disk. */ GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED, /** The modified files are being written to disk. */ GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED, /** The stash was applied successfully. */ GIT_STASH_APPLY_PROGRESS_DONE, } git_stash_apply_progress_t; /** * Stash application progress notification function. * Return 0 to continue processing, or a negative value to * abort the stash application. */ typedef int GIT_CALLBACK(git_stash_apply_progress_cb)( git_stash_apply_progress_t progress, void *payload); /** * Stash application options structure * * Initialize with `GIT_STASH_APPLY_OPTIONS_INIT`. Alternatively, you can * use `git_stash_apply_options_init`. * */ typedef struct git_stash_apply_options { unsigned int version; /** See `git_stash_apply_flags`, above. */ uint32_t flags; /** Options to use when writing files to the working directory. */ git_checkout_options checkout_options; /** Optional callback to notify the consumer of application progress. */ git_stash_apply_progress_cb progress_cb; void *progress_payload; } git_stash_apply_options; #define GIT_STASH_APPLY_OPTIONS_VERSION 1 #define GIT_STASH_APPLY_OPTIONS_INIT { \ GIT_STASH_APPLY_OPTIONS_VERSION, \ GIT_STASH_APPLY_DEFAULT, \ GIT_CHECKOUT_OPTIONS_INIT } /** * Initialize git_stash_apply_options structure * * Initializes a `git_stash_apply_options` with default values. Equivalent to * creating an instance with `GIT_STASH_APPLY_OPTIONS_INIT`. * * @param opts The `git_stash_apply_options` struct to initialize. * @param version The struct version; pass `GIT_STASH_APPLY_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_stash_apply_options_init( git_stash_apply_options *opts, unsigned int version); /** * Apply a single stashed state from the stash list. * * If local changes in the working directory conflict with changes in the * stash then GIT_EMERGECONFLICT will be returned. In this case, the index * will always remain unmodified and all files in the working directory will * remain unmodified. However, if you are restoring untracked files or * ignored files and there is a conflict when applying the modified files, * then those files will remain in the working directory. * * If passing the GIT_STASH_APPLY_REINSTATE_INDEX flag and there would be * conflicts when reinstating the index, the function will return * GIT_EMERGECONFLICT and both the working directory and index will be left * unmodified. * * Note that a minimum checkout strategy of `GIT_CHECKOUT_SAFE` is implied. * * @param repo The owning repository. * @param index The position within the stash list. 0 points to the * most recent stashed state. * @param options Optional options to control how stashes are applied. * * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the * given index, GIT_EMERGECONFLICT if changes exist in the working * directory, or an error code */ GIT_EXTERN(int) git_stash_apply( git_repository *repo, size_t index, const git_stash_apply_options *options); /** * This is a callback function you can provide to iterate over all the * stashed states that will be invoked per entry. * * @param index The position within the stash list. 0 points to the * most recent stashed state. * @param message The stash message. * @param stash_id The commit oid of the stashed state. * @param payload Extra parameter to callback function. * @return 0 to continue iterating or non-zero to stop. */ typedef int GIT_CALLBACK(git_stash_cb)( size_t index, const char *message, const git_oid *stash_id, void *payload); /** * Loop over all the stashed states and issue a callback for each one. * * If the callback returns a non-zero value, this will stop looping. * * @param repo Repository where to find the stash. * * @param callback Callback to invoke per found stashed state. The most * recent stash state will be enumerated first. * * @param payload Extra parameter to callback function. * * @return 0 on success, non-zero callback return value, or error code. */ GIT_EXTERN(int) git_stash_foreach( git_repository *repo, git_stash_cb callback, void *payload); /** * Remove a single stashed state from the stash list. * * @param repo The owning repository. * * @param index The position within the stash list. 0 points to the * most recent stashed state. * * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given * index, or error code. */ GIT_EXTERN(int) git_stash_drop( git_repository *repo, size_t index); /** * Apply a single stashed state from the stash list and remove it from the list * if successful. * * @param repo The owning repository. * @param index The position within the stash list. 0 points to the * most recent stashed state. * @param options Optional options to control how stashes are applied. * * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given * index, or error code. (see git_stash_apply() above for details) */ GIT_EXTERN(int) git_stash_pop( git_repository *repo, size_t index, const git_stash_apply_options *options); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/config.h0000644000175000017500000006211714125111754017474 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_config_h__ #define INCLUDE_git_config_h__ #include "common.h" #include "types.h" #include "buffer.h" /** * @file git2/config.h * @brief Git config management routines * @defgroup git_config Git config management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Priority level of a config file. * These priority levels correspond to the natural escalation logic * (from higher to lower) when searching for config entries in git.git. * * git_config_open_default() and git_repository_config() honor those * priority levels as well. */ typedef enum { /** System-wide on Windows, for compatibility with portable git */ GIT_CONFIG_LEVEL_PROGRAMDATA = 1, /** System-wide configuration file; /etc/gitconfig on Linux systems */ GIT_CONFIG_LEVEL_SYSTEM = 2, /** XDG compatible configuration file; typically ~/.config/git/config */ GIT_CONFIG_LEVEL_XDG = 3, /** User-specific configuration file (also called Global configuration * file); typically ~/.gitconfig */ GIT_CONFIG_LEVEL_GLOBAL = 4, /** Repository specific configuration file; $WORK_DIR/.git/config on * non-bare repos */ GIT_CONFIG_LEVEL_LOCAL = 5, /** Application specific configuration file; freely defined by applications */ GIT_CONFIG_LEVEL_APP = 6, /** Represents the highest level available config file (i.e. the most * specific config file available that actually is loaded) */ GIT_CONFIG_HIGHEST_LEVEL = -1, } git_config_level_t; /** * An entry in a configuration file */ typedef struct git_config_entry { const char *name; /**< Name of the entry (normalised) */ const char *value; /**< String value of the entry */ unsigned int include_depth; /**< Depth of includes where this variable was found */ git_config_level_t level; /**< Which config file this was found in */ void GIT_CALLBACK(free)(struct git_config_entry *entry); /**< Free function for this entry */ void *payload; /**< Opaque value for the free function. Do not read or write */ } git_config_entry; /** * Free a config entry */ GIT_EXTERN(void) git_config_entry_free(git_config_entry *); /** * A config enumeration callback * * @param entry the entry currently being enumerated * @param payload a user-specified pointer */ typedef int GIT_CALLBACK(git_config_foreach_cb)(const git_config_entry *entry, void *payload); /** * An opaque structure for a configuration iterator */ typedef struct git_config_iterator git_config_iterator; /** * Config var type */ typedef enum { GIT_CONFIGMAP_FALSE = 0, GIT_CONFIGMAP_TRUE = 1, GIT_CONFIGMAP_INT32, GIT_CONFIGMAP_STRING } git_configmap_t; /** * Mapping from config variables to values. */ typedef struct { git_configmap_t type; const char *str_match; int map_value; } git_configmap; /** * Locate the path to the global configuration file * * The user or global configuration file is usually * located in `$HOME/.gitconfig`. * * This method will try to guess the full path to that * file, if the file exists. The returned path * may be used on any `git_config` call to load the * global configuration file. * * This method will not guess the path to the xdg compatible * config file (.config/git/config). * * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a global configuration file has been found. Its path will be stored in `out`. */ GIT_EXTERN(int) git_config_find_global(git_buf *out); /** * Locate the path to the global xdg compatible configuration file * * The xdg compatible configuration file is usually * located in `$HOME/.config/git/config`. * * This method will try to guess the full path to that * file, if the file exists. The returned path * may be used on any `git_config` call to load the * xdg compatible configuration file. * * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a xdg compatible configuration file has been * found. Its path will be stored in `out`. */ GIT_EXTERN(int) git_config_find_xdg(git_buf *out); /** * Locate the path to the system configuration file * * If /etc/gitconfig doesn't exist, it will look for * %PROGRAMFILES%\Git\etc\gitconfig. * * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a system configuration file has been * found. Its path will be stored in `out`. */ GIT_EXTERN(int) git_config_find_system(git_buf *out); /** * Locate the path to the configuration file in ProgramData * * Look for the file in %PROGRAMDATA%\Git\config used by portable git. * * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a ProgramData configuration file has been * found. Its path will be stored in `out`. */ GIT_EXTERN(int) git_config_find_programdata(git_buf *out); /** * Open the global, XDG and system configuration files * * Utility wrapper that finds the global, XDG and system configuration files * and opens them into a single prioritized config object that can be * used when accessing default config data outside a repository. * * @param out Pointer to store the config instance * @return 0 or an error code */ GIT_EXTERN(int) git_config_open_default(git_config **out); /** * Allocate a new configuration object * * This object is empty, so you have to add a file to it before you * can do anything with it. * * @param out pointer to the new configuration * @return 0 or an error code */ GIT_EXTERN(int) git_config_new(git_config **out); /** * Add an on-disk config file instance to an existing config * * The on-disk file pointed at by `path` will be opened and * parsed; it's expected to be a native Git config file following * the default Git config syntax (see man git-config). * * If the file does not exist, the file will still be added and it * will be created the first time we write to it. * * Note that the configuration object will free the file * automatically. * * Further queries on this config object will access each * of the config file instances in order (instances with * a higher priority level will be accessed first). * * @param cfg the configuration to add the file to * @param path path to the configuration file to add * @param level the priority level of the backend * @param force replace config file at the given priority level * @param repo optional repository to allow parsing of * conditional includes * @return 0 on success, GIT_EEXISTS when adding more than one file * for a given priority level (and force_replace set to 0), * GIT_ENOTFOUND when the file doesn't exist or error code */ GIT_EXTERN(int) git_config_add_file_ondisk( git_config *cfg, const char *path, git_config_level_t level, const git_repository *repo, int force); /** * Create a new config instance containing a single on-disk file * * This method is a simple utility wrapper for the following sequence * of calls: * - git_config_new * - git_config_add_file_ondisk * * @param out The configuration instance to create * @param path Path to the on-disk file to open * @return 0 on success, or an error code */ GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path); /** * Build a single-level focused config object from a multi-level one. * * The returned config object can be used to perform get/set/delete operations * on a single specific level. * * Getting several times the same level from the same parent multi-level config * will return different config instances, but containing the same config_file * instance. * * @param out The configuration instance to create * @param parent Multi-level config to search for the given level * @param level Configuration level to search for * @return 0, GIT_ENOTFOUND if the passed level cannot be found in the * multi-level parent config, or an error code */ GIT_EXTERN(int) git_config_open_level( git_config **out, const git_config *parent, git_config_level_t level); /** * Open the global/XDG configuration file according to git's rules * * Git allows you to store your global configuration at * `$HOME/.gitconfig` or `$XDG_CONFIG_HOME/git/config`. For backwards * compatibility, the XDG file shouldn't be used unless the use has * created it explicitly. With this function you'll open the correct * one to write to. * * @param out pointer in which to store the config object * @param config the config object in which to look */ GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config); /** * Create a snapshot of the configuration * * Create a snapshot of the current state of a configuration, which * allows you to look into a consistent view of the configuration for * looking up complex values (e.g. a remote, submodule). * * The string returned when querying such a config object is valid * until it is freed. * * @param out pointer in which to store the snapshot config object * @param config configuration to snapshot * @return 0 or an error code */ GIT_EXTERN(int) git_config_snapshot(git_config **out, git_config *config); /** * Free the configuration and its associated memory and files * * @param cfg the configuration to free */ GIT_EXTERN(void) git_config_free(git_config *cfg); /** * Get the git_config_entry of a config variable. * * Free the git_config_entry after use with `git_config_entry_free()`. * * @param out pointer to the variable git_config_entry * @param cfg where to look for the variable * @param name the variable's name * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_entry( git_config_entry **out, const git_config *cfg, const char *name); /** * Get the value of an integer config variable. * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The * first occurrence of the variable will be returned here. * * @param out pointer to the variable where the value should be stored * @param cfg where to look for the variable * @param name the variable's name * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_int32(int32_t *out, const git_config *cfg, const char *name); /** * Get the value of a long integer config variable. * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The * first occurrence of the variable will be returned here. * * @param out pointer to the variable where the value should be stored * @param cfg where to look for the variable * @param name the variable's name * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_int64(int64_t *out, const git_config *cfg, const char *name); /** * Get the value of a boolean config variable. * * This function uses the usual C convention of 0 being false and * anything else true. * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The * first occurrence of the variable will be returned here. * * @param out pointer to the variable where the value should be stored * @param cfg where to look for the variable * @param name the variable's name * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char *name); /** * Get the value of a path config variable. * * A leading '~' will be expanded to the global search path (which * defaults to the user's home directory but can be overridden via * `git_libgit2_opts()`. * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The * first occurrence of the variable will be returned here. * * @param out the buffer in which to store the result * @param cfg where to look for the variable * @param name the variable's name * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_path(git_buf *out, const git_config *cfg, const char *name); /** * Get the value of a string config variable. * * This function can only be used on snapshot config objects. The * string is owned by the config and should not be freed by the * user. The pointer will be valid until the config is freed. * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The * first occurrence of the variable will be returned here. * * @param out pointer to the string * @param cfg where to look for the variable * @param name the variable's name * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name); /** * Get the value of a string config variable. * * The value of the config will be copied into the buffer. * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The * first occurrence of the variable will be returned here. * * @param out buffer in which to store the string * @param cfg where to look for the variable * @param name the variable's name * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_string_buf(git_buf *out, const git_config *cfg, const char *name); /** * Get each value of a multivar in a foreach callback * * The callback will be called on each variable found * * The regular expression is applied case-sensitively on the normalized form of * the variable name: the section and variable parts are lower-cased. The * subsection is left unchanged. * * @param cfg where to look for the variable * @param name the variable's name * @param regexp regular expression to filter which variables we're * interested in. Use NULL to indicate all * @param callback the function to be called on each value of the variable * @param payload opaque pointer to pass to the callback */ GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload); /** * Get each value of a multivar * * The regular expression is applied case-sensitively on the normalized form of * the variable name: the section and variable parts are lower-cased. The * subsection is left unchanged. * * @param out pointer to store the iterator * @param cfg where to look for the variable * @param name the variable's name * @param regexp regular expression to filter which variables we're * interested in. Use NULL to indicate all */ GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp); /** * Return the current entry and advance the iterator * * The pointers returned by this function are valid until the iterator * is freed. * * @param entry pointer to store the entry * @param iter the iterator * @return 0 or an error code. GIT_ITEROVER if the iteration has completed */ GIT_EXTERN(int) git_config_next(git_config_entry **entry, git_config_iterator *iter); /** * Free a config iterator * * @param iter the iterator to free */ GIT_EXTERN(void) git_config_iterator_free(git_config_iterator *iter); /** * Set the value of an integer config variable in the config file * with the highest level (usually the local one). * * @param cfg where to look for the variable * @param name the variable's name * @param value Integer value for the variable * @return 0 or an error code */ GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t value); /** * Set the value of a long integer config variable in the config file * with the highest level (usually the local one). * * @param cfg where to look for the variable * @param name the variable's name * @param value Long integer value for the variable * @return 0 or an error code */ GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t value); /** * Set the value of a boolean config variable in the config file * with the highest level (usually the local one). * * @param cfg where to look for the variable * @param name the variable's name * @param value the value to store * @return 0 or an error code */ GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value); /** * Set the value of a string config variable in the config file * with the highest level (usually the local one). * * A copy of the string is made and the user is free to use it * afterwards. * * @param cfg where to look for the variable * @param name the variable's name * @param value the string to store. * @return 0 or an error code */ GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value); /** * Set a multivar in the local config file. * * The regular expression is applied case-sensitively on the value. * * @param cfg where to look for the variable * @param name the variable's name * @param regexp a regular expression to indicate which values to replace * @param value the new value. */ GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); /** * Delete a config variable from the config file * with the highest level (usually the local one). * * @param cfg the configuration * @param name the variable to delete */ GIT_EXTERN(int) git_config_delete_entry(git_config *cfg, const char *name); /** * Deletes one or several entries from a multivar in the local config file. * * The regular expression is applied case-sensitively on the value. * * @param cfg where to look for the variables * @param name the variable's name * @param regexp a regular expression to indicate which values to delete * * @return 0 or an error code */ GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp); /** * Perform an operation on each config variable. * * The callback receives the normalized name and value of each variable * in the config backend, and the data pointer passed to this function. * If the callback returns a non-zero value, the function stops iterating * and returns that value to the caller. * * The pointers passed to the callback are only valid as long as the * iteration is ongoing. * * @param cfg where to get the variables from * @param callback the function to call on each variable * @param payload the data to pass to the callback * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_config_foreach( const git_config *cfg, git_config_foreach_cb callback, void *payload); /** * Iterate over all the config variables * * Use `git_config_next` to advance the iteration and * `git_config_iterator_free` when done. * * @param out pointer to store the iterator * @param cfg where to ge the variables from */ GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg); /** * Iterate over all the config variables whose name matches a pattern * * Use `git_config_next` to advance the iteration and * `git_config_iterator_free` when done. * * The regular expression is applied case-sensitively on the normalized form of * the variable name: the section and variable parts are lower-cased. The * subsection is left unchanged. * * @param out pointer to store the iterator * @param cfg where to ge the variables from * @param regexp regular expression to match the names */ GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp); /** * Perform an operation on each config variable matching a regular expression. * * This behaves like `git_config_foreach` with an additional filter of a * regular expression that filters which config keys are passed to the * callback. * * The regular expression is applied case-sensitively on the normalized form of * the variable name: the section and variable parts are lower-cased. The * subsection is left unchanged. * * The regular expression is applied case-sensitively on the normalized form of * the variable name: the case-insensitive parts are lower-case. * * @param cfg where to get the variables from * @param regexp regular expression to match against config names * @param callback the function to call on each variable * @param payload the data to pass to the callback * @return 0 or the return value of the callback which didn't return 0 */ GIT_EXTERN(int) git_config_foreach_match( const git_config *cfg, const char *regexp, git_config_foreach_cb callback, void *payload); /** * Query the value of a config variable and return it mapped to * an integer constant. * * This is a helper method to easily map different possible values * to a variable to integer constants that easily identify them. * * A mapping array looks as follows: * * git_configmap autocrlf_mapping[] = { * {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE}, * {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE}, * {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}, * {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}}; * * On any "false" value for the variable (e.g. "false", "FALSE", "no"), the * mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter. * * The same thing applies for any "true" value such as "true", "yes" or "1", storing * the `GIT_AUTO_CRLF_TRUE` variable. * * Otherwise, if the value matches the string "input" (with case insensitive comparison), * the given constant will be stored in `out`, and likewise for "default". * * If not a single match can be made to store in `out`, an error code will be * returned. * * @param out place to store the result of the mapping * @param cfg config file to get the variables from * @param name name of the config variable to lookup * @param maps array of `git_configmap` objects specifying the possible mappings * @param map_n number of mapping objects in `maps` * @return 0 on success, error code otherwise */ GIT_EXTERN(int) git_config_get_mapped( int *out, const git_config *cfg, const char *name, const git_configmap *maps, size_t map_n); /** * Maps a string value to an integer constant * * @param out place to store the result of the parsing * @param maps array of `git_configmap` objects specifying the possible mappings * @param map_n number of mapping objects in `maps` * @param value value to parse */ GIT_EXTERN(int) git_config_lookup_map_value( int *out, const git_configmap *maps, size_t map_n, const char *value); /** * Parse a string value as a bool. * * Valid values for true are: 'true', 'yes', 'on', 1 or any * number different from 0 * Valid values for false are: 'false', 'no', 'off', 0 * * @param out place to store the result of the parsing * @param value value to parse */ GIT_EXTERN(int) git_config_parse_bool(int *out, const char *value); /** * Parse a string value as an int32. * * An optional value suffix of 'k', 'm', or 'g' will * cause the value to be multiplied by 1024, 1048576, * or 1073741824 prior to output. * * @param out place to store the result of the parsing * @param value value to parse */ GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value); /** * Parse a string value as an int64. * * An optional value suffix of 'k', 'm', or 'g' will * cause the value to be multiplied by 1024, 1048576, * or 1073741824 prior to output. * * @param out place to store the result of the parsing * @param value value to parse */ GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value); /** * Parse a string value as a path. * * A leading '~' will be expanded to the global search path (which * defaults to the user's home directory but can be overridden via * `git_libgit2_opts()`. * * If the value does not begin with a tilde, the input will be * returned. * * @param out placae to store the result of parsing * @param value the path to evaluate */ GIT_EXTERN(int) git_config_parse_path(git_buf *out, const char *value); /** * Perform an operation on each config variable in a given config backend, * matching a regular expression. * * This behaves like `git_config_foreach_match` except that only config * entries from the given backend entry are enumerated. * * The regular expression is applied case-sensitively on the normalized form of * the variable name: the section and variable parts are lower-cased. The * subsection is left unchanged. * * @param backend where to get the variables from * @param regexp regular expression to match against config names (can be NULL) * @param callback the function to call on each variable * @param payload the data to pass to the callback */ GIT_EXTERN(int) git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, git_config_foreach_cb callback, void *payload); /** * Lock the backend with the highest priority * * Locking disallows anybody else from writing to that backend. Any * updates made after locking will not be visible to a reader until * the file is unlocked. * * You can apply the changes by calling `git_transaction_commit()` * before freeing the transaction. Either of these actions will unlock * the config. * * @param tx the resulting transaction, use this to commit or undo the * changes * @param cfg the configuration in which to lock * @return 0 or an error code */ GIT_EXTERN(int) git_config_lock(git_transaction **tx, git_config *cfg); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/diff.h0000644000175000017500000014541314125111754017140 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_diff_h__ #define INCLUDE_git_diff_h__ #include "common.h" #include "types.h" #include "oid.h" #include "tree.h" #include "refs.h" /** * @file git2/diff.h * @brief Git tree and file differencing routines. * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Flags for diff options. A combination of these flags can be passed * in via the `flags` value in the `git_diff_options`. */ typedef enum { /** Normal diff, the default */ GIT_DIFF_NORMAL = 0, /* * Options controlling which files will be in the diff */ /** Reverse the sides of the diff */ GIT_DIFF_REVERSE = (1u << 0), /** Include ignored files in the diff */ GIT_DIFF_INCLUDE_IGNORED = (1u << 1), /** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory * will be marked with only a single entry in the diff; this flag * adds all files under the directory as IGNORED entries, too. */ GIT_DIFF_RECURSE_IGNORED_DIRS = (1u << 2), /** Include untracked files in the diff */ GIT_DIFF_INCLUDE_UNTRACKED = (1u << 3), /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked * directory will be marked with only a single entry in the diff * (a la what core Git does in `git status`); this flag adds *all* * files under untracked directories as UNTRACKED entries, too. */ GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1u << 4), /** Include unmodified files in the diff */ GIT_DIFF_INCLUDE_UNMODIFIED = (1u << 5), /** Normally, a type change between files will be converted into a * DELETED record for the old and an ADDED record for the new; this * options enabled the generation of TYPECHANGE delta records. */ GIT_DIFF_INCLUDE_TYPECHANGE = (1u << 6), /** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still * generally show as a DELETED blob. This flag tries to correctly * label blob->tree transitions as TYPECHANGE records with new_file's * mode set to tree. Note: the tree SHA will not be available. */ GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1u << 7), /** Ignore file mode changes */ GIT_DIFF_IGNORE_FILEMODE = (1u << 8), /** Treat all submodules as unmodified */ GIT_DIFF_IGNORE_SUBMODULES = (1u << 9), /** Use case insensitive filename comparisons */ GIT_DIFF_IGNORE_CASE = (1u << 10), /** May be combined with `GIT_DIFF_IGNORE_CASE` to specify that a file * that has changed case will be returned as an add/delete pair. */ GIT_DIFF_INCLUDE_CASECHANGE = (1u << 11), /** If the pathspec is set in the diff options, this flags indicates * that the paths will be treated as literal paths instead of * fnmatch patterns. Each path in the list must either be a full * path to a file or a directory. (A trailing slash indicates that * the path will _only_ match a directory). If a directory is * specified, all children will be included. */ GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12), /** Disable updating of the `binary` flag in delta records. This is * useful when iterating over a diff if you don't need hunk and data * callbacks and want to avoid having to load file completely. */ GIT_DIFF_SKIP_BINARY_CHECK = (1u << 13), /** When diff finds an untracked directory, to match the behavior of * core Git, it scans the contents for IGNORED and UNTRACKED files. * If *all* contents are IGNORED, then the directory is IGNORED; if * any contents are not IGNORED, then the directory is UNTRACKED. * This is extra work that may not matter in many cases. This flag * turns off that scan and immediately labels an untracked directory * as UNTRACKED (changing the behavior to not match core Git). */ GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14), /** When diff finds a file in the working directory with stat * information different from the index, but the OID ends up being the * same, write the correct stat information into the index. Note: * without this flag, diff will always leave the index untouched. */ GIT_DIFF_UPDATE_INDEX = (1u << 15), /** Include unreadable files in the diff */ GIT_DIFF_INCLUDE_UNREADABLE = (1u << 16), /** Include unreadable files in the diff */ GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17), /* * Options controlling how output will be generated */ /** Use a heuristic that takes indentation and whitespace into account * which generally can produce better diffs when dealing with ambiguous * diff hunks. */ GIT_DIFF_INDENT_HEURISTIC = (1u << 18), /** Ignore blank lines */ GIT_DIFF_IGNORE_BLANK_LINES = (1u << 19), /** Treat all files as text, disabling binary attributes & detection */ GIT_DIFF_FORCE_TEXT = (1u << 20), /** Treat all files as binary, disabling text diffs */ GIT_DIFF_FORCE_BINARY = (1u << 21), /** Ignore all whitespace */ GIT_DIFF_IGNORE_WHITESPACE = (1u << 22), /** Ignore changes in amount of whitespace */ GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1u << 23), /** Ignore whitespace at end of line */ GIT_DIFF_IGNORE_WHITESPACE_EOL = (1u << 24), /** When generating patch text, include the content of untracked * files. This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but * it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS. Add that * flag if you want the content of every single UNTRACKED file. */ GIT_DIFF_SHOW_UNTRACKED_CONTENT = (1u << 25), /** When generating output, include the names of unmodified files if * they are included in the git_diff. Normally these are skipped in * the formats that list files (e.g. name-only, name-status, raw). * Even with this, these will not be included in patch format. */ GIT_DIFF_SHOW_UNMODIFIED = (1u << 26), /** Use the "patience diff" algorithm */ GIT_DIFF_PATIENCE = (1u << 28), /** Take extra time to find minimal diff */ GIT_DIFF_MINIMAL = (1u << 29), /** Include the necessary deflate / delta information so that `git-apply` * can apply given diff information to binary files. */ GIT_DIFF_SHOW_BINARY = (1u << 30), } git_diff_option_t; /** * The diff object that contains all individual file deltas. * * A `diff` represents the cumulative list of differences between two * snapshots of a repository (possibly filtered by a set of file name * patterns). * * Calculating diffs is generally done in two phases: building a list of * diffs then traversing it. This makes is easier to share logic across * the various types of diffs (tree vs tree, workdir vs index, etc.), and * also allows you to insert optional diff post-processing phases, * such as rename detection, in between the steps. When you are done with * a diff object, it must be freed. * * This is an opaque structure which will be allocated by one of the diff * generator functions below (such as `git_diff_tree_to_tree`). You are * responsible for releasing the object memory when done, using the * `git_diff_free()` function. * */ typedef struct git_diff git_diff; /** * Flags for the delta object and the file objects on each side. * * These flags are used for both the `flags` value of the `git_diff_delta` * and the flags for the `git_diff_file` objects representing the old and * new sides of the delta. Values outside of this public range should be * considered reserved for internal or future use. */ typedef enum { GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */ GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */ GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */ GIT_DIFF_FLAG_EXISTS = (1u << 3), /**< file exists at this side of the delta */ } git_diff_flag_t; /** * What type of change is described by a git_diff_delta? * * `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED` will only show up if you run * `git_diff_find_similar()` on the diff object. * * `GIT_DELTA_TYPECHANGE` only shows up given `GIT_DIFF_INCLUDE_TYPECHANGE` * in the option flags (otherwise type changes will be split into ADDED / * DELETED pairs). */ typedef enum { GIT_DELTA_UNMODIFIED = 0, /**< no changes */ GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */ GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */ GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */ GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */ GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */ GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */ GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */ GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */ GIT_DELTA_CONFLICTED = 10, /**< entry in the index is conflicted */ } git_delta_t; /** * Description of one side of a delta. * * Although this is called a "file", it could represent a file, a symbolic * link, a submodule commit id, or even a tree (although that only if you * are tracking type changes or ignored/untracked directories). */ typedef struct { /** * The `git_oid` of the item. If the entry represents an * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta), * then the oid will be zeroes. */ git_oid id; /** * The NUL-terminated path to the entry relative to the working * directory of the repository. */ const char *path; /** * The size of the entry in bytes. */ git_object_size_t size; /** * A combination of the `git_diff_flag_t` types */ uint32_t flags; /** * Roughly, the stat() `st_mode` value for the item. This will * be restricted to one of the `git_filemode_t` values. */ uint16_t mode; /** * Represents the known length of the `id` field, when * converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this * delta was created from reading a patch file, in which case it may be * abbreviated to something reasonable, like 7 characters. */ uint16_t id_abbrev; } git_diff_file; /** * Description of changes to one entry. * * A `delta` is a file pair with an old and new revision. The old version * may be absent if the file was just created and the new version may be * absent if the file was deleted. A diff is mostly just a list of deltas. * * When iterating over a diff, this will be passed to most callbacks and * you can use the contents to understand exactly what has changed. * * The `old_file` represents the "from" side of the diff and the `new_file` * represents to "to" side of the diff. What those means depend on the * function that was used to generate the diff and will be documented below. * You can also use the `GIT_DIFF_REVERSE` flag to flip it around. * * Although the two sides of the delta are named "old_file" and "new_file", * they actually may correspond to entries that represent a file, a symbolic * link, a submodule commit id, or even a tree (if you are tracking type * changes or ignored/untracked directories). * * Under some circumstances, in the name of efficiency, not all fields will * be filled in, but we generally try to fill in as much as possible. One * example is that the "flags" field may not have either the `BINARY` or the * `NOT_BINARY` flag set to avoid examining file contents if you do not pass * in hunk and/or line callbacks to the diff foreach iteration function. It * will just use the git attributes for those files. * * The similarity score is zero unless you call `git_diff_find_similar()` * which does a similarity analysis of files in the diff. Use that * function to do rename and copy detection, and to split heavily modified * files in add/delete pairs. After that call, deltas with a status of * GIT_DELTA_RENAMED or GIT_DELTA_COPIED will have a similarity score * between 0 and 100 indicating how similar the old and new sides are. * * If you ask `git_diff_find_similar` to find heavily modified files to * break, but to not *actually* break the records, then GIT_DELTA_MODIFIED * records may have a non-zero similarity score if the self-similarity is * below the split threshold. To display this value like core Git, invert * the score (a la `printf("M%03d", 100 - delta->similarity)`). */ typedef struct { git_delta_t status; uint32_t flags; /**< git_diff_flag_t values */ uint16_t similarity; /**< for RENAMED and COPIED, value 0-100 */ uint16_t nfiles; /**< number of files in this delta */ git_diff_file old_file; git_diff_file new_file; } git_diff_delta; /** * Diff notification callback function. * * The callback will be called for each file, just before the `git_diff_delta` * gets inserted into the diff. * * When the callback: * - returns < 0, the diff process will be aborted. * - returns > 0, the delta will not be inserted into the diff, but the * diff process continues. * - returns 0, the delta is inserted into the diff, and the diff process * continues. */ typedef int GIT_CALLBACK(git_diff_notify_cb)( const git_diff *diff_so_far, const git_diff_delta *delta_to_add, const char *matched_pathspec, void *payload); /** * Diff progress callback. * * Called before each file comparison. * * @param diff_so_far The diff being generated. * @param old_path The path to the old file or NULL. * @param new_path The path to the new file or NULL. * @return Non-zero to abort the diff. */ typedef int GIT_CALLBACK(git_diff_progress_cb)( const git_diff *diff_so_far, const char *old_path, const char *new_path, void *payload); /** * Structure describing options about how the diff should be executed. * * Setting all values of the structure to zero will yield the default * values. Similarly, passing NULL for the options structure will * give the defaults. The default values are marked below. * */ typedef struct { unsigned int version; /**< version for the struct */ /** * A combination of `git_diff_option_t` values above. * Defaults to GIT_DIFF_NORMAL */ uint32_t flags; /* options controlling which files are in the diff */ /** Overrides the submodule ignore setting for all submodules in the diff. */ git_submodule_ignore_t ignore_submodules; /** * An array of paths / fnmatch patterns to constrain diff. * All paths are included by default. */ git_strarray pathspec; /** * An optional callback function, notifying the consumer of changes to * the diff as new deltas are added. */ git_diff_notify_cb notify_cb; /** * An optional callback function, notifying the consumer of which files * are being examined as the diff is generated. */ git_diff_progress_cb progress_cb; /** The payload to pass to the callback functions. */ void *payload; /* options controlling how to diff text is generated */ /** * The number of unchanged lines that define the boundary of a hunk * (and to display before and after). Defaults to 3. */ uint32_t context_lines; /** * The maximum number of unchanged lines between hunk boundaries before * the hunks will be merged into one. Defaults to 0. */ uint32_t interhunk_lines; /** * The abbreviation length to use when formatting object ids. * Defaults to the value of 'core.abbrev' from the config, or 7 if unset. */ uint16_t id_abbrev; /** * A size (in bytes) above which a blob will be marked as binary * automatically; pass a negative value to disable. * Defaults to 512MB. */ git_off_t max_size; /** * The virtual "directory" prefix for old file names in hunk headers. * Default is "a". */ const char *old_prefix; /** * The virtual "directory" prefix for new file names in hunk headers. * Defaults to "b". */ const char *new_prefix; } git_diff_options; /* The current version of the diff options structure */ #define GIT_DIFF_OPTIONS_VERSION 1 /* Stack initializer for diff options. Alternatively use * `git_diff_options_init` programmatic initialization. */ #define GIT_DIFF_OPTIONS_INIT \ {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3} /** * Initialize git_diff_options structure * * Initializes a `git_diff_options` with default values. Equivalent to creating * an instance with GIT_DIFF_OPTIONS_INIT. * * @param opts The `git_diff_options` struct to initialize. * @param version The struct version; pass `GIT_DIFF_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_diff_options_init( git_diff_options *opts, unsigned int version); /** * When iterating over a diff, callback that will be made per file. * * @param delta A pointer to the delta data for the file * @param progress Goes from 0 to 1 over the diff * @param payload User-specified pointer from foreach function */ typedef int GIT_CALLBACK(git_diff_file_cb)( const git_diff_delta *delta, float progress, void *payload); #define GIT_DIFF_HUNK_HEADER_SIZE 128 /** * When producing a binary diff, the binary data returned will be * either the deflated full ("literal") contents of the file, or * the deflated binary delta between the two sides (whichever is * smaller). */ typedef enum { /** There is no binary delta. */ GIT_DIFF_BINARY_NONE, /** The binary data is the literal contents of the file. */ GIT_DIFF_BINARY_LITERAL, /** The binary data is the delta from one side to the other. */ GIT_DIFF_BINARY_DELTA, } git_diff_binary_t; /** The contents of one of the files in a binary diff. */ typedef struct { /** The type of binary data for this file. */ git_diff_binary_t type; /** The binary data, deflated. */ const char *data; /** The length of the binary data. */ size_t datalen; /** The length of the binary data after inflation. */ size_t inflatedlen; } git_diff_binary_file; /** * Structure describing the binary contents of a diff. * * A `binary` file / delta is a file (or pair) for which no text diffs * should be generated. A diff can contain delta entries that are * binary, but no diff content will be output for those files. There is * a base heuristic for binary detection and you can further tune the * behavior with git attributes or diff flags and option settings. */ typedef struct { /** * Whether there is data in this binary structure or not. * * If this is `1`, then this was produced and included binary content. * If this is `0` then this was generated knowing only that a binary * file changed but without providing the data, probably from a patch * that said `Binary files a/file.txt and b/file.txt differ`. */ unsigned int contains_data; git_diff_binary_file old_file; /**< The contents of the old file. */ git_diff_binary_file new_file; /**< The contents of the new file. */ } git_diff_binary; /** * When iterating over a diff, callback that will be made for * binary content within the diff. */ typedef int GIT_CALLBACK(git_diff_binary_cb)( const git_diff_delta *delta, const git_diff_binary *binary, void *payload); /** * Structure describing a hunk of a diff. * * A `hunk` is a span of modified lines in a delta along with some stable * surrounding context. You can configure the amount of context and other * properties of how hunks are generated. Each hunk also comes with a * header that described where it starts and ends in both the old and new * versions in the delta. */ typedef struct { int old_start; /**< Starting line number in old_file */ int old_lines; /**< Number of lines in old_file */ int new_start; /**< Starting line number in new_file */ int new_lines; /**< Number of lines in new_file */ size_t header_len; /**< Number of bytes in header text */ char header[GIT_DIFF_HUNK_HEADER_SIZE]; /**< Header text, NUL-byte terminated */ } git_diff_hunk; /** * When iterating over a diff, callback that will be made per hunk. */ typedef int GIT_CALLBACK(git_diff_hunk_cb)( const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload); /** * Line origin constants. * * These values describe where a line came from and will be passed to * the git_diff_line_cb when iterating over a diff. There are some * special origin constants at the end that are used for the text * output callbacks to demarcate lines that are actually part of * the file or hunk headers. */ typedef enum { /* These values will be sent to `git_diff_line_cb` along with the line */ GIT_DIFF_LINE_CONTEXT = ' ', GIT_DIFF_LINE_ADDITION = '+', GIT_DIFF_LINE_DELETION = '-', GIT_DIFF_LINE_CONTEXT_EOFNL = '=', /**< Both files have no LF at end */ GIT_DIFF_LINE_ADD_EOFNL = '>', /**< Old has no LF at end, new does */ GIT_DIFF_LINE_DEL_EOFNL = '<', /**< Old has LF at end, new does not */ /* The following values will only be sent to a `git_diff_line_cb` when * the content of a diff is being formatted through `git_diff_print`. */ GIT_DIFF_LINE_FILE_HDR = 'F', GIT_DIFF_LINE_HUNK_HDR = 'H', GIT_DIFF_LINE_BINARY = 'B' /**< For "Binary files x and y differ" */ } git_diff_line_t; /** * Structure describing a line (or data span) of a diff. * * A `line` is a range of characters inside a hunk. It could be a context * line (i.e. in both old and new versions), an added line (i.e. only in * the new version), or a removed line (i.e. only in the old version). * Unfortunately, we don't know anything about the encoding of data in the * file being diffed, so we cannot tell you much about the line content. * Line data will not be NUL-byte terminated, however, because it will be * just a span of bytes inside the larger file. */ typedef struct { char origin; /**< A git_diff_line_t value */ int old_lineno; /**< Line number in old file or -1 for added line */ int new_lineno; /**< Line number in new file or -1 for deleted line */ int num_lines; /**< Number of newline characters in content */ size_t content_len; /**< Number of bytes of data */ git_off_t content_offset; /**< Offset in the original file to the content */ const char *content; /**< Pointer to diff text, not NUL-byte terminated */ } git_diff_line; /** * When iterating over a diff, callback that will be made per text diff * line. In this context, the provided range will be NULL. * * When printing a diff, callback that will be made to output each line * of text. This uses some extra GIT_DIFF_LINE_... constants for output * of lines of file and hunk headers. */ typedef int GIT_CALLBACK(git_diff_line_cb)( const git_diff_delta *delta, /**< delta that contains this data */ const git_diff_hunk *hunk, /**< hunk containing this data */ const git_diff_line *line, /**< line data */ void *payload); /**< user reference data */ /** * Flags to control the behavior of diff rename/copy detection. */ typedef enum { /** Obey `diff.renames`. Overridden by any other GIT_DIFF_FIND_... flag. */ GIT_DIFF_FIND_BY_CONFIG = 0, /** Look for renames? (`--find-renames`) */ GIT_DIFF_FIND_RENAMES = (1u << 0), /** Consider old side of MODIFIED for renames? (`--break-rewrites=N`) */ GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1u << 1), /** Look for copies? (a la `--find-copies`). */ GIT_DIFF_FIND_COPIES = (1u << 2), /** Consider UNMODIFIED as copy sources? (`--find-copies-harder`). * * For this to work correctly, use GIT_DIFF_INCLUDE_UNMODIFIED when * the initial `git_diff` is being generated. */ GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1u << 3), /** Mark significant rewrites for split (`--break-rewrites=/M`) */ GIT_DIFF_FIND_REWRITES = (1u << 4), /** Actually split large rewrites into delete/add pairs */ GIT_DIFF_BREAK_REWRITES = (1u << 5), /** Mark rewrites for split and break into delete/add pairs */ GIT_DIFF_FIND_AND_BREAK_REWRITES = (GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES), /** Find renames/copies for UNTRACKED items in working directory. * * For this to work correctly, use GIT_DIFF_INCLUDE_UNTRACKED when the * initial `git_diff` is being generated (and obviously the diff must * be against the working directory for this to make sense). */ GIT_DIFF_FIND_FOR_UNTRACKED = (1u << 6), /** Turn on all finding features. */ GIT_DIFF_FIND_ALL = (0x0ff), /** Measure similarity ignoring leading whitespace (default) */ GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0, /** Measure similarity ignoring all whitespace */ GIT_DIFF_FIND_IGNORE_WHITESPACE = (1u << 12), /** Measure similarity including all data */ GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1u << 13), /** Measure similarity only by comparing SHAs (fast and cheap) */ GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1u << 14), /** Do not break rewrites unless they contribute to a rename. * * Normally, GIT_DIFF_FIND_AND_BREAK_REWRITES will measure the self- * similarity of modified files and split the ones that have changed a * lot into a DELETE / ADD pair. Then the sides of that pair will be * considered candidates for rename and copy detection. * * If you add this flag in and the split pair is *not* used for an * actual rename or copy, then the modified record will be restored to * a regular MODIFIED record instead of being split. */ GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY = (1u << 15), /** Remove any UNMODIFIED deltas after find_similar is done. * * Using GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED to emulate the * --find-copies-harder behavior requires building a diff with the * GIT_DIFF_INCLUDE_UNMODIFIED flag. If you do not want UNMODIFIED * records in the final result, pass this flag to have them removed. */ GIT_DIFF_FIND_REMOVE_UNMODIFIED = (1u << 16), } git_diff_find_t; /** * Pluggable similarity metric */ typedef struct { int GIT_CALLBACK(file_signature)( void **out, const git_diff_file *file, const char *fullpath, void *payload); int GIT_CALLBACK(buffer_signature)( void **out, const git_diff_file *file, const char *buf, size_t buflen, void *payload); void GIT_CALLBACK(free_signature)(void *sig, void *payload); int GIT_CALLBACK(similarity)(int *score, void *siga, void *sigb, void *payload); void *payload; } git_diff_similarity_metric; /** * Control behavior of rename and copy detection * * These options mostly mimic parameters that can be passed to git-diff. */ typedef struct { unsigned int version; /** * Combination of git_diff_find_t values (default GIT_DIFF_FIND_BY_CONFIG). * NOTE: if you don't explicitly set this, `diff.renames` could be set * to false, resulting in `git_diff_find_similar` doing nothing. */ uint32_t flags; /** * Threshold above which similar files will be considered renames. * This is equivalent to the -M option. Defaults to 50. */ uint16_t rename_threshold; /** * Threshold below which similar files will be eligible to be a rename source. * This is equivalent to the first part of the -B option. Defaults to 50. */ uint16_t rename_from_rewrite_threshold; /** * Threshold above which similar files will be considered copies. * This is equivalent to the -C option. Defaults to 50. */ uint16_t copy_threshold; /** * Treshold below which similar files will be split into a delete/add pair. * This is equivalent to the last part of the -B option. Defaults to 60. */ uint16_t break_rewrite_threshold; /** * Maximum number of matches to consider for a particular file. * * This is a little different from the `-l` option from Git because we * will still process up to this many matches before abandoning the search. * Defaults to 200. */ size_t rename_limit; /** * The `metric` option allows you to plug in a custom similarity metric. * * Set it to NULL to use the default internal metric. * * The default metric is based on sampling hashes of ranges of data in * the file, which is a pretty good similarity approximation that should * work fairly well for both text and binary data while still being * pretty fast with a fixed memory overhead. */ git_diff_similarity_metric *metric; } git_diff_find_options; #define GIT_DIFF_FIND_OPTIONS_VERSION 1 #define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION} /** * Initialize git_diff_find_options structure * * Initializes a `git_diff_find_options` with default values. Equivalent to creating * an instance with GIT_DIFF_FIND_OPTIONS_INIT. * * @param opts The `git_diff_find_options` struct to initialize. * @param version The struct version; pass `GIT_DIFF_FIND_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_diff_find_options_init( git_diff_find_options *opts, unsigned int version); /** @name Diff Generator Functions * * These are the functions you would use to create (or destroy) a * git_diff from various objects in a repository. */ /**@{*/ /** * Deallocate a diff. * * @param diff The previously created diff; cannot be used after free. */ GIT_EXTERN(void) git_diff_free(git_diff *diff); /** * Create a diff with the difference between two tree objects. * * This is equivalent to `git diff ` * * The first tree will be used for the "old_file" side of the delta and the * second tree will be used for the "new_file" side of the delta. You can * pass NULL to indicate an empty tree, although it is an error to pass * NULL for both the `old_tree` and `new_tree`. * * @param diff Output pointer to a git_diff pointer to be allocated. * @param repo The repository containing the trees. * @param old_tree A git_tree object to diff from, or NULL for empty tree. * @param new_tree A git_tree object to diff to, or NULL for empty tree. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_tree_to_tree( git_diff **diff, git_repository *repo, git_tree *old_tree, git_tree *new_tree, const git_diff_options *opts); /** * Create a diff between a tree and repository index. * * This is equivalent to `git diff --cached ` or if you pass * the HEAD tree, then like `git diff --cached`. * * The tree you pass will be used for the "old_file" side of the delta, and * the index will be used for the "new_file" side of the delta. * * If you pass NULL for the index, then the existing index of the `repo` * will be used. In this case, the index will be refreshed from disk * (if it has changed) before the diff is generated. * * @param diff Output pointer to a git_diff pointer to be allocated. * @param repo The repository containing the tree and index. * @param old_tree A git_tree object to diff from, or NULL for empty tree. * @param index The index to diff with; repo index used if NULL. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_tree_to_index( git_diff **diff, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts); /** * Create a diff between the repository index and the workdir directory. * * This matches the `git diff` command. See the note below on * `git_diff_tree_to_workdir` for a discussion of the difference between * `git diff` and `git diff HEAD` and how to emulate a `git diff ` * using libgit2. * * The index will be used for the "old_file" side of the delta, and the * working directory will be used for the "new_file" side of the delta. * * If you pass NULL for the index, then the existing index of the `repo` * will be used. In this case, the index will be refreshed from disk * (if it has changed) before the diff is generated. * * @param diff Output pointer to a git_diff pointer to be allocated. * @param repo The repository. * @param index The index to diff from; repo index used if NULL. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_index_to_workdir( git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts); /** * Create a diff between a tree and the working directory. * * The tree you provide will be used for the "old_file" side of the delta, * and the working directory will be used for the "new_file" side. * * This is not the same as `git diff ` or `git diff-index * `. Those commands use information from the index, whereas this * function strictly returns the differences between the tree and the files * in the working directory, regardless of the state of the index. Use * `git_diff_tree_to_workdir_with_index` to emulate those commands. * * To see difference between this and `git_diff_tree_to_workdir_with_index`, * consider the example of a staged file deletion where the file has then * been put back into the working dir and further modified. The * tree-to-workdir diff for that file is 'modified', but `git diff` would * show status 'deleted' since there is a staged delete. * * @param diff A pointer to a git_diff pointer that will be allocated. * @param repo The repository containing the tree. * @param old_tree A git_tree object to diff from, or NULL for empty tree. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_tree_to_workdir( git_diff **diff, git_repository *repo, git_tree *old_tree, const git_diff_options *opts); /** * Create a diff between a tree and the working directory using index data * to account for staged deletes, tracked files, etc. * * This emulates `git diff ` by diffing the tree to the index and * the index to the working directory and blending the results into a * single diff that includes staged deleted, etc. * * @param diff A pointer to a git_diff pointer that will be allocated. * @param repo The repository containing the tree. * @param old_tree A git_tree object to diff from, or NULL for empty tree. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_tree_to_workdir_with_index( git_diff **diff, git_repository *repo, git_tree *old_tree, const git_diff_options *opts); /** * Create a diff with the difference between two index objects. * * The first index will be used for the "old_file" side of the delta and the * second index will be used for the "new_file" side of the delta. * * @param diff Output pointer to a git_diff pointer to be allocated. * @param repo The repository containing the indexes. * @param old_index A git_index object to diff from. * @param new_index A git_index object to diff to. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_index_to_index( git_diff **diff, git_repository *repo, git_index *old_index, git_index *new_index, const git_diff_options *opts); /** * Merge one diff into another. * * This merges items from the "from" list into the "onto" list. The * resulting diff will have all items that appear in either list. * If an item appears in both lists, then it will be "merged" to appear * as if the old version was from the "onto" list and the new version * is from the "from" list (with the exception that if the item has a * pending DELETE in the middle, then it will show as deleted). * * @param onto Diff to merge into. * @param from Diff to merge. */ GIT_EXTERN(int) git_diff_merge( git_diff *onto, const git_diff *from); /** * Transform a diff marking file renames, copies, etc. * * This modifies a diff in place, replacing old entries that look * like renames or copies with new entries reflecting those changes. * This also will, if requested, break modified files into add/remove * pairs if the amount of change is above a threshold. * * @param diff diff to run detection algorithms on * @param options Control how detection should be run, NULL for defaults * @return 0 on success, -1 on failure */ GIT_EXTERN(int) git_diff_find_similar( git_diff *diff, const git_diff_find_options *options); /**@}*/ /** @name Diff Processor Functions * * These are the functions you apply to a diff to process it * or read it in some way. */ /**@{*/ /** * Query how many diff records are there in a diff. * * @param diff A git_diff generated by one of the above functions * @return Count of number of deltas in the list */ GIT_EXTERN(size_t) git_diff_num_deltas(const git_diff *diff); /** * Query how many diff deltas are there in a diff filtered by type. * * This works just like `git_diff_num_deltas()` with an extra parameter * that is a `git_delta_t` and returns just the count of how many deltas * match that particular type. * * @param diff A git_diff generated by one of the above functions * @param type A git_delta_t value to filter the count * @return Count of number of deltas matching delta_t type */ GIT_EXTERN(size_t) git_diff_num_deltas_of_type( const git_diff *diff, git_delta_t type); /** * Return the diff delta for an entry in the diff list. * * The `git_diff_delta` pointer points to internal data and you do not * have to release it when you are done with it. It will go away when * the * `git_diff` (or any associated `git_patch`) goes away. * * Note that the flags on the delta related to whether it has binary * content or not may not be set if there are no attributes set for the * file and there has been no reason to load the file data at this point. * For now, if you need those flags to be up to date, your only option is * to either use `git_diff_foreach` or create a `git_patch`. * * @param diff Diff list object * @param idx Index into diff list * @return Pointer to git_diff_delta (or NULL if `idx` out of range) */ GIT_EXTERN(const git_diff_delta *) git_diff_get_delta( const git_diff *diff, size_t idx); /** * Check if deltas are sorted case sensitively or insensitively. * * @param diff diff to check * @return 0 if case sensitive, 1 if case is ignored */ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff); /** * Loop over all deltas in a diff issuing callbacks. * * This will iterate through all of the files described in a diff. You * should provide a file callback to learn about each file. * * The "hunk" and "line" callbacks are optional, and the text diff of the * files will only be calculated if they are not NULL. Of course, these * callbacks will not be invoked for binary files on the diff or for * files whose only changed is a file mode change. * * Returning a non-zero value from any of the callbacks will terminate * the iteration and return the value to the user. * * @param diff A git_diff generated by one of the above functions. * @param file_cb Callback function to make per file in the diff. * @param binary_cb Optional callback to make for binary files. * @param hunk_cb Optional callback to make per hunk of text diff. This * callback is called to describe a range of lines in the * diff. It will not be issued for binary files. * @param line_cb Optional callback to make per line of diff text. This * same callback will be made for context lines, added, and * removed lines, and even for a deleted trailing newline. * @param payload Reference pointer that will be passed to your callbacks. * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_foreach( git_diff *diff, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); /** * Look up the single character abbreviation for a delta status code. * * When you run `git diff --name-status` it uses single letter codes in * the output such as 'A' for added, 'D' for deleted, 'M' for modified, * etc. This function converts a git_delta_t value into these letters for * your own purposes. GIT_DELTA_UNTRACKED will return a space (i.e. ' '). * * @param status The git_delta_t value to look up * @return The single character label for that code */ GIT_EXTERN(char) git_diff_status_char(git_delta_t status); /** * Possible output formats for diff data */ typedef enum { GIT_DIFF_FORMAT_PATCH = 1u, /**< full git diff */ GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */ GIT_DIFF_FORMAT_RAW = 3u, /**< like git diff --raw */ GIT_DIFF_FORMAT_NAME_ONLY = 4u, /**< like git diff --name-only */ GIT_DIFF_FORMAT_NAME_STATUS = 5u, /**< like git diff --name-status */ GIT_DIFF_FORMAT_PATCH_ID = 6u, /**< git diff as used by git patch-id */ } git_diff_format_t; /** * Iterate over a diff generating formatted text output. * * Returning a non-zero value from the callbacks will terminate the * iteration and return the non-zero value to the caller. * * @param diff A git_diff generated by one of the above functions. * @param format A git_diff_format_t value to pick the text format. * @param print_cb Callback to make per line of diff text. * @param payload Reference pointer that will be passed to your callback. * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_print( git_diff *diff, git_diff_format_t format, git_diff_line_cb print_cb, void *payload); /** * Produce the complete formatted text output from a diff into a * buffer. * * @param out A pointer to a user-allocated git_buf that will * contain the diff text * @param diff A git_diff generated by one of the above functions. * @param format A git_diff_format_t value to pick the text format. * @return 0 on success or error code */ GIT_EXTERN(int) git_diff_to_buf( git_buf *out, git_diff *diff, git_diff_format_t format); /**@}*/ /* * Misc */ /** * Directly run a diff on two blobs. * * Compared to a file, a blob lacks some contextual information. As such, * the `git_diff_file` given to the callback will have some fake data; i.e. * `mode` will be 0 and `path` will be NULL. * * NULL is allowed for either `old_blob` or `new_blob` and will be treated * as an empty blob, with the `oid` set to NULL in the `git_diff_file` data. * Passing NULL for both blobs is a noop; no callbacks will be made at all. * * We do run a binary content check on the blob content and if either blob * looks like binary data, the `git_diff_delta` binary attribute will be set * to 1 and no call to the hunk_cb nor line_cb will be made (unless you pass * `GIT_DIFF_FORCE_TEXT` of course). * * @param old_blob Blob for old side of diff, or NULL for empty blob * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param new_blob Blob for new side of diff, or NULL for empty blob * @param new_as_path Treat new blob as if it had this filename; can be NULL * @param options Options for diff, or NULL for default options * @param file_cb Callback for "file"; made once if there is a diff; can be NULL * @param binary_cb Callback for binary files; can be NULL * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blobs( const git_blob *old_blob, const char *old_as_path, const git_blob *new_blob, const char *new_as_path, const git_diff_options *options, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); /** * Directly run a diff between a blob and a buffer. * * As with `git_diff_blobs`, comparing a blob and buffer lacks some context, * so the `git_diff_file` parameters to the callbacks will be faked a la the * rules for `git_diff_blobs()`. * * Passing NULL for `old_blob` will be treated as an empty blob (i.e. the * `file_cb` will be invoked with GIT_DELTA_ADDED and the diff will be the * entire content of the buffer added). Passing NULL to the buffer will do * the reverse, with GIT_DELTA_REMOVED and blob content removed. * * @param old_blob Blob for old side of diff, or NULL for empty blob * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param buffer Raw data for new side of diff, or NULL for empty * @param buffer_len Length of raw data for new side of diff * @param buffer_as_path Treat buffer as if it had this filename; can be NULL * @param options Options for diff, or NULL for default options * @param file_cb Callback for "file"; made once if there is a diff; can be NULL * @param binary_cb Callback for binary files; can be NULL * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blob_to_buffer( const git_blob *old_blob, const char *old_as_path, const char *buffer, size_t buffer_len, const char *buffer_as_path, const git_diff_options *options, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); /** * Directly run a diff between two buffers. * * Even more than with `git_diff_blobs`, comparing two buffer lacks * context, so the `git_diff_file` parameters to the callbacks will be * faked a la the rules for `git_diff_blobs()`. * * @param old_buffer Raw data for old side of diff, or NULL for empty * @param old_len Length of the raw data for old side of the diff * @param old_as_path Treat old buffer as if it had this filename; can be NULL * @param new_buffer Raw data for new side of diff, or NULL for empty * @param new_len Length of raw data for new side of diff * @param new_as_path Treat buffer as if it had this filename; can be NULL * @param options Options for diff, or NULL for default options * @param file_cb Callback for "file"; made once if there is a diff; can be NULL * @param binary_cb Callback for binary files; can be NULL * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_buffers( const void *old_buffer, size_t old_len, const char *old_as_path, const void *new_buffer, size_t new_len, const char *new_as_path, const git_diff_options *options, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); /** * Read the contents of a git patch file into a `git_diff` object. * * The diff object produced is similar to the one that would be * produced if you actually produced it computationally by comparing * two trees, however there may be subtle differences. For example, * a patch file likely contains abbreviated object IDs, so the * object IDs in a `git_diff_delta` produced by this function will * also be abbreviated. * * This function will only read patch files created by a git * implementation, it will not read unified diffs produced by * the `diff` program, nor any other types of patch files. * * @param out A pointer to a git_diff pointer that will be allocated. * @param content The contents of a patch file * @param content_len The length of the patch file contents * @return 0 or an error code */ GIT_EXTERN(int) git_diff_from_buffer( git_diff **out, const char *content, size_t content_len); /** * This is an opaque structure which is allocated by `git_diff_get_stats`. * You are responsible for releasing the object memory when done, using the * `git_diff_stats_free()` function. */ typedef struct git_diff_stats git_diff_stats; /** * Formatting options for diff stats */ typedef enum { /** No stats*/ GIT_DIFF_STATS_NONE = 0, /** Full statistics, equivalent of `--stat` */ GIT_DIFF_STATS_FULL = (1u << 0), /** Short statistics, equivalent of `--shortstat` */ GIT_DIFF_STATS_SHORT = (1u << 1), /** Number statistics, equivalent of `--numstat` */ GIT_DIFF_STATS_NUMBER = (1u << 2), /** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */ GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3), } git_diff_stats_format_t; /** * Accumulate diff statistics for all patches. * * @param out Structure containg the diff statistics. * @param diff A git_diff generated by one of the above functions. * @return 0 on success; non-zero on error */ GIT_EXTERN(int) git_diff_get_stats( git_diff_stats **out, git_diff *diff); /** * Get the total number of files changed in a diff * * @param stats A `git_diff_stats` generated by one of the above functions. * @return total number of files changed in the diff */ GIT_EXTERN(size_t) git_diff_stats_files_changed( const git_diff_stats *stats); /** * Get the total number of insertions in a diff * * @param stats A `git_diff_stats` generated by one of the above functions. * @return total number of insertions in the diff */ GIT_EXTERN(size_t) git_diff_stats_insertions( const git_diff_stats *stats); /** * Get the total number of deletions in a diff * * @param stats A `git_diff_stats` generated by one of the above functions. * @return total number of deletions in the diff */ GIT_EXTERN(size_t) git_diff_stats_deletions( const git_diff_stats *stats); /** * Print diff statistics to a `git_buf`. * * @param out buffer to store the formatted diff statistics in. * @param stats A `git_diff_stats` generated by one of the above functions. * @param format Formatting option. * @param width Target width for output (only affects GIT_DIFF_STATS_FULL) * @return 0 on success; non-zero on error */ GIT_EXTERN(int) git_diff_stats_to_buf( git_buf *out, const git_diff_stats *stats, git_diff_stats_format_t format, size_t width); /** * Deallocate a `git_diff_stats`. * * @param stats The previously created statistics object; * cannot be used after free. */ GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats); /** * Patch ID options structure * * Initialize with `GIT_PATCHID_OPTIONS_INIT`. Alternatively, you can * use `git_diff_patchid_options_init`. * */ typedef struct git_diff_patchid_options { unsigned int version; } git_diff_patchid_options; #define GIT_DIFF_PATCHID_OPTIONS_VERSION 1 #define GIT_DIFF_PATCHID_OPTIONS_INIT { GIT_DIFF_PATCHID_OPTIONS_VERSION } /** * Initialize git_diff_patchid_options structure * * Initializes a `git_diff_patchid_options` with default values. Equivalent to * creating an instance with `GIT_DIFF_PATCHID_OPTIONS_INIT`. * * @param opts The `git_diff_patchid_options` struct to initialize. * @param version The struct version; pass `GIT_DIFF_PATCHID_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_diff_patchid_options_init( git_diff_patchid_options *opts, unsigned int version); /** * Calculate the patch ID for the given patch. * * Calculate a stable patch ID for the given patch by summing the * hash of the file diffs, ignoring whitespace and line numbers. * This can be used to derive whether two diffs are the same with * a high probability. * * Currently, this function only calculates stable patch IDs, as * defined in git-patch-id(1), and should in fact generate the * same IDs as the upstream git project does. * * @param out Pointer where the calculated patch ID should be stored * @param diff The diff to calculate the ID for * @param opts Options for how to calculate the patch ID. This is * intended for future changes, as currently no options are * available. * @return 0 on success, an error code otherwise. */ GIT_EXTERN(int) git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts); GIT_END_DECL /** @} */ #endif git2r/src/libgit2/include/git2/blob.h0000644000175000017500000002171714125111754017146 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_blob_h__ #define INCLUDE_git_blob_h__ #include "common.h" #include "types.h" #include "oid.h" #include "object.h" #include "buffer.h" /** * @file git2/blob.h * @brief Git blob load and write routines * @defgroup git_blob Git blob load and write routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Lookup a blob object from a repository. * * @param blob pointer to the looked up blob * @param repo the repo to use when locating the blob. * @param id identity of the blob to locate. * @return 0 or an error code */ GIT_EXTERN(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id); /** * Lookup a blob object from a repository, * given a prefix of its identifier (short id). * * @see git_object_lookup_prefix * * @param blob pointer to the looked up blob * @param repo the repo to use when locating the blob. * @param id identity of the blob to locate. * @param len the length of the short identifier * @return 0 or an error code */ GIT_EXTERN(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len); /** * Close an open blob * * This is a wrapper around git_object_free() * * IMPORTANT: * It *is* necessary to call this method when you stop * using a blob. Failure to do so will cause a memory leak. * * @param blob the blob to close */ GIT_EXTERN(void) git_blob_free(git_blob *blob); /** * Get the id of a blob. * * @param blob a previously loaded blob. * @return SHA1 hash for this blob. */ GIT_EXTERN(const git_oid *) git_blob_id(const git_blob *blob); /** * Get the repository that contains the blob. * * @param blob A previously loaded blob. * @return Repository that contains this blob. */ GIT_EXTERN(git_repository *) git_blob_owner(const git_blob *blob); /** * Get a read-only buffer with the raw content of a blob. * * A pointer to the raw content of a blob is returned; * this pointer is owned internally by the object and shall * not be free'd. The pointer may be invalidated at a later * time. * * @param blob pointer to the blob * @return the pointer, or NULL on error */ GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob); /** * Get the size in bytes of the contents of a blob * * @param blob pointer to the blob * @return size on bytes */ GIT_EXTERN(git_object_size_t) git_blob_rawsize(const git_blob *blob); /** * Flags to control the functionality of `git_blob_filter`. */ typedef enum { /** When set, filters will not be applied to binary files. */ GIT_BLOB_FILTER_CHECK_FOR_BINARY = (1 << 0), /** * When set, filters will not load configuration from the * system-wide `gitattributes` in `/etc` (or system equivalent). */ GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES = (1 << 1), /** * When set, filters will be loaded from a `.gitattributes` file * in the HEAD commit. */ GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD = (1 << 2), /** * When set, filters will be loaded from a `.gitattributes` file * in the specified commit. */ GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT = (1 << 3), } git_blob_filter_flag_t; /** * The options used when applying filter options to a file. * * Initialize with `GIT_BLOB_FILTER_OPTIONS_INIT`. Alternatively, you can * use `git_blob_filter_options_init`. * */ typedef struct { int version; /** Flags to control the filtering process, see `git_blob_filter_flag_t` above */ uint32_t flags; #ifdef GIT_DEPRECATE_HARD void *reserved; #else git_oid *commit_id; #endif /** * The commit to load attributes from, when * `GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. */ git_oid attr_commit_id; } git_blob_filter_options; #define GIT_BLOB_FILTER_OPTIONS_VERSION 1 #define GIT_BLOB_FILTER_OPTIONS_INIT {GIT_BLOB_FILTER_OPTIONS_VERSION, GIT_BLOB_FILTER_CHECK_FOR_BINARY} /** * Initialize git_blob_filter_options structure * * Initializes a `git_blob_filter_options` with default values. Equivalent * to creating an instance with `GIT_BLOB_FILTER_OPTIONS_INIT`. * * @param opts The `git_blob_filter_options` struct to initialize. * @param version The struct version; pass `GIT_BLOB_FILTER_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_blob_filter_options_init(git_blob_filter_options *opts, unsigned int version); /** * Get a buffer with the filtered content of a blob. * * This applies filters as if the blob was being checked out to the * working directory under the specified filename. This may apply * CRLF filtering or other types of changes depending on the file * attributes set for the blob and the content detected in it. * * The output is written into a `git_buf` which the caller must free * when done (via `git_buf_dispose`). * * If no filters need to be applied, then the `out` buffer will just * be populated with a pointer to the raw content of the blob. In * that case, be careful to *not* free the blob until done with the * buffer or copy it into memory you own. * * @param out The git_buf to be filled in * @param blob Pointer to the blob * @param as_path Path used for file attribute lookups, etc. * @param opts Options to use for filtering the blob * @return 0 on success or an error code */ GIT_EXTERN(int) git_blob_filter( git_buf *out, git_blob *blob, const char *as_path, git_blob_filter_options *opts); /** * Read a file from the working folder of a repository * and write it to the Object Database as a loose blob * * @param id return the id of the written blob * @param repo repository where the blob will be written. * this repository cannot be bare * @param relative_path file from which the blob will be created, * relative to the repository's working dir * @return 0 or an error code */ GIT_EXTERN(int) git_blob_create_from_workdir(git_oid *id, git_repository *repo, const char *relative_path); /** * Read a file from the filesystem and write its content * to the Object Database as a loose blob * * @param id return the id of the written blob * @param repo repository where the blob will be written. * this repository can be bare or not * @param path file from which the blob will be created * @return 0 or an error code */ GIT_EXTERN(int) git_blob_create_from_disk(git_oid *id, git_repository *repo, const char *path); /** * Create a stream to write a new blob into the object db * * This function may need to buffer the data on disk and will in * general not be the right choice if you know the size of the data * to write. If you have data in memory, use * `git_blob_create_from_buffer()`. If you do not, but know the size of * the contents (and don't want/need to perform filtering), use * `git_odb_open_wstream()`. * * Don't close this stream yourself but pass it to * `git_blob_create_from_stream_commit()` to commit the write to the * object db and get the object id. * * If the `hintpath` parameter is filled, it will be used to determine * what git filters should be applied to the object before it is written * to the object database. * * @param out the stream into which to write * @param repo Repository where the blob will be written. * This repository can be bare or not. * @param hintpath If not NULL, will be used to select data filters * to apply onto the content of the blob to be created. * @return 0 or error code */ GIT_EXTERN(int) git_blob_create_from_stream( git_writestream **out, git_repository *repo, const char *hintpath); /** * Close the stream and write the blob to the object db * * The stream will be closed and freed. * * @param out the id of the new blob * @param stream the stream to close * @return 0 or an error code */ GIT_EXTERN(int) git_blob_create_from_stream_commit( git_oid *out, git_writestream *stream); /** * Write an in-memory buffer to the ODB as a blob * * @param id return the id of the written blob * @param repo repository where the blob will be written * @param buffer data to be written into the blob * @param len length of the data * @return 0 or an error code */ GIT_EXTERN(int) git_blob_create_from_buffer( git_oid *id, git_repository *repo, const void *buffer, size_t len); /** * Determine if the blob content is most certainly binary or not. * * The heuristic used to guess if a file is binary is taken from core git: * Searching for NUL bytes and looking for a reasonable ratio of printable * to non-printable characters among the first 8000 bytes. * * @param blob The blob which content should be analyzed * @return 1 if the content of the blob is detected * as binary; 0 otherwise. */ GIT_EXTERN(int) git_blob_is_binary(const git_blob *blob); /** * Create an in-memory copy of a blob. The copy must be explicitly * free'd or it will leak. * * @param out Pointer to store the copy of the object * @param source Original object to copy */ GIT_EXTERN(int) git_blob_dup(git_blob **out, git_blob *source); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/repository.h0000644000175000017500000007562614125111754020457 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_repository_h__ #define INCLUDE_git_repository_h__ #include "common.h" #include "types.h" #include "oid.h" #include "buffer.h" /** * @file git2/repository.h * @brief Git repository management routines * @defgroup git_repository Git repository management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Open a git repository. * * The 'path' argument must point to either a git repository * folder, or an existing work dir. * * The method will automatically detect if 'path' is a normal * or bare repository or fail is 'path' is neither. * * @param out pointer to the repo which will be opened * @param path the path to the repository * @return 0 or an error code */ GIT_EXTERN(int) git_repository_open(git_repository **out, const char *path); /** * Open working tree as a repository * * Open the working directory of the working tree as a normal * repository that can then be worked on. * * @param out Output pointer containing opened repository * @param wt Working tree to open * @return 0 or an error code */ GIT_EXTERN(int) git_repository_open_from_worktree(git_repository **out, git_worktree *wt); /** * Create a "fake" repository to wrap an object database * * Create a repository object to wrap an object database to be used * with the API when all you have is an object database. This doesn't * have any paths associated with it, so use with care. * * @param out pointer to the repo * @param odb the object database to wrap * @return 0 or an error code */ GIT_EXTERN(int) git_repository_wrap_odb(git_repository **out, git_odb *odb); /** * Look for a git repository and copy its path in the given buffer. * The lookup start from base_path and walk across parent directories * if nothing has been found. The lookup ends when the first repository * is found, or when reaching a directory referenced in ceiling_dirs * or when the filesystem changes (in case across_fs is true). * * The method will automatically detect if the repository is bare * (if there is a repository). * * @param out A pointer to a user-allocated git_buf which will contain * the found path. * * @param start_path The base path where the lookup starts. * * @param across_fs If true, then the lookup will not stop when a * filesystem device change is detected while exploring parent directories. * * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR separated list of * absolute symbolic link free paths. The lookup will stop when any * of this paths is reached. Note that the lookup always performs on * start_path no matter start_path appears in ceiling_dirs ceiling_dirs * might be NULL (which is equivalent to an empty string) * * @return 0 or an error code */ GIT_EXTERN(int) git_repository_discover( git_buf *out, const char *start_path, int across_fs, const char *ceiling_dirs); /** * Option flags for `git_repository_open_ext`. */ typedef enum { /** * Only open the repository if it can be immediately found in the * start_path. Do not walk up from the start_path looking at parent * directories. */ GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0), /** * Unless this flag is set, open will not continue searching across * filesystem boundaries (i.e. when `st_dev` changes from the `stat` * system call). For example, searching in a user's home directory at * "/home/user/source/" will not return "/.git/" as the found repo if * "/" is a different filesystem than "/home". */ GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1), /** * Open repository as a bare repo regardless of core.bare config, and * defer loading config file for faster setup. * Unlike `git_repository_open_bare`, this can follow gitlinks. */ GIT_REPOSITORY_OPEN_BARE = (1 << 2), /** * Do not check for a repository by appending /.git to the start_path; * only open the repository if start_path itself points to the git * directory. */ GIT_REPOSITORY_OPEN_NO_DOTGIT = (1 << 3), /** * Find and open a git repository, respecting the environment variables * used by the git command-line tools. * If set, `git_repository_open_ext` will ignore the other flags and * the `ceiling_dirs` argument, and will allow a NULL `path` to use * `GIT_DIR` or search from the current directory. * The search for a repository will respect $GIT_CEILING_DIRECTORIES and * $GIT_DISCOVERY_ACROSS_FILESYSTEM. The opened repository will * respect $GIT_INDEX_FILE, $GIT_NAMESPACE, $GIT_OBJECT_DIRECTORY, and * $GIT_ALTERNATE_OBJECT_DIRECTORIES. * In the future, this flag will also cause `git_repository_open_ext` * to respect $GIT_WORK_TREE and $GIT_COMMON_DIR; currently, * `git_repository_open_ext` with this flag will error out if either * $GIT_WORK_TREE or $GIT_COMMON_DIR is set. */ GIT_REPOSITORY_OPEN_FROM_ENV = (1 << 4), } git_repository_open_flag_t; /** * Find and open a repository with extended controls. * * @param out Pointer to the repo which will be opened. This can * actually be NULL if you only want to use the error code to * see if a repo at this path could be opened. * @param path Path to open as git repository. If the flags * permit "searching", then this can be a path to a subdirectory * inside the working directory of the repository. May be NULL if * flags is GIT_REPOSITORY_OPEN_FROM_ENV. * @param flags A combination of the GIT_REPOSITORY_OPEN flags above. * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path * prefixes at which the search for a containing repository should * terminate. * @return 0 on success, GIT_ENOTFOUND if no repository could be found, * or -1 if there was a repository but open failed for some reason * (such as repo corruption or system errors). */ GIT_EXTERN(int) git_repository_open_ext( git_repository **out, const char *path, unsigned int flags, const char *ceiling_dirs); /** * Open a bare repository on the serverside. * * This is a fast open for bare repositories that will come in handy * if you're e.g. hosting git repositories and need to access them * efficiently * * @param out Pointer to the repo which will be opened. * @param bare_path Direct path to the bare repository * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path); /** * Free a previously allocated repository * * Note that after a repository is free'd, all the objects it has spawned * will still exist until they are manually closed by the user * with `git_object_free`, but accessing any of the attributes of * an object without a backing repository will result in undefined * behavior * * @param repo repository handle to close. If NULL nothing occurs. */ GIT_EXTERN(void) git_repository_free(git_repository *repo); /** * Creates a new Git repository in the given folder. * * TODO: * - Reinit the repository * * @param out pointer to the repo which will be created or reinitialized * @param path the path to the repository * @param is_bare if true, a Git repository without a working directory is * created at the pointed path. If false, provided path will be * considered as the working directory into which the .git directory * will be created. * * @return 0 or an error code */ GIT_EXTERN(int) git_repository_init( git_repository **out, const char *path, unsigned is_bare); /** * Option flags for `git_repository_init_ext`. * * These flags configure extra behaviors to `git_repository_init_ext`. * In every case, the default behavior is the zero value (i.e. flag is * not set). Just OR the flag values together for the `flags` parameter * when initializing a new repo. */ typedef enum { /** * Create a bare repository with no working directory. */ GIT_REPOSITORY_INIT_BARE = (1u << 0), /** * Return an GIT_EEXISTS error if the repo_path appears to already be * an git repository. */ GIT_REPOSITORY_INIT_NO_REINIT = (1u << 1), /** * Normally a "/.git/" will be appended to the repo path for * non-bare repos (if it is not already there), but passing this flag * prevents that behavior. */ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = (1u << 2), /** * Make the repo_path (and workdir_path) as needed. Init is always willing * to create the ".git" directory even without this flag. This flag tells * init to create the trailing component of the repo and workdir paths * as needed. */ GIT_REPOSITORY_INIT_MKDIR = (1u << 3), /** * Recursively make all components of the repo and workdir paths as * necessary. */ GIT_REPOSITORY_INIT_MKPATH = (1u << 4), /** * libgit2 normally uses internal templates to initialize a new repo. * This flags enables external templates, looking the "template_path" from * the options if set, or the `init.templatedir` global config if not, * or falling back on "/usr/share/git-core/templates" if it exists. */ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5), /** * If an alternate workdir is specified, use relative paths for the gitdir * and core.worktree. */ GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6), } git_repository_init_flag_t; /** * Mode options for `git_repository_init_ext`. * * Set the mode field of the `git_repository_init_options` structure * either to the custom mode that you would like, or to one of the * defined modes. */ typedef enum { /** * Use permissions configured by umask - the default. */ GIT_REPOSITORY_INIT_SHARED_UMASK = 0, /** * Use "--shared=group" behavior, chmod'ing the new repo to be group * writable and "g+sx" for sticky group assignment. */ GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775, /** * Use "--shared=all" behavior, adding world readability. */ GIT_REPOSITORY_INIT_SHARED_ALL = 0002777, } git_repository_init_mode_t; /** * Extended options structure for `git_repository_init_ext`. * * This contains extra options for `git_repository_init_ext` that enable * additional initialization features. */ typedef struct { unsigned int version; /** * Combination of GIT_REPOSITORY_INIT flags above. */ uint32_t flags; /** * Set to one of the standard GIT_REPOSITORY_INIT_SHARED_... constants * above, or to a custom value that you would like. */ uint32_t mode; /** * The path to the working dir or NULL for default (i.e. repo_path parent * on non-bare repos). IF THIS IS RELATIVE PATH, IT WILL BE EVALUATED * RELATIVE TO THE REPO_PATH. If this is not the "natural" working * directory, a .git gitlink file will be created here linking to the * repo_path. */ const char *workdir_path; /** * If set, this will be used to initialize the "description" file in the * repository, instead of using the template content. */ const char *description; /** * When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set, this contains * the path to use for the template directory. If this is NULL, the config * or default directory options will be used instead. */ const char *template_path; /** * The name of the head to point HEAD at. If NULL, then this will be * treated as "master" and the HEAD ref will be set to "refs/heads/master". * If this begins with "refs/" it will be used verbatim; * otherwise "refs/heads/" will be prefixed. */ const char *initial_head; /** * If this is non-NULL, then after the rest of the repository * initialization is completed, an "origin" remote will be added * pointing to this URL. */ const char *origin_url; } git_repository_init_options; #define GIT_REPOSITORY_INIT_OPTIONS_VERSION 1 #define GIT_REPOSITORY_INIT_OPTIONS_INIT {GIT_REPOSITORY_INIT_OPTIONS_VERSION} /** * Initialize git_repository_init_options structure * * Initializes a `git_repository_init_options` with default values. Equivalent to * creating an instance with `GIT_REPOSITORY_INIT_OPTIONS_INIT`. * * @param opts The `git_repository_init_options` struct to initialize. * @param version The struct version; pass `GIT_REPOSITORY_INIT_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_repository_init_options_init( git_repository_init_options *opts, unsigned int version); /** * Create a new Git repository in the given folder with extended controls. * * This will initialize a new git repository (creating the repo_path * if requested by flags) and working directory as needed. It will * auto-detect the case sensitivity of the file system and if the * file system supports file mode bits correctly. * * @param out Pointer to the repo which will be created or reinitialized. * @param repo_path The path to the repository. * @param opts Pointer to git_repository_init_options struct. * @return 0 or an error code on failure. */ GIT_EXTERN(int) git_repository_init_ext( git_repository **out, const char *repo_path, git_repository_init_options *opts); /** * Retrieve and resolve the reference pointed at by HEAD. * * The returned `git_reference` will be owned by caller and * `git_reference_free()` must be called when done with it to release the * allocated memory and prevent a leak. * * @param out pointer to the reference which will be retrieved * @param repo a repository object * * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing * branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise */ GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo); /** * Retrieve the referenced HEAD for the worktree * * @param out pointer to the reference which will be retrieved * @param repo a repository object * @param name name of the worktree to retrieve HEAD for * @return 0 when successful, error-code otherwise */ GIT_EXTERN(int) git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name); /** * Check if a repository's HEAD is detached * * A repository's HEAD is detached when it points directly to a commit * instead of a branch. * * @param repo Repo to test * @return 1 if HEAD is detached, 0 if it's not; error code if there * was an error. */ GIT_EXTERN(int) git_repository_head_detached(git_repository *repo); /** * Check if a worktree's HEAD is detached * * A worktree's HEAD is detached when it points directly to a * commit instead of a branch. * * @param repo a repository object * @param name name of the worktree to retrieve HEAD for * @return 1 if HEAD is detached, 0 if its not; error code if * there was an error */ GIT_EXTERN(int) git_repository_head_detached_for_worktree(git_repository *repo, const char *name); /** * Check if the current branch is unborn * * An unborn branch is one named from HEAD but which doesn't exist in * the refs namespace, because it doesn't have any commit to point to. * * @param repo Repo to test * @return 1 if the current branch is unborn, 0 if it's not; error * code if there was an error */ GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo); /** * Check if a repository is empty * * An empty repository has just been initialized and contains no references * apart from HEAD, which must be pointing to the unborn master branch. * * @param repo Repo to test * @return 1 if the repository is empty, 0 if it isn't, error code * if the repository is corrupted */ GIT_EXTERN(int) git_repository_is_empty(git_repository *repo); /** * List of items which belong to the git repository layout */ typedef enum { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM_WORKDIR, GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_INDEX, GIT_REPOSITORY_ITEM_OBJECTS, GIT_REPOSITORY_ITEM_REFS, GIT_REPOSITORY_ITEM_PACKED_REFS, GIT_REPOSITORY_ITEM_REMOTES, GIT_REPOSITORY_ITEM_CONFIG, GIT_REPOSITORY_ITEM_INFO, GIT_REPOSITORY_ITEM_HOOKS, GIT_REPOSITORY_ITEM_LOGS, GIT_REPOSITORY_ITEM_MODULES, GIT_REPOSITORY_ITEM_WORKTREES, GIT_REPOSITORY_ITEM__LAST } git_repository_item_t; /** * Get the location of a specific repository file or directory * * This function will retrieve the path of a specific repository * item. It will thereby honor things like the repository's * common directory, gitdir, etc. In case a file path cannot * exist for a given item (e.g. the working directory of a bare * repository), GIT_ENOTFOUND is returned. * * @param out Buffer to store the path at * @param repo Repository to get path for * @param item The repository item for which to retrieve the path * @return 0, GIT_ENOTFOUND if the path cannot exist or an error code */ GIT_EXTERN(int) git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item); /** * Get the path of this repository * * This is the path of the `.git` folder for normal repositories, * or of the repository itself for bare repositories. * * @param repo A repository object * @return the path to the repository */ GIT_EXTERN(const char *) git_repository_path(const git_repository *repo); /** * Get the path of the working directory for this repository * * If the repository is bare, this function will always return * NULL. * * @param repo A repository object * @return the path to the working dir, if it exists */ GIT_EXTERN(const char *) git_repository_workdir(const git_repository *repo); /** * Get the path of the shared common directory for this repository. * * If the repository is bare, it is the root directory for the repository. * If the repository is a worktree, it is the parent repo's gitdir. * Otherwise, it is the gitdir. * * @param repo A repository object * @return the path to the common dir */ GIT_EXTERN(const char *) git_repository_commondir(const git_repository *repo); /** * Set the path to the working directory for this repository * * The working directory doesn't need to be the same one * that contains the `.git` folder for this repository. * * If this repository is bare, setting its working directory * will turn it into a normal repository, capable of performing * all the common workdir operations (checkout, status, index * manipulation, etc). * * @param repo A repository object * @param workdir The path to a working directory * @param update_gitlink Create/update gitlink in workdir and set config * "core.worktree" (if workdir is not the parent of the .git directory) * @return 0, or an error code */ GIT_EXTERN(int) git_repository_set_workdir( git_repository *repo, const char *workdir, int update_gitlink); /** * Check if a repository is bare * * @param repo Repo to test * @return 1 if the repository is bare, 0 otherwise. */ GIT_EXTERN(int) git_repository_is_bare(const git_repository *repo); /** * Check if a repository is a linked work tree * * @param repo Repo to test * @return 1 if the repository is a linked work tree, 0 otherwise. */ GIT_EXTERN(int) git_repository_is_worktree(const git_repository *repo); /** * Get the configuration file for this repository. * * If a configuration file has not been set, the default * config set for the repository will be returned, including * global and system configurations (if they are available). * * The configuration file must be freed once it's no longer * being used by the user. * * @param out Pointer to store the loaded configuration * @param repo A repository object * @return 0, or an error code */ GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo); /** * Get a snapshot of the repository's configuration * * Convenience function to take a snapshot from the repository's * configuration. The contents of this snapshot will not change, * even if the underlying config files are modified. * * The configuration file must be freed once it's no longer * being used by the user. * * @param out Pointer to store the loaded configuration * @param repo the repository * @return 0, or an error code */ GIT_EXTERN(int) git_repository_config_snapshot(git_config **out, git_repository *repo); /** * Get the Object Database for this repository. * * If a custom ODB has not been set, the default * database for the repository will be returned (the one * located in `.git/objects`). * * The ODB must be freed once it's no longer being used by * the user. * * @param out Pointer to store the loaded ODB * @param repo A repository object * @return 0, or an error code */ GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo); /** * Get the Reference Database Backend for this repository. * * If a custom refsdb has not been set, the default database for * the repository will be returned (the one that manipulates loose * and packed references in the `.git` directory). * * The refdb must be freed once it's no longer being used by * the user. * * @param out Pointer to store the loaded refdb * @param repo A repository object * @return 0, or an error code */ GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo); /** * Get the Index file for this repository. * * If a custom index has not been set, the default * index for the repository will be returned (the one * located in `.git/index`). * * The index must be freed once it's no longer being used by * the user. * * @param out Pointer to store the loaded index * @param repo A repository object * @return 0, or an error code */ GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo); /** * Retrieve git's prepared message * * Operations such as git revert/cherry-pick/merge with the -n option * stop just short of creating a commit with the changes and save * their prepared message in .git/MERGE_MSG so the next git-commit * execution can present it to the user for them to amend if they * wish. * * Use this function to get the contents of this file. Don't forget to * remove the file after you create the commit. * * @param out git_buf to write data into * @param repo Repository to read prepared message from * @return 0, GIT_ENOTFOUND if no message exists or an error code */ GIT_EXTERN(int) git_repository_message(git_buf *out, git_repository *repo); /** * Remove git's prepared message. * * Remove the message that `git_repository_message` retrieves. */ GIT_EXTERN(int) git_repository_message_remove(git_repository *repo); /** * Remove all the metadata associated with an ongoing command like merge, * revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc. * * @param repo A repository object * @return 0 on success, or error */ GIT_EXTERN(int) git_repository_state_cleanup(git_repository *repo); /** * Callback used to iterate over each FETCH_HEAD entry * * @see git_repository_fetchhead_foreach * * @param ref_name The reference name * @param remote_url The remote URL * @param oid The reference target OID * @param is_merge Was the reference the result of a merge * @param payload Payload passed to git_repository_fetchhead_foreach * @return non-zero to terminate the iteration */ typedef int GIT_CALLBACK(git_repository_fetchhead_foreach_cb)(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload); /** * Invoke 'callback' for each entry in the given FETCH_HEAD file. * * Return a non-zero value from the callback to stop the loop. * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if * there is no FETCH_HEAD file, or other error code. */ GIT_EXTERN(int) git_repository_fetchhead_foreach( git_repository *repo, git_repository_fetchhead_foreach_cb callback, void *payload); /** * Callback used to iterate over each MERGE_HEAD entry * * @see git_repository_mergehead_foreach * * @param oid The merge OID * @param payload Payload passed to git_repository_mergehead_foreach * @return non-zero to terminate the iteration */ typedef int GIT_CALLBACK(git_repository_mergehead_foreach_cb)(const git_oid *oid, void *payload); /** * If a merge is in progress, invoke 'callback' for each commit ID in the * MERGE_HEAD file. * * Return a non-zero value from the callback to stop the loop. * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if * there is no MERGE_HEAD file, or other error code. */ GIT_EXTERN(int) git_repository_mergehead_foreach( git_repository *repo, git_repository_mergehead_foreach_cb callback, void *payload); /** * Calculate hash of file using repository filtering rules. * * If you simply want to calculate the hash of a file on disk with no filters, * you can just use the `git_odb_hashfile()` API. However, if you want to * hash a file in the repository and you want to apply filtering rules (e.g. * crlf filters) before generating the SHA, then use this function. * * Note: if the repository has `core.safecrlf` set to fail and the * filtering triggers that failure, then this function will return an * error and not calculate the hash of the file. * * @param out Output value of calculated SHA * @param repo Repository pointer * @param path Path to file on disk whose contents should be hashed. This * may be an absolute path or a relative path, in which case it * will be treated as a path within the working directory. * @param type The object type to hash as (e.g. GIT_OBJECT_BLOB) * @param as_path The path to use to look up filtering rules. If this is * an empty string then no filters will be applied when * calculating the hash. If this is `NULL` and the `path` * parameter is a file within the repository's working * directory, then the `path` will be used. * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_hashfile( git_oid *out, git_repository *repo, const char *path, git_object_t type, const char *as_path); /** * Make the repository HEAD point to the specified reference. * * If the provided reference points to a Tree or a Blob, the HEAD is * unaltered and -1 is returned. * * If the provided reference points to a branch, the HEAD will point * to that branch, staying attached, or become attached if it isn't yet. * If the branch doesn't exist yet, no error will be return. The HEAD * will then be attached to an unborn branch. * * Otherwise, the HEAD will be detached and will directly point to * the Commit. * * @param repo Repository pointer * @param refname Canonical name of the reference the HEAD should point at * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_head( git_repository *repo, const char *refname); /** * Make the repository HEAD directly point to the Commit. * * If the provided committish cannot be found in the repository, the HEAD * is unaltered and GIT_ENOTFOUND is returned. * * If the provided commitish cannot be peeled into a commit, the HEAD * is unaltered and -1 is returned. * * Otherwise, the HEAD will eventually be detached and will directly point to * the peeled Commit. * * @param repo Repository pointer * @param commitish Object id of the Commit the HEAD should point to * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_head_detached( git_repository *repo, const git_oid *commitish); /** * Make the repository HEAD directly point to the Commit. * * This behaves like `git_repository_set_head_detached()` but takes an * annotated commit, which lets you specify which extended sha syntax * string was specified by a user, allowing for more exact reflog * messages. * * See the documentation for `git_repository_set_head_detached()`. * * @see git_repository_set_head_detached */ GIT_EXTERN(int) git_repository_set_head_detached_from_annotated( git_repository *repo, const git_annotated_commit *commitish); /** * Detach the HEAD. * * If the HEAD is already detached and points to a Commit, 0 is returned. * * If the HEAD is already detached and points to a Tag, the HEAD is * updated into making it point to the peeled Commit, and 0 is returned. * * If the HEAD is already detached and points to a non commitish, the HEAD is * unaltered, and -1 is returned. * * Otherwise, the HEAD will be detached and point to the peeled Commit. * * @param repo Repository pointer * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing * branch or an error code */ GIT_EXTERN(int) git_repository_detach_head( git_repository *repo); /** * Repository state * * These values represent possible states for the repository to be in, * based on the current operation which is ongoing. */ typedef enum { GIT_REPOSITORY_STATE_NONE, GIT_REPOSITORY_STATE_MERGE, GIT_REPOSITORY_STATE_REVERT, GIT_REPOSITORY_STATE_REVERT_SEQUENCE, GIT_REPOSITORY_STATE_CHERRYPICK, GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE, GIT_REPOSITORY_STATE_BISECT, GIT_REPOSITORY_STATE_REBASE, GIT_REPOSITORY_STATE_REBASE_INTERACTIVE, GIT_REPOSITORY_STATE_REBASE_MERGE, GIT_REPOSITORY_STATE_APPLY_MAILBOX, GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE, } git_repository_state_t; /** * Determines the status of a git repository - ie, whether an operation * (merge, cherry-pick, etc) is in progress. * * @param repo Repository pointer * @return The state of the repository */ GIT_EXTERN(int) git_repository_state(git_repository *repo); /** * Sets the active namespace for this Git Repository * * This namespace affects all reference operations for the repo. * See `man gitnamespaces` * * @param repo The repo * @param nmspace The namespace. This should not include the refs * folder, e.g. to namespace all references under `refs/namespaces/foo/`, * use `foo` as the namespace. * @return 0 on success, -1 on error */ GIT_EXTERN(int) git_repository_set_namespace(git_repository *repo, const char *nmspace); /** * Get the currently active namespace for this repository * * @param repo The repo * @return the active namespace, or NULL if there isn't one */ GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo); /** * Determine if the repository was a shallow clone * * @param repo The repository * @return 1 if shallow, zero if not */ GIT_EXTERN(int) git_repository_is_shallow(git_repository *repo); /** * Retrieve the configured identity to use for reflogs * * The memory is owned by the repository and must not be freed by the * user. * * @param name where to store the pointer to the name * @param email where to store the pointer to the email * @param repo the repository */ GIT_EXTERN(int) git_repository_ident(const char **name, const char **email, const git_repository *repo); /** * Set the identity to be used for writing reflogs * * If both are set, this name and email will be used to write to the * reflog. Pass NULL to unset. When unset, the identity will be taken * from the repository's configuration. * * @param repo the repository to configure * @param name the name to use for the reflog entries * @param email the email to use for the reflog entries */ GIT_EXTERN(int) git_repository_set_ident(git_repository *repo, const char *name, const char *email); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/apply.h0000644000175000017500000001007214125111754017345 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_apply_h__ #define INCLUDE_git_apply_h__ #include "common.h" #include "types.h" #include "oid.h" #include "diff.h" /** * @file git2/apply.h * @brief Git patch application routines * @defgroup git_apply Git patch application routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * When applying a patch, callback that will be made per delta (file). * * When the callback: * - returns < 0, the apply process will be aborted. * - returns > 0, the delta will not be applied, but the apply process * continues * - returns 0, the delta is applied, and the apply process continues. * * @param delta The delta to be applied * @param payload User-specified payload */ typedef int GIT_CALLBACK(git_apply_delta_cb)( const git_diff_delta *delta, void *payload); /** * When applying a patch, callback that will be made per hunk. * * When the callback: * - returns < 0, the apply process will be aborted. * - returns > 0, the hunk will not be applied, but the apply process * continues * - returns 0, the hunk is applied, and the apply process continues. * * @param hunk The hunk to be applied * @param payload User-specified payload */ typedef int GIT_CALLBACK(git_apply_hunk_cb)( const git_diff_hunk *hunk, void *payload); /** Flags controlling the behavior of git_apply */ typedef enum { /** * Don't actually make changes, just test that the patch applies. * This is the equivalent of `git apply --check`. */ GIT_APPLY_CHECK = (1 << 0), } git_apply_flags_t; /** * Apply options structure * * Initialize with `GIT_APPLY_OPTIONS_INIT`. Alternatively, you can * use `git_apply_options_init`. * * @see git_apply_to_tree, git_apply */ typedef struct { unsigned int version; /**< The version */ /** When applying a patch, callback that will be made per delta (file). */ git_apply_delta_cb delta_cb; /** When applying a patch, callback that will be made per hunk. */ git_apply_hunk_cb hunk_cb; /** Payload passed to both delta_cb & hunk_cb. */ void *payload; /** Bitmask of git_apply_flags_t */ unsigned int flags; } git_apply_options; #define GIT_APPLY_OPTIONS_VERSION 1 #define GIT_APPLY_OPTIONS_INIT {GIT_APPLY_OPTIONS_VERSION} GIT_EXTERN(int) git_apply_options_init(git_apply_options *opts, unsigned int version); /** * Apply a `git_diff` to a `git_tree`, and return the resulting image * as an index. * * @param out the postimage of the application * @param repo the repository to apply * @param preimage the tree to apply the diff to * @param diff the diff to apply * @param options the options for the apply (or null for defaults) * @return 0 or an error code */ GIT_EXTERN(int) git_apply_to_tree( git_index **out, git_repository *repo, git_tree *preimage, git_diff *diff, const git_apply_options *options); /** Possible application locations for git_apply */ typedef enum { /** * Apply the patch to the workdir, leaving the index untouched. * This is the equivalent of `git apply` with no location argument. */ GIT_APPLY_LOCATION_WORKDIR = 0, /** * Apply the patch to the index, leaving the working directory * untouched. This is the equivalent of `git apply --cached`. */ GIT_APPLY_LOCATION_INDEX = 1, /** * Apply the patch to both the working directory and the index. * This is the equivalent of `git apply --index`. */ GIT_APPLY_LOCATION_BOTH = 2, } git_apply_location_t; /** * Apply a `git_diff` to the given repository, making changes directly * in the working directory, the index, or both. * * @param repo the repository to apply to * @param diff the diff to apply * @param location the location to apply (workdir, index or both) * @param options the options for the apply (or null for defaults) * @return 0 or an error code */ GIT_EXTERN(int) git_apply( git_repository *repo, git_diff *diff, git_apply_location_t location, const git_apply_options *options); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/merge.h0000644000175000017500000004473214125111754017331 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_merge_h__ #define INCLUDE_git_merge_h__ #include "common.h" #include "types.h" #include "oid.h" #include "oidarray.h" #include "checkout.h" #include "index.h" #include "annotated_commit.h" /** * @file git2/merge.h * @brief Git merge routines * @defgroup git_merge Git merge routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * The file inputs to `git_merge_file`. Callers should populate the * `git_merge_file_input` structure with descriptions of the files in * each side of the conflict for use in producing the merge file. */ typedef struct { unsigned int version; /** Pointer to the contents of the file. */ const char *ptr; /** Size of the contents pointed to in `ptr`. */ size_t size; /** File name of the conflicted file, or `NULL` to not merge the path. */ const char *path; /** File mode of the conflicted file, or `0` to not merge the mode. */ unsigned int mode; } git_merge_file_input; #define GIT_MERGE_FILE_INPUT_VERSION 1 #define GIT_MERGE_FILE_INPUT_INIT {GIT_MERGE_FILE_INPUT_VERSION} /** * Initializes a `git_merge_file_input` with default values. Equivalent to * creating an instance with GIT_MERGE_FILE_INPUT_INIT. * * @param opts the `git_merge_file_input` instance to initialize. * @param version the version of the struct; you should pass * `GIT_MERGE_FILE_INPUT_VERSION` here. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_merge_file_input_init( git_merge_file_input *opts, unsigned int version); /** * Flags for `git_merge` options. A combination of these flags can be * passed in via the `flags` value in the `git_merge_options`. */ typedef enum { /** * Detect renames that occur between the common ancestor and the "ours" * side or the common ancestor and the "theirs" side. This will enable * the ability to merge between a modified and renamed file. */ GIT_MERGE_FIND_RENAMES = (1 << 0), /** * If a conflict occurs, exit immediately instead of attempting to * continue resolving conflicts. The merge operation will fail with * GIT_EMERGECONFLICT and no index will be returned. */ GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1), /** * Do not write the REUC extension on the generated index */ GIT_MERGE_SKIP_REUC = (1 << 2), /** * If the commits being merged have multiple merge bases, do not build * a recursive merge base (by merging the multiple merge bases), * instead simply use the first base. This flag provides a similar * merge base to `git-merge-resolve`. */ GIT_MERGE_NO_RECURSIVE = (1 << 3), } git_merge_flag_t; /** * Merge file favor options for `git_merge_options` instruct the file-level * merging functionality how to deal with conflicting regions of the files. */ typedef enum { /** * When a region of a file is changed in both branches, a conflict * will be recorded in the index so that `git_checkout` can produce * a merge file with conflict markers in the working directory. * This is the default. */ GIT_MERGE_FILE_FAVOR_NORMAL = 0, /** * When a region of a file is changed in both branches, the file * created in the index will contain the "ours" side of any conflicting * region. The index will not record a conflict. */ GIT_MERGE_FILE_FAVOR_OURS = 1, /** * When a region of a file is changed in both branches, the file * created in the index will contain the "theirs" side of any conflicting * region. The index will not record a conflict. */ GIT_MERGE_FILE_FAVOR_THEIRS = 2, /** * When a region of a file is changed in both branches, the file * created in the index will contain each unique line from each side, * which has the result of combining both files. The index will not * record a conflict. */ GIT_MERGE_FILE_FAVOR_UNION = 3, } git_merge_file_favor_t; /** * File merging flags */ typedef enum { /** Defaults */ GIT_MERGE_FILE_DEFAULT = 0, /** Create standard conflicted merge files */ GIT_MERGE_FILE_STYLE_MERGE = (1 << 0), /** Create diff3-style files */ GIT_MERGE_FILE_STYLE_DIFF3 = (1 << 1), /** Condense non-alphanumeric regions for simplified diff file */ GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 2), /** Ignore all whitespace */ GIT_MERGE_FILE_IGNORE_WHITESPACE = (1 << 3), /** Ignore changes in amount of whitespace */ GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = (1 << 4), /** Ignore whitespace at end of line */ GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = (1 << 5), /** Use the "patience diff" algorithm */ GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6), /** Take extra time to find minimal diff */ GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7), } git_merge_file_flag_t; #define GIT_MERGE_CONFLICT_MARKER_SIZE 7 /** * Options for merging a file */ typedef struct { unsigned int version; /** * Label for the ancestor file side of the conflict which will be prepended * to labels in diff3-format merge files. */ const char *ancestor_label; /** * Label for our file side of the conflict which will be prepended * to labels in merge files. */ const char *our_label; /** * Label for their file side of the conflict which will be prepended * to labels in merge files. */ const char *their_label; /** The file to favor in region conflicts. */ git_merge_file_favor_t favor; /** see `git_merge_file_flag_t` above */ uint32_t flags; /** The size of conflict markers (eg, "<<<<<<<"). Default is * GIT_MERGE_CONFLICT_MARKER_SIZE. */ unsigned short marker_size; } git_merge_file_options; #define GIT_MERGE_FILE_OPTIONS_VERSION 1 #define GIT_MERGE_FILE_OPTIONS_INIT {GIT_MERGE_FILE_OPTIONS_VERSION} /** * Initialize git_merge_file_options structure * * Initializes a `git_merge_file_options` with default values. Equivalent to * creating an instance with `GIT_MERGE_FILE_OPTIONS_INIT`. * * @param opts The `git_merge_file_options` struct to initialize. * @param version The struct version; pass `GIT_MERGE_FILE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_merge_file_options_init(git_merge_file_options *opts, unsigned int version); /** * Information about file-level merging */ typedef struct { /** * True if the output was automerged, false if the output contains * conflict markers. */ unsigned int automergeable; /** * The path that the resultant merge file should use, or NULL if a * filename conflict would occur. */ const char *path; /** The mode that the resultant merge file should use. */ unsigned int mode; /** The contents of the merge. */ const char *ptr; /** The length of the merge contents. */ size_t len; } git_merge_file_result; /** * Merging options */ typedef struct { unsigned int version; /** See `git_merge_flag_t` above */ uint32_t flags; /** * Similarity to consider a file renamed (default 50). If * `GIT_MERGE_FIND_RENAMES` is enabled, added files will be compared * with deleted files to determine their similarity. Files that are * more similar than the rename threshold (percentage-wise) will be * treated as a rename. */ unsigned int rename_threshold; /** * Maximum similarity sources to examine for renames (default 200). * If the number of rename candidates (add / delete pairs) is greater * than this value, inexact rename detection is aborted. * * This setting overrides the `merge.renameLimit` configuration value. */ unsigned int target_limit; /** Pluggable similarity metric; pass NULL to use internal metric */ git_diff_similarity_metric *metric; /** * Maximum number of times to merge common ancestors to build a * virtual merge base when faced with criss-cross merges. When this * limit is reached, the next ancestor will simply be used instead of * attempting to merge it. The default is unlimited. */ unsigned int recursion_limit; /** * Default merge driver to be used when both sides of a merge have * changed. The default is the `text` driver. */ const char *default_driver; /** * Flags for handling conflicting content, to be used with the standard * (`text`) merge driver. */ git_merge_file_favor_t file_favor; /** see `git_merge_file_flag_t` above */ uint32_t file_flags; } git_merge_options; #define GIT_MERGE_OPTIONS_VERSION 1 #define GIT_MERGE_OPTIONS_INIT { \ GIT_MERGE_OPTIONS_VERSION, GIT_MERGE_FIND_RENAMES } /** * Initialize git_merge_options structure * * Initializes a `git_merge_options` with default values. Equivalent to * creating an instance with `GIT_MERGE_OPTIONS_INIT`. * * @param opts The `git_merge_options` struct to initialize. * @param version The struct version; pass `GIT_MERGE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_merge_options_init(git_merge_options *opts, unsigned int version); /** * The results of `git_merge_analysis` indicate the merge opportunities. */ typedef enum { /** No merge is possible. (Unused.) */ GIT_MERGE_ANALYSIS_NONE = 0, /** * A "normal" merge; both HEAD and the given merge input have diverged * from their common ancestor. The divergent commits must be merged. */ GIT_MERGE_ANALYSIS_NORMAL = (1 << 0), /** * All given merge inputs are reachable from HEAD, meaning the * repository is up-to-date and no merge needs to be performed. */ GIT_MERGE_ANALYSIS_UP_TO_DATE = (1 << 1), /** * The given merge input is a fast-forward from HEAD and no merge * needs to be performed. Instead, the client can check out the * given merge input. */ GIT_MERGE_ANALYSIS_FASTFORWARD = (1 << 2), /** * The HEAD of the current repository is "unborn" and does not point to * a valid commit. No merge can be performed, but the caller may wish * to simply set HEAD to the target commit(s). */ GIT_MERGE_ANALYSIS_UNBORN = (1 << 3), } git_merge_analysis_t; /** * The user's stated preference for merges. */ typedef enum { /** * No configuration was found that suggests a preferred behavior for * merge. */ GIT_MERGE_PREFERENCE_NONE = 0, /** * There is a `merge.ff=false` configuration setting, suggesting that * the user does not want to allow a fast-forward merge. */ GIT_MERGE_PREFERENCE_NO_FASTFORWARD = (1 << 0), /** * There is a `merge.ff=only` configuration setting, suggesting that * the user only wants fast-forward merges. */ GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY = (1 << 1), } git_merge_preference_t; /** * Analyzes the given branch(es) and determines the opportunities for * merging them into the HEAD of the repository. * * @param analysis_out analysis enumeration that the result is written into * @param repo the repository to merge * @param their_heads the heads to merge into * @param their_heads_len the number of heads to merge * @return 0 on success or error code */ GIT_EXTERN(int) git_merge_analysis( git_merge_analysis_t *analysis_out, git_merge_preference_t *preference_out, git_repository *repo, const git_annotated_commit **their_heads, size_t their_heads_len); /** * Analyzes the given branch(es) and determines the opportunities for * merging them into a reference. * * @param analysis_out analysis enumeration that the result is written into * @param repo the repository to merge * @param our_ref the reference to perform the analysis from * @param their_heads the heads to merge into * @param their_heads_len the number of heads to merge * @return 0 on success or error code */ GIT_EXTERN(int) git_merge_analysis_for_ref( git_merge_analysis_t *analysis_out, git_merge_preference_t *preference_out, git_repository *repo, git_reference *our_ref, const git_annotated_commit **their_heads, size_t their_heads_len); /** * Find a merge base between two commits * * @param out the OID of a merge base between 'one' and 'two' * @param repo the repository where the commits exist * @param one one of the commits * @param two the other commit * @return 0 on success, GIT_ENOTFOUND if not found or error code */ GIT_EXTERN(int) git_merge_base( git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two); /** * Find merge bases between two commits * * @param out array in which to store the resulting ids * @param repo the repository where the commits exist * @param one one of the commits * @param two the other commit * @return 0 on success, GIT_ENOTFOUND if not found or error code */ GIT_EXTERN(int) git_merge_bases( git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two); /** * Find a merge base given a list of commits * * @param out the OID of a merge base considering all the commits * @param repo the repository where the commits exist * @param length The number of commits in the provided `input_array` * @param input_array oids of the commits * @return Zero on success; GIT_ENOTFOUND or -1 on failure. */ GIT_EXTERN(int) git_merge_base_many( git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]); /** * Find all merge bases given a list of commits * * @param out array in which to store the resulting ids * @param repo the repository where the commits exist * @param length The number of commits in the provided `input_array` * @param input_array oids of the commits * @return Zero on success; GIT_ENOTFOUND or -1 on failure. */ GIT_EXTERN(int) git_merge_bases_many( git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[]); /** * Find a merge base in preparation for an octopus merge * * @param out the OID of a merge base considering all the commits * @param repo the repository where the commits exist * @param length The number of commits in the provided `input_array` * @param input_array oids of the commits * @return Zero on success; GIT_ENOTFOUND or -1 on failure. */ GIT_EXTERN(int) git_merge_base_octopus( git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]); /** * Merge two files as they exist in the in-memory data structures, using * the given common ancestor as the baseline, producing a * `git_merge_file_result` that reflects the merge result. The * `git_merge_file_result` must be freed with `git_merge_file_result_free`. * * Note that this function does not reference a repository and any * configuration must be passed as `git_merge_file_options`. * * @param out The git_merge_file_result to be filled in * @param ancestor The contents of the ancestor file * @param ours The contents of the file in "our" side * @param theirs The contents of the file in "their" side * @param opts The merge file options or `NULL` for defaults * @return 0 on success or error code */ GIT_EXTERN(int) git_merge_file( git_merge_file_result *out, const git_merge_file_input *ancestor, const git_merge_file_input *ours, const git_merge_file_input *theirs, const git_merge_file_options *opts); /** * Merge two files as they exist in the index, using the given common * ancestor as the baseline, producing a `git_merge_file_result` that * reflects the merge result. The `git_merge_file_result` must be freed with * `git_merge_file_result_free`. * * @param out The git_merge_file_result to be filled in * @param repo The repository * @param ancestor The index entry for the ancestor file (stage level 1) * @param ours The index entry for our file (stage level 2) * @param theirs The index entry for their file (stage level 3) * @param opts The merge file options or NULL * @return 0 on success or error code */ GIT_EXTERN(int) git_merge_file_from_index( git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts); /** * Frees a `git_merge_file_result`. * * @param result The result to free or `NULL` */ GIT_EXTERN(void) git_merge_file_result_free(git_merge_file_result *result); /** * Merge two trees, producing a `git_index` that reflects the result of * the merge. The index may be written as-is to the working directory * or checked out. If the index is to be converted to a tree, the caller * should resolve any conflicts that arose as part of the merge. * * The returned index must be freed explicitly with `git_index_free`. * * @param out pointer to store the index result in * @param repo repository that contains the given trees * @param ancestor_tree the common ancestor between the trees (or null if none) * @param our_tree the tree that reflects the destination tree * @param their_tree the tree to merge in to `our_tree` * @param opts the merge tree options (or null for defaults) * @return 0 on success or error code */ GIT_EXTERN(int) git_merge_trees( git_index **out, git_repository *repo, const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, const git_merge_options *opts); /** * Merge two commits, producing a `git_index` that reflects the result of * the merge. The index may be written as-is to the working directory * or checked out. If the index is to be converted to a tree, the caller * should resolve any conflicts that arose as part of the merge. * * The returned index must be freed explicitly with `git_index_free`. * * @param out pointer to store the index result in * @param repo repository that contains the given trees * @param our_commit the commit that reflects the destination tree * @param their_commit the commit to merge in to `our_commit` * @param opts the merge tree options (or null for defaults) * @return 0 on success or error code */ GIT_EXTERN(int) git_merge_commits( git_index **out, git_repository *repo, const git_commit *our_commit, const git_commit *their_commit, const git_merge_options *opts); /** * Merges the given commit(s) into HEAD, writing the results into the working * directory. Any changes are staged for commit and any conflicts are written * to the index. Callers should inspect the repository's index after this * completes, resolve any conflicts and prepare a commit. * * For compatibility with git, the repository is put into a merging * state. Once the commit is done (or if the uses wishes to abort), * you should clear this state by calling * `git_repository_state_cleanup()`. * * @param repo the repository to merge * @param their_heads the heads to merge into * @param their_heads_len the number of heads to merge * @param merge_opts merge options * @param checkout_opts checkout options * @return 0 on success or error code */ GIT_EXTERN(int) git_merge( git_repository *repo, const git_annotated_commit **their_heads, size_t their_heads_len, const git_merge_options *merge_opts, const git_checkout_options *checkout_opts); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/revert.h0000644000175000017500000000510614125111754017531 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_revert_h__ #define INCLUDE_git_revert_h__ #include "common.h" #include "types.h" #include "merge.h" /** * @file git2/revert.h * @brief Git revert routines * @defgroup git_revert Git revert routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Options for revert */ typedef struct { unsigned int version; /** For merge commits, the "mainline" is treated as the parent. */ unsigned int mainline; git_merge_options merge_opts; /**< Options for the merging */ git_checkout_options checkout_opts; /**< Options for the checkout */ } git_revert_options; #define GIT_REVERT_OPTIONS_VERSION 1 #define GIT_REVERT_OPTIONS_INIT {GIT_REVERT_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT} /** * Initialize git_revert_options structure * * Initializes a `git_revert_options` with default values. Equivalent to * creating an instance with `GIT_REVERT_OPTIONS_INIT`. * * @param opts The `git_revert_options` struct to initialize. * @param version The struct version; pass `GIT_REVERT_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_revert_options_init( git_revert_options *opts, unsigned int version); /** * Reverts the given commit against the given "our" commit, producing an * index that reflects the result of the revert. * * The returned index must be freed explicitly with `git_index_free`. * * @param out pointer to store the index result in * @param repo the repository that contains the given commits * @param revert_commit the commit to revert * @param our_commit the commit to revert against (eg, HEAD) * @param mainline the parent of the revert commit, if it is a merge * @param merge_options the merge options (or null for defaults) * @return zero on success, -1 on failure. */ GIT_EXTERN(int) git_revert_commit( git_index **out, git_repository *repo, git_commit *revert_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_options); /** * Reverts the given commit, producing changes in the index and working directory. * * @param repo the repository to revert * @param commit the commit to revert * @param given_opts the revert options (or null for defaults) * @return zero on success, -1 on failure. */ GIT_EXTERN(int) git_revert( git_repository *repo, git_commit *commit, const git_revert_options *given_opts); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/object.h0000644000175000017500000001557114125111754017477 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_object_h__ #define INCLUDE_git_object_h__ #include "common.h" #include "types.h" #include "oid.h" #include "buffer.h" /** * @file git2/object.h * @brief Git revision object management routines * @defgroup git_object Git revision object management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL #define GIT_OBJECT_SIZE_MAX UINT64_MAX /** * Lookup a reference to one of the objects in a repository. * * The generated reference is owned by the repository and * should be closed with the `git_object_free` method * instead of free'd manually. * * The 'type' parameter must match the type of the object * in the odb; the method will fail otherwise. * The special value 'GIT_OBJECT_ANY' may be passed to let * the method guess the object's type. * * @param object pointer to the looked-up object * @param repo the repository to look up the object * @param id the unique identifier for the object * @param type the type of the object * @return 0 or an error code */ GIT_EXTERN(int) git_object_lookup( git_object **object, git_repository *repo, const git_oid *id, git_object_t type); /** * Lookup a reference to one of the objects in a repository, * given a prefix of its identifier (short id). * * The object obtained will be so that its identifier * matches the first 'len' hexadecimal characters * (packets of 4 bits) of the given 'id'. * 'len' must be at least GIT_OID_MINPREFIXLEN, and * long enough to identify a unique object matching * the prefix; otherwise the method will fail. * * The generated reference is owned by the repository and * should be closed with the `git_object_free` method * instead of free'd manually. * * The 'type' parameter must match the type of the object * in the odb; the method will fail otherwise. * The special value 'GIT_OBJECT_ANY' may be passed to let * the method guess the object's type. * * @param object_out pointer where to store the looked-up object * @param repo the repository to look up the object * @param id a short identifier for the object * @param len the length of the short identifier * @param type the type of the object * @return 0 or an error code */ GIT_EXTERN(int) git_object_lookup_prefix( git_object **object_out, git_repository *repo, const git_oid *id, size_t len, git_object_t type); /** * Lookup an object that represents a tree entry. * * @param out buffer that receives a pointer to the object (which must be freed * by the caller) * @param treeish root object that can be peeled to a tree * @param path relative path from the root object to the desired object * @param type type of object desired * @return 0 on success, or an error code */ GIT_EXTERN(int) git_object_lookup_bypath( git_object **out, const git_object *treeish, const char *path, git_object_t type); /** * Get the id (SHA1) of a repository object * * @param obj the repository object * @return the SHA1 id */ GIT_EXTERN(const git_oid *) git_object_id(const git_object *obj); /** * Get a short abbreviated OID string for the object * * This starts at the "core.abbrev" length (default 7 characters) and * iteratively extends to a longer string if that length is ambiguous. * The result will be unambiguous (at least until new objects are added to * the repository). * * @param out Buffer to write string into * @param obj The object to get an ID for * @return 0 on success, <0 for error */ GIT_EXTERN(int) git_object_short_id(git_buf *out, const git_object *obj); /** * Get the object type of an object * * @param obj the repository object * @return the object's type */ GIT_EXTERN(git_object_t) git_object_type(const git_object *obj); /** * Get the repository that owns this object * * Freeing or calling `git_repository_close` on the * returned pointer will invalidate the actual object. * * Any other operation may be run on the repository without * affecting the object. * * @param obj the object * @return the repository who owns this object */ GIT_EXTERN(git_repository *) git_object_owner(const git_object *obj); /** * Close an open object * * This method instructs the library to close an existing * object; note that git_objects are owned and cached by the repository * so the object may or may not be freed after this library call, * depending on how aggressive is the caching mechanism used * by the repository. * * IMPORTANT: * It *is* necessary to call this method when you stop using * an object. Failure to do so will cause a memory leak. * * @param object the object to close */ GIT_EXTERN(void) git_object_free(git_object *object); /** * Convert an object type to its string representation. * * The result is a pointer to a string in static memory and * should not be free()'ed. * * @param type object type to convert. * @return the corresponding string representation. */ GIT_EXTERN(const char *) git_object_type2string(git_object_t type); /** * Convert a string object type representation to it's git_object_t. * * @param str the string to convert. * @return the corresponding git_object_t. */ GIT_EXTERN(git_object_t) git_object_string2type(const char *str); /** * Determine if the given git_object_t is a valid loose object type. * * @param type object type to test. * @return true if the type represents a valid loose object type, * false otherwise. */ GIT_EXTERN(int) git_object_typeisloose(git_object_t type); /** * Recursively peel an object until an object of the specified type is met. * * If the query cannot be satisfied due to the object model, * GIT_EINVALIDSPEC will be returned (e.g. trying to peel a blob to a * tree). * * If you pass `GIT_OBJECT_ANY` as the target type, then the object will * be peeled until the type changes. A tag will be peeled until the * referenced object is no longer a tag, and a commit will be peeled * to a tree. Any other object type will return GIT_EINVALIDSPEC. * * If peeling a tag we discover an object which cannot be peeled to * the target type due to the object model, GIT_EPEEL will be * returned. * * You must free the returned object. * * @param peeled Pointer to the peeled git_object * @param object The object to be processed * @param target_type The type of the requested object (a GIT_OBJECT_ value) * @return 0 on success, GIT_EINVALIDSPEC, GIT_EPEEL, or an error code */ GIT_EXTERN(int) git_object_peel( git_object **peeled, const git_object *object, git_object_t target_type); /** * Create an in-memory copy of a Git object. The copy must be * explicitly free'd or it will leak. * * @param dest Pointer to store the copy of the object * @param source Original object to copy */ GIT_EXTERN(int) git_object_dup(git_object **dest, git_object *source); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/clone.h0000644000175000017500000001355114125111754017325 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_clone_h__ #define INCLUDE_git_clone_h__ #include "common.h" #include "types.h" #include "indexer.h" #include "checkout.h" #include "remote.h" #include "transport.h" /** * @file git2/clone.h * @brief Git cloning routines * @defgroup git_clone Git cloning routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Options for bypassing the git-aware transport on clone. Bypassing * it means that instead of a fetch, libgit2 will copy the object * database directory instead of figuring out what it needs, which is * faster. If possible, it will hardlink the files to save space. */ typedef enum { /** * Auto-detect (default), libgit2 will bypass the git-aware * transport for local paths, but use a normal fetch for * `file://` urls. */ GIT_CLONE_LOCAL_AUTO, /** * Bypass the git-aware transport even for a `file://` url. */ GIT_CLONE_LOCAL, /** * Do no bypass the git-aware transport */ GIT_CLONE_NO_LOCAL, /** * Bypass the git-aware transport, but do not try to use * hardlinks. */ GIT_CLONE_LOCAL_NO_LINKS, } git_clone_local_t; /** * The signature of a function matching git_remote_create, with an additional * void* as a callback payload. * * Callers of git_clone may provide a function matching this signature to override * the remote creation and customization process during a clone operation. * * @param out the resulting remote * @param repo the repository in which to create the remote * @param name the remote's name * @param url the remote's url * @param payload an opaque payload * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code */ typedef int GIT_CALLBACK(git_remote_create_cb)( git_remote **out, git_repository *repo, const char *name, const char *url, void *payload); /** * The signature of a function matchin git_repository_init, with an * aditional void * as callback payload. * * Callers of git_clone my provide a function matching this signature * to override the repository creation and customization process * during a clone operation. * * @param out the resulting repository * @param path path in which to create the repository * @param bare whether the repository is bare. This is the value from the clone options * @param payload payload specified by the options * @return 0, or a negative value to indicate error */ typedef int GIT_CALLBACK(git_repository_create_cb)( git_repository **out, const char *path, int bare, void *payload); /** * Clone options structure * * Initialize with `GIT_CLONE_OPTIONS_INIT`. Alternatively, you can * use `git_clone_options_init`. * */ typedef struct git_clone_options { unsigned int version; /** * These options are passed to the checkout step. To disable * checkout, set the `checkout_strategy` to * `GIT_CHECKOUT_NONE`. */ git_checkout_options checkout_opts; /** * Options which control the fetch, including callbacks. * * The callbacks are used for reporting fetch progress, and for acquiring * credentials in the event they are needed. */ git_fetch_options fetch_opts; /** * Set to zero (false) to create a standard repo, or non-zero * for a bare repo */ int bare; /** * Whether to use a fetch or copy the object database. */ git_clone_local_t local; /** * The name of the branch to checkout. NULL means use the * remote's default branch. */ const char *checkout_branch; /** * A callback used to create the new repository into which to * clone. If NULL, the 'bare' field will be used to determine * whether to create a bare repository. */ git_repository_create_cb repository_cb; /** * An opaque payload to pass to the git_repository creation callback. * This parameter is ignored unless repository_cb is non-NULL. */ void *repository_cb_payload; /** * A callback used to create the git_remote, prior to its being * used to perform the clone operation. See the documentation for * git_remote_create_cb for details. This parameter may be NULL, * indicating that git_clone should provide default behavior. */ git_remote_create_cb remote_cb; /** * An opaque payload to pass to the git_remote creation callback. * This parameter is ignored unless remote_cb is non-NULL. */ void *remote_cb_payload; } git_clone_options; #define GIT_CLONE_OPTIONS_VERSION 1 #define GIT_CLONE_OPTIONS_INIT { GIT_CLONE_OPTIONS_VERSION, \ { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \ GIT_FETCH_OPTIONS_INIT } /** * Initialize git_clone_options structure * * Initializes a `git_clone_options` with default values. Equivalent to creating * an instance with GIT_CLONE_OPTIONS_INIT. * * @param opts The `git_clone_options` struct to initialize. * @param version The struct version; pass `GIT_CLONE_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_clone_options_init( git_clone_options *opts, unsigned int version); /** * Clone a remote repository. * * By default this creates its repository and initial remote to match * git's defaults. You can use the options in the callback to * customize how these are created. * * @param out pointer that will receive the resulting repository object * @param url the remote repository to clone * @param local_path local directory to clone to * @param options configuration options for the clone. If NULL, the * function works as though GIT_OPTIONS_INIT were passed. * @return 0 on success, any non-zero return value from a callback * function, or a negative value to indicate an error (use * `git_error_last` for a detailed error message) */ GIT_EXTERN(int) git_clone( git_repository **out, const char *url, const char *local_path, const git_clone_options *options); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/errors.h0000644000175000017500000001311214125111754017532 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_errors_h__ #define INCLUDE_git_errors_h__ #include "common.h" /** * @file git2/errors.h * @brief Git error handling routines and variables * @ingroup Git * @{ */ GIT_BEGIN_DECL /** Generic return codes */ typedef enum { GIT_OK = 0, /**< No error */ GIT_ERROR = -1, /**< Generic error */ GIT_ENOTFOUND = -3, /**< Requested object could not be found */ GIT_EEXISTS = -4, /**< Object exists preventing operation */ GIT_EAMBIGUOUS = -5, /**< More than one object matches */ GIT_EBUFS = -6, /**< Output buffer too short to hold data */ /** * GIT_EUSER is a special error that is never generated by libgit2 * code. You can return it from a callback (e.g to stop an iteration) * to know that it was generated by the callback and not by libgit2. */ GIT_EUSER = -7, GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */ GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */ GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */ GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */ GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */ GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */ GIT_ELOCKED = -14, /**< Lock file prevented operation */ GIT_EMODIFIED = -15, /**< Reference value does not match expected */ GIT_EAUTH = -16, /**< Authentication error */ GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */ GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */ GIT_EPEEL = -19, /**< The requested peel operation is not possible */ GIT_EEOF = -20, /**< Unexpected EOF */ GIT_EINVALID = -21, /**< Invalid operation or input */ GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */ GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */ GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */ GIT_PASSTHROUGH = -30, /**< A user-configured callback refused to act */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ GIT_RETRY = -32, /**< Internal only */ GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */ GIT_EINDEXDIRTY = -34, /**< Unsaved changes in the index would be overwritten */ GIT_EAPPLYFAIL = -35, /**< Patch application failed */ } git_error_code; /** * Structure to store extra details of the last error that occurred. * * This is kept on a per-thread basis if GIT_THREADS was defined when the * library was build, otherwise one is kept globally for the library */ typedef struct { char *message; int klass; } git_error; /** Error classes */ typedef enum { GIT_ERROR_NONE = 0, GIT_ERROR_NOMEMORY, GIT_ERROR_OS, GIT_ERROR_INVALID, GIT_ERROR_REFERENCE, GIT_ERROR_ZLIB, GIT_ERROR_REPOSITORY, GIT_ERROR_CONFIG, GIT_ERROR_REGEX, GIT_ERROR_ODB, GIT_ERROR_INDEX, GIT_ERROR_OBJECT, GIT_ERROR_NET, GIT_ERROR_TAG, GIT_ERROR_TREE, GIT_ERROR_INDEXER, GIT_ERROR_SSL, GIT_ERROR_SUBMODULE, GIT_ERROR_THREAD, GIT_ERROR_STASH, GIT_ERROR_CHECKOUT, GIT_ERROR_FETCHHEAD, GIT_ERROR_MERGE, GIT_ERROR_SSH, GIT_ERROR_FILTER, GIT_ERROR_REVERT, GIT_ERROR_CALLBACK, GIT_ERROR_CHERRYPICK, GIT_ERROR_DESCRIBE, GIT_ERROR_REBASE, GIT_ERROR_FILESYSTEM, GIT_ERROR_PATCH, GIT_ERROR_WORKTREE, GIT_ERROR_SHA1, GIT_ERROR_HTTP, GIT_ERROR_INTERNAL } git_error_t; /** * Return the last `git_error` object that was generated for the * current thread. * * The default behaviour of this function is to return NULL if no previous error has occurred. * However, libgit2's error strings are not cleared aggressively, so a prior * (unrelated) error may be returned. This can be avoided by only calling * this function if the prior call to a libgit2 API returned an error. * * @return A git_error object. */ GIT_EXTERN(const git_error *) git_error_last(void); /** * Clear the last library error that occurred for this thread. */ GIT_EXTERN(void) git_error_clear(void); /** * Set the error message string for this thread. * * This function is public so that custom ODB backends and the like can * relay an error message through libgit2. Most regular users of libgit2 * will never need to call this function -- actually, calling it in most * circumstances (for example, calling from within a callback function) * will just end up having the value overwritten by libgit2 internals. * * This error message is stored in thread-local storage and only applies * to the particular thread that this libgit2 call is made from. * * @param error_class One of the `git_error_t` enum above describing the * general subsystem that is responsible for the error. * @param string The formatted error message to keep * @return 0 on success or -1 on failure */ GIT_EXTERN(int) git_error_set_str(int error_class, const char *string); /** * Set the error message to a special value for memory allocation failure. * * The normal `git_error_set_str()` function attempts to `strdup()` the * string that is passed in. This is not a good idea when the error in * question is a memory allocation failure. That circumstance has a * special setter function that sets the error string to a known and * statically allocated internal value. */ GIT_EXTERN(void) git_error_set_oom(void); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/transport.h0000644000175000017500000000211114125111754020247 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_transport_h__ #define INCLUDE_git_transport_h__ #include "indexer.h" #include "net.h" #include "types.h" #include "cert.h" #include "credential.h" /** * @file git2/transport.h * @brief Git transport interfaces and functions * @defgroup git_transport interfaces and functions * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Callback for messages received by the transport. * * Return a negative value to cancel the network operation. * * @param str The message from the transport * @param len The length of the message * @param payload Payload provided by the caller */ typedef int GIT_CALLBACK(git_transport_message_cb)(const char *str, int len, void *payload); /** Signature of a function which creates a transport */ typedef int GIT_CALLBACK(git_transport_cb)(git_transport **out, git_remote *owner, void *param); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/attr.h0000644000175000017500000003107314125111754017176 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_attr_h__ #define INCLUDE_git_attr_h__ #include "common.h" #include "types.h" /** * @file git2/attr.h * @brief Git attribute management routines * @defgroup git_attr Git attribute management routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * GIT_ATTR_TRUE checks if an attribute is set on. In core git * parlance, this the value for "Set" attributes. * * For example, if the attribute file contains: * * *.c foo * * Then for file `xyz.c` looking up attribute "foo" gives a value for * which `GIT_ATTR_TRUE(value)` is true. */ #define GIT_ATTR_IS_TRUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_TRUE) /** * GIT_ATTR_FALSE checks if an attribute is set off. In core git * parlance, this is the value for attributes that are "Unset" (not to * be confused with values that a "Unspecified"). * * For example, if the attribute file contains: * * *.h -foo * * Then for file `zyx.h` looking up attribute "foo" gives a value for * which `GIT_ATTR_FALSE(value)` is true. */ #define GIT_ATTR_IS_FALSE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_FALSE) /** * GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This * may be due to the attribute not being mentioned at all or because * the attribute was explicitly set unspecified via the `!` operator. * * For example, if the attribute file contains: * * *.c foo * *.h -foo * onefile.c !foo * * Then for `onefile.c` looking up attribute "foo" yields a value with * `GIT_ATTR_UNSPECIFIED(value)` of true. Also, looking up "foo" on * file `onefile.rb` or looking up "bar" on any file will all give * `GIT_ATTR_UNSPECIFIED(value)` of true. */ #define GIT_ATTR_IS_UNSPECIFIED(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_UNSPECIFIED) /** * GIT_ATTR_HAS_VALUE checks if an attribute is set to a value (as * opposed to TRUE, FALSE or UNSPECIFIED). This would be the case if * for a file with something like: * * *.txt eol=lf * * Given this, looking up "eol" for `onefile.txt` will give back the * string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true. */ #define GIT_ATTR_HAS_VALUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_STRING) /** * Possible states for an attribute */ typedef enum { GIT_ATTR_VALUE_UNSPECIFIED = 0, /**< The attribute has been left unspecified */ GIT_ATTR_VALUE_TRUE, /**< The attribute has been set */ GIT_ATTR_VALUE_FALSE, /**< The attribute has been unset */ GIT_ATTR_VALUE_STRING, /**< This attribute has a value */ } git_attr_value_t; /** * Return the value type for a given attribute. * * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute * was not set at all), or `VALUE`, if the attribute was set to an * actual string. * * If the attribute has a `VALUE` string, it can be accessed normally * as a NULL-terminated C string. * * @param attr The attribute * @return the value type for the attribute */ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr); /** * Check attribute flags: Reading values from index and working directory. * * When checking attributes, it is possible to check attribute files * in both the working directory (if there is one) and the index (if * there is one). You can explicitly choose where to check and in * which order using the following flags. * * Core git usually checks the working directory then the index, * except during a checkout when it checks the index first. It will * use index only for creating archives or for a bare repo (if an * index has been specified for the bare repo). */ #define GIT_ATTR_CHECK_FILE_THEN_INDEX 0 #define GIT_ATTR_CHECK_INDEX_THEN_FILE 1 #define GIT_ATTR_CHECK_INDEX_ONLY 2 /** * Check attribute flags: controlling extended attribute behavior. * * Normally, attribute checks include looking in the /etc (or system * equivalent) directory for a `gitattributes` file. Passing this * flag will cause attribute checks to ignore that file. * equivalent) directory for a `gitattributes` file. Passing the * `GIT_ATTR_CHECK_NO_SYSTEM` flag will cause attribute checks to * ignore that file. * * Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes * from a `.gitattributes` file in the repository at the HEAD revision. * * Passing the `GIT_ATTR_CHECK_INCLUDE_COMMIT` flag will use attributes * from a `.gitattributes` file in a specific commit. */ #define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2) #define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3) #define GIT_ATTR_CHECK_INCLUDE_COMMIT (1 << 4) /** * An options structure for querying attributes. */ typedef struct { unsigned int version; /** A combination of GIT_ATTR_CHECK flags */ unsigned int flags; #ifdef GIT_DEPRECATE_HARD void *reserved; #else git_oid *commit_id; #endif /** * The commit to load attributes from, when * `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified. */ git_oid attr_commit_id; } git_attr_options; #define GIT_ATTR_OPTIONS_VERSION 1 #define GIT_ATTR_OPTIONS_INIT {GIT_ATTR_OPTIONS_VERSION} /** * Look up the value of one git attribute for path. * * @param value_out Output of the value of the attribute. Use the GIT_ATTR_... * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just * use the string value for attributes set to a value. You * should NOT modify or free this value. * @param repo The repository containing the path. * @param flags A combination of GIT_ATTR_CHECK... flags. * @param path The path to check for attributes. Relative paths are * interpreted relative to the repo root. The file does * not have to exist, but if it does not, then it will be * treated as a plain file (not a directory). * @param name The name of the attribute to look up. */ GIT_EXTERN(int) git_attr_get( const char **value_out, git_repository *repo, uint32_t flags, const char *path, const char *name); /** * Look up the value of one git attribute for path with extended options. * * @param value_out Output of the value of the attribute. Use the GIT_ATTR_... * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just * use the string value for attributes set to a value. You * should NOT modify or free this value. * @param repo The repository containing the path. * @param opts The `git_attr_options` to use when querying these attributes. * @param path The path to check for attributes. Relative paths are * interpreted relative to the repo root. The file does * not have to exist, but if it does not, then it will be * treated as a plain file (not a directory). * @param name The name of the attribute to look up. */ GIT_EXTERN(int) git_attr_get_ext( const char **value_out, git_repository *repo, git_attr_options *opts, const char *path, const char *name); /** * Look up a list of git attributes for path. * * Use this if you have a known list of attributes that you want to * look up in a single call. This is somewhat more efficient than * calling `git_attr_get()` multiple times. * * For example, you might write: * * const char *attrs[] = { "crlf", "diff", "foo" }; * const char **values[3]; * git_attr_get_many(values, repo, 0, "my/fun/file.c", 3, attrs); * * Then you could loop through the 3 values to get the settings for * the three attributes you asked about. * * @param values_out An array of num_attr entries that will have string * pointers written into it for the values of the attributes. * You should not modify or free the values that are written * into this array (although of course, you should free the * array itself if you allocated it). * @param repo The repository containing the path. * @param flags A combination of GIT_ATTR_CHECK... flags. * @param path The path inside the repo to check attributes. This * does not have to exist, but if it does not, then * it will be treated as a plain file (i.e. not a directory). * @param num_attr The number of attributes being looked up * @param names An array of num_attr strings containing attribute names. */ GIT_EXTERN(int) git_attr_get_many( const char **values_out, git_repository *repo, uint32_t flags, const char *path, size_t num_attr, const char **names); /** * Look up a list of git attributes for path with extended options. * * @param values_out An array of num_attr entries that will have string * pointers written into it for the values of the attributes. * You should not modify or free the values that are written * into this array (although of course, you should free the * array itself if you allocated it). * @param repo The repository containing the path. * @param opts The `git_attr_options` to use when querying these attributes. * @param path The path inside the repo to check attributes. This * does not have to exist, but if it does not, then * it will be treated as a plain file (i.e. not a directory). * @param num_attr The number of attributes being looked up * @param names An array of num_attr strings containing attribute names. */ GIT_EXTERN(int) git_attr_get_many_ext( const char **values_out, git_repository *repo, git_attr_options *opts, const char *path, size_t num_attr, const char **names); /** * The callback used with git_attr_foreach. * * This callback will be invoked only once per attribute name, even if there * are multiple rules for a given file. The highest priority rule will be * used. * * @see git_attr_foreach. * * @param name The attribute name. * @param value The attribute value. May be NULL if the attribute is explicitly * set to UNSPECIFIED using the '!' sign. * @param payload A user-specified pointer. * @return 0 to continue looping, non-zero to stop. This value will be returned * from git_attr_foreach. */ typedef int GIT_CALLBACK(git_attr_foreach_cb)(const char *name, const char *value, void *payload); /** * Loop over all the git attributes for a path. * * @param repo The repository containing the path. * @param flags A combination of GIT_ATTR_CHECK... flags. * @param path Path inside the repo to check attributes. This does not have * to exist, but if it does not, then it will be treated as a * plain file (i.e. not a directory). * @param callback Function to invoke on each attribute name and value. * See git_attr_foreach_cb. * @param payload Passed on as extra parameter to callback function. * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_attr_foreach( git_repository *repo, uint32_t flags, const char *path, git_attr_foreach_cb callback, void *payload); /** * Loop over all the git attributes for a path with extended options. * * @param repo The repository containing the path. * @param opts The `git_attr_options` to use when querying these attributes. * @param path Path inside the repo to check attributes. This does not have * to exist, but if it does not, then it will be treated as a * plain file (i.e. not a directory). * @param callback Function to invoke on each attribute name and value. * See git_attr_foreach_cb. * @param payload Passed on as extra parameter to callback function. * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_attr_foreach_ext( git_repository *repo, git_attr_options *opts, const char *path, git_attr_foreach_cb callback, void *payload); /** * Flush the gitattributes cache. * * Call this if you have reason to believe that the attributes files on * disk no longer match the cached contents of memory. This will cause * the attributes files to be reloaded the next time that an attribute * access function is called. * * @param repo The repository containing the gitattributes cache * @return 0 on success, or an error code */ GIT_EXTERN(int) git_attr_cache_flush( git_repository *repo); /** * Add a macro definition. * * Macros will automatically be loaded from the top level `.gitattributes` * file of the repository (plus the build-in "binary" macro). This * function allows you to add others. For example, to add the default * macro, you would call: * * git_attr_add_macro(repo, "binary", "-diff -crlf"); */ GIT_EXTERN(int) git_attr_add_macro( git_repository *repo, const char *name, const char *values); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/filter.h0000644000175000017500000001760314125111754017514 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_filter_h__ #define INCLUDE_git_filter_h__ #include "common.h" #include "types.h" #include "oid.h" #include "buffer.h" /** * @file git2/filter.h * @brief Git filter APIs * * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Filters are applied in one of two directions: smudging - which is * exporting a file from the Git object database to the working directory, * and cleaning - which is importing a file from the working directory to * the Git object database. These values control which direction of * change is being applied. */ typedef enum { GIT_FILTER_TO_WORKTREE = 0, GIT_FILTER_SMUDGE = GIT_FILTER_TO_WORKTREE, GIT_FILTER_TO_ODB = 1, GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB, } git_filter_mode_t; /** * Filter option flags. */ typedef enum { GIT_FILTER_DEFAULT = 0u, /** Don't error for `safecrlf` violations, allow them to continue. */ GIT_FILTER_ALLOW_UNSAFE = (1u << 0), /** Don't load `/etc/gitattributes` (or the system equivalent) */ GIT_FILTER_NO_SYSTEM_ATTRIBUTES = (1u << 1), /** Load attributes from `.gitattributes` in the root of HEAD */ GIT_FILTER_ATTRIBUTES_FROM_HEAD = (1u << 2), /** * Load attributes from `.gitattributes` in a given commit. * This can only be specified in a `git_filter_options`. */ GIT_FILTER_ATTRIBUTES_FROM_COMMIT = (1u << 3), } git_filter_flag_t; /** * Filtering options */ typedef struct { unsigned int version; /** See `git_filter_flag_t` above */ uint32_t flags; #ifdef GIT_DEPRECATE_HARD void *reserved; #else git_oid *commit_id; #endif /** * The commit to load attributes from, when * `GIT_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. */ git_oid attr_commit_id; } git_filter_options; #define GIT_FILTER_OPTIONS_VERSION 1 #define GIT_FILTER_OPTIONS_INIT {GIT_FILTER_OPTIONS_VERSION} /** * A filter that can transform file data * * This represents a filter that can be used to transform or even replace * file data. Libgit2 includes one built in filter and it is possible to * write your own (see git2/sys/filter.h for information on that). * * The two builtin filters are: * * * "crlf" which uses the complex rules with the "text", "eol", and * "crlf" file attributes to decide how to convert between LF and CRLF * line endings * * "ident" which replaces "$Id$" in a blob with "$Id: $" upon * checkout and replaced "$Id: $" with "$Id$" on checkin. */ typedef struct git_filter git_filter; /** * List of filters to be applied * * This represents a list of filters to be applied to a file / blob. You * can build the list with one call, apply it with another, and dispose it * with a third. In typical usage, there are not many occasions where a * git_filter_list is needed directly since the library will generally * handle conversions for you, but it can be convenient to be able to * build and apply the list sometimes. */ typedef struct git_filter_list git_filter_list; /** * Load the filter list for a given path. * * This will return 0 (success) but set the output git_filter_list to NULL * if no filters are requested for the given file. * * @param filters Output newly created git_filter_list (or NULL) * @param repo Repository object that contains `path` * @param blob The blob to which the filter will be applied (if known) * @param path Relative path of the file to be filtered * @param mode Filtering direction (WT->ODB or ODB->WT) * @param flags Combination of `git_filter_flag_t` flags * @return 0 on success (which could still return NULL if no filters are * needed for the requested file), <0 on error */ GIT_EXTERN(int) git_filter_list_load( git_filter_list **filters, git_repository *repo, git_blob *blob, /* can be NULL */ const char *path, git_filter_mode_t mode, uint32_t flags); /** * Load the filter list for a given path. * * This will return 0 (success) but set the output git_filter_list to NULL * if no filters are requested for the given file. * * @param filters Output newly created git_filter_list (or NULL) * @param repo Repository object that contains `path` * @param blob The blob to which the filter will be applied (if known) * @param path Relative path of the file to be filtered * @param mode Filtering direction (WT->ODB or ODB->WT) * @param opts The `git_filter_options` to use when loading filters * @return 0 on success (which could still return NULL if no filters are * needed for the requested file), <0 on error */ GIT_EXTERN(int) git_filter_list_load_ext( git_filter_list **filters, git_repository *repo, git_blob *blob, const char *path, git_filter_mode_t mode, git_filter_options *opts); /** * Query the filter list to see if a given filter (by name) will run. * The built-in filters "crlf" and "ident" can be queried, otherwise this * is the name of the filter specified by the filter attribute. * * This will return 0 if the given filter is not in the list, or 1 if * the filter will be applied. * * @param filters A loaded git_filter_list (or NULL) * @param name The name of the filter to query * @return 1 if the filter is in the list, 0 otherwise */ GIT_EXTERN(int) git_filter_list_contains( git_filter_list *filters, const char *name); /** * Apply filter list to a data buffer. * * @param out Buffer to store the result of the filtering * @param filters A loaded git_filter_list (or NULL) * @param in Buffer containing the data to filter * @param in_len The length of the input buffer * @return 0 on success, an error code otherwise */ GIT_EXTERN(int) git_filter_list_apply_to_buffer( git_buf *out, git_filter_list *filters, const char *in, size_t in_len); /** * Apply a filter list to the contents of a file on disk * * @param out buffer into which to store the filtered file * @param filters the list of filters to apply * @param repo the repository in which to perform the filtering * @param path the path of the file to filter, a relative path will be * taken as relative to the workdir */ GIT_EXTERN(int) git_filter_list_apply_to_file( git_buf *out, git_filter_list *filters, git_repository *repo, const char *path); /** * Apply a filter list to the contents of a blob * * @param out buffer into which to store the filtered file * @param filters the list of filters to apply * @param blob the blob to filter */ GIT_EXTERN(int) git_filter_list_apply_to_blob( git_buf *out, git_filter_list *filters, git_blob *blob); /** * Apply a filter list to an arbitrary buffer as a stream * * @param filters the list of filters to apply * @param buffer the buffer to filter * @param len the size of the buffer * @param target the stream into which the data will be written */ GIT_EXTERN(int) git_filter_list_stream_buffer( git_filter_list *filters, const char *buffer, size_t len, git_writestream *target); /** * Apply a filter list to a file as a stream * * @param filters the list of filters to apply * @param repo the repository in which to perform the filtering * @param path the path of the file to filter, a relative path will be * taken as relative to the workdir * @param target the stream into which the data will be written */ GIT_EXTERN(int) git_filter_list_stream_file( git_filter_list *filters, git_repository *repo, const char *path, git_writestream *target); /** * Apply a filter list to a blob as a stream * * @param filters the list of filters to apply * @param blob the blob to filter * @param target the stream into which the data will be written */ GIT_EXTERN(int) git_filter_list_stream_blob( git_filter_list *filters, git_blob *blob, git_writestream *target); /** * Free a git_filter_list * * @param filters A git_filter_list created by `git_filter_list_load` */ GIT_EXTERN(void) git_filter_list_free(git_filter_list *filters); GIT_END_DECL /** @} */ #endif git2r/src/libgit2/include/git2/common.h0000644000175000017500000003644714125111754017526 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_common_h__ #define INCLUDE_git_common_h__ #include #include #ifdef __cplusplus # define GIT_BEGIN_DECL extern "C" { # define GIT_END_DECL } #else /** Start declarations in C mode */ # define GIT_BEGIN_DECL /* empty */ /** End declarations in C mode */ # define GIT_END_DECL /* empty */ #endif #if defined(_MSC_VER) && _MSC_VER < 1800 # include #elif !defined(__CLANG_INTTYPES_H) # include #endif #ifdef DOCURIUM /* * This is so clang's doc parser acknowledges comments on functions * with size_t parameters. */ typedef size_t size_t; #endif /** Declare a public function exported for application use. */ #if __GNUC__ >= 4 # define GIT_EXTERN(type) extern \ __attribute__((visibility("default"))) \ type #elif defined(_MSC_VER) # define GIT_EXTERN(type) __declspec(dllexport) type __cdecl #else # define GIT_EXTERN(type) extern type #endif /** Declare a callback function for application use. */ #if defined(_MSC_VER) # define GIT_CALLBACK(name) (__cdecl *name) #else # define GIT_CALLBACK(name) (*name) #endif /** Declare a function as deprecated. */ #if defined(__GNUC__) # define GIT_DEPRECATED(func) \ __attribute__((deprecated)) \ __attribute__((used)) \ func #elif defined(_MSC_VER) # define GIT_DEPRECATED(func) __declspec(deprecated) func #else # define GIT_DEPRECATED(func) func #endif /** Declare a function's takes printf style arguments. */ #ifdef __GNUC__ # define GIT_FORMAT_PRINTF(a,b) __attribute__((format (printf, a, b))) #else # define GIT_FORMAT_PRINTF(a,b) /* empty */ #endif #if (defined(_WIN32)) && !defined(__CYGWIN__) #define GIT_WIN32 1 #endif #ifdef __amigaos4__ #include #endif /** * @file git2/common.h * @brief Git common platform definitions * @defgroup git_common Git common platform definitions * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * The separator used in path list strings (ie like in the PATH * environment variable). A semi-colon ";" is used on Windows and * AmigaOS, and a colon ":" for all other systems. */ #if defined(GIT_WIN32) || defined(AMIGA) #define GIT_PATH_LIST_SEPARATOR ';' #else #define GIT_PATH_LIST_SEPARATOR ':' #endif /** * The maximum length of a valid git path. */ #define GIT_PATH_MAX 4096 /** * The string representation of the null object ID. */ #define GIT_OID_HEX_ZERO "0000000000000000000000000000000000000000" /** * Return the version of the libgit2 library * being currently used. * * @param major Store the major version number * @param minor Store the minor version number * @param rev Store the revision (patch) number * @return 0 on success or an error code on failure */ GIT_EXTERN(int) git_libgit2_version(int *major, int *minor, int *rev); /** * Combinations of these values describe the features with which libgit2 * was compiled */ typedef enum { /** * If set, libgit2 was built thread-aware and can be safely used from multiple * threads. */ GIT_FEATURE_THREADS = (1 << 0), /** * If set, libgit2 was built with and linked against a TLS implementation. * Custom TLS streams may still be added by the user to support HTTPS * regardless of this. */ GIT_FEATURE_HTTPS = (1 << 1), /** * If set, libgit2 was built with and linked against libssh2. A custom * transport may still be added by the user to support libssh2 regardless of * this. */ GIT_FEATURE_SSH = (1 << 2), /** * If set, libgit2 was built with support for sub-second resolution in file * modification times. */ GIT_FEATURE_NSEC = (1 << 3), } git_feature_t; /** * Query compile time options for libgit2. * * @return A combination of GIT_FEATURE_* values. * * - GIT_FEATURE_THREADS * Libgit2 was compiled with thread support. Note that thread support is * still to be seen as a 'work in progress' - basic object lookups are * believed to be threadsafe, but other operations may not be. * * - GIT_FEATURE_HTTPS * Libgit2 supports the https:// protocol. This requires the openssl * library to be found when compiling libgit2. * * - GIT_FEATURE_SSH * Libgit2 supports the SSH protocol for network operations. This requires * the libssh2 library to be found when compiling libgit2 */ GIT_EXTERN(int) git_libgit2_features(void); /** * Global library options * * These are used to select which global option to set or get and are * used in `git_libgit2_opts()`. */ typedef enum { GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE, GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH, GIT_OPT_SET_CACHE_OBJECT_LIMIT, GIT_OPT_SET_CACHE_MAX_SIZE, GIT_OPT_ENABLE_CACHING, GIT_OPT_GET_CACHED_MEMORY, GIT_OPT_GET_TEMPLATE_PATH, GIT_OPT_SET_TEMPLATE_PATH, GIT_OPT_SET_SSL_CERT_LOCATIONS, GIT_OPT_SET_USER_AGENT, GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, GIT_OPT_SET_SSL_CIPHERS, GIT_OPT_GET_USER_AGENT, GIT_OPT_ENABLE_OFS_DELTA, GIT_OPT_ENABLE_FSYNC_GITDIR, GIT_OPT_GET_WINDOWS_SHAREMODE, GIT_OPT_SET_WINDOWS_SHAREMODE, GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, GIT_OPT_SET_ALLOCATOR, GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, GIT_OPT_GET_PACK_MAX_OBJECTS, GIT_OPT_SET_PACK_MAX_OBJECTS, GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, GIT_OPT_GET_MWINDOW_FILE_LIMIT, GIT_OPT_SET_MWINDOW_FILE_LIMIT, GIT_OPT_SET_ODB_PACKED_PRIORITY, GIT_OPT_SET_ODB_LOOSE_PRIORITY, GIT_OPT_GET_EXTENSIONS, GIT_OPT_SET_EXTENSIONS } git_libgit2_opt_t; /** * Set or query a library global option * * Available options: * * * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *): * * > Get the maximum mmap window size * * * opts(GIT_OPT_SET_MWINDOW_SIZE, size_t): * * > Set the maximum mmap window size * * * opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *): * * > Get the maximum memory that will be mapped in total by the library * * * opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t): * * > Set the maximum amount of memory that can be mapped at any time * > by the library * * * opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, size_t *): * * > Get the maximum number of files that will be mapped at any time by the * > library * * * opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, size_t): * * > Set the maximum number of files that can be mapped at any time * > by the library. The default (0) is unlimited. * * * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf) * * > Get the search path for a given level of config data. "level" must * > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, * > `GIT_CONFIG_LEVEL_XDG`, or `GIT_CONFIG_LEVEL_PROGRAMDATA`. * > The search path is written to the `out` buffer. * * * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) * * > Set the search path for a level of config data. The search path * > applied to shared attributes and ignore files, too. * > * > - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR. * > Pass NULL to reset to the default (generally based on environment * > variables). Use magic path `$PATH` to include the old value * > of the path (if you want to prepend or append, for instance). * > * > - `level` must be `GIT_CONFIG_LEVEL_SYSTEM`, * > `GIT_CONFIG_LEVEL_GLOBAL`, `GIT_CONFIG_LEVEL_XDG`, or * > `GIT_CONFIG_LEVEL_PROGRAMDATA`. * * * opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_object_t type, size_t size) * * > Set the maximum data size for the given type of object to be * > considered eligible for caching in memory. Setting to value to * > zero means that that type of object will not be cached. * > Defaults to 0 for GIT_OBJECT_BLOB (i.e. won't cache blobs) and 4k * > for GIT_OBJECT_COMMIT, GIT_OBJECT_TREE, and GIT_OBJECT_TAG. * * * opts(GIT_OPT_SET_CACHE_MAX_SIZE, ssize_t max_storage_bytes) * * > Set the maximum total data size that will be cached in memory * > across all repositories before libgit2 starts evicting objects * > from the cache. This is a soft limit, in that the library might * > briefly exceed it, but will start aggressively evicting objects * > from cache when that happens. The default cache size is 256MB. * * * opts(GIT_OPT_ENABLE_CACHING, int enabled) * * > Enable or disable caching completely. * > * > Because caches are repository-specific, disabling the cache * > cannot immediately clear all cached objects, but each cache will * > be cleared on the next attempt to update anything in it. * * * opts(GIT_OPT_GET_CACHED_MEMORY, ssize_t *current, ssize_t *allowed) * * > Get the current bytes in cache and the maximum that would be * > allowed in the cache. * * * opts(GIT_OPT_GET_TEMPLATE_PATH, git_buf *out) * * > Get the default template path. * > The path is written to the `out` buffer. * * * opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path) * * > Set the default template path. * > * > - `path` directory of template. * * * opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, const char *file, const char *path) * * > Set the SSL certificate-authority locations. * > * > - `file` is the location of a file containing several * > certificates concatenated together. * > - `path` is the location of a directory holding several * > certificates, one per file. * > * > Either parameter may be `NULL`, but not both. * * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent) * * > Set the value of the User-Agent header. This value will be * > appended to "git/1.0", for compatibility with other git clients. * > * > - `user_agent` is the value that will be delivered as the * > User-Agent header on HTTP requests. * * * opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value) * * > Set the share mode used when opening files on Windows. * > For more information, see the documentation for CreateFile. * > The default is: FILE_SHARE_READ | FILE_SHARE_WRITE. This is * > ignored and unused on non-Windows platforms. * * * opts(GIT_OPT_GET_WINDOWS_SHAREMODE, unsigned long *value) * * > Get the share mode used when opening files on Windows. * * * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled) * * > Enable strict input validation when creating new objects * > to ensure that all inputs to the new objects are valid. For * > example, when this is enabled, the parent(s) and tree inputs * > will be validated when creating a new commit. This defaults * > to enabled. * * * opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, int enabled) * * > Validate the target of a symbolic ref when creating it. For * > example, `foobar` is not a valid ref, therefore `foobar` is * > not a valid target for a symbolic ref by default, whereas * > `refs/heads/foobar` is. Disabling this bypasses validation * > so that an arbitrary strings such as `foobar` can be used * > for a symbolic ref target. This defaults to enabled. * * * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers) * * > Set the SSL ciphers use for HTTPS connections. * > * > - `ciphers` is the list of ciphers that are eanbled. * * * opts(GIT_OPT_GET_USER_AGENT, git_buf *out) * * > Get the value of the User-Agent header. * > The User-Agent is written to the `out` buffer. * * * opts(GIT_OPT_ENABLE_OFS_DELTA, int enabled) * * > Enable or disable the use of "offset deltas" when creating packfiles, * > and the negotiation of them when talking to a remote server. * > Offset deltas store a delta base location as an offset into the * > packfile from the current location, which provides a shorter encoding * > and thus smaller resultant packfiles. * > Packfiles containing offset deltas can still be read. * > This defaults to enabled. * * * opts(GIT_OPT_ENABLE_FSYNC_GITDIR, int enabled) * * > Enable synchronized writes of files in the gitdir using `fsync` * > (or the platform equivalent) to ensure that new object data * > is written to permanent storage, not simply cached. This * > defaults to disabled. * * opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, int enabled) * * > Enable strict verification of object hashsums when reading * > objects from disk. This may impact performance due to an * > additional checksum calculation on each object. This defaults * > to enabled. * * opts(GIT_OPT_SET_ALLOCATOR, git_allocator *allocator) * * > Set the memory allocator to a different memory allocator. This * > allocator will then be used to make all memory allocations for * > libgit2 operations. If the given `allocator` is NULL, then the * > system default will be restored. * * opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, int enabled) * * > Ensure that there are no unsaved changes in the index before * > beginning any operation that reloads the index from disk (eg, * > checkout). If there are unsaved changes, the instruction will * > fail. (Using the FORCE flag to checkout will still overwrite * > these changes.) * * opts(GIT_OPT_GET_PACK_MAX_OBJECTS, size_t *out) * * > Get the maximum number of objects libgit2 will allow in a pack * > file when downloading a pack file from a remote. This can be * > used to limit maximum memory usage when fetching from an untrusted * > remote. * * opts(GIT_OPT_SET_PACK_MAX_OBJECTS, size_t objects) * * > Set the maximum number of objects libgit2 will allow in a pack * > file when downloading a pack file from a remote. * * opts(GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, int enabled) * > This will cause .keep file existence checks to be skipped when * > accessing packfiles, which can help performance with remote filesystems. * * opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, int enabled) * > When connecting to a server using NTLM or Negotiate * > authentication, use expect/continue when POSTing data. * > This option is not available on Windows. * * opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, int priority) * > Override the default priority of the packed ODB backend which * > is added when default backends are assigned to a repository * * opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, int priority) * > Override the default priority of the loose ODB backend which * > is added when default backends are assigned to a repository * * opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out) * > Returns the list of git extensions that are supported. This * > is the list of built-in extensions supported by libgit2 and * > custom extensions that have been added with * > `GIT_OPT_SET_EXTENSIONS`. Extensions that have been negated * > will not be returned. The returned list should be released * > with `git_strarray_dispose`. * * opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len) * > Set that the given git extensions are supported by the caller. * > Extensions supported by libgit2 may be negated by prefixing * > them with a `!`. For example: setting extensions to * > { "!noop", "newext" } indicates that the caller does not want * > to support repositories with the `noop` extension but does want * > to support repositories with the `newext` extension. * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_libgit2_opts(int option, ...); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/indexer.h0000644000175000017500000001010414125111754017652 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef _INCLUDE_git_indexer_h__ #define _INCLUDE_git_indexer_h__ #include "common.h" #include "types.h" #include "oid.h" GIT_BEGIN_DECL /** A git indexer object */ typedef struct git_indexer git_indexer; /** * This structure is used to provide callers information about the * progress of indexing a packfile, either directly or part of a * fetch or clone that downloads a packfile. */ typedef struct git_indexer_progress { /** number of objects in the packfile being indexed */ unsigned int total_objects; /** received objects that have been hashed */ unsigned int indexed_objects; /** received_objects: objects which have been downloaded */ unsigned int received_objects; /** * locally-available objects that have been injected in order * to fix a thin pack */ unsigned int local_objects; /** number of deltas in the packfile being indexed */ unsigned int total_deltas; /** received deltas that have been indexed */ unsigned int indexed_deltas; /** size of the packfile received up to now */ size_t received_bytes; } git_indexer_progress; /** * Type for progress callbacks during indexing. Return a value less * than zero to cancel the indexing or download. * * @param stats Structure containing information about the state of the transfer * @param payload Payload provided by caller */ typedef int GIT_CALLBACK(git_indexer_progress_cb)(const git_indexer_progress *stats, void *payload); /** * Options for indexer configuration */ typedef struct git_indexer_options { unsigned int version; /** progress_cb function to call with progress information */ git_indexer_progress_cb progress_cb; /** progress_cb_payload payload for the progress callback */ void *progress_cb_payload; /** Do connectivity checks for the received pack */ unsigned char verify; } git_indexer_options; #define GIT_INDEXER_OPTIONS_VERSION 1 #define GIT_INDEXER_OPTIONS_INIT { GIT_INDEXER_OPTIONS_VERSION } /** * Initializes a `git_indexer_options` with default values. Equivalent to * creating an instance with GIT_INDEXER_OPTIONS_INIT. * * @param opts the `git_indexer_options` struct to initialize. * @param version Version of struct; pass `GIT_INDEXER_OPTIONS_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_indexer_options_init( git_indexer_options *opts, unsigned int version); /** * Create a new indexer instance * * @param out where to store the indexer instance * @param path to the directory where the packfile should be stored * @param mode permissions to use creating packfile or 0 for defaults * @param odb object database from which to read base objects when * fixing thin packs. Pass NULL if no thin pack is expected (an error * will be returned if there are bases missing) * @param opts Optional structure containing additional options. See * `git_indexer_options` above. */ GIT_EXTERN(int) git_indexer_new( git_indexer **out, const char *path, unsigned int mode, git_odb *odb, git_indexer_options *opts); /** * Add data to the indexer * * @param idx the indexer * @param data the data to add * @param size the size of the data in bytes * @param stats stat storage */ GIT_EXTERN(int) git_indexer_append(git_indexer *idx, const void *data, size_t size, git_indexer_progress *stats); /** * Finalize the pack and index * * Resolve any pending deltas and write out the index file * * @param idx the indexer */ GIT_EXTERN(int) git_indexer_commit(git_indexer *idx, git_indexer_progress *stats); /** * Get the packfile's hash * * A packfile's name is derived from the sorted hashing of all object * names. This is only correct after the index has been finalized. * * @param idx the indexer instance */ GIT_EXTERN(const git_oid *) git_indexer_hash(const git_indexer *idx); /** * Free the indexer and its resources * * @param idx the indexer to free */ GIT_EXTERN(void) git_indexer_free(git_indexer *idx); GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/0000755000175000017500000000000014125111754016665 5ustar nileshnileshgit2r/src/libgit2/include/git2/sys/commit.h0000644000175000017500000000452514125111754020334 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_commit_h__ #define INCLUDE_sys_git_commit_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/oid.h" /** * @file git2/sys/commit.h * @brief Low-level Git commit creation * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create new commit in the repository from a list of `git_oid` values. * * See documentation for `git_commit_create()` for information about the * parameters, as the meaning is identical excepting that `tree` and * `parents` now take `git_oid`. This is a dangerous API in that nor * the `tree`, neither the `parents` list of `git_oid`s are checked for * validity. * * @see git_commit_create */ GIT_EXTERN(int) git_commit_create_from_ids( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_oid *tree, size_t parent_count, const git_oid *parents[]); /** * Callback function to return parents for commit. * * This is invoked with the count of the number of parents processed so far * along with the user supplied payload. This should return a git_oid of * the next parent or NULL if all parents have been provided. */ typedef const git_oid * GIT_CALLBACK(git_commit_parent_callback)(size_t idx, void *payload); /** * Create a new commit in the repository with an callback to supply parents. * * See documentation for `git_commit_create()` for information about the * parameters, as the meaning is identical excepting that `tree` takes a * `git_oid` and doesn't check for validity, and `parent_cb` is invoked * with `parent_payload` and should return `git_oid` values or NULL to * indicate that all parents are accounted for. * * @see git_commit_create */ GIT_EXTERN(int) git_commit_create_from_callback( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_oid *tree, git_commit_parent_callback parent_cb, void *parent_payload); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/refdb_backend.h0000644000175000017500000003070714125111754021576 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_refdb_backend_h__ #define INCLUDE_sys_git_refdb_backend_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/oid.h" /** * @file git2/refdb_backend.h * @brief Git custom refs backend functions * @defgroup git_refdb_backend Git custom refs backend API * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Every backend's iterator must have a pointer to itself as the first * element, so the API can talk to it. You'd define your iterator as * * struct my_iterator { * git_reference_iterator parent; * ... * } * * and assign `iter->parent.backend` to your `git_refdb_backend`. */ struct git_reference_iterator { git_refdb *db; /** * Return the current reference and advance the iterator. */ int GIT_CALLBACK(next)( git_reference **ref, git_reference_iterator *iter); /** * Return the name of the current reference and advance the iterator */ int GIT_CALLBACK(next_name)( const char **ref_name, git_reference_iterator *iter); /** * Free the iterator */ void GIT_CALLBACK(free)( git_reference_iterator *iter); }; /** An instance for a custom backend */ struct git_refdb_backend { unsigned int version; /**< The backend API version */ /** * Queries the refdb backend for the existence of a reference. * * A refdb implementation must provide this function. * * @arg exists The implementation shall set this to `0` if a ref does * not exist, otherwise to `1`. * @arg ref_name The reference's name that should be checked for * existence. * @return `0` on success, a negative error value code. */ int GIT_CALLBACK(exists)( int *exists, git_refdb_backend *backend, const char *ref_name); /** * Queries the refdb backend for a given reference. * * A refdb implementation must provide this function. * * @arg out The implementation shall set this to the allocated * reference, if it could be found, otherwise to `NULL`. * @arg ref_name The reference's name that should be checked for * existence. * @return `0` on success, `GIT_ENOTFOUND` if the reference does * exist, otherwise a negative error code. */ int GIT_CALLBACK(lookup)( git_reference **out, git_refdb_backend *backend, const char *ref_name); /** * Allocate an iterator object for the backend. * * A refdb implementation must provide this function. * * @arg out The implementation shall set this to the allocated * reference iterator. A custom structure may be used with an * embedded `git_reference_iterator` structure. Both `next` * and `next_name` functions of `git_reference_iterator` need * to be populated. * @arg glob A pattern to filter references by. If given, the iterator * shall only return references that match the glob when * passed to `wildmatch`. * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(iterator)( git_reference_iterator **iter, struct git_refdb_backend *backend, const char *glob); /** * Writes the given reference to the refdb. * * A refdb implementation must provide this function. * * @arg ref The reference to persist. May either be a symbolic or * direct reference. * @arg force Whether to write the reference if a reference with the * same name already exists. * @arg who The person updating the reference. Shall be used to create * a reflog entry. * @arg message The message detailing what kind of reference update is * performed. Shall be used to create a reflog entry. * @arg old If not `NULL` and `force` is not set, then the * implementation needs to ensure that the reference is currently at * the given OID before writing the new value. If both `old` * and `old_target` are `NULL`, then the reference should not * exist at the point of writing. * @arg old_target If not `NULL` and `force` is not set, then the * implementation needs to ensure that the symbolic * reference is currently at the given target before * writing the new value. If both `old` and * `old_target` are `NULL`, then the reference should * not exist at the point of writing. * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(write)(git_refdb_backend *backend, const git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old, const char *old_target); /** * Rename a reference in the refdb. * * A refdb implementation must provide this function. * * @arg out The implementation shall set this to the newly created * reference or `NULL` on error. * @arg old_name The current name of the reference that is to be renamed. * @arg new_name The new name that the old reference shall be renamed to. * @arg force Whether to write the reference if a reference with the * target name already exists. * @arg who The person updating the reference. Shall be used to create * a reflog entry. * @arg message The message detailing what kind of reference update is * performed. Shall be used to create a reflog entry. * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(rename)( git_reference **out, git_refdb_backend *backend, const char *old_name, const char *new_name, int force, const git_signature *who, const char *message); /** * Deletes the given reference from the refdb. * * If it exists, its reflog should be deleted as well. * * A refdb implementation must provide this function. * * @arg ref_name The name of the reference name that shall be deleted. * @arg old_id If not `NULL` and `force` is not set, then the * implementation needs to ensure that the reference is currently at * the given OID before writing the new value. * @arg old_target If not `NULL` and `force` is not set, then the * implementation needs to ensure that the symbolic * reference is currently at the given target before * writing the new value. * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target); /** * Suggests that the given refdb compress or optimize its references. * * This mechanism is implementation specific. For on-disk reference * databases, this may pack all loose references. * * A refdb implementation may provide this function; if it is not * provided, nothing will be done. * * @return `0` on success a negative error code otherwise */ int GIT_CALLBACK(compress)(git_refdb_backend *backend); /** * Query whether a particular reference has a log (may be empty) * * Shall return 1 if it has a reflog, 0 it it doesn't and negative in * case an error occurred. * * A refdb implementation must provide this function. * * @return `0` on success, `1` if the reflog for the given reference * exists, a negative error code otherwise */ int GIT_CALLBACK(has_log)(git_refdb_backend *backend, const char *refname); /** * Make sure a particular reference will have a reflog which * will be appended to on writes. * * A refdb implementation must provide this function. * * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(ensure_log)(git_refdb_backend *backend, const char *refname); /** * Frees any resources held by the refdb (including the `git_refdb_backend` * itself). * * A refdb backend implementation must provide this function. */ void GIT_CALLBACK(free)(git_refdb_backend *backend); /** * Read the reflog for the given reference name. * * A refdb implementation must provide this function. * * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_read)(git_reflog **out, git_refdb_backend *backend, const char *name); /** * Write a reflog to disk. * * A refdb implementation must provide this function. * * @arg reflog The complete reference log for a given reference. Note * that this may contain entries that have already been * written to disk. * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_write)(git_refdb_backend *backend, git_reflog *reflog); /** * Rename a reflog. * * A refdb implementation must provide this function. * * @arg old_name The name of old reference whose reflog shall be renamed from. * @arg new_name The name of new reference whose reflog shall be renamed to. * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_rename)(git_refdb_backend *_backend, const char *old_name, const char *new_name); /** * Remove a reflog. * * A refdb implementation must provide this function. * * @arg name The name of the reference whose reflog shall be deleted. * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_delete)(git_refdb_backend *backend, const char *name); /** * Lock a reference. * * A refdb implementation may provide this function; if it is not * provided, the transaction API will fail to work. * * @arg payload_out Opaque parameter that will be passed verbosely to * `unlock`. * @arg refname Reference that shall be locked. * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(lock)(void **payload_out, git_refdb_backend *backend, const char *refname); /** * Unlock a reference. * * Only one of target or symbolic_target will be set. * `success` will be true if the reference should be update, false if * the lock must be discarded. * * A refdb implementation must provide this function if a `lock` * implementation is provided. * * @arg payload The payload returned by `lock`. * @arg success `1` if a reference should be updated, `2` if * a reference should be deleted, `0` if the lock must be * discarded. * @arg update_reflog `1` in case the reflog should be updated, `0` * otherwise. * @arg ref The reference which should be unlocked. * @arg who The person updating the reference. Shall be used to create * a reflog entry in case `update_reflog` is set. * @arg message The message detailing what kind of reference update is * performed. Shall be used to create a reflog entry in * case `update_reflog` is set. * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(unlock)(git_refdb_backend *backend, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message); }; #define GIT_REFDB_BACKEND_VERSION 1 #define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION} /** * Initializes a `git_refdb_backend` with default values. Equivalent to * creating an instance with GIT_REFDB_BACKEND_INIT. * * @param backend the `git_refdb_backend` struct to initialize * @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_refdb_init_backend( git_refdb_backend *backend, unsigned int version); /** * Constructors for default filesystem-based refdb backend * * Under normal usage, this is called for you when the repository is * opened / created, but you can use this to explicitly construct a * filesystem refdb backend for a repository. * * @param backend_out Output pointer to the git_refdb_backend object * @param repo Git repository to access * @return 0 on success, <0 error code on failure */ GIT_EXTERN(int) git_refdb_backend_fs( git_refdb_backend **backend_out, git_repository *repo); /** * Sets the custom backend to an existing reference DB * * The `git_refdb` will take ownership of the `git_refdb_backend` so you * should NOT free it after calling this function. * * @param refdb database to add the backend to * @param backend pointer to a git_refdb_backend instance * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_refdb_set_backend( git_refdb *refdb, git_refdb_backend *backend); GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/openssl.h0000644000175000017500000000212114125111754020515 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_openssl_h__ #define INCLUDE_git_openssl_h__ #include "git2/common.h" GIT_BEGIN_DECL /** * Initialize the OpenSSL locks * * OpenSSL requires the application to determine how it performs * locking. * * This is a last-resort convenience function which libgit2 provides for * allocating and initializing the locks as well as setting the * locking function to use the system's native locking functions. * * The locking function will be cleared and the memory will be freed * when you call git_threads_sutdown(). * * If your programming language has an OpenSSL package/bindings, it * likely sets up locking. You should very strongly prefer that over * this function. * * @return 0 on success, -1 if there are errors or if libgit2 was not * built with OpenSSL and threading support. */ GIT_EXTERN(int) git_openssl_set_locking(void); GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/odb_backend.h0000644000175000017500000001335314125111754021256 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_odb_backend_h__ #define INCLUDE_sys_git_odb_backend_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/oid.h" #include "git2/odb.h" /** * @file git2/sys/backend.h * @brief Git custom backend implementors functions * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * An instance for a custom backend */ struct git_odb_backend { unsigned int version; git_odb *odb; /* read and read_prefix each return to libgit2 a buffer which * will be freed later. The buffer should be allocated using * the function git_odb_backend_data_alloc to ensure that libgit2 * can safely free it later. */ int GIT_CALLBACK(read)( void **, size_t *, git_object_t *, git_odb_backend *, const git_oid *); /* To find a unique object given a prefix of its oid. The oid given * must be so that the remaining (GIT_OID_HEXSZ - len)*4 bits are 0s. */ int GIT_CALLBACK(read_prefix)( git_oid *, void **, size_t *, git_object_t *, git_odb_backend *, const git_oid *, size_t); int GIT_CALLBACK(read_header)( size_t *, git_object_t *, git_odb_backend *, const git_oid *); /** * Write an object into the backend. The id of the object has * already been calculated and is passed in. */ int GIT_CALLBACK(write)( git_odb_backend *, const git_oid *, const void *, size_t, git_object_t); int GIT_CALLBACK(writestream)( git_odb_stream **, git_odb_backend *, git_object_size_t, git_object_t); int GIT_CALLBACK(readstream)( git_odb_stream **, size_t *, git_object_t *, git_odb_backend *, const git_oid *); int GIT_CALLBACK(exists)( git_odb_backend *, const git_oid *); int GIT_CALLBACK(exists_prefix)( git_oid *, git_odb_backend *, const git_oid *, size_t); /** * If the backend implements a refreshing mechanism, it should be exposed * through this endpoint. Each call to `git_odb_refresh()` will invoke it. * * However, the backend implementation should try to stay up-to-date as much * as possible by itself as libgit2 will not automatically invoke * `git_odb_refresh()`. For instance, a potential strategy for the backend * implementation to achieve this could be to internally invoke this * endpoint on failed lookups (ie. `exists()`, `read()`, `read_header()`). */ int GIT_CALLBACK(refresh)(git_odb_backend *); int GIT_CALLBACK(foreach)( git_odb_backend *, git_odb_foreach_cb cb, void *payload); int GIT_CALLBACK(writepack)( git_odb_writepack **, git_odb_backend *, git_odb *odb, git_indexer_progress_cb progress_cb, void *progress_payload); /** * If the backend supports pack files, this will create a * `multi-pack-index` file which will contain an index of all objects * across all the `.pack` files. */ int GIT_CALLBACK(writemidx)(git_odb_backend *); /** * "Freshens" an already existing object, updating its last-used * time. This occurs when `git_odb_write` was called, but the * object already existed (and will not be re-written). The * underlying implementation may want to update last-used timestamps. * * If callers implement this, they should return `0` if the object * exists and was freshened, and non-zero otherwise. */ int GIT_CALLBACK(freshen)(git_odb_backend *, const git_oid *); /** * Frees any resources held by the odb (including the `git_odb_backend` * itself). An odb backend implementation must provide this function. */ void GIT_CALLBACK(free)(git_odb_backend *); }; #define GIT_ODB_BACKEND_VERSION 1 #define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION} /** * Initializes a `git_odb_backend` with default values. Equivalent to * creating an instance with GIT_ODB_BACKEND_INIT. * * @param backend the `git_odb_backend` struct to initialize. * @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_odb_init_backend( git_odb_backend *backend, unsigned int version); /** * Allocate data for an ODB object. Custom ODB backends may use this * to provide data back to the ODB from their read function. This * memory should not be freed once it is returned to libgit2. If a * custom ODB uses this function but encounters an error and does not * return this data to libgit2, then they should use the corresponding * git_odb_backend_data_free function. * * @param backend the ODB backend that is allocating this memory * @param len the number of bytes to allocate * @return the allocated buffer on success or NULL if out of memory */ GIT_EXTERN(void *) git_odb_backend_data_alloc(git_odb_backend *backend, size_t len); /** * Frees custom allocated ODB data. This should only be called when * memory allocated using git_odb_backend_data_alloc is not returned * to libgit2 because the backend encountered an error in the read * function after allocation and did not return this data to libgit2. * * @param backend the ODB backend that is freeing this memory * @param data the buffer to free */ GIT_EXTERN(void) git_odb_backend_data_free(git_odb_backend *backend, void *data); /* * Users can avoid deprecated functions by defining `GIT_DEPRECATE_HARD`. */ #ifndef GIT_DEPRECATE_HARD /** * Allocate memory for an ODB object from a custom backend. This is * an alias of `git_odb_backend_data_alloc` and is preserved for * backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated git_odb_backend_data_alloc * @see git_odb_backend_data_alloc */ GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len); #endif GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/midx.h0000644000175000017500000000336714125111754020010 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_midx_h__ #define INCLUDE_sys_git_midx_h__ #include "git2/common.h" #include "git2/types.h" /** * @file git2/midx.h * @brief Git multi-pack-index routines * @defgroup git_midx Git multi-pack-index routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a new writer for `multi-pack-index` files. * * @param out location to store the writer pointer. * @param pack_dir the directory where the `.pack` and `.idx` files are. The * `multi-pack-index` file will be written in this directory, too. * @return 0 or an error code */ GIT_EXTERN(int) git_midx_writer_new( git_midx_writer **out, const char *pack_dir); /** * Free the multi-pack-index writer and its resources. * * @param w the writer to free. If NULL no action is taken. */ GIT_EXTERN(void) git_midx_writer_free(git_midx_writer *w); /** * Add an `.idx` file to the writer. * * @param w the writer * @param idx_path the path of an `.idx` file. * @return 0 or an error code */ GIT_EXTERN(int) git_midx_writer_add( git_midx_writer *w, const char *idx_path); /** * Write a `multi-pack-index` file to a file. * * @param w the writer * @return 0 or an error code */ GIT_EXTERN(int) git_midx_writer_commit( git_midx_writer *w); /** * Dump the contents of the `multi-pack-index` to an in-memory buffer. * * @param midx Buffer where to store the contents of the `multi-pack-index`. * @param w the writer * @return 0 or an error code */ GIT_EXTERN(int) git_midx_writer_dump( git_buf *midx, git_midx_writer *w); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/reflog.h0000644000175000017500000000101414125111754020310 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_reflog_h__ #define INCLUDE_sys_git_reflog_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/oid.h" GIT_BEGIN_DECL GIT_EXTERN(git_reflog_entry *) git_reflog_entry__alloc(void); GIT_EXTERN(void) git_reflog_entry__free(git_reflog_entry *entry); GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/stream.h0000644000175000017500000000770614125111754020343 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_stream_h__ #define INCLUDE_sys_git_stream_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/proxy.h" GIT_BEGIN_DECL #define GIT_STREAM_VERSION 1 /** * Every stream must have this struct as its first element, so the * API can talk to it. You'd define your stream as * * struct my_stream { * git_stream parent; * ... * } * * and fill the functions */ typedef struct git_stream { int version; int encrypted; int proxy_support; int GIT_CALLBACK(connect)(struct git_stream *); int GIT_CALLBACK(certificate)(git_cert **, struct git_stream *); int GIT_CALLBACK(set_proxy)(struct git_stream *, const git_proxy_options *proxy_opts); ssize_t GIT_CALLBACK(read)(struct git_stream *, void *, size_t); ssize_t GIT_CALLBACK(write)(struct git_stream *, const char *, size_t, int); int GIT_CALLBACK(close)(struct git_stream *); void GIT_CALLBACK(free)(struct git_stream *); } git_stream; typedef struct { /** The `version` field should be set to `GIT_STREAM_VERSION`. */ int version; /** * Called to create a new connection to a given host. * * @param out The created stream * @param host The hostname to connect to; may be a hostname or * IP address * @param port The port to connect to; may be a port number or * service name * @return 0 or an error code */ int GIT_CALLBACK(init)(git_stream **out, const char *host, const char *port); /** * Called to create a new connection on top of the given stream. If * this is a TLS stream, then this function may be used to proxy a * TLS stream over an HTTP CONNECT session. If this is unset, then * HTTP CONNECT proxies will not be supported. * * @param out The created stream * @param in An existing stream to add TLS to * @param host The hostname that the stream is connected to, * for certificate validation * @return 0 or an error code */ int GIT_CALLBACK(wrap)(git_stream **out, git_stream *in, const char *host); } git_stream_registration; /** * The type of stream to register. */ typedef enum { /** A standard (non-TLS) socket. */ GIT_STREAM_STANDARD = 1, /** A TLS-encrypted socket. */ GIT_STREAM_TLS = 2, } git_stream_t; /** * Register stream constructors for the library to use * * If a registration structure is already set, it will be overwritten. * Pass `NULL` in order to deregister the current constructor and return * to the system defaults. * * The type parameter may be a bitwise AND of types. * * @param type the type or types of stream to register * @param registration the registration data * @return 0 or an error code */ GIT_EXTERN(int) git_stream_register( git_stream_t type, git_stream_registration *registration); #ifndef GIT_DEPRECATE_HARD /** @name Deprecated TLS Stream Registration Functions * * These functions are retained for backward compatibility. The newer * versions of these values should be preferred in all new code. * * There is no plan to remove these backward compatibility values at * this time. */ /**@{*/ /** * @deprecated Provide a git_stream_registration to git_stream_register * @see git_stream_registration */ typedef int GIT_CALLBACK(git_stream_cb)(git_stream **out, const char *host, const char *port); /** * Register a TLS stream constructor for the library to use. This stream * will not support HTTP CONNECT proxies. This internally calls * `git_stream_register` and is preserved for backward compatibility. * * This function is deprecated, but there is no plan to remove this * function at this time. * * @deprecated Provide a git_stream_registration to git_stream_register * @see git_stream_register */ GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor); /**@}*/ #endif GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/hashsig.h0000644000175000017500000000551114125111754020466 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_hashsig_h__ #define INCLUDE_sys_hashsig_h__ #include "git2/common.h" GIT_BEGIN_DECL /** * Similarity signature of arbitrary text content based on line hashes */ typedef struct git_hashsig git_hashsig; /** * Options for hashsig computation * * The options GIT_HASHSIG_NORMAL, GIT_HASHSIG_IGNORE_WHITESPACE, * GIT_HASHSIG_SMART_WHITESPACE are exclusive and should not be combined. */ typedef enum { /** * Use all data */ GIT_HASHSIG_NORMAL = 0, /** * Ignore whitespace */ GIT_HASHSIG_IGNORE_WHITESPACE = (1 << 0), /** * Ignore \r and all space after \n */ GIT_HASHSIG_SMART_WHITESPACE = (1 << 1), /** * Allow hashing of small files */ GIT_HASHSIG_ALLOW_SMALL_FILES = (1 << 2) } git_hashsig_option_t; /** * Compute a similarity signature for a text buffer * * If you have passed the option GIT_HASHSIG_IGNORE_WHITESPACE, then the * whitespace will be removed from the buffer while it is being processed, * modifying the buffer in place. Sorry about that! * * @param out The computed similarity signature. * @param buf The input buffer. * @param buflen The input buffer size. * @param opts The signature computation options (see above). * @return 0 on success, GIT_EBUFS if the buffer doesn't contain enough data to * compute a valid signature (unless GIT_HASHSIG_ALLOW_SMALL_FILES is set), or * error code. */ GIT_EXTERN(int) git_hashsig_create( git_hashsig **out, const char *buf, size_t buflen, git_hashsig_option_t opts); /** * Compute a similarity signature for a text file * * This walks through the file, only loading a maximum of 4K of file data at * a time. Otherwise, it acts just like `git_hashsig_create`. * * @param out The computed similarity signature. * @param path The path to the input file. * @param opts The signature computation options (see above). * @return 0 on success, GIT_EBUFS if the buffer doesn't contain enough data to * compute a valid signature (unless GIT_HASHSIG_ALLOW_SMALL_FILES is set), or * error code. */ GIT_EXTERN(int) git_hashsig_create_fromfile( git_hashsig **out, const char *path, git_hashsig_option_t opts); /** * Release memory for a content similarity signature * * @param sig The similarity signature to free. */ GIT_EXTERN(void) git_hashsig_free(git_hashsig *sig); /** * Measure similarity score between two similarity signatures * * @param a The first similarity signature to compare. * @param b The second similarity signature to compare. * @return [0 to 100] on success as the similarity score, or error code. */ GIT_EXTERN(int) git_hashsig_compare( const git_hashsig *a, const git_hashsig *b); GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/mempack.h0000644000175000017500000000540614125111754020460 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_odb_mempack_h__ #define INCLUDE_sys_git_odb_mempack_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/oid.h" #include "git2/odb.h" #include "git2/buffer.h" /** * @file git2/sys/mempack.h * @brief Custom ODB backend that permits packing objects in-memory * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Instantiate a new mempack backend. * * The backend must be added to an existing ODB with the highest * priority. * * git_mempack_new(&mempacker); * git_repository_odb(&odb, repository); * git_odb_add_backend(odb, mempacker, 999); * * Once the backend has been loaded, all writes to the ODB will * instead be queued in memory, and can be finalized with * `git_mempack_dump`. * * Subsequent reads will also be served from the in-memory store * to ensure consistency, until the memory store is dumped. * * @param out Pointer where to store the ODB backend * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_mempack_new(git_odb_backend **out); /** * Dump all the queued in-memory writes to a packfile. * * The contents of the packfile will be stored in the given buffer. * It is the caller's responsibility to ensure that the generated * packfile is available to the repository (e.g. by writing it * to disk, or doing something crazy like distributing it across * several copies of the repository over a network). * * Once the generated packfile is available to the repository, * call `git_mempack_reset` to cleanup the memory store. * * Calling `git_mempack_reset` before the packfile has been * written to disk will result in an inconsistent repository * (the objects in the memory store won't be accessible). * * @param pack Buffer where to store the raw packfile * @param repo The active repository where the backend is loaded * @param backend The mempack backend * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend); /** * Reset the memory packer by clearing all the queued objects. * * This assumes that `git_mempack_dump` has been called before to * store all the queued objects into a single packfile. * * Alternatively, call `reset` without a previous dump to "undo" * all the recently written objects, giving transaction-like * semantics to the Git repository. * * @param backend The mempack backend * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_mempack_reset(git_odb_backend *backend); GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/credential.h0000644000175000017500000000466614125111754021164 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_credential_h__ #define INCLUDE_sys_git_credential_h__ #include "git2/common.h" #include "git2/credential.h" /** * @file git2/sys/cred.h * @brief Git credentials low-level implementation * @defgroup git_credential Git credentials low-level implementation * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * The base structure for all credential types */ struct git_credential { git_credential_t credtype; /**< A type of credential */ /** The deallocator for this type of credentials */ void GIT_CALLBACK(free)(git_credential *cred); }; /** A plaintext username and password */ struct git_credential_userpass_plaintext { git_credential parent; /**< The parent credential */ char *username; /**< The username to authenticate as */ char *password; /**< The password to use */ }; /** Username-only credential information */ struct git_credential_username { git_credential parent; /**< The parent credential */ char username[1]; /**< The username to authenticate as */ }; /** * A ssh key from disk */ struct git_credential_ssh_key { git_credential parent; /**< The parent credential */ char *username; /**< The username to authenticate as */ char *publickey; /**< The path to a public key */ char *privatekey; /**< The path to a private key */ char *passphrase; /**< Passphrase to decrypt the private key */ }; /** * Keyboard-interactive based ssh authentication */ struct git_credential_ssh_interactive { git_credential parent; /**< The parent credential */ char *username; /**< The username to authenticate as */ /** * Callback used for authentication. */ git_credential_ssh_interactive_cb prompt_callback; void *payload; /**< Payload passed to prompt_callback */ }; /** * A key with a custom signature function */ struct git_credential_ssh_custom { git_credential parent; /**< The parent credential */ char *username; /**< The username to authenticate as */ char *publickey; /**< The public key data */ size_t publickey_len; /**< Length of the public key */ /** * Callback used to sign the data. */ git_credential_sign_cb sign_callback; void *payload; /**< Payload passed to prompt_callback */ }; GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/index.h0000644000175000017500000001225614125111754020153 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_index_h__ #define INCLUDE_sys_git_index_h__ #include "git2/common.h" #include "git2/types.h" /** * @file git2/sys/index.h * @brief Low-level Git index manipulation routines * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** Representation of a rename conflict entry in the index. */ typedef struct git_index_name_entry { char *ancestor; char *ours; char *theirs; } git_index_name_entry; /** Representation of a resolve undo entry in the index. */ typedef struct git_index_reuc_entry { uint32_t mode[3]; git_oid oid[3]; char *path; } git_index_reuc_entry; /** @name Conflict Name entry functions * * These functions work on rename conflict entries. */ /**@{*/ /** * Get the count of filename conflict entries currently in the index. * * @param index an existing index object * @return integer of count of current filename conflict entries */ GIT_EXTERN(size_t) git_index_name_entrycount(git_index *index); /** * Get a filename conflict entry from the index. * * The returned entry is read-only and should not be modified * or freed by the caller. * * @param index an existing index object * @param n the position of the entry * @return a pointer to the filename conflict entry; NULL if out of bounds */ GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex( git_index *index, size_t n); /** * Record the filenames involved in a rename conflict. * * @param index an existing index object * @param ancestor the path of the file as it existed in the ancestor * @param ours the path of the file as it existed in our tree * @param theirs the path of the file as it existed in their tree */ GIT_EXTERN(int) git_index_name_add(git_index *index, const char *ancestor, const char *ours, const char *theirs); /** * Remove all filename conflict entries. * * @param index an existing index object * @return 0 or an error code */ GIT_EXTERN(int) git_index_name_clear(git_index *index); /**@}*/ /** @name Resolve Undo (REUC) index entry manipulation. * * These functions work on the Resolve Undo index extension and contains * data about the original files that led to a merge conflict. */ /**@{*/ /** * Get the count of resolve undo entries currently in the index. * * @param index an existing index object * @return integer of count of current resolve undo entries */ GIT_EXTERN(size_t) git_index_reuc_entrycount(git_index *index); /** * Finds the resolve undo entry that points to the given path in the Git * index. * * @param at_pos the address to which the position of the reuc entry is written (optional) * @param index an existing index object * @param path path to search * @return 0 if found, < 0 otherwise (GIT_ENOTFOUND) */ GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path); /** * Get a resolve undo entry from the index. * * The returned entry is read-only and should not be modified * or freed by the caller. * * @param index an existing index object * @param path path to search * @return the resolve undo entry; NULL if not found */ GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path); /** * Get a resolve undo entry from the index. * * The returned entry is read-only and should not be modified * or freed by the caller. * * @param index an existing index object * @param n the position of the entry * @return a pointer to the resolve undo entry; NULL if out of bounds */ GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n); /** * Adds a resolve undo entry for a file based on the given parameters. * * The resolve undo entry contains the OIDs of files that were involved * in a merge conflict after the conflict has been resolved. This allows * conflicts to be re-resolved later. * * If there exists a resolve undo entry for the given path in the index, * it will be removed. * * This method will fail in bare index instances. * * @param index an existing index object * @param path filename to add * @param ancestor_mode mode of the ancestor file * @param ancestor_id oid of the ancestor file * @param our_mode mode of our file * @param our_id oid of our file * @param their_mode mode of their file * @param their_id oid of their file * @return 0 or an error code */ GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path, int ancestor_mode, const git_oid *ancestor_id, int our_mode, const git_oid *our_id, int their_mode, const git_oid *their_id); /** * Remove an resolve undo entry from the index * * @param index an existing index object * @param n position of the resolve undo entry to remove * @return 0 or an error code */ GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n); /** * Remove all resolve undo entries from the index * * @param index an existing index object * @return 0 or an error code */ GIT_EXTERN(int) git_index_reuc_clear(git_index *index); /**@}*/ /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/alloc.h0000644000175000017500000000653114125111754020135 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_alloc_h__ #define INCLUDE_sys_git_alloc_h__ #include "git2/common.h" GIT_BEGIN_DECL /** * An instance for a custom memory allocator * * Setting the pointers of this structure allows the developer to implement * custom memory allocators. The global memory allocator can be set by using * "GIT_OPT_SET_ALLOCATOR" with the `git_libgit2_opts` function. Keep in mind * that all fields need to be set to a proper function. */ typedef struct { /** Allocate `n` bytes of memory */ void * GIT_CALLBACK(gmalloc)(size_t n, const char *file, int line); /** * Allocate memory for an array of `nelem` elements, where each element * has a size of `elsize`. Returned memory shall be initialized to * all-zeroes */ void * GIT_CALLBACK(gcalloc)(size_t nelem, size_t elsize, const char *file, int line); /** Allocate memory for the string `str` and duplicate its contents. */ char * GIT_CALLBACK(gstrdup)(const char *str, const char *file, int line); /** * Equivalent to the `gstrdup` function, but only duplicating at most * `n + 1` bytes */ char * GIT_CALLBACK(gstrndup)(const char *str, size_t n, const char *file, int line); /** * Equivalent to `gstrndup`, but will always duplicate exactly `n` bytes * of `str`. Thus, out of bounds reads at `str` may happen. */ char * GIT_CALLBACK(gsubstrdup)(const char *str, size_t n, const char *file, int line); /** * This function shall deallocate the old object `ptr` and return a * pointer to a new object that has the size specified by `size`. In * case `ptr` is `NULL`, a new array shall be allocated. */ void * GIT_CALLBACK(grealloc)(void *ptr, size_t size, const char *file, int line); /** * This function shall be equivalent to `grealloc`, but allocating * `neleme * elsize` bytes. */ void * GIT_CALLBACK(greallocarray)(void *ptr, size_t nelem, size_t elsize, const char *file, int line); /** * This function shall allocate a new array of `nelem` elements, where * each element has a size of `elsize` bytes. */ void * GIT_CALLBACK(gmallocarray)(size_t nelem, size_t elsize, const char *file, int line); /** * This function shall free the memory pointed to by `ptr`. In case * `ptr` is `NULL`, this shall be a no-op. */ void GIT_CALLBACK(gfree)(void *ptr); } git_allocator; /** * Initialize the allocator structure to use the `stdalloc` pointer. * * Set up the structure so that all of its members are using the standard * "stdalloc" allocator functions. The structure can then be used with * `git_allocator_setup`. * * @param allocator The allocator that is to be initialized. * @return An error code or 0. */ int git_stdalloc_init_allocator(git_allocator *allocator); /** * Initialize the allocator structure to use the `crtdbg` pointer. * * Set up the structure so that all of its members are using the "crtdbg" * allocator functions. Note that this allocator is only available on Windows * platforms and only if libgit2 is being compiled with "-DMSVC_CRTDBG". * * @param allocator The allocator that is to be initialized. * @return An error code or 0. */ int git_win32_crtdbg_init_allocator(git_allocator *allocator); GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/cred.h0000644000175000017500000000061314125111754017753 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_cred_h__ #define INCLUDE_sys_git_cred_h__ /* These declarations have moved. */ #ifndef GIT_DEPRECATE_HARD # include "git2/sys/credential.h" #endif #endif git2r/src/libgit2/include/git2/sys/email.h0000644000175000017500000000241114125111754020123 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_email_h__ #define INCLUDE_sys_git_email_h__ /** * @file git2/sys/email.h * @brief Advanced git email creation routines * @defgroup git_email Advanced git email creation routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a diff for a commit in mbox format for sending via email. * * @param out buffer to store the e-mail patch in * @param diff the changes to include in the email * @param patch_idx the patch index * @param patch_count the total number of patches that will be included * @param commit_id the commit id for this change * @param summary the commit message for this change * @param body optional text to include above the diffstat * @param author the person who authored this commit * @param opts email creation options */ GIT_EXTERN(int) git_email_create_from_diff( git_buf *out, git_diff *diff, size_t patch_idx, size_t patch_count, const git_oid *commit_id, const char *summary, const char *body, const git_signature *author, const git_email_create_options *opts); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/commit_graph.h0000644000175000017500000001165214125111754021514 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_commit_graph_h__ #define INCLUDE_sys_git_commit_graph_h__ #include "git2/common.h" #include "git2/types.h" /** * @file git2/sys/commit_graph.h * @brief Git commit-graph * @defgroup git_commit_graph Git commit-graph APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Opens a `git_commit_graph` from a path to an objects directory. * * This finds, opens, and validates the `commit-graph` file. * * @param cgraph_out the `git_commit_graph` struct to initialize. * @param objects_dir the path to a git objects directory. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir); /** * Frees commit-graph data. This should only be called when memory allocated * using `git_commit_graph_open` is not returned to libgit2 because it was not * associated with the ODB through a successful call to * `git_odb_set_commit_graph`. * * @param cgraph the commit-graph object to free. If NULL, no action is taken. */ GIT_EXTERN(void) git_commit_graph_free(git_commit_graph *cgraph); /** * Create a new writer for `commit-graph` files. * * @param out Location to store the writer pointer. * @param objects_info_dir The `objects/info` directory. * The `commit-graph` file will be written in this directory. * @return 0 or an error code */ GIT_EXTERN(int) git_commit_graph_writer_new( git_commit_graph_writer **out, const char *objects_info_dir); /** * Free the commit-graph writer and its resources. * * @param w The writer to free. If NULL no action is taken. */ GIT_EXTERN(void) git_commit_graph_writer_free(git_commit_graph_writer *w); /** * Add an `.idx` file (associated to a packfile) to the writer. * * @param w The writer. * @param repo The repository that owns the `.idx` file. * @param idx_path The path of an `.idx` file. * @return 0 or an error code */ GIT_EXTERN(int) git_commit_graph_writer_add_index_file( git_commit_graph_writer *w, git_repository *repo, const char *idx_path); /** * Add a revwalk to the writer. This will add all the commits from the revwalk * to the commit-graph. * * @param w The writer. * @param walk The git_revwalk. * @return 0 or an error code */ GIT_EXTERN(int) git_commit_graph_writer_add_revwalk( git_commit_graph_writer *w, git_revwalk *walk); /** * The strategy to use when adding a new set of commits to a pre-existing * commit-graph chain. */ typedef enum { /** * Do not split commit-graph files. The other split strategy-related option * fields are ignored. */ GIT_COMMIT_GRAPH_SPLIT_STRATEGY_SINGLE_FILE = 0, } git_commit_graph_split_strategy_t; /** * Options structure for * `git_commit_graph_writer_commit`/`git_commit_graph_writer_dump`. * * Initialize with `GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT`. Alternatively, you * can use `git_commit_graph_writer_options_init`. */ typedef struct { unsigned int version; /** * The strategy to use when adding new commits to a pre-existing commit-graph * chain. */ git_commit_graph_split_strategy_t split_strategy; /** * The number of commits in level N is less than X times the number of * commits in level N + 1. Default is 2. */ float size_multiple; /** * The number of commits in level N + 1 is more than C commits. * Default is 64000. */ size_t max_commits; } git_commit_graph_writer_options; #define GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION 1 #define GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT { \ GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION \ } /** * Initialize git_commit_graph_writer_options structure * * Initializes a `git_commit_graph_writer_options` with default values. Equivalent to * creating an instance with `GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT`. * * @param opts The `git_commit_graph_writer_options` struct to initialize. * @param version The struct version; pass `GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_commit_graph_writer_options_init( git_commit_graph_writer_options *opts, unsigned int version); /** * Write a `commit-graph` file to a file. * * @param w The writer * @param opts Pointer to git_commit_graph_writer_options struct. * @return 0 or an error code */ GIT_EXTERN(int) git_commit_graph_writer_commit( git_commit_graph_writer *w, git_commit_graph_writer_options *opts); /** * Dump the contents of the `commit-graph` to an in-memory buffer. * * @param buffer Buffer where to store the contents of the `commit-graph`. * @param w The writer. * @param opts Pointer to git_commit_graph_writer_options struct. * @return 0 or an error code */ GIT_EXTERN(int) git_commit_graph_writer_dump( git_buf *buffer, git_commit_graph_writer *w, git_commit_graph_writer_options *opts); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/refs.h0000644000175000017500000000225514125111754020001 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_refdb_h__ #define INCLUDE_sys_git_refdb_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/oid.h" /** * @file git2/sys/refs.h * @brief Low-level Git ref creation * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a new direct reference from an OID. * * @param name the reference name * @param oid the object id for a direct reference * @param peel the first non-tag object's OID, or NULL * @return the created git_reference or NULL on error */ GIT_EXTERN(git_reference *) git_reference__alloc( const char *name, const git_oid *oid, const git_oid *peel); /** * Create a new symbolic reference. * * @param name the reference name * @param target the target for a symbolic reference * @return the created git_reference or NULL on error */ GIT_EXTERN(git_reference *) git_reference__alloc_symbolic( const char *name, const char *target); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/config.h0000644000175000017500000001024214125111754020302 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_config_backend_h__ #define INCLUDE_sys_git_config_backend_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/config.h" /** * @file git2/sys/config.h * @brief Git config backend routines * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Every iterator must have this struct as its first element, so the * API can talk to it. You'd define your iterator as * * struct my_iterator { * git_config_iterator parent; * ... * } * * and assign `iter->parent.backend` to your `git_config_backend`. */ struct git_config_iterator { git_config_backend *backend; unsigned int flags; /** * Return the current entry and advance the iterator. The * memory belongs to the library. */ int GIT_CALLBACK(next)(git_config_entry **entry, git_config_iterator *iter); /** * Free the iterator */ void GIT_CALLBACK(free)(git_config_iterator *iter); }; /** * Generic backend that implements the interface to * access a configuration file */ struct git_config_backend { unsigned int version; /** True if this backend is for a snapshot */ int readonly; struct git_config *cfg; /* Open means open the file/database and parse if necessary */ int GIT_CALLBACK(open)(struct git_config_backend *, git_config_level_t level, const git_repository *repo); int GIT_CALLBACK(get)(struct git_config_backend *, const char *key, git_config_entry **entry); int GIT_CALLBACK(set)(struct git_config_backend *, const char *key, const char *value); int GIT_CALLBACK(set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value); int GIT_CALLBACK(del)(struct git_config_backend *, const char *key); int GIT_CALLBACK(del_multivar)(struct git_config_backend *, const char *key, const char *regexp); int GIT_CALLBACK(iterator)(git_config_iterator **, struct git_config_backend *); /** Produce a read-only version of this backend */ int GIT_CALLBACK(snapshot)(struct git_config_backend **, struct git_config_backend *); /** * Lock this backend. * * Prevent any writes to the data store backing this * backend. Any updates must not be visible to any other * readers. */ int GIT_CALLBACK(lock)(struct git_config_backend *); /** * Unlock the data store backing this backend. If success is * true, the changes should be committed, otherwise rolled * back. */ int GIT_CALLBACK(unlock)(struct git_config_backend *, int success); void GIT_CALLBACK(free)(struct git_config_backend *); }; #define GIT_CONFIG_BACKEND_VERSION 1 #define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION} /** * Initializes a `git_config_backend` with default values. Equivalent to * creating an instance with GIT_CONFIG_BACKEND_INIT. * * @param backend the `git_config_backend` struct to initialize. * @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_config_init_backend( git_config_backend *backend, unsigned int version); /** * Add a generic config file instance to an existing config * * Note that the configuration object will free the file * automatically. * * Further queries on this config object will access each * of the config file instances in order (instances with * a higher priority level will be accessed first). * * @param cfg the configuration to add the file to * @param file the configuration file (backend) to add * @param level the priority level of the backend * @param repo optional repository to allow parsing of * conditional includes * @param force if a config file already exists for the given * priority level, replace it * @return 0 on success, GIT_EEXISTS when adding more than one file * for a given priority level (and force_replace set to 0), or error code */ GIT_EXTERN(int) git_config_add_backend( git_config *cfg, git_config_backend *file, git_config_level_t level, const git_repository *repo, int force); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/diff.h0000644000175000017500000000561314125111754017753 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_diff_h__ #define INCLUDE_sys_git_diff_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/oid.h" #include "git2/diff.h" #include "git2/status.h" /** * @file git2/sys/diff.h * @brief Low-level Git diff utilities * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Diff print callback that writes to a git_buf. * * This function is provided not for you to call it directly, but instead * so you can use it as a function pointer to the `git_diff_print` or * `git_patch_print` APIs. When using those APIs, you specify a callback * to actually handle the diff and/or patch data. * * Use this callback to easily write that data to a `git_buf` buffer. You * must pass a `git_buf *` value as the payload to the `git_diff_print` * and/or `git_patch_print` function. The data will be appended to the * buffer (after any existing content). */ GIT_EXTERN(int) git_diff_print_callback__to_buf( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload); /**< payload must be a `git_buf *` */ /** * Diff print callback that writes to stdio FILE handle. * * This function is provided not for you to call it directly, but instead * so you can use it as a function pointer to the `git_diff_print` or * `git_patch_print` APIs. When using those APIs, you specify a callback * to actually handle the diff and/or patch data. * * Use this callback to easily write that data to a stdio FILE handle. You * must pass a `FILE *` value (such as `stdout` or `stderr` or the return * value from `fopen()`) as the payload to the `git_diff_print` * and/or `git_patch_print` function. If you pass NULL, this will write * data to `stdout`. */ GIT_EXTERN(int) git_diff_print_callback__to_file_handle( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload); /**< payload must be a `FILE *` */ /** * Performance data from diffing */ typedef struct { unsigned int version; size_t stat_calls; /**< Number of stat() calls performed */ size_t oid_calculations; /**< Number of ID calculations */ } git_diff_perfdata; #define GIT_DIFF_PERFDATA_VERSION 1 #define GIT_DIFF_PERFDATA_INIT {GIT_DIFF_PERFDATA_VERSION,0,0} /** * Get performance data for a diff object. * * @param out Structure to be filled with diff performance data * @param diff Diff to read performance data from * @return 0 for success, <0 for error */ GIT_EXTERN(int) git_diff_get_perfdata( git_diff_perfdata *out, const git_diff *diff); /** * Get performance data for diffs from a git_status_list */ GIT_EXTERN(int) git_status_list_get_perfdata( git_diff_perfdata *out, const git_status_list *status); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/repository.h0000644000175000017500000001351614125111754021263 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_repository_h__ #define INCLUDE_sys_git_repository_h__ #include "git2/common.h" #include "git2/types.h" /** * @file git2/sys/repository.h * @brief Git repository custom implementation routines * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Create a new repository with neither backends nor config object * * Note that this is only useful if you wish to associate the repository * with a non-filesystem-backed object database and config store. * * Caveats: since this repository has no physical location, some systems * can fail to function properly: locations under $GIT_DIR, $GIT_COMMON_DIR, * or $GIT_INFO_DIR are impacted. * * @param out The blank repository * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_new(git_repository **out); /** * Reset all the internal state in a repository. * * This will free all the mapped memory and internal objects * of the repository and leave it in a "blank" state. * * There's no need to call this function directly unless you're * trying to aggressively cleanup the repo before its * deallocation. `git_repository_free` already performs this operation * before deallocating the repo. * * @param repo The repository to clean up * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository__cleanup(git_repository *repo); /** * Update the filesystem config settings for an open repository * * When a repository is initialized, config values are set based on the * properties of the filesystem that the repository is on, such as * "core.ignorecase", "core.filemode", "core.symlinks", etc. If the * repository is moved to a new filesystem, these properties may no * longer be correct and API calls may not behave as expected. This * call reruns the phase of repository initialization that sets those * properties to compensate for the current filesystem of the repo. * * @param repo A repository object * @param recurse_submodules Should submodules be updated recursively * @return 0 on success, < 0 on error */ GIT_EXTERN(int) git_repository_reinit_filesystem( git_repository *repo, int recurse_submodules); /** * Set the configuration file for this repository * * This configuration file will be used for all configuration * queries involving this repository. * * The repository will keep a reference to the config file; * the user must still free the config after setting it * to the repository, or it will leak. * * @param repo A repository object * @param config A Config object * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_config(git_repository *repo, git_config *config); /** * Set the Object Database for this repository * * The ODB will be used for all object-related operations * involving this repository. * * The repository will keep a reference to the ODB; the user * must still free the ODB object after setting it to the * repository, or it will leak. * * @param repo A repository object * @param odb An ODB object * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_odb(git_repository *repo, git_odb *odb); /** * Set the Reference Database Backend for this repository * * The refdb will be used for all reference related operations * involving this repository. * * The repository will keep a reference to the refdb; the user * must still free the refdb object after setting it to the * repository, or it will leak. * * @param repo A repository object * @param refdb An refdb object * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_refdb(git_repository *repo, git_refdb *refdb); /** * Set the index file for this repository * * This index will be used for all index-related operations * involving this repository. * * The repository will keep a reference to the index file; * the user must still free the index after setting it * to the repository, or it will leak. * * @param repo A repository object * @param index An index object * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_index(git_repository *repo, git_index *index); /** * Set a repository to be bare. * * Clear the working directory and set core.bare to true. You may also * want to call `git_repository_set_index(repo, NULL)` since a bare repo * typically does not have an index, but this function will not do that * for you. * * @param repo Repo to make bare * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_repository_set_bare(git_repository *repo); /** * Load and cache all submodules. * * Because the `.gitmodules` file is unstructured, loading submodules is an * O(N) operation. Any operation (such as `git_rebase_init`) that requires * accessing all submodules is O(N^2) in the number of submodules, if it * has to look each one up individually. This function loads all submodules * and caches them so that subsequent calls to `git_submodule_lookup` are O(1). * * @param repo the repository whose submodules will be cached. */ GIT_EXTERN(int) git_repository_submodule_cache_all( git_repository *repo); /** * Clear the submodule cache. * * Clear the submodule cache populated by `git_repository_submodule_cache_all`. * If there is no cache, do nothing. * * The cache incorporates data from the repository's configuration, as well * as the state of the working tree, the index, and HEAD. So any time any * of these has changed, the cache might become invalid. * * @param repo the repository whose submodule cache will be cleared */ GIT_EXTERN(int) git_repository_submodule_cache_clear( git_repository *repo); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/merge.h0000644000175000017500000001451214125111754020140 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_merge_h__ #define INCLUDE_sys_git_merge_h__ #include "git2/common.h" #include "git2/types.h" #include "git2/index.h" #include "git2/merge.h" /** * @file git2/sys/merge.h * @brief Git merge driver backend and plugin routines * @defgroup git_merge Git merge driver APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL typedef struct git_merge_driver git_merge_driver; /** * Look up a merge driver by name * * @param name The name of the merge driver * @return Pointer to the merge driver object or NULL if not found */ GIT_EXTERN(git_merge_driver *) git_merge_driver_lookup(const char *name); #define GIT_MERGE_DRIVER_TEXT "text" #define GIT_MERGE_DRIVER_BINARY "binary" #define GIT_MERGE_DRIVER_UNION "union" /** * A merge driver source represents the file to be merged */ typedef struct git_merge_driver_source git_merge_driver_source; /** Get the repository that the source data is coming from. */ GIT_EXTERN(git_repository *) git_merge_driver_source_repo( const git_merge_driver_source *src); /** Gets the ancestor of the file to merge. */ GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ancestor( const git_merge_driver_source *src); /** Gets the ours side of the file to merge. */ GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ours( const git_merge_driver_source *src); /** Gets the theirs side of the file to merge. */ GIT_EXTERN(const git_index_entry *) git_merge_driver_source_theirs( const git_merge_driver_source *src); /** Gets the merge file options that the merge was invoked with */ GIT_EXTERN(const git_merge_file_options *) git_merge_driver_source_file_options( const git_merge_driver_source *src); /** * Initialize callback on merge driver * * Specified as `driver.initialize`, this is an optional callback invoked * before a merge driver is first used. It will be called once at most * per library lifetime. * * If non-NULL, the merge driver's `initialize` callback will be invoked * right before the first use of the driver, so you can defer expensive * initialization operations (in case libgit2 is being used in a way that * doesn't need the merge driver). */ typedef int GIT_CALLBACK(git_merge_driver_init_fn)(git_merge_driver *self); /** * Shutdown callback on merge driver * * Specified as `driver.shutdown`, this is an optional callback invoked * when the merge driver is unregistered or when libgit2 is shutting down. * It will be called once at most and should release resources as needed. * This may be called even if the `initialize` callback was not made. * * Typically this function will free the `git_merge_driver` object itself. */ typedef void GIT_CALLBACK(git_merge_driver_shutdown_fn)(git_merge_driver *self); /** * Callback to perform the merge. * * Specified as `driver.apply`, this is the callback that actually does the * merge. If it can successfully perform a merge, it should populate * `path_out` with a pointer to the filename to accept, `mode_out` with * the resultant mode, and `merged_out` with the buffer of the merged file * and then return 0. If the driver returns `GIT_PASSTHROUGH`, then the * default merge driver should instead be run. It can also return * `GIT_EMERGECONFLICT` if the driver is not able to produce a merge result, * and the file will remain conflicted. Any other errors will fail and * return to the caller. * * The `filter_name` contains the name of the filter that was invoked, as * specified by the file's attributes. * * The `src` contains the data about the file to be merged. */ typedef int GIT_CALLBACK(git_merge_driver_apply_fn)( git_merge_driver *self, const char **path_out, uint32_t *mode_out, git_buf *merged_out, const char *filter_name, const git_merge_driver_source *src); /** * Merge driver structure used to register custom merge drivers. * * To associate extra data with a driver, allocate extra data and put the * `git_merge_driver` struct at the start of your data buffer, then cast * the `self` pointer to your larger structure when your callback is invoked. */ struct git_merge_driver { /** The `version` should be set to `GIT_MERGE_DRIVER_VERSION`. */ unsigned int version; /** Called when the merge driver is first used for any file. */ git_merge_driver_init_fn initialize; /** Called when the merge driver is unregistered from the system. */ git_merge_driver_shutdown_fn shutdown; /** * Called to merge the contents of a conflict. If this function * returns `GIT_PASSTHROUGH` then the default (`text`) merge driver * will instead be invoked. If this function returns * `GIT_EMERGECONFLICT` then the file will remain conflicted. */ git_merge_driver_apply_fn apply; }; #define GIT_MERGE_DRIVER_VERSION 1 /** * Register a merge driver under a given name. * * As mentioned elsewhere, the initialize callback will not be invoked * immediately. It is deferred until the driver is used in some way. * * Currently the merge driver registry is not thread safe, so any * registering or deregistering of merge drivers must be done outside of * any possible usage of the drivers (i.e. during application setup or * shutdown). * * @param name The name of this driver to match an attribute. Attempting * to register with an in-use name will return GIT_EEXISTS. * @param driver The merge driver definition. This pointer will be stored * as is by libgit2 so it must be a durable allocation (either * static or on the heap). * @return 0 on successful registry, error code <0 on failure */ GIT_EXTERN(int) git_merge_driver_register( const char *name, git_merge_driver *driver); /** * Remove the merge driver with the given name. * * Attempting to remove the builtin libgit2 merge drivers is not permitted * and will return an error. * * Currently the merge driver registry is not thread safe, so any * registering or deregistering of drivers must be done outside of any * possible usage of the drivers (i.e. during application setup or shutdown). * * @param name The name under which the merge driver was registered * @return 0 on success, error code <0 on failure */ GIT_EXTERN(int) git_merge_driver_unregister(const char *name); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/transport.h0000644000175000017500000003242114125111754021074 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_transport_h #define INCLUDE_sys_git_transport_h #include "git2/net.h" #include "git2/transport.h" #include "git2/types.h" #include "git2/strarray.h" #include "git2/proxy.h" /** * @file git2/sys/transport.h * @brief Git custom transport registration interfaces and functions * @defgroup git_transport Git custom transport registration * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Flags to pass to transport * * Currently unused. */ typedef enum { GIT_TRANSPORTFLAGS_NONE = 0, } git_transport_flags_t; struct git_transport { unsigned int version; /**< The struct version */ /** Set progress and error callbacks */ int GIT_CALLBACK(set_callbacks)( git_transport *transport, git_transport_message_cb progress_cb, git_transport_message_cb error_cb, git_transport_certificate_check_cb certificate_check_cb, void *payload); /** Set custom headers for HTTP requests */ int GIT_CALLBACK(set_custom_headers)( git_transport *transport, const git_strarray *custom_headers); /** * Connect the transport to the remote repository, using the given * direction. */ int GIT_CALLBACK(connect)( git_transport *transport, const char *url, git_credential_acquire_cb cred_acquire_cb, void *cred_acquire_payload, const git_proxy_options *proxy_opts, int direction, int flags); /** * Get the list of available references in the remote repository. * * This function may be called after a successful call to * `connect()`. The array returned is owned by the transport and * must be kept valid until the next call to one of its functions. */ int GIT_CALLBACK(ls)( const git_remote_head ***out, size_t *size, git_transport *transport); /** Executes the push whose context is in the git_push object. */ int GIT_CALLBACK(push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks); /** * Negotiate a fetch with the remote repository. * * This function may be called after a successful call to `connect()`, * when the direction is GIT_DIRECTION_FETCH. The function performs a * negotiation to calculate the `wants` list for the fetch. */ int GIT_CALLBACK(negotiate_fetch)( git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count); /** * Start downloading the packfile from the remote repository. * * This function may be called after a successful call to * negotiate_fetch(), when the direction is GIT_DIRECTION_FETCH. */ int GIT_CALLBACK(download_pack)( git_transport *transport, git_repository *repo, git_indexer_progress *stats, git_indexer_progress_cb progress_cb, void *progress_payload); /** Checks to see if the transport is connected */ int GIT_CALLBACK(is_connected)(git_transport *transport); /** Reads the flags value previously passed into connect() */ int GIT_CALLBACK(read_flags)(git_transport *transport, int *flags); /** Cancels any outstanding transport operation */ void GIT_CALLBACK(cancel)(git_transport *transport); /** * Close the connection to the remote repository. * * This function is the reverse of connect() -- it terminates the * connection to the remote end. */ int GIT_CALLBACK(close)(git_transport *transport); /** Frees/destructs the git_transport object. */ void GIT_CALLBACK(free)(git_transport *transport); }; #define GIT_TRANSPORT_VERSION 1 #define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION} /** * Initializes a `git_transport` with default values. Equivalent to * creating an instance with GIT_TRANSPORT_INIT. * * @param opts the `git_transport` struct to initialize * @param version Version of struct; pass `GIT_TRANSPORT_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_transport_init( git_transport *opts, unsigned int version); /** * Function to use to create a transport from a URL. The transport database * is scanned to find a transport that implements the scheme of the URI (i.e. * git:// or http://) and a transport object is returned to the caller. * * @param out The newly created transport (out) * @param owner The git_remote which will own this transport * @param url The URL to connect to * @return 0 or an error code */ GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url); /** * Create an ssh transport with custom git command paths * * This is a factory function suitable for setting as the transport * callback in a remote (or for a clone in the options). * * The payload argument must be a strarray pointer with the paths for * the `git-upload-pack` and `git-receive-pack` at index 0 and 1. * * @param out the resulting transport * @param owner the owning remote * @param payload a strarray with the paths * @return 0 or an error code */ GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload); /** * Add a custom transport definition, to be used in addition to the built-in * set of transports that come with libgit2. * * The caller is responsible for synchronizing calls to git_transport_register * and git_transport_unregister with other calls to the library that * instantiate transports. * * @param prefix The scheme (ending in "://") to match, i.e. "git://" * @param cb The callback used to create an instance of the transport * @param param A fixed parameter to pass to cb at creation time * @return 0 or an error code */ GIT_EXTERN(int) git_transport_register( const char *prefix, git_transport_cb cb, void *param); /** * Unregister a custom transport definition which was previously registered * with git_transport_register. * * The caller is responsible for synchronizing calls to git_transport_register * and git_transport_unregister with other calls to the library that * instantiate transports. * * @param prefix From the previous call to git_transport_register * @return 0 or an error code */ GIT_EXTERN(int) git_transport_unregister( const char *prefix); /* Transports which come with libgit2 (match git_transport_cb). The expected * value for "param" is listed in-line below. */ /** * Create an instance of the dummy transport. * * @param out The newly created transport (out) * @param owner The git_remote which will own this transport * @param payload You must pass NULL for this parameter. * @return 0 or an error code */ GIT_EXTERN(int) git_transport_dummy( git_transport **out, git_remote *owner, /* NULL */ void *payload); /** * Create an instance of the local transport. * * @param out The newly created transport (out) * @param owner The git_remote which will own this transport * @param payload You must pass NULL for this parameter. * @return 0 or an error code */ GIT_EXTERN(int) git_transport_local( git_transport **out, git_remote *owner, /* NULL */ void *payload); /** * Create an instance of the smart transport. * * @param out The newly created transport (out) * @param owner The git_remote which will own this transport * @param payload A pointer to a git_smart_subtransport_definition * @return 0 or an error code */ GIT_EXTERN(int) git_transport_smart( git_transport **out, git_remote *owner, /* (git_smart_subtransport_definition *) */ void *payload); /** * Call the certificate check for this transport. * * @param transport a smart transport * @param cert the certificate to pass to the caller * @param valid whether we believe the certificate is valid * @param hostname the hostname we connected to * @return the return value of the callback: 0 for no error, GIT_PASSTHROUGH * to indicate that there is no callback registered (or the callback * refused to validate the certificate and callers should behave as * if no callback was set), or < 0 for an error */ GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname); /** * Call the credentials callback for this transport * * @param out the pointer where the creds are to be stored * @param transport a smart transport * @param user the user we saw on the url (if any) * @param methods available methods for authentication * @return the return value of the callback: 0 for no error, GIT_PASSTHROUGH * to indicate that there is no callback registered (or the callback * refused to provide credentials and callers should behave as if no * callback was set), or < 0 for an error */ GIT_EXTERN(int) git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods); /** * Get a copy of the proxy options * * The url is copied and must be freed by the caller. * * @param out options struct to fill * @param transport the transport to extract the data from. */ GIT_EXTERN(int) git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport); /* *** End of base transport interface *** *** Begin interface for subtransports for the smart transport *** */ /** Actions that the smart transport can ask a subtransport to perform */ typedef enum { GIT_SERVICE_UPLOADPACK_LS = 1, GIT_SERVICE_UPLOADPACK = 2, GIT_SERVICE_RECEIVEPACK_LS = 3, GIT_SERVICE_RECEIVEPACK = 4, } git_smart_service_t; typedef struct git_smart_subtransport git_smart_subtransport; typedef struct git_smart_subtransport_stream git_smart_subtransport_stream; /** * A stream used by the smart transport to read and write data * from a subtransport. * * This provides a customization point in case you need to * support some other communication method. */ struct git_smart_subtransport_stream { git_smart_subtransport *subtransport; /**< The owning subtransport */ /** * Read available data from the stream. * * The implementation may read less than requested. */ int GIT_CALLBACK(read)( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read); /** * Write data to the stream * * The implementation must write all data or return an error. */ int GIT_CALLBACK(write)( git_smart_subtransport_stream *stream, const char *buffer, size_t len); /** Free the stream */ void GIT_CALLBACK(free)( git_smart_subtransport_stream *stream); }; /** * An implementation of a subtransport which carries data for the * smart transport */ struct git_smart_subtransport { /** * Setup a subtransport stream for the requested action. */ int GIT_CALLBACK(action)( git_smart_subtransport_stream **out, git_smart_subtransport *transport, const char *url, git_smart_service_t action); /** * Close the subtransport. * * Subtransports are guaranteed a call to close() between * calls to action(), except for the following two "natural" progressions * of actions against a constant URL: * * - UPLOADPACK_LS -> UPLOADPACK * - RECEIVEPACK_LS -> RECEIVEPACK */ int GIT_CALLBACK(close)(git_smart_subtransport *transport); /** Free the subtransport */ void GIT_CALLBACK(free)(git_smart_subtransport *transport); }; /** A function which creates a new subtransport for the smart transport */ typedef int GIT_CALLBACK(git_smart_subtransport_cb)( git_smart_subtransport **out, git_transport *owner, void *param); /** * Definition for a "subtransport" * * The smart transport knows how to speak the git protocol, but it has no * knowledge of how to establish a connection between it and another endpoint, * or how to move data back and forth. For this, a subtransport interface is * declared, and the smart transport delegates this work to the subtransports. * * Three subtransports are provided by libgit2: ssh, git, http(s). * * Subtransports can either be RPC = 0 (persistent connection) or RPC = 1 * (request/response). The smart transport handles the differences in its own * logic. The git subtransport is RPC = 0, while http is RPC = 1. */ typedef struct git_smart_subtransport_definition { /** The function to use to create the git_smart_subtransport */ git_smart_subtransport_cb callback; /** * True if the protocol is stateless; false otherwise. For example, * http:// is stateless, but git:// is not. */ unsigned rpc; /** User-specified parameter passed to the callback */ void *param; } git_smart_subtransport_definition; /* Smart transport subtransports that come with libgit2 */ /** * Create an instance of the http subtransport. * * This subtransport also supports https. * * @param out The newly created subtransport * @param owner The smart transport to own this subtransport * @return 0 or an error code */ GIT_EXTERN(int) git_smart_subtransport_http( git_smart_subtransport **out, git_transport *owner, void *param); /** * Create an instance of the git subtransport. * * @param out The newly created subtransport * @param owner The smart transport to own this subtransport * @return 0 or an error code */ GIT_EXTERN(int) git_smart_subtransport_git( git_smart_subtransport **out, git_transport *owner, void *param); /** * Create an instance of the ssh subtransport. * * @param out The newly created subtransport * @param owner The smart transport to own this subtransport * @return 0 or an error code */ GIT_EXTERN(int) git_smart_subtransport_ssh( git_smart_subtransport **out, git_transport *owner, void *param); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/filter.h0000644000175000017500000003021714125111754020326 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_filter_h__ #define INCLUDE_sys_git_filter_h__ #include "git2/filter.h" /** * @file git2/sys/filter.h * @brief Git filter backend and plugin routines * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Look up a filter by name * * @param name The name of the filter * @return Pointer to the filter object or NULL if not found */ GIT_EXTERN(git_filter *) git_filter_lookup(const char *name); #define GIT_FILTER_CRLF "crlf" #define GIT_FILTER_IDENT "ident" /** * This is priority that the internal CRLF filter will be registered with */ #define GIT_FILTER_CRLF_PRIORITY 0 /** * This is priority that the internal ident filter will be registered with */ #define GIT_FILTER_IDENT_PRIORITY 100 /** * This is priority to use with a custom filter to imitate a core Git * filter driver, so that it will be run last on checkout and first on * checkin. You do not have to use this, but it helps compatibility. */ #define GIT_FILTER_DRIVER_PRIORITY 200 /** * Create a new empty filter list * * Normally you won't use this because `git_filter_list_load` will create * the filter list for you, but you can use this in combination with the * `git_filter_lookup` and `git_filter_list_push` functions to assemble * your own chains of filters. */ GIT_EXTERN(int) git_filter_list_new( git_filter_list **out, git_repository *repo, git_filter_mode_t mode, uint32_t options); /** * Add a filter to a filter list with the given payload. * * Normally you won't have to do this because the filter list is created * by calling the "check" function on registered filters when the filter * attributes are set, but this does allow more direct manipulation of * filter lists when desired. * * Note that normally the "check" function can set up a payload for the * filter. Using this function, you can either pass in a payload if you * know the expected payload format, or you can pass NULL. Some filters * may fail with a NULL payload. Good luck! */ GIT_EXTERN(int) git_filter_list_push( git_filter_list *fl, git_filter *filter, void *payload); /** * Look up how many filters are in the list * * We will attempt to apply all of these filters to any data passed in, * but note that the filter apply action still has the option of skipping * data that is passed in (for example, the CRLF filter will skip data * that appears to be binary). * * @param fl A filter list * @return The number of filters in the list */ GIT_EXTERN(size_t) git_filter_list_length(const git_filter_list *fl); /** * A filter source represents a file/blob to be processed */ typedef struct git_filter_source git_filter_source; /** * Get the repository that the source data is coming from. */ GIT_EXTERN(git_repository *) git_filter_source_repo(const git_filter_source *src); /** * Get the path that the source data is coming from. */ GIT_EXTERN(const char *) git_filter_source_path(const git_filter_source *src); /** * Get the file mode of the source file * If the mode is unknown, this will return 0 */ GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src); /** * Get the OID of the source * If the OID is unknown (often the case with GIT_FILTER_CLEAN) then * this will return NULL. */ GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src); /** * Get the git_filter_mode_t to be used */ GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src); /** * Get the combination git_filter_flag_t options to be applied */ GIT_EXTERN(uint32_t) git_filter_source_flags(const git_filter_source *src); /** * Initialize callback on filter * * Specified as `filter.initialize`, this is an optional callback invoked * before a filter is first used. It will be called once at most. * * If non-NULL, the filter's `initialize` callback will be invoked right * before the first use of the filter, so you can defer expensive * initialization operations (in case libgit2 is being used in a way that * doesn't need the filter). */ typedef int GIT_CALLBACK(git_filter_init_fn)(git_filter *self); /** * Shutdown callback on filter * * Specified as `filter.shutdown`, this is an optional callback invoked * when the filter is unregistered or when libgit2 is shutting down. It * will be called once at most and should release resources as needed. * This may be called even if the `initialize` callback was not made. * * Typically this function will free the `git_filter` object itself. */ typedef void GIT_CALLBACK(git_filter_shutdown_fn)(git_filter *self); /** * Callback to decide if a given source needs this filter * * Specified as `filter.check`, this is an optional callback that checks * if filtering is needed for a given source. * * It should return 0 if the filter should be applied (i.e. success), * GIT_PASSTHROUGH if the filter should not be applied, or an error code * to fail out of the filter processing pipeline and return to the caller. * * The `attr_values` will be set to the values of any attributes given in * the filter definition. See `git_filter` below for more detail. * * The `payload` will be a pointer to a reference payload for the filter. * This will start as NULL, but `check` can assign to this pointer for * later use by the `stream` callback. Note that the value should be heap * allocated (not stack), so that it doesn't go away before the `stream` * callback can use it. If a filter allocates and assigns a value to the * `payload`, it will need a `cleanup` callback to free the payload. */ typedef int GIT_CALLBACK(git_filter_check_fn)( git_filter *self, void **payload, /* NULL on entry, may be set */ const git_filter_source *src, const char **attr_values); #ifndef GIT_DEPRECATE_HARD /** * Callback to actually perform the data filtering * * Specified as `filter.apply`, this is the callback that actually filters * data. If it successfully writes the output, it should return 0. Like * `check`, it can return GIT_PASSTHROUGH to indicate that the filter * doesn't want to run. Other error codes will stop filter processing and * return to the caller. * * The `payload` value will refer to any payload that was set by the * `check` callback. It may be read from or written to as needed. * * @deprecated use git_filter_stream_fn */ typedef int GIT_CALLBACK(git_filter_apply_fn)( git_filter *self, void **payload, /* may be read and/or set */ git_buf *to, const git_buf *from, const git_filter_source *src); #endif /** * Callback to perform the data filtering. * * Specified as `filter.stream`, this is a callback that filters data * in a streaming manner. This function will provide a * `git_writestream` that will the original data will be written to; * with that data, the `git_writestream` will then perform the filter * translation and stream the filtered data out to the `next` location. */ typedef int GIT_CALLBACK(git_filter_stream_fn)( git_writestream **out, git_filter *self, void **payload, const git_filter_source *src, git_writestream *next); /** * Callback to clean up after filtering has been applied * * Specified as `filter.cleanup`, this is an optional callback invoked * after the filter has been applied. If the `check`, `apply`, or * `stream` callbacks allocated a `payload` to keep per-source filter * state, use this callback to free that payload and release resources * as required. */ typedef void GIT_CALLBACK(git_filter_cleanup_fn)( git_filter *self, void *payload); /** * Filter structure used to register custom filters. * * To associate extra data with a filter, allocate extra data and put the * `git_filter` struct at the start of your data buffer, then cast the * `self` pointer to your larger structure when your callback is invoked. */ struct git_filter { /** The `version` field should be set to `GIT_FILTER_VERSION`. */ unsigned int version; /** * A whitespace-separated list of attribute names to check for this * filter (e.g. "eol crlf text"). If the attribute name is bare, it * will be simply loaded and passed to the `check` callback. If it * has a value (i.e. "name=value"), the attribute must match that * value for the filter to be applied. The value may be a wildcard * (eg, "name=*"), in which case the filter will be invoked for any * value for the given attribute name. See the attribute parameter * of the `check` callback for the attribute value that was specified. */ const char *attributes; /** Called when the filter is first used for any file. */ git_filter_init_fn initialize; /** Called when the filter is removed or unregistered from the system. */ git_filter_shutdown_fn shutdown; /** * Called to determine whether the filter should be invoked for a * given file. If this function returns `GIT_PASSTHROUGH` then the * `stream` or `apply` functions will not be invoked and the * contents will be passed through unmodified. */ git_filter_check_fn check; #ifdef GIT_DEPRECATE_HARD void *reserved; #else /** * Provided for backward compatibility; this will apply the * filter to the given contents in a `git_buf`. Callers should * provide a `stream` function instead. */ git_filter_apply_fn apply; #endif /** * Called to apply the filter, this function will provide a * `git_writestream` that will the original data will be * written to; with that data, the `git_writestream` will then * perform the filter translation and stream the filtered data * out to the `next` location. */ git_filter_stream_fn stream; /** Called when the system is done filtering for a file. */ git_filter_cleanup_fn cleanup; }; #define GIT_FILTER_VERSION 1 #define GIT_FILTER_INIT {GIT_FILTER_VERSION} /** * Initializes a `git_filter` with default values. Equivalent to * creating an instance with GIT_FILTER_INIT. * * @param filter the `git_filter` struct to initialize. * @param version Version the struct; pass `GIT_FILTER_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_filter_init(git_filter *filter, unsigned int version); /** * Register a filter under a given name with a given priority. * * As mentioned elsewhere, the initialize callback will not be invoked * immediately. It is deferred until the filter is used in some way. * * A filter's attribute checks and `check` and `stream` (or `apply`) * callbacks will be issued in order of `priority` on smudge (to * workdir), and in reverse order of `priority` on clean (to odb). * * Two filters are preregistered with libgit2: * - GIT_FILTER_CRLF with priority 0 * - GIT_FILTER_IDENT with priority 100 * * Currently the filter registry is not thread safe, so any registering or * deregistering of filters must be done outside of any possible usage of * the filters (i.e. during application setup or shutdown). * * @param name A name by which the filter can be referenced. Attempting * to register with an in-use name will return GIT_EEXISTS. * @param filter The filter definition. This pointer will be stored as is * by libgit2 so it must be a durable allocation (either static * or on the heap). * @param priority The priority for filter application * @return 0 on successful registry, error code <0 on failure */ GIT_EXTERN(int) git_filter_register( const char *name, git_filter *filter, int priority); /** * Remove the filter with the given name * * Attempting to remove the builtin libgit2 filters is not permitted and * will return an error. * * Currently the filter registry is not thread safe, so any registering or * deregistering of filters must be done outside of any possible usage of * the filters (i.e. during application setup or shutdown). * * @param name The name under which the filter was registered * @return 0 on success, error code <0 on failure */ GIT_EXTERN(int) git_filter_unregister(const char *name); /** @} */ GIT_END_DECL #endif git2r/src/libgit2/include/git2/sys/path.h0000644000175000017500000000352014125111754017772 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_sys_git_path_h__ #define INCLUDE_sys_git_path_h__ #include "git2/common.h" GIT_BEGIN_DECL /** * The kinds of git-specific files we know about. * * The order needs to stay the same to not break the `gitfiles` * array in path.c */ typedef enum { /** Check for the .gitignore file */ GIT_PATH_GITFILE_GITIGNORE, /** Check for the .gitmodules file */ GIT_PATH_GITFILE_GITMODULES, /** Check for the .gitattributes file */ GIT_PATH_GITFILE_GITATTRIBUTES } git_path_gitfile; /** * The kinds of checks to perform according to which filesystem we are trying to * protect. */ typedef enum { /** Do both NTFS- and HFS-specific checks */ GIT_PATH_FS_GENERIC, /** Do NTFS-specific checks only */ GIT_PATH_FS_NTFS, /** Do HFS-specific checks only */ GIT_PATH_FS_HFS } git_path_fs; /** * Check whether a path component corresponds to a .git$SUFFIX * file. * * As some filesystems do special things to filenames when * writing files to disk, you cannot always do a plain string * comparison to verify whether a file name matches an expected * path or not. This function can do the comparison for you, * depending on the filesystem you're on. * * @param path the path component to check * @param pathlen the length of `path` that is to be checked * @param gitfile which file to check against * @param fs which filesystem-specific checks to use * @return 0 in case the file does not match, a positive value if * it does; -1 in case of an error */ GIT_EXTERN(int) git_path_is_gitfile(const char *path, size_t pathlen, git_path_gitfile gitfile, git_path_fs fs); GIT_END_DECL #endif /* INCLUDE_sys_git_path */ git2r/src/libgit2/include/git2/checkout.h0000644000175000017500000003450614125111754020035 0ustar nileshnilesh/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_checkout_h__ #define INCLUDE_git_checkout_h__ #include "common.h" #include "types.h" #include "diff.h" /** * @file git2/checkout.h * @brief Git checkout routines * @defgroup git_checkout Git checkout routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Checkout behavior flags * * In libgit2, checkout is used to update the working directory and index * to match a target tree. Unlike git checkout, it does not move the HEAD * commit for you - use `git_repository_set_head` or the like to do that. * * Checkout looks at (up to) four things: the "target" tree you want to * check out, the "baseline" tree of what was checked out previously, the * working directory for actual files, and the index for staged changes. * * You give checkout one of three strategies for update: * * - `GIT_CHECKOUT_NONE` is a dry-run strategy that checks for conflicts, * etc., but doesn't make any actual changes. * * - `GIT_CHECKOUT_FORCE` is at the opposite extreme, taking any action to * make the working directory match the target (including potentially * discarding modified files). * * - `GIT_CHECKOUT_SAFE` is between these two options, it will only make * modifications that will not lose changes. * * | target == baseline | target != baseline | * ---------------------|-----------------------|----------------------| * workdir == baseline | no action | create, update, or | * | | delete file | * ---------------------|-----------------------|----------------------| * workdir exists and | no action | conflict (notify | * is != baseline | notify dirty MODIFIED | and cancel checkout) | * ---------------------|-----------------------|----------------------| * workdir missing, | notify dirty DELETED | create file | * baseline present | | | * ---------------------|-----------------------|----------------------| * * To emulate `git checkout`, use `GIT_CHECKOUT_SAFE` with a checkout * notification callback (see below) that displays information about dirty * files. The default behavior will cancel checkout on conflicts. * * To emulate `git checkout-index`, use `GIT_CHECKOUT_SAFE` with a * notification callback that cancels the operation if a dirty-but-existing * file is found in the working directory. This core git command isn't * quite "force" but is sensitive about some types of changes. * * To emulate `git checkout -f`, use `GIT_CHECKOUT_FORCE`. * * * There are some additional flags to modify the behavior of checkout: * * - GIT_CHECKOUT_ALLOW_CONFLICTS makes SAFE mode apply safe file updates * even if there are conflicts (instead of cancelling the checkout). * * - GIT_CHECKOUT_REMOVE_UNTRACKED means remove untracked files (i.e. not * in target, baseline, or index, and not ignored) from the working dir. * * - GIT_CHECKOUT_REMOVE_IGNORED means remove ignored files (that are also * untracked) from the working directory as well. * * - GIT_CHECKOUT_UPDATE_ONLY means to only update the content of files that * already exist. Files will not be created nor deleted. This just skips * applying adds, deletes, and typechanges. * * - GIT_CHECKOUT_DONT_UPDATE_INDEX prevents checkout from writing the * updated files' information to the index. * * - Normally, checkout will reload the index and git attributes from disk * before any operations. GIT_CHECKOUT_NO_REFRESH prevents this reload. * * - Unmerged index entries are conflicts. GIT_CHECKOUT_SKIP_UNMERGED skips * files with unmerged index entries instead. GIT_CHECKOUT_USE_OURS and * GIT_CHECKOUT_USE_THEIRS to proceed with the checkout using either the * stage 2 ("ours") or stage 3 ("theirs") version of files in the index. * * - GIT_CHECKOUT_DONT_OVERWRITE_IGNORED prevents ignored files from being * overwritten. Normally, files that are ignored in the working directory * are not considered "precious" and may be overwritten if the checkout * target contains that file. * * - GIT_CHECKOUT_DONT_REMOVE_EXISTING prevents checkout from removing * files or folders that fold to the same name on case insensitive * filesystems. This can cause files to retain their existing names * and write through existing symbolic links. */ typedef enum { GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */ /** * Allow safe updates that cannot overwrite uncommitted data. * If the uncommitted changes don't conflict with the checked out files, * the checkout will still proceed, leaving the changes intact. * * Mutually exclusive with GIT_CHECKOUT_FORCE. * GIT_CHECKOUT_FORCE takes precedence over GIT_CHECKOUT_SAFE. */ GIT_CHECKOUT_SAFE = (1u << 0), /** * Allow all updates to force working directory to look like index. * * Mutually exclusive with GIT_CHECKOUT_SAFE. * GIT_CHECKOUT_FORCE takes precedence over GIT_CHECKOUT_SAFE. */ GIT_CHECKOUT_FORCE = (1u << 1), /** Allow checkout to recreate missing files */ GIT_CHECKOUT_RECREATE_MISSING = (1u << 2), /** Allow checkout to make safe updates even if conflicts are found */ GIT_CHECKOUT_ALLOW_CONFLICTS = (1u << 4), /** Remove untracked files not in index (that are not ignored) */ GIT_CHECKOUT_REMOVE_UNTRACKED = (1u << 5), /** Remove ignored files not in index */ GIT_CHECKOUT_REMOVE_IGNORED = (1u << 6), /** Only update existing files, don't create new ones */ GIT_CHECKOUT_UPDATE_ONLY = (1u << 7), /** * Normally checkout updates index entries as it goes; this stops that. * Implies `GIT_CHECKOUT_DONT_WRITE_INDEX`. */ GIT_CHECKOUT_DONT_UPDATE_INDEX = (1u << 8), /** Don't refresh index/config/etc before doing checkout */ GIT_CHECKOUT_NO_REFRESH = (1u << 9), /** Allow checkout to skip unmerged files */ GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10), /** For unmerged files, checkout stage 2 from index */ GIT_CHECKOUT_USE_OURS = (1u << 11), /** For unmerged files, checkout stage 3 from index */ GIT_CHECKOUT_USE_THEIRS = (1u << 12), /** Treat pathspec as simple list of exact match file paths */ GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13), /** Ignore directories in use, they will be left empty */ GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18), /** Don't overwrite ignored files that exist in the checkout target */ GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19), /** Write normal merge files for conflicts */ GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20), /** Include common ancestor data in diff3 format files for conflicts */ GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21), /** Don't overwrite existing files or folders */ GIT_CHECKOUT_DONT_REMOVE_EXISTING = (1u << 22), /** Normally checkout writes the index upon completion; this prevents that. */ GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23), /** * Show what would be done by a checkout. Stop after sending * notifications; don't update the working directory or index. */ GIT_CHECKOUT_DRY_RUN = (1u << 24), /** * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED */ /** Recursively checkout submodules with same options (NOT IMPLEMENTED) */ GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16), /** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */ GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1u << 17), } git_checkout_strategy_t; /** * Checkout notification flags * * Checkout will invoke an options notification callback (`notify_cb`) for * certain cases - you pick which ones via `notify_flags`: * * Returning a non-zero value from this callback will cancel the checkout. * The non-zero return value will be propagated back and returned by the * git_checkout_... call. * * Notification callbacks are made prior to modifying any files on disk, * so canceling on any notification will still happen prior to any files * being modified. */ typedef enum { GIT_CHECKOUT_NOTIFY_NONE = 0, /** * Invokes checkout on conflicting paths. */ GIT_CHECKOUT_NOTIFY_CONFLICT = (1u << 0), /** * Notifies about "dirty" files, i.e. those that do not need an update * but no longer match the baseline. Core git displays these files when * checkout runs, but won't stop the checkout. */ GIT_CHECKOUT_NOTIFY_DIRTY = (1u << 1), /** * Sends notification for any file changed. */ GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2), /** * Notifies about untracked files. */ GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3), /** * Notifies about ignored files. */ GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4), GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu } git_checkout_notify_t; /** Checkout performance-reporting structure */ typedef struct { size_t mkdir_calls; size_t stat_calls; size_t chmod_calls; } git_checkout_perfdata; /** Checkout notification callback function */ typedef int GIT_CALLBACK(git_checkout_notify_cb)( git_checkout_notify_t why, const char *path, const git_diff_file *baseline, const git_diff_file *target, const git_diff_file *workdir, void *payload); /** Checkout progress notification function */ typedef void GIT_CALLBACK(git_checkout_progress_cb)( const char *path, size_t completed_steps, size_t total_steps, void *payload); /** Checkout perfdata notification function */ typedef void GIT_CALLBACK(git_checkout_perfdata_cb)( const git_checkout_perfdata *perfdata, void *payload); /** * Checkout options structure * * Initialize with `GIT_CHECKOUT_OPTIONS_INIT`. Alternatively, you can * use `git_checkout_options_init`. * */ typedef struct git_checkout_options { unsigned int version; /**< The version */ unsigned int checkout_strategy; /**< default will be a safe checkout */ int disable_filters; /**< don't apply filters like CRLF conversion */ unsigned int dir_mode; /**< default is 0755 */ unsigned int file_mode; /**< default is 0644 or 0755 as dictated by blob */ int file_open_flags; /**< default is O_CREAT | O_TRUNC | O_WRONLY */ unsigned int notify_flags; /**< see `git_checkout_notify_t` above */ /** * Optional callback to get notifications on specific file states. * @see git_checkout_notify_t */ git_checkout_notify_cb notify_cb; /** Payload passed to notify_cb */ void *notify_payload; /** Optional callback to notify the consumer of checkout progress. */ git_checkout_progress_cb progress_cb; /** Payload passed to progress_cb */ void *progress_payload; /** * A list of wildmatch patterns or paths. * * By default, all paths are processed. If you pass an array of wildmatch * patterns, those will be used to filter which paths should be taken into * account. * * Use GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH to treat as a simple list. */ git_strarray paths; /** * The expected content of the working directory; defaults to HEAD. * * If the working directory does not match this baseline information, * that will produce a checkout conflict. */ git_tree *baseline; /** * Like `baseline` above, though expressed as an index. This * option overrides `baseline`. */ git_index *baseline_index; const char *target_directory; /**< alternative checkout path to workdir */ const char *ancestor_label; /**< the name of the common ancestor side of conflicts */ const char *our_label; /**< the name of the "our" side of conflicts */ const char *their_label; /**< the name of the "their" side of conflicts */ /** Optional callback to notify the consumer of performance data. */ git_checkout_perfdata_cb perfdata_cb; /** Payload passed to perfdata_cb */ void *perfdata_payload; } git_checkout_options; #define GIT_CHECKOUT_OPTIONS_VERSION 1 #define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE} /** * Initialize git_checkout_options structure * * Initializes a `git_checkout_options` with default values. Equivalent to creating * an instance with GIT_CHECKOUT_OPTIONS_INIT. * * @param opts The `git_checkout_options` struct to initialize. * @param version The struct version; pass `GIT_CHECKOUT_OPTIONS_VERSION`. * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_checkout_options_init( git_checkout_options *opts, unsigned int version); /** * Updates files in the index and the working tree to match the content of * the commit pointed at by HEAD. * * Note that this is _not_ the correct mechanism used to switch branches; * do not change your `HEAD` and then call this method, that would leave * you with checkout conflicts since your working directory would then * appear to be dirty. Instead, checkout the target of the branch and * then update `HEAD` using `git_repository_set_head` to point to the * branch you checked out. * * @param repo repository to check out (must be non-bare) * @param opts specifies checkout options (may be NULL) * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non * existing branch, non-zero value returned by `notify_cb`, or * other error code < 0 (use git_error_last for error details) */ GIT_EXTERN(int) git_checkout_head( git_repository *repo, const git_checkout_options *opts); /** * Updates files in the working tree to match the content of the index. * * @param repo repository into which to check out (must be non-bare) * @param index index to be checked out (or NULL to use repository index) * @param opts specifies checkout options (may be NULL) * @return 0 on success, non-zero return value from `notify_cb`, or error * code < 0 (use git_error_last for error details) */ GIT_EXTERN(int) git_checkout_index( git_repository *repo, git_index *index, const git_checkout_options *opts); /** * Updates files in the index and working tree to match the content of the * tree pointed at by the treeish. * * @param repo repository to check out (must be non-bare) * @param treeish a commit, tag or tree which content will be used to update * the working directory (or NULL to use HEAD) * @param opts specifies checkout options (may be NULL) * @return 0 on success, non-zero return value from `notify_cb`, or error * code < 0 (use git_error_last for error details) */ GIT_EXTERN(int) git_checkout_tree( git_repository *repo, const git_object *treeish, const git_checkout_options *opts); /** @} */ GIT_END_DECL #endif git2r/src/git2r_commit.h0000644000175000017500000000231513273077667015015 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_commit_h #define INCLUDE_git2r_commit_h #include #include #include SEXP git2r_commit( SEXP repo, SEXP message, SEXP author, SEXP committer); int git2r_commit_lookup( git_commit **out, git_repository *repository, SEXP commit); SEXP git2r_commit_tree(SEXP commit); void git2r_commit_init(git_commit *source, SEXP repo, SEXP dest); SEXP git2r_commit_parent_list(SEXP commit); #endif git2r/src/git2r_reference.c0000644000175000017500000001322213671131056015436 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_reference.h" #include "git2r_repository.h" #include "git2r_S3.h" /** * Init slots in S3 class git_reference. * * @param source A git_reference pointer * @param repo S3 class git_repository that contains the reference * @param dest S3 class git_reference to initialize * @return void */ static void git2r_reference_init( git_reference *source, SEXP repo, SEXP dest) { char sha[GIT_OID_HEXSZ + 1]; SET_VECTOR_ELT( dest, git2r_S3_item__git_reference__name, Rf_mkString(git_reference_name(source))); SET_VECTOR_ELT( dest, git2r_S3_item__git_reference__shorthand, Rf_mkString(git_reference_shorthand(source))); switch (git_reference_type(source)) { case GIT2R_REFERENCE_DIRECT: SET_VECTOR_ELT( dest, git2r_S3_item__git_reference__type, Rf_ScalarInteger(GIT2R_REFERENCE_DIRECT)); git_oid_fmt(sha, git_reference_target(source)); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT( dest, git2r_S3_item__git_reference__sha, Rf_mkString(sha)); break; case GIT2R_REFERENCE_SYMBOLIC: SET_VECTOR_ELT( dest, git2r_S3_item__git_reference__type, Rf_ScalarInteger(GIT2R_REFERENCE_SYMBOLIC)); SET_VECTOR_ELT( dest, git2r_S3_item__git_reference__target, Rf_mkString(git_reference_symbolic_target(source))); break; default: git2r_error(__func__, NULL, git2r_err_reference, NULL); } if (Rf_isNull(VECTOR_ELT(dest, git2r_S3_item__git_reference__target))) { SET_VECTOR_ELT( dest, git2r_S3_item__git_reference__target, Rf_ScalarString(NA_STRING)); } SET_VECTOR_ELT(dest, git2r_S3_item__git_reference__repo, Rf_duplicate(repo)); } /** * Lookup the full name of a reference by DWIMing its short name * * @param repo S3 class git_repository * @param shorthand The short name for the reference * @return S3 class git_reference object */ SEXP attribute_hidden git2r_reference_dwim( SEXP repo, SEXP shorthand) { int error, nprotect = 0; SEXP result = R_NilValue; git_reference* reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_string(shorthand)) git2r_error(__func__, NULL, "'shorthand'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_reference_dwim( &reference, repository, CHAR(STRING_ELT(shorthand, 0))); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_reference)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_reference)); git2r_reference_init(reference, repo, result); cleanup: git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get all references that can be found in a repository. * * @param repo S3 class git_repository * @return VECXSP with S3 objects of class git_reference */ SEXP attribute_hidden git2r_reference_list( SEXP repo) { int error, nprotect = 0; size_t i; git_strarray ref_list; SEXP result = R_NilValue; SEXP names = R_NilValue; git_repository *repository = NULL; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_reference_list(&ref_list, repository); if (error) goto cleanup; PROTECT(result = Rf_allocVector(VECSXP, ref_list.count)); nprotect++; Rf_setAttrib( result, R_NamesSymbol, names = Rf_allocVector(STRSXP, ref_list.count)); for (i = 0; i < ref_list.count; i++) { SEXP reference; git_reference *ref = NULL; error = git_reference_lookup(&ref, repository, ref_list.strings[i]); if (error) goto cleanup; SET_VECTOR_ELT( result, i, reference = Rf_mkNamed(VECSXP, git2r_S3_items__git_reference)); Rf_setAttrib(reference, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_reference)); git2r_reference_init(ref, repo, reference); SET_STRING_ELT(names, i, Rf_mkChar(ref_list.strings[i])); git_reference_free(ref); } cleanup: git_strarray_free(&ref_list); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_note.h0000644000175000017500000000221713137360640014454 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_note_h #define INCLUDE_git2r_note_h #include #include SEXP git2r_note_create( SEXP repo, SEXP sha, SEXP message, SEXP ref, SEXP author, SEXP committer, SEXP force); SEXP git2r_note_default_ref(SEXP repo); SEXP git2r_notes(SEXP repo, SEXP ref); SEXP git2r_note_remove( SEXP note, SEXP author, SEXP committer); #endif git2r/src/git2r_transfer.h0000644000175000017500000000364713647041736015353 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_transfer_h #define INCLUDE_git2r_transfer_h #include #include #include #include "git2r_deprecated.h" /** * Data structure to hold information when performing a clone, fetch * or push operation. */ typedef struct { int received_progress; int received_done; int verbose; /* Used in the 'git2r_cred_acquire_cb' callback to determine if to * use the 'ssh-agent' to find the ssh key for authentication. * Only used when credentials equals R_NilValue. */ int use_ssh_agent; /* Used in the 'git2r_cred_acquire_cb' callback to determine if to * to search for the 'id_rsa' ssh key for authentication. Only * used when credentials equals R_NilValue. FIXME: This is * currently always set to zero, i.e. git2r does not automatically * search for the 'id_rsa' ssh key. */ int use_ssh_key; SEXP credentials; } git2r_transfer_data; #ifdef WIN32 # define GIT2R_TRANSFER_DATA_INIT {0, 0, 0, 1, 0, R_NilValue} #else # define GIT2R_TRANSFER_DATA_INIT {0, 0, 0, 1, 0, R_NilValue} #endif void git2r_transfer_progress_init( const GIT2R_INDEXER_PROGRESS *source, SEXP dest); #endif git2r/src/git2r_tag.h0000644000175000017500000000212613453665310014264 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2019 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_tag_h #define INCLUDE_git2r_tag_h #include #include #include void git2r_tag_init(git_tag *source, SEXP repo, SEXP dest); SEXP git2r_tag_create(SEXP repo, SEXP name, SEXP message, SEXP tagger, SEXP force); SEXP git2r_tag_delete(SEXP repo, SEXP name); SEXP git2r_tag_list(SEXP repo); #endif git2r/src/git2r_reflog.c0000644000175000017500000001106513671131056014761 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_reflog.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" /** * Init slots in S3 class git_reflog_entry * * @param source The source reflog entry * @param index The entry index * @param repo S3 class git_repository * @param ref Reference to read from * @param dest S3 class git_reflog_entry to initialize * @return void */ static void git2r_reflog_entry_init( const git_reflog_entry *source, size_t index, SEXP repo, SEXP ref, SEXP dest) { SEXP i; const char *message; const git_signature *committer; char sha[GIT_OID_HEXSZ + 1]; git_oid_fmt(sha, git_reflog_entry_id_new(source)); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT( dest, git2r_S3_item__git_reflog_entry__sha, Rf_mkString(sha)); SET_VECTOR_ELT( dest, git2r_S3_item__git_reflog_entry__index, i = Rf_allocVector(INTSXP, 1)); INTEGER(i)[0] = index; committer = git_reflog_entry_committer(source); if (committer) { if (Rf_isNull(VECTOR_ELT(dest, git2r_S3_item__git_reflog_entry__committer))) { SEXP item; SET_VECTOR_ELT( dest, git2r_S3_item__git_reflog_entry__committer, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_signature)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_signature)); } git2r_signature_init( committer, VECTOR_ELT(dest, git2r_S3_item__git_reflog_entry__committer)); } message = git_reflog_entry_message(source); if (message) { SET_VECTOR_ELT( dest, git2r_S3_item__git_reflog_entry__message, Rf_mkString(message)); } else { SET_VECTOR_ELT( dest, git2r_S3_item__git_reflog_entry__message, Rf_ScalarString(NA_STRING)); } SET_VECTOR_ELT( dest, git2r_S3_item__git_reflog_entry__refname, ref); SET_VECTOR_ELT( dest, git2r_S3_item__git_reflog_entry__repo, Rf_duplicate(repo)); } /** * List the reflog within a specified reference. * * @param repo S3 class git_repository * @param ref Reference to read from. * @return VECXSP with S3 objects of class git_reflog */ SEXP attribute_hidden git2r_reflog_list( SEXP repo, SEXP ref) { int error, nprotect = 0; size_t i, n; SEXP result = R_NilValue; git_reflog *reflog = NULL; git_repository *repository = NULL; if (git2r_arg_check_string(ref)) git2r_error(__func__, NULL, "'ref'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_reflog_read(&reflog, repository, CHAR(STRING_ELT(ref, 0))); if (error) goto cleanup; n = git_reflog_entrycount(reflog); PROTECT(result = Rf_allocVector(VECSXP, n)); nprotect++; for (i = 0; i < n; i++) { const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i); if (entry) { SEXP item; SET_VECTOR_ELT( result, i, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_reflog_entry)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_reflog_entry)); git2r_reflog_entry_init(entry, i, repo, ref, item); } } cleanup: git_reflog_free(reflog); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_odb.h0000644000175000017500000000175613226357765014277 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_odb_h #define INCLUDE_git2r_odb_h #include #include SEXP git2r_odb_blobs(SEXP repo); SEXP git2r_odb_hash(SEXP data); SEXP git2r_odb_hashfile(SEXP path); SEXP git2r_odb_objects(SEXP repo); #endif git2r/src/git2r_deprecated.h0000644000175000017500000000771413765350077015630 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_deprecated_h #define INCLUDE_git2r_deprecated_h /* The constants GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, * GIT_OBJ_TAG_GIT_OBJ_TREE and GIT_REF_OID are deprecated in * libgit2. Use GIT_OBJECT_ANY, GIT_OBJECT_BLOB, GIT_OBJECT_COMMIT, * GIT_OBJECT_TAG_GIT_OBJECT_TREE and GIT_REFERENCE_DIRECT, if * available, instead. */ #if defined(GIT2R_HAVE_OBJECT_ANY) # define GIT2R_OBJECT_ANY GIT_OBJECT_ANY # define GIT2R_OBJECT_BLOB GIT_OBJECT_BLOB # define GIT2R_OBJECT_COMMIT GIT_OBJECT_COMMIT # define GIT2R_OBJECT_TAG GIT_OBJECT_TAG # define GIT2R_OBJECT_TREE GIT_OBJECT_TREE # define GIT2R_REFERENCE_DIRECT GIT_REFERENCE_DIRECT # define GIT2R_REFERENCE_SYMBOLIC GIT_REFERENCE_SYMBOLIC #else # define GIT2R_OBJECT_ANY GIT_OBJ_ANY # define GIT2R_OBJECT_BLOB GIT_OBJ_BLOB # define GIT2R_OBJECT_COMMIT GIT_OBJ_COMMIT # define GIT2R_OBJECT_TAG GIT_OBJ_TAG # define GIT2R_OBJECT_TREE GIT_OBJ_TREE # define GIT2R_REFERENCE_DIRECT GIT_REF_OID # define GIT2R_REFERENCE_SYMBOLIC GIT_REF_SYMBOLIC #endif /* The function 'git_buf_free' is deprecated in libgit2. Use * 'git_buf_dispose', if available, instead. */ #if defined(GIT2R_HAVE_BUF_DISPOSE) # define GIT2R_BUF_DISPOSE git_buf_dispose #else # define GIT2R_BUF_DISPOSE git_buf_free #endif #if defined(GIT2R_HAVE_GIT_ERROR) # define GIT2R_ERROR_SET_STR git_error_set_str # define GIT2R_ERROR_LAST git_error_last # define GIT2R_ERROR_SET_OOM git_error_set_oom # define GIT2R_ERROR_NONE GIT_ERROR_NONE # define GIT2R_ERROR_OS GIT_ERROR_OS # define GIT2R_ERROR_NOMEMORY GIT_ERROR_NOMEMORY # define GIT2R_ERROR_CONFIG GIT_ERROR_CONFIG # define GIT2R_OBJECT_T git_object_t #else # define GIT2R_ERROR_SET_STR giterr_set_str # define GIT2R_ERROR_LAST giterr_last # define GIT2R_ERROR_SET_OOM giterr_set_oom # define GIT2R_ERROR_NONE GITERR_NONE # define GIT2R_ERROR_OS GITERR_OS # define GIT2R_ERROR_NOMEMORY GITERR_NOMEMORY # define GIT2R_ERROR_CONFIG GITERR_CONFIG # define GIT2R_OBJECT_T git_otype #endif #if defined(GIT2R_LIBGIT2_V0_99_0_RENAMES) # define GIT2R_CREDENTIAL git_credential # define GIT2R_CREDENTIAL_SSH_KEY GIT_CREDENTIAL_SSH_KEY # define GIT2R_CREDENTIAL_SSH_KEY_NEW git_credential_ssh_key_new # define GIT2R_CREDENTIAL_USERPASS_PLAINTEXT_NEW git_credential_userpass_plaintext_new # define GIT2R_CREDENTIAL_SSH_KEY_FROM_AGENT git_credential_ssh_key_from_agent # define GIT2R_CREDENTIAL_USERPASS_PLAINTEXT GIT_CREDENTIAL_USERPASS_PLAINTEXT # define GIT2R_INDEXER_PROGRESS git_indexer_progress # define GIT2R_OID_IS_ZERO git_oid_is_zero # define GIT2R_BLOB_CREATE_FROM_DISK git_blob_create_from_disk # define GIT2R_BLOB_CREATE_FROM_WORKDIR git_blob_create_from_workdir #else # define GIT2R_CREDENTIAL git_cred # define GIT2R_CREDENTIAL_SSH_KEY GIT_CREDTYPE_SSH_KEY # define GIT2R_CREDENTIAL_SSH_KEY_NEW git_cred_ssh_key_new # define GIT2R_CREDENTIAL_USERPASS_PLAINTEXT_NEW git_cred_userpass_plaintext_new # define GIT2R_CREDENTIAL_SSH_KEY_FROM_AGENT git_cred_ssh_key_from_agent # define GIT2R_CREDENTIAL_USERPASS_PLAINTEXT GIT_CREDTYPE_USERPASS_PLAINTEXT # define GIT2R_INDEXER_PROGRESS git_transfer_progress # define GIT2R_OID_IS_ZERO git_oid_iszero # define GIT2R_BLOB_CREATE_FROM_DISK git_blob_create_fromdisk # define GIT2R_BLOB_CREATE_FROM_WORKDIR git_blob_create_fromworkdir #endif #endif git2r/src/git2r_blame.c0000644000175000017500000001371313671131056014565 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_blame.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" /** * Init slots in S3 class git_blame * * Iterates over and init each git_blame_hunk * @param source a blame object * @param repo S3 class git_repository that contains the blame * @param path The path to the file to get the blame * @param dest S3 class git_blame to initialize * @return void */ void static git2r_blame_init( git_blame *source, SEXP repo, SEXP path, SEXP dest) { SEXP hunks; size_t i, n; n = git_blame_get_hunk_count(source); SET_VECTOR_ELT( dest, git2r_S3_item__git_blame__hunks, hunks = Rf_allocVector(VECSXP, n)); for (i = 0; i < n; i++) { const git_blame_hunk *hunk; hunk = git_blame_get_hunk_byindex(source, i); if (hunk) { SEXP item, signature; char sha[GIT_OID_HEXSZ + 1]; SET_VECTOR_ELT( hunks, i, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_blame_hunk)); Rf_setAttrib( item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_blame_hunk)); SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__lines_in_hunk, Rf_ScalarInteger(hunk->lines_in_hunk)); git_oid_fmt(sha, &(hunk->final_commit_id)); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__final_commit_id, Rf_mkString(sha)); SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__final_start_line_number, Rf_ScalarInteger(hunk->final_start_line_number)); SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__final_signature, signature = Rf_mkNamed(VECSXP, git2r_S3_items__git_signature)); Rf_setAttrib( signature, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_signature)); git2r_signature_init(hunk->final_signature, signature); git_oid_fmt(sha, &(hunk->orig_commit_id)); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__orig_commit_id, Rf_mkString(sha)); SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__orig_start_line_number, Rf_ScalarInteger(hunk->orig_start_line_number)); SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__orig_signature, signature = Rf_mkNamed(VECSXP, git2r_S3_items__git_signature)); Rf_setAttrib(signature, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_signature)); git2r_signature_init(hunk->orig_signature, signature); SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__orig_path, Rf_mkString(hunk->orig_path)); if (hunk->boundary) { SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__boundary, Rf_ScalarLogical(1)); } else { SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__boundary, Rf_ScalarLogical(0)); } SET_VECTOR_ELT( item, git2r_S3_item__git_blame_hunk__repo, Rf_duplicate(repo)); } } SET_VECTOR_ELT(dest, git2r_S3_item__git_blame__path, path); SET_VECTOR_ELT(dest, git2r_S3_item__git_blame__repo, Rf_duplicate(repo)); } /** * Get the blame for a single file * * @param repo S3 class git_repository that contains the blob * @param path The path to the file to get the blame * @return S3 class git_blame */ SEXP attribute_hidden git2r_blame_file( SEXP repo, SEXP path) { int error, nprotect = 0; SEXP result = R_NilValue; git_blame *blame = NULL; git_repository *repository = NULL; git_blame_options blame_opts = GIT_BLAME_OPTIONS_INIT; if (git2r_arg_check_string(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_blame_file( &blame, repository, CHAR(STRING_ELT(path, 0)), &blame_opts); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_blame)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_blame)); git2r_blame_init(blame, repo, path, result); cleanup: git_blame_free(blame); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_commit.c0000644000175000017500000003442213671131056014775 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2019 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_oid.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" #include "git2r_tree.h" /** * Check for any changes in index * * @param repository The repository * @return 0 if ok, else error code. */ static int git2r_any_changes_in_index( git_repository *repository) { int error; int changes_in_index = 0; size_t i, count; git_status_list *status = NULL; git_status_options opts = GIT_STATUS_OPTIONS_INIT; opts.show = GIT_STATUS_SHOW_INDEX_ONLY; error = git_status_list_new(&status, repository, &opts); if (error) goto cleanup; count = git_status_list_entrycount(status); for (i = 0; i < count; ++i) { const git_status_entry *s = git_status_byindex(status, i); if (s->status == GIT_STATUS_CURRENT) continue; if (s->status & GIT_STATUS_INDEX_NEW) changes_in_index = 1; else if (s->status & GIT_STATUS_INDEX_MODIFIED) changes_in_index = 1; else if (s->status & GIT_STATUS_INDEX_DELETED) changes_in_index = 1; else if (s->status & GIT_STATUS_INDEX_RENAMED) changes_in_index = 1; else if (s->status & GIT_STATUS_INDEX_TYPECHANGE) changes_in_index = 1; if (changes_in_index) break; } if (!changes_in_index) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_nothing_added_to_commit); error = GIT_ERROR; } cleanup: git_status_list_free(status); return error; } /** * Close the commits in parents and free memory of parents. * * @param parents The parent vector of commits. * @param n_parents The number of parents. * @return void */ static void git2r_parents_free( git_commit **parents, size_t n_parents) { if (parents) { size_t i; for (i = 0; i < n_parents; i++) { if (parents[i]) git_commit_free(parents[i]); } free(parents); } } /** * Data structure to hold information when iterating over MERGE_HEAD * entries. */ typedef struct { size_t n; git_repository *repository; git_commit **parents; } git2r_merge_head_cb_data; /** * Invoked 'callback' for each ID in the MERGE_HEAD file. * * @param oid The id of the merge head * @param payload Payload data passed to 'git_repository_mergehead_foreach' * @return 0 */ static int git2r_repository_mergehead_foreach_cb( const git_oid *oid, void *payload) { int error = 0; git2r_merge_head_cb_data *cb_data = (git2r_merge_head_cb_data*)payload; if (cb_data->parents) error = git_commit_lookup( &(cb_data->parents[cb_data->n]), cb_data->repository, oid); cb_data->n += 1; return error; } /** * Retrieve parents of the commit under construction * * @param parents The vector of parents to create and populate. * @param n_parents The length of parents vector * @param repository The repository * @return 0 on succes, or error code */ static int git2r_retrieve_parents( git_commit ***parents, size_t *n_parents, git_repository *repository) { int error; git_oid oid; git2r_merge_head_cb_data cb_data = {0, NULL, NULL}; git_repository_state_t state; error = git_repository_head_unborn(repository); if (1 == error) { *n_parents = 0; return GIT_OK; } else if (0 != error) { return error; } state = git_repository_state(repository); if (state == GIT_REPOSITORY_STATE_MERGE) { /* Count number of merge heads */ error = git_repository_mergehead_foreach( repository, git2r_repository_mergehead_foreach_cb, &cb_data); if (error) return error; } *parents = calloc(cb_data.n + 1, sizeof(git_commit*)); if (!parents) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_NONE, git2r_err_alloc_memory_buffer); return GIT_ERROR; } *n_parents = cb_data.n + 1; error = git_reference_name_to_id(&oid, repository, "HEAD"); if (error) return error; error = git_commit_lookup(&**parents, repository, &oid); if (error) return error; if (state == GIT_REPOSITORY_STATE_MERGE) { /* Append merge heads to parents */ cb_data.n = 0; cb_data.repository = repository; cb_data.parents = *parents + 1; error = git_repository_mergehead_foreach( repository, git2r_repository_mergehead_foreach_cb, &cb_data); if (error) return error; } return GIT_OK; } /** * Create a commit * * @param out The oid of the newly created commit * @param repository The repository * @param index The index * @param message The commit message * @param author Who is the author of the commit * @param committer Who is the committer * @return 0 on success, or error code */ int attribute_hidden git2r_commit_create( git_oid *out, git_repository *repository, git_index *index, const char *message, git_signature *author, git_signature *committer) { int error; git_oid oid; git_tree *tree = NULL; git_commit **parents = NULL; size_t n_parents = 0; error = git_index_write_tree(&oid, index); if (error) goto cleanup; error = git_tree_lookup(&tree, repository, &oid); if (error) goto cleanup; error = git2r_retrieve_parents(&parents, &n_parents, repository); if (error) goto cleanup; error = git_commit_create( out, repository, "HEAD", author, committer, NULL, message, tree, n_parents, (const git_commit**)parents); if (error) goto cleanup; error = git_repository_state_cleanup(repository); cleanup: git2r_parents_free(parents, n_parents); git_tree_free(tree); return error; } /** * Commit * * @param repo S3 class git_repository * @param message The message for the commit * @param author S3 class git_signature * @param committer S3 class git_signature * @return S3 class git_commit */ SEXP attribute_hidden git2r_commit( SEXP repo, SEXP message, SEXP author, SEXP committer) { int error, nprotect = 0; SEXP result = R_NilValue; git_signature *c_author = NULL; git_signature *c_committer = NULL; git_index *index = NULL; git_oid oid; git_repository *repository = NULL; git_commit *commit = NULL; if (git2r_arg_check_string(message)) git2r_error(__func__, NULL, "'message'", git2r_err_string_arg); if (git2r_arg_check_signature(author)) git2r_error(__func__, NULL, "'author'", git2r_err_signature_arg); if (git2r_arg_check_signature(committer)) git2r_error(__func__, NULL, "'committer'", git2r_err_signature_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_signature_from_arg(&c_author, author); if (error) goto cleanup; error = git2r_signature_from_arg(&c_committer, committer); if (error) goto cleanup; error = git2r_any_changes_in_index(repository); if (error) goto cleanup; error = git_repository_index(&index, repository); if (error) goto cleanup; error = git2r_commit_create( &oid, repository, index, CHAR(STRING_ELT(message, 0)), c_author, c_committer); if (error) goto cleanup; error = git_commit_lookup(&commit, repository, &oid); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init(commit, repo, result); cleanup: git_signature_free(c_author); git_signature_free(c_committer); git_index_free(index); git_repository_free(repository); git_commit_free(commit); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get commit object from S3 class git_commit * * @param out Pointer to the looked up commit * @param repository The repository * @param commit S3 class git_commit * @return 0 or an error code */ int attribute_hidden git2r_commit_lookup( git_commit **out, git_repository *repository, SEXP commit) { SEXP sha; git_oid oid; sha = git2r_get_list_element(commit, "sha"); git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); return git_commit_lookup(out, repository, &oid); } /** * Get the tree pointed to by a commit * * @param commit S3 class git_commit or git_stash * @return S3 class git_tree */ SEXP attribute_hidden git2r_commit_tree( SEXP commit) { int error, nprotect = 0; SEXP result = R_NilValue; SEXP repo; git_commit *commit_obj = NULL; git_repository *repository = NULL; git_tree *tree = NULL; if (git2r_arg_check_commit_stash(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_stash_arg); repo = git2r_get_list_element(commit, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_commit_lookup(&commit_obj, repository, commit); if (error) goto cleanup; error = git_commit_tree(&tree, commit_obj); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_tree)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_tree)); git2r_tree_init((git_tree*)tree, repo, result); cleanup: git_commit_free(commit_obj); git_tree_free(tree); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Init slots in S3 class git_commit * * @param source a commit object * @param repo S3 class git_repository that contains the blob * @param dest S3 class git_commit to initialize * @return void */ void attribute_hidden git2r_commit_init( git_commit *source, SEXP repo, SEXP dest) { const char *str; const git_signature *signature; char sha[GIT_OID_HEXSZ + 1]; git_oid_fmt(sha, git_commit_id(source)); sha[GIT_OID_HEXSZ] = '\0'; SET_VECTOR_ELT(dest, git2r_S3_item__git_commit__sha, Rf_mkString(sha)); signature = git_commit_author(source); if (signature) { SEXP elem = VECTOR_ELT(dest, git2r_S3_item__git_commit__author); if (Rf_isNull(elem)) { SET_VECTOR_ELT( dest, git2r_S3_item__git_commit__author, elem = Rf_mkNamed(VECSXP, git2r_S3_items__git_signature)); Rf_setAttrib(elem, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_signature)); } git2r_signature_init(signature, elem); } signature = git_commit_committer(source); if (signature) { SEXP elem = VECTOR_ELT(dest, git2r_S3_item__git_commit__committer); if (Rf_isNull(elem)) { SET_VECTOR_ELT( dest, git2r_S3_item__git_commit__author, elem = Rf_mkNamed(VECSXP, git2r_S3_items__git_signature)); Rf_setAttrib(elem, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_signature)); } git2r_signature_init(signature, elem); } str = git_commit_summary(source); if (str) SET_VECTOR_ELT(dest, git2r_S3_item__git_commit__summary, Rf_mkString(str)); str = git_commit_message(source); if (str) SET_VECTOR_ELT(dest, git2r_S3_item__git_commit__message, Rf_mkString(str)); SET_VECTOR_ELT(dest, git2r_S3_item__git_commit__repo, Rf_duplicate(repo)); } /** * Parents of a commit * * @param commit S3 class git_commit * @return list of S3 class git_commit objects */ SEXP attribute_hidden git2r_commit_parent_list( SEXP commit) { int error, nprotect = 0; size_t i, n; SEXP repo; SEXP list = R_NilValue; git_commit *commit_obj = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg); repo = git2r_get_list_element(commit, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_commit_lookup(&commit_obj, repository, commit); if (error) goto cleanup; n = git_commit_parentcount(commit_obj); PROTECT(list = Rf_allocVector(VECSXP, n)); nprotect++; for (i = 0; i < n; i++) { git_commit *parent = NULL; SEXP item; error = git_commit_parent(&parent, commit_obj, i); if (error) goto cleanup; SET_VECTOR_ELT( list, i, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init(parent, repo, item); git_commit_free(parent); } cleanup: git_commit_free(commit_obj); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return list; } git2r/src/git2r_stash.c0000644000175000017500000002300113671131056014616 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" #include "git2r_stash.h" /** * Data structure to hold information when iterating over stash * objects. */ typedef struct { size_t n; SEXP list; SEXP repo; git_repository *repository; } git2r_stash_list_cb_data; /** * Apply a single stashed state from the stash list. * * @param repo S3 class git_repository that contains the stash * @param index The index to the stash. 0 is the most recent stash. * @return R_NilValue */ SEXP attribute_hidden git2r_stash_apply( SEXP repo, SEXP index) { int error; git_repository *repository = NULL; if (git2r_arg_check_integer_gte_zero(index)) git2r_error(__func__, NULL, "'index'", git2r_err_integer_gte_zero_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_stash_apply(repository, INTEGER(index)[0], NULL); if (error == GIT_ENOTFOUND) error = 0; git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Remove a stash from the stash list * * @param repo S3 class git_repository that contains the stash * @param index The index to the stash. 0 is the most recent stash. * @return R_NilValue */ SEXP attribute_hidden git2r_stash_drop( SEXP repo, SEXP index) { int error; git_repository *repository = NULL; if (git2r_arg_check_integer_gte_zero(index)) git2r_error(__func__, NULL, "'index'", git2r_err_integer_gte_zero_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_stash_drop(repository, INTEGER(index)[0]); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Apply a single stashed state from the stash list and remove it from * the list if successful. * * @param repo S3 class git_repository that contains the stash * @param index The index to the stash. 0 is the most recent stash. * @return R_NilValue */ SEXP attribute_hidden git2r_stash_pop( SEXP repo, SEXP index) { int error; git_repository *repository = NULL; if (git2r_arg_check_integer_gte_zero(index)) git2r_error(__func__, NULL, "'index'", git2r_err_integer_gte_zero_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_stash_pop(repository, INTEGER(index)[0], NULL); if (error == GIT_ENOTFOUND) error = 0; git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Init slots in S3 class git_stash * * @param source The commit oid of the stashed state. * @param repository The repository * @param repo S3 class git_repository that contains the stash * @param dest S3 class git_stash to initialize * @return int 0 on success, or an error code. */ static int git2r_stash_init( const git_oid *source, git_repository *repository, SEXP repo, SEXP dest) { int error; git_commit *commit = NULL; error = git_commit_lookup(&commit, repository, source); if (error) return error; git2r_commit_init(commit, repo, dest); git_commit_free(commit); return 0; } /** * Callback when iterating over stashes * * @param index The position within the stash list. 0 points to the * most recent stashed state. * @param message The stash message. * @param stash_id The commit oid of the stashed state. * @param payload Pointer to a git2r_stash_list_cb_data data structure. * @return 0 if OK, else error code */ static int git2r_stash_list_cb( size_t index, const char* message, const git_oid *stash_id, void *payload) { int error = 0, nprotect = 0; SEXP stash, class; git2r_stash_list_cb_data *cb_data = (git2r_stash_list_cb_data*)payload; GIT2R_UNUSED(index); GIT2R_UNUSED(message); /* Check if we have a list to populate */ if (!Rf_isNull(cb_data->list)) { PROTECT(class = Rf_allocVector(STRSXP, 2)); nprotect++; SET_STRING_ELT(class, 0, Rf_mkChar("git_stash")); SET_STRING_ELT(class, 1, Rf_mkChar("git_commit")); PROTECT(stash = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(stash, R_ClassSymbol, class); error = git2r_stash_init( stash_id, cb_data->repository, cb_data->repo, stash); if (error) goto cleanup; SET_VECTOR_ELT(cb_data->list, cb_data->n, stash); } cb_data->n += 1; cleanup: if (nprotect) UNPROTECT(nprotect); return error; } /** * List stashes in a repository * * @param repo S3 class git_repository * @return VECXSP with S3 objects of class git_stash */ SEXP attribute_hidden git2r_stash_list( SEXP repo) { SEXP list = R_NilValue; int error, nprotect = 0; git2r_stash_list_cb_data cb_data = {0, R_NilValue, R_NilValue, NULL}; git_repository *repository = NULL; repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); /* Count number of stashes before creating the list */ error = git_stash_foreach(repository, &git2r_stash_list_cb, &cb_data); if (error) goto cleanup; PROTECT(list = Rf_allocVector(VECSXP, cb_data.n)); nprotect++; cb_data.n = 0; cb_data.list = list; cb_data.repo = repo; cb_data.repository = repository; error = git_stash_foreach(repository, &git2r_stash_list_cb, &cb_data); cleanup: git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return list; } /** * Stash * * @param repo The repository * @param message Optional description * @param index All changes already added to the index are left * intact in the working directory. Default is FALSE * @param untracked All untracked files are also stashed and then * cleaned up from the working directory. Default is FALSE * @param ignored All ignored files are also stashed and then cleaned * up from the working directory. Default is FALSE * @param stasher Signature with stasher and time of stash * @return S3 class git_stash */ SEXP attribute_hidden git2r_stash_save( SEXP repo, SEXP message, SEXP index, SEXP untracked, SEXP ignored, SEXP stasher) { int error, nprotect = 0; SEXP result = R_NilValue, class; git_oid oid; git_stash_flags flags = GIT_STASH_DEFAULT; git_commit *commit = NULL; git_repository *repository = NULL; git_signature *c_stasher = NULL; if (git2r_arg_check_logical(index)) git2r_error(__func__, NULL, "'index'", git2r_err_logical_arg); if (git2r_arg_check_logical(untracked)) git2r_error(__func__, NULL, "'untracked'", git2r_err_logical_arg); if (git2r_arg_check_logical(ignored)) git2r_error(__func__, NULL, "'ignored'", git2r_err_logical_arg); if (git2r_arg_check_signature(stasher)) git2r_error(__func__, NULL, "'stasher'", git2r_err_signature_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); if (LOGICAL(index)[0]) flags |= GIT_STASH_KEEP_INDEX; if (LOGICAL(untracked)[0]) flags |= GIT_STASH_INCLUDE_UNTRACKED; if (LOGICAL(ignored)[0]) flags |= GIT_STASH_INCLUDE_IGNORED; error = git2r_signature_from_arg(&c_stasher, stasher); if (error) goto cleanup; error = git_stash_save( &oid, repository, c_stasher, CHAR(STRING_ELT(message, 0)), flags); if (error) { if (GIT_ENOTFOUND == error) error = GIT_OK; goto cleanup; } PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, class = Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(class, 0, Rf_mkChar("git_stash")); SET_STRING_ELT(class, 1, Rf_mkChar("git_commit")); error = git2r_stash_init(&oid, repository, repo, result); cleanup: git_commit_free(commit); git_signature_free(c_stasher); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r-win.def0000644000175000017500000000007013671131056014524 0ustar nileshnileshLIBRARY git2r.dll EXPORTS R_init_git2r R_unload_git2r git2r/src/git2r_revwalk.c0000644000175000017500000003563514075750704015175 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2021 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_oid.h" #include "git2r_repository.h" #include "git2r_S3.h" /** * Count number of revisions. * * @param walker The walker to pop the commit from. * @param max_n n The upper limit of the number of commits to * output. Use max_n < 0 for unlimited number of commits. * @return The number of revisions */ static int git2r_revwalk_count( git_revwalk *walker, int max_n) { int n = 0; git_oid oid; while (!git_revwalk_next(&oid, walker)) { if (max_n < 0 || n < max_n) n++; else break; } return n; } /* Helper to find how many files in a commit changed from its nth * parent. */ static int git2r_match_with_parent( int *out, git_commit *commit, unsigned int i, git_diff_options *opts) { int error; git_commit *parent = NULL; git_tree *a = NULL, *b = NULL; git_diff *diff = NULL; if ((error = git_commit_parent(&parent, commit, i)) < 0) goto cleanup; if ((error = git_commit_tree(&a, parent)) < 0) goto cleanup; if ((error = git_commit_tree(&b, commit)) < 0) goto cleanup; if ((error = git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts)) < 0) goto cleanup; *out = (git_diff_num_deltas(diff) > 0); cleanup: git_diff_free(diff); git_tree_free(a); git_tree_free(b); git_commit_free(parent); return error; } /** * List revisions * * @param repo S3 class git_repository * @param sha id of the commit to start from. * @param topological Sort the commits by topological order; Can be * combined with time. * @param time Sort the commits by commit time; can be combined with * topological. * @param reverse Sort the commits in reverse order * @param max_n n The upper limit of the number of commits to * output. Use max_n < 0 for unlimited number of commits. * @return list with S3 class git_commit objects */ SEXP attribute_hidden git2r_revwalk_list( SEXP repo, SEXP sha, SEXP topological, SEXP time, SEXP reverse, SEXP max_n) { int error = GIT_OK, nprotect = 0; SEXP result = R_NilValue; int i, n; unsigned int sort_mode = GIT_SORT_NONE; git_revwalk *walker = NULL; git_repository *repository = NULL; git_oid oid; if (git2r_arg_check_sha(sha)) git2r_error(__func__, NULL, "'sha'", git2r_err_sha_arg); if (git2r_arg_check_logical(topological)) git2r_error(__func__, NULL, "'topological'", git2r_err_logical_arg); if (git2r_arg_check_logical(time)) git2r_error(__func__, NULL, "'time'", git2r_err_logical_arg); if (git2r_arg_check_logical(reverse)) git2r_error(__func__, NULL, "'reverse'", git2r_err_logical_arg); if (git2r_arg_check_integer(max_n)) git2r_error(__func__, NULL, "'max_n'", git2r_err_integer_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); if (git_repository_is_empty(repository)) { /* No commits, create empty list */ PROTECT(result = Rf_allocVector(VECSXP, 0)); nprotect++; goto cleanup; } if (LOGICAL(topological)[0]) sort_mode |= GIT_SORT_TOPOLOGICAL; if (LOGICAL(time)[0]) sort_mode |= GIT_SORT_TIME; if (LOGICAL(reverse)[0]) sort_mode |= GIT_SORT_REVERSE; error = git_revwalk_new(&walker, repository); if (error) goto cleanup; git2r_oid_from_sha_sexp(sha, &oid); error = git_revwalk_push(walker, &oid); if (error) goto cleanup; git_revwalk_sorting(walker, sort_mode); /* Count number of revisions before creating the list */ n = git2r_revwalk_count(walker, INTEGER(max_n)[0]); /* Create list to store result */ PROTECT(result = Rf_allocVector(VECSXP, n)); nprotect++; git_revwalk_reset(walker); error = git_revwalk_push(walker, &oid); if (error) goto cleanup; git_revwalk_sorting(walker, sort_mode); for (i = 0; i < n; i++) { git_commit *commit; SEXP item; git_oid oid; error = git_revwalk_next(&oid, walker); if (error) { if (GIT_ITEROVER == error) error = GIT_OK; goto cleanup; } error = git_commit_lookup(&commit, repository, &oid); if (error) goto cleanup; SET_VECTOR_ELT( result, i, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init(commit, repo, item); git_commit_free(commit); } cleanup: git_revwalk_free(walker); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * List revisions modifying a particular path. * * @param repo S3 class git_repository * @param sha id of the commit to start from. * @param topological Sort the commits by topological order; Can be * combined with time. * @param time Sort the commits by commit time; can be combined with * topological. * @param reverse Sort the commits in reverse order * @param max_n n The upper limit of the number of commits to * output. Use max_n < 0 for unlimited number of commits. * @param path Only commits modifying this path are selected * @return list with S3 class git_commit objects */ SEXP attribute_hidden git2r_revwalk_list2 ( SEXP repo, SEXP sha, SEXP topological, SEXP time, SEXP reverse, SEXP max_n, SEXP path) { int i = 0; int error = GIT_OK; int nprotect = 0; SEXP result = R_NilValue; int n; unsigned int sort_mode = GIT_SORT_NONE; git_revwalk *walker = NULL; git_repository *repository = NULL; git_oid oid; git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; git_pathspec *ps = NULL; if (git2r_arg_check_sha(sha)) git2r_error(__func__, NULL, "'sha'", git2r_err_sha_arg); if (git2r_arg_check_logical(topological)) git2r_error(__func__, NULL, "'topological'", git2r_err_logical_arg); if (git2r_arg_check_logical(time)) git2r_error(__func__, NULL, "'time'", git2r_err_logical_arg); if (git2r_arg_check_logical(reverse)) git2r_error(__func__, NULL, "'reverse'", git2r_err_logical_arg); if (git2r_arg_check_integer(max_n)) git2r_error(__func__, NULL, "'max_n'", git2r_err_integer_arg); if (git2r_arg_check_string(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_arg); /* Set up git pathspec. */ error = git2r_copy_string_vec(&(diffopts.pathspec), path); if (error || !diffopts.pathspec.count) goto cleanup; error = git_pathspec_new(&ps, &diffopts.pathspec); if (error) goto cleanup; /* Open the repository. */ repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); /* If there are no commits, create an empty list. */ if (git_repository_is_empty(repository)) { PROTECT(result = Rf_allocVector(VECSXP, 0)); nprotect++; goto cleanup; } if (LOGICAL(topological)[0]) sort_mode |= GIT_SORT_TOPOLOGICAL; if (LOGICAL(time)[0]) sort_mode |= GIT_SORT_TIME; if (LOGICAL(reverse)[0]) sort_mode |= GIT_SORT_REVERSE; /* Create a new "revwalker". */ error = git_revwalk_new(&walker, repository); if (error) goto cleanup; git2r_oid_from_sha_sexp(sha, &oid); error = git_revwalk_push(walker, &oid); if (error) goto cleanup; git_revwalk_sorting(walker, sort_mode); error = git_revwalk_push_head(walker); if (error) goto cleanup; n = Rf_asInteger(max_n); if (n < 0) { /* Count number of revisions before creating the list. */ n = git2r_revwalk_count(walker, n); } /* Restart the revwalker. */ git_revwalk_reset(walker); git_revwalk_sorting(walker, sort_mode); error = git_revwalk_push(walker, &oid); if (error) goto cleanup; /* Create the list to store the result. */ PROTECT(result = Rf_allocVector(VECSXP, n)); nprotect++; while (i < n) { git_commit *commit; SEXP item; git_oid oid; unsigned int parents, unmatched; int match; error = git_revwalk_next(&oid, walker); if (error) { if (GIT_ITEROVER == error) error = GIT_OK; goto cleanup; } error = git_commit_lookup(&commit, repository, &oid); if (error) goto cleanup; /* Check whether it is a "touching" commit---that is, a commit that has modified the selected path. */ parents = git_commit_parentcount(commit); unmatched = parents; if (parents == 0) { git_tree *tree; if ((error = git_commit_tree(&tree, commit)) < 0) { git_commit_free(commit); goto cleanup; } error = git_pathspec_match_tree( NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps); git_tree_free(tree); if (error == GIT_ENOTFOUND) { error = 0; unmatched = 1; } else if (error < 0) { git_commit_free(commit); goto cleanup; } } else if (parents == 1) { if ((error = git2r_match_with_parent(&match, commit, 0, &diffopts)) < 0) { git_commit_free(commit); goto cleanup; } unmatched = match ? 0 : 1; } else { unsigned int j; for (j = 0; j < parents; j++) { if ((error = git2r_match_with_parent(&match, commit, j, &diffopts)) < 0) { git_commit_free(commit); goto cleanup; } if (match && unmatched) unmatched--; } } if (unmatched > 0) { git_commit_free(commit); continue; } SET_VECTOR_ELT( result, i, item = Rf_mkNamed(VECSXP, git2r_S3_items__git_commit)); Rf_setAttrib(item, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_commit)); git2r_commit_init(commit, repo, item); git_commit_free(commit); i++; } cleanup: free(diffopts.pathspec.strings); git_revwalk_free(walker); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get list with contributions. * * @param repo S3 class git_repository * @param topological Sort the commits by topological order; Can be * combined with time. * @param time Sort the commits by commit time; can be combined with * topological. * @param reverse Sort the commits in reverse order * @return list with S3 class git_commit objects */ SEXP attribute_hidden git2r_revwalk_contributions( SEXP repo, SEXP topological, SEXP time, SEXP reverse) { int error = GIT_OK, nprotect = 0; SEXP result = R_NilValue; SEXP names = R_NilValue; SEXP when = R_NilValue; SEXP author = R_NilValue; SEXP email = R_NilValue; size_t i, n = 0; unsigned int sort_mode = GIT_SORT_NONE; git_revwalk *walker = NULL; git_repository *repository = NULL; if (git2r_arg_check_logical(topological)) git2r_error(__func__, NULL, "'topological'", git2r_err_logical_arg); if (git2r_arg_check_logical(time)) git2r_error(__func__, NULL, "'time'", git2r_err_logical_arg); if (git2r_arg_check_logical(reverse)) git2r_error(__func__, NULL, "'reverse'", git2r_err_logical_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); if (git_repository_is_empty(repository)) goto cleanup; if (LOGICAL(topological)[0]) sort_mode |= GIT_SORT_TOPOLOGICAL; if (LOGICAL(time)[0]) sort_mode |= GIT_SORT_TIME; if (LOGICAL(reverse)[0]) sort_mode |= GIT_SORT_REVERSE; error = git_revwalk_new(&walker, repository); if (error) goto cleanup; error = git_revwalk_push_head(walker); if (error) goto cleanup; git_revwalk_sorting(walker, sort_mode); /* Count number of revisions before creating the list */ n = git2r_revwalk_count(walker, -1); /* Create vectors to store result */ PROTECT(result = Rf_allocVector(VECSXP, 3)); nprotect++; Rf_setAttrib(result, R_NamesSymbol, names = Rf_allocVector(STRSXP, 3)); SET_VECTOR_ELT(result, 0, when = Rf_allocVector(REALSXP, n)); SET_STRING_ELT(names, 0, Rf_mkChar("when")); SET_VECTOR_ELT(result, 1, author = Rf_allocVector(STRSXP, n)); SET_STRING_ELT(names, 1, Rf_mkChar("author")); SET_VECTOR_ELT(result, 2, email = Rf_allocVector(STRSXP, n)); SET_STRING_ELT(names, 2, Rf_mkChar("email")); git_revwalk_reset(walker); error = git_revwalk_push_head(walker); if (error) goto cleanup; git_revwalk_sorting(walker, sort_mode); for (i = 0; i < n; i++) { git_commit *commit; const git_signature *c_author; git_oid oid; error = git_revwalk_next(&oid, walker); if (error) { if (GIT_ITEROVER == error) error = GIT_OK; goto cleanup; } error = git_commit_lookup(&commit, repository, &oid); if (error) goto cleanup; c_author = git_commit_author(commit); REAL(when)[i] = (double)(c_author->when.time) + 60.0 * (double)(c_author->when.offset); SET_STRING_ELT(author, i, Rf_mkChar(c_author->name)); SET_STRING_ELT(author, i, Rf_mkChar(c_author->email)); git_commit_free(commit); } cleanup: git_revwalk_free(walker); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_branch.c0000644000175000017500000005542713671131056014752 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_branch.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_reference.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_signature.h" /** * Count number of branches. * * @param repo S3 class git_repository * @param flags Filtering flags for the branch listing. Valid values * are 1 (LOCAL), 2 (REMOTE) and 3 (ALL) * @param n The number of branches * @return 0 on success, or an error code. */ static int git2r_branch_count( git_repository *repo, int flags, size_t *n) { int error; git_branch_iterator *iter; git_branch_t type; git_reference *ref; *n = 0; error = git_branch_iterator_new(&iter, repo, flags); if (error) return error; for (;;) { error = git_branch_next(&ref, &type, iter); if (error) break; git_reference_free(ref); (*n)++; } git_branch_iterator_free(iter); if (GIT_ITEROVER != error) return error; return 0; } /** * Init slots in S3 class git_branch * * @param source a reference * @param repository the repository * @param type the branch type; local or remote * @param repo S3 class git_repository that contains the blob * @param dest S3 class git_branch to initialize * @return int; < 0 if error, else 0 */ int attribute_hidden git2r_branch_init( const git_reference *source, git_branch_t type, SEXP repo, SEXP dest) { int error; const char *name; error = git_branch_name(&name, source); if (error) goto cleanup; SET_VECTOR_ELT(dest, git2r_S3_item__git_branch__name, Rf_mkString(name)); SET_VECTOR_ELT(dest, git2r_S3_item__git_branch__type, Rf_ScalarInteger(type)); SET_VECTOR_ELT(dest, git2r_S3_item__git_branch__repo, Rf_duplicate(repo)); cleanup: return error; } /** * Create a new branch * * @param branch_name Name for the branch * @param commit Commit to which branch should point. * @param force Overwrite existing branch * @return S3 class git_branch */ SEXP attribute_hidden git2r_branch_create( SEXP branch_name, SEXP commit, SEXP force) { SEXP repo, result = R_NilValue; int error, nprotect = 0, overwrite = 0; git_commit *target = NULL; git_reference *reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_string(branch_name)) git2r_error(__func__, NULL, "'branch_name'", git2r_err_string_arg); if (git2r_arg_check_commit(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg); if (git2r_arg_check_logical(force)) git2r_error(__func__, NULL, "'force'", git2r_err_logical_arg); repo = git2r_get_list_element(commit, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_commit_lookup(&target, repository, commit); if (error) goto cleanup; if (LOGICAL(force)[0]) overwrite = 1; error = git_branch_create( &reference, repository, CHAR(STRING_ELT(branch_name, 0)), target, overwrite); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_branch)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_branch)); error = git2r_branch_init(reference, GIT_BRANCH_LOCAL, repo, result); cleanup: git_reference_free(reference); git_commit_free(target); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Delete branch * * @param branch S3 class git_branch * @return R_NilValue */ SEXP attribute_hidden git2r_branch_delete( SEXP branch) { int error; const char *name; git_branch_t type; git_reference *reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); repository = git2r_repository_open(git2r_get_list_element(branch, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; error = git_branch_delete(reference); cleanup: git_reference_free(reference); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Determine if the current local branch is pointed at by HEAD * * @param branch S3 class git_branch * @return TRUE if head, FALSE if not */ SEXP attribute_hidden git2r_branch_is_head( SEXP branch) { SEXP result = R_NilValue; int error, nprotect = 0; const char *name; git_reference *reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); repository = git2r_repository_open(git2r_get_list_element(branch, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); error = git_branch_lookup( &reference, repository, name, INTEGER(git2r_get_list_element(branch, "type"))[0]); if (error) goto cleanup; error = git_branch_is_head(reference); if (0 == error || 1 == error) { PROTECT(result = Rf_allocVector(LGLSXP, 1)); nprotect++; LOGICAL(result)[0] = error; error = 0; } cleanup: git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * List branches in a repository * * @param repo S3 class git_repository * @param flags Filtering flags for the branch listing. Valid values * are 1 (LOCAL), 2 (REMOTE) and 3 (ALL) * @return VECXSP with S3 objects of class git_branch */ SEXP attribute_hidden git2r_branch_list( SEXP repo, SEXP flags) { SEXP names, result = R_NilValue; int error, nprotect = 0; git_branch_iterator *iter = NULL; size_t i, n = 0; git_repository *repository = NULL; git_reference *reference = NULL; git_branch_t type; if (git2r_arg_check_integer(flags)) git2r_error(__func__, NULL, "'flags'", git2r_err_integer_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); /* Count number of branches before creating the list */ error = git2r_branch_count(repository, INTEGER(flags)[0], &n); if (error) goto cleanup; PROTECT(result = Rf_allocVector(VECSXP, n)); nprotect++; Rf_setAttrib(result, R_NamesSymbol, names = Rf_allocVector(STRSXP, n)); error = git_branch_iterator_new(&iter, repository, INTEGER(flags)[0]); if (error) goto cleanup; for (i = 0; i < n; i++) { SEXP branch; error = git_branch_next(&reference, &type, iter); if (error) { if (GIT_ITEROVER == error) error = GIT_OK; goto cleanup; } SET_VECTOR_ELT(result, i, branch = Rf_mkNamed(VECSXP, git2r_S3_items__git_branch)); Rf_setAttrib(branch, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_branch)); error = git2r_branch_init(reference, type, repo, branch); if (error) goto cleanup; SET_STRING_ELT( names, i, STRING_ELT(git2r_get_list_element(branch, "name"), 0)); if (reference) git_reference_free(reference); reference = NULL; } cleanup: git_branch_iterator_free(iter); git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get the full name of a branch * * @param branch S3 class git_branch * @return character string with full name of branch. */ SEXP attribute_hidden git2r_branch_canonical_name( SEXP branch) { int error, nprotect = 0; SEXP result = R_NilValue; const char *name; git_branch_t type; git_reference *reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); repository = git2r_repository_open(git2r_get_list_element(branch, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(git_reference_name(reference))); cleanup: git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get the configured canonical name of the upstream branch, given a * local branch, i.e "branch.branch_name.merge" property of the config * file. * * @param branch S3 class git_branch. * @return Character vector of length one with upstream canonical name. */ SEXP attribute_hidden git2r_branch_upstream_canonical_name( SEXP branch) { int error, nprotect = 0; SEXP result = R_NilValue; SEXP repo; const char *name; git_branch_t type; const char *branch_name; size_t branch_name_len; char *buf = NULL; size_t buf_len; git_config *cfg = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; if (GIT_BRANCH_LOCAL != type) git2r_error(__func__, NULL, git2r_err_branch_not_local, NULL); repo = git2r_get_list_element(branch, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_repository_config_snapshot(&cfg, repository); if (error) goto cleanup; branch_name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); branch_name_len = strlen(branch_name); while (branch_name[0] == '.') { branch_name++; branch_name_len--; } while (branch_name_len >= 1 && branch_name[branch_name_len - 1] == '.') { branch_name_len--; } buf_len = branch_name_len + sizeof("branch." ".merge"); buf = malloc(buf_len); if (!buf) { GIT2R_ERROR_SET_OOM(); error = GIT2R_ERROR_NOMEMORY; goto cleanup; } error = snprintf(buf, buf_len, "branch.%.*s.merge", (int)branch_name_len, branch_name); if (error < 0 || (size_t)error >= buf_len) { GIT2R_ERROR_SET_STR(GIT2R_ERROR_OS, "Failed to snprintf branch config."); error = GIT2R_ERROR_OS; goto cleanup; } error = git_config_get_string(&name, cfg, buf); if (error) goto cleanup; PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(name)); cleanup: if (buf) free(buf); git_config_free(cfg); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get remote name of branch * * @param branch S3 class git_branch * @return character string with remote name. */ SEXP attribute_hidden git2r_branch_remote_name( SEXP branch) { int error, nprotect = 0; SEXP result = R_NilValue; const char *name; git_buf buf = {0}; git_branch_t type; git_reference *reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; if (GIT_BRANCH_REMOTE != type) git2r_error(__func__, NULL, git2r_err_branch_not_remote, NULL); repository = git2r_repository_open(git2r_get_list_element(branch, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; error = git_branch_remote_name( &buf, repository, git_reference_name(reference)); if (error) goto cleanup; PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(buf.ptr)); GIT2R_BUF_DISPOSE(&buf); cleanup: git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get remote url of branch * * @param branch S3 class git_branch * @return character string with remote url. */ SEXP attribute_hidden git2r_branch_remote_url( SEXP branch) { int error, nprotect = 0; SEXP result = R_NilValue; const char *name; git_buf buf = {0}; git_branch_t type; git_reference *reference = NULL; git_remote *remote = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; if (GIT_BRANCH_REMOTE != type) git2r_error(__func__, NULL, git2r_err_branch_not_remote, NULL); repository = git2r_repository_open(git2r_get_list_element(branch, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; error = git_branch_remote_name( &buf, repository, git_reference_name(reference)); if (error) goto cleanup; error = git_remote_lookup(&remote, repository, buf.ptr); if (error) { error = git_remote_create_anonymous(&remote, repository, buf.ptr); if (error) { GIT2R_BUF_DISPOSE(&buf); goto cleanup; } } GIT2R_BUF_DISPOSE(&buf); PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; SET_STRING_ELT(result, 0, Rf_mkChar(git_remote_url(remote))); cleanup: git_remote_free(remote); git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Rename a branch * * @param branch Branch to rename * @param new_branch_name The new name for the branch * @param force Overwrite existing branch * @return The renamed S3 class git_branch */ SEXP attribute_hidden git2r_branch_rename( SEXP branch, SEXP new_branch_name, SEXP force) { SEXP repo, result = R_NilValue; int error, nprotect = 0, overwrite = 0; const char *name = NULL; git_branch_t type; git_reference *reference = NULL, *new_reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); if (git2r_arg_check_string(new_branch_name)) git2r_error(__func__, NULL, "'new_branch_name'", git2r_err_string_arg); if (git2r_arg_check_logical(force)) git2r_error(__func__, NULL, "'force'", git2r_err_logical_arg); repo = git2r_get_list_element(branch, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; if (LOGICAL(force)[0]) overwrite = 1; error = git_branch_move( &new_reference, reference, CHAR(STRING_ELT(new_branch_name, 0)), overwrite); if (error) goto cleanup; PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_branch)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_branch)); error = git2r_branch_init(new_reference, type, repo, result); cleanup: git_reference_free(reference); git_reference_free(new_reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get sha pointed to by a branch * * @param branch S3 class git_branch * @return The 40 character sha if the reference is direct, else NA */ SEXP attribute_hidden git2r_branch_target( SEXP branch) { int error, nprotect = 0; SEXP result = R_NilValue; const char *name; char sha[GIT_OID_HEXSZ + 1]; git_branch_t type; git_reference *reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); repository = git2r_repository_open(git2r_get_list_element(branch, "repo")); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; PROTECT(result = Rf_allocVector(STRSXP, 1)); nprotect++; if (git_reference_type(reference) == GIT2R_REFERENCE_DIRECT) { git_oid_fmt(sha, git_reference_target(reference)); sha[GIT_OID_HEXSZ] = '\0'; SET_STRING_ELT(result, 0, Rf_mkChar(sha)); } else { SET_STRING_ELT(result, 0, NA_STRING); } cleanup: git_reference_free(reference); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Get remote tracking branch, given a local branch. * * @param branch S3 class git_branch * @return S3 class git_branch or R_NilValue if no remote tracking branch. */ SEXP attribute_hidden git2r_branch_get_upstream( SEXP branch) { int error, nprotect = 0; SEXP repo, result = R_NilValue; const char *name; git_branch_t type; git_reference *reference = NULL, *upstream = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); repo = git2r_get_list_element(branch, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; error = git_branch_upstream(&upstream, reference); if (error) { if (GIT_ENOTFOUND == error) error = GIT_OK; goto cleanup; } PROTECT(result = Rf_mkNamed(VECSXP, git2r_S3_items__git_branch)); nprotect++; Rf_setAttrib(result, R_ClassSymbol, Rf_mkString(git2r_S3_class__git_branch)); error = git2r_branch_init(upstream, GIT_BRANCH_REMOTE, repo, result); cleanup: git_reference_free(reference); git_reference_free(upstream); git_repository_free(repository); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } /** * Set remote tracking branch * * Set the upstream configuration for a given local branch * @param branch The branch to configure * @param upstream_name remote-tracking or local branch to set as * upstream. Pass NULL to unset. * @return R_NilValue */ SEXP attribute_hidden git2r_branch_set_upstream( SEXP branch, SEXP upstream_name) { int error; SEXP repo; const char *name; const char *u_name = NULL; git_branch_t type; git_reference *reference = NULL; git_repository *repository = NULL; if (git2r_arg_check_branch(branch)) git2r_error(__func__, NULL, "'branch'", git2r_err_branch_arg); if (!Rf_isNull(upstream_name)) { if (git2r_arg_check_string(upstream_name)) git2r_error(__func__, NULL, "'upstream_name'", git2r_err_string_arg); u_name = CHAR(STRING_ELT(upstream_name, 0)); } repo = git2r_get_list_element(branch, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name = CHAR(STRING_ELT(git2r_get_list_element(branch, "name"), 0)); type = INTEGER(git2r_get_list_element(branch, "type"))[0]; error = git_branch_lookup(&reference, repository, name, type); if (error) goto cleanup; error = git_branch_set_upstream(reference, u_name); cleanup: git_reference_free(reference); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } git2r/src/git2r_index.c0000644000175000017500000000710413671131056014611 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_index.h" #include "git2r_repository.h" /** * Add or update index entries matching files in the working * directory. * * @param repo S3 class git_repository * @param path array of path patterns * @param force if TRUE, add ignored files. * @return R_NilValue */ SEXP attribute_hidden git2r_index_add_all( SEXP repo, SEXP path, SEXP force) { int error = 0; unsigned int flags = 0; git_strarray pathspec = {0}; git_index *index = NULL; git_repository *repository = NULL; if (git2r_arg_check_string_vec(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_vec_arg); if (git2r_arg_check_logical(force)) git2r_error(__func__, NULL, "'force'", git2r_err_logical_arg); repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_copy_string_vec(&pathspec, path); if (error || !pathspec.count) goto cleanup; error = git_repository_index(&index, repository); if (error) goto cleanup; if (LOGICAL(force)[0]) flags |= GIT_INDEX_ADD_FORCE; error = git_index_add_all(index, &pathspec, flags, NULL, NULL); if (error) goto cleanup; error = git_index_write(index); cleanup: free(pathspec.strings); git_index_free(index); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Remove an index entry corresponding to a file relative to the * repository's working folder. * * @param repo S3 class git_repository * @param path array of path patterns * @return R_NilValue */ SEXP attribute_hidden git2r_index_remove_bypath( SEXP repo, SEXP path) { int error = 0; size_t i, len; git_index *index = NULL; git_repository *repository = NULL; if (git2r_arg_check_string_vec(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_vec_arg); len = Rf_length(path); if (!len) goto cleanup; repository= git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git_repository_index(&index, repository); if (error) goto cleanup; for (i = 0; i < len; i++) { if (NA_STRING != STRING_ELT(path, i)) { error = git_index_remove_bypath(index, CHAR(STRING_ELT(path, i))); if (error) goto cleanup; } } error = git_index_write(index); cleanup: git_index_free(index); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } git2r/src/git2r_reference.h0000644000175000017500000000171613415432334015447 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2015 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INCLUDE_git2r_reference_h #define INCLUDE_git2r_reference_h #include #include SEXP git2r_reference_dwim(SEXP repo, SEXP shorthand); SEXP git2r_reference_list(SEXP repo); #endif git2r/src/git2r_tree.c0000644000175000017500000001654013671131056014445 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_S3.h" #include "git2r_tree.h" /** * Init slots in S3 class git_tree * * @param source a tree * @param repo S3 class git_repository that contains the tree * @param dest S3 class git_tree to initialize * @return void */ void attribute_hidden git2r_tree_init( const git_tree *source, SEXP repo, SEXP dest) { SEXP filemode, id, type, name; int *filemode_ptr; size_t i, n; const git_oid *oid; char sha[GIT_OID_HEXSZ + 1]; const git_tree_entry *entry; oid = git_tree_id(source); git_oid_tostr(sha, sizeof(sha), oid); SET_VECTOR_ELT(dest, git2r_S3_item__git_tree__sha, Rf_mkString(sha)); n = git_tree_entrycount(source); SET_VECTOR_ELT( dest, git2r_S3_item__git_tree__filemode, filemode = Rf_allocVector(INTSXP, n)); SET_VECTOR_ELT( dest, git2r_S3_item__git_tree__id, id = Rf_allocVector(STRSXP, n)); SET_VECTOR_ELT( dest, git2r_S3_item__git_tree__type, type = Rf_allocVector(STRSXP, n)); SET_VECTOR_ELT( dest, git2r_S3_item__git_tree__name, name = Rf_allocVector(STRSXP, n)); filemode_ptr = INTEGER(filemode); for (i = 0; i < n; ++i) { entry = git_tree_entry_byindex(source, i); git_oid_tostr(sha, sizeof(sha), git_tree_entry_id(entry)); filemode_ptr[i] = git_tree_entry_filemode(entry); SET_STRING_ELT(id, i, Rf_mkChar(sha)); SET_STRING_ELT(type, i, Rf_mkChar(git_object_type2string(git_tree_entry_type(entry)))); SET_STRING_ELT(name, i, Rf_mkChar(git_tree_entry_name(entry))); } SET_VECTOR_ELT(dest, git2r_S3_item__git_tree__repo, Rf_duplicate(repo)); } /** * Data structure to hold information for the tree traversal. */ typedef struct { size_t n; SEXP list; int recursive; git_repository *repository; } git2r_tree_walk_cb_data; /** * Callback for the tree traversal method. * */ static int git2r_tree_walk_cb( const char *root, const git_tree_entry *entry, void *payload) { int error = 0; git2r_tree_walk_cb_data *p = (git2r_tree_walk_cb_data*)payload; if (p->recursive) { if (git_tree_entry_type(entry) != GIT2R_OBJECT_BLOB) return 0; } else if (*root) { return 1; } if (!Rf_isNull(p->list)) { char mode[23]; /* enums are int/32-bit, but this is enough for even a 64-bit int */ git_object *blob = NULL, *obj = NULL; char sha[GIT_OID_HEXSZ + 1]; /* mode */ error = snprintf(mode, sizeof(mode), "%06o", git_tree_entry_filemode(entry)); if (error < 0 || (size_t)error >= sizeof(mode)) { error = -1; goto cleanup; } SET_STRING_ELT(VECTOR_ELT(p->list, 0), p->n, Rf_mkChar(mode)); /* type */ SET_STRING_ELT(VECTOR_ELT(p->list, 1), p->n, Rf_mkChar(git_object_type2string(git_tree_entry_type(entry)))); /* sha */ git_oid_tostr(sha, sizeof(sha), git_tree_entry_id(entry)); SET_STRING_ELT(VECTOR_ELT(p->list, 2), p->n, Rf_mkChar(sha)); /* path */ SET_STRING_ELT(VECTOR_ELT(p->list, 3), p->n, Rf_mkChar(root)); /* name */ SET_STRING_ELT(VECTOR_ELT(p->list, 4), p->n, Rf_mkChar(git_tree_entry_name(entry))); /* length */ if (git_tree_entry_type(entry) == GIT2R_OBJECT_BLOB) { error = git_tree_entry_to_object(&obj, p->repository, entry); if (error) goto cleanup; error = git_object_peel(&blob, obj, GIT2R_OBJECT_BLOB); if (error) goto cleanup; INTEGER(VECTOR_ELT(p->list, 5))[p->n] = git_blob_rawsize((git_blob *)blob); } else { INTEGER(VECTOR_ELT(p->list, 5))[p->n] = NA_INTEGER; } cleanup: git_object_free(obj); git_object_free(blob); } p->n += 1; return error; } /** * Traverse the entries in a tree and its subtrees. * * @param tree S3 class git_tree * @param recursive recurse into sub-trees. * @return A list with entries */ SEXP attribute_hidden git2r_tree_walk( SEXP tree, SEXP recursive) { int error, nprotect = 0; git_oid oid; git_tree *tree_obj = NULL; git_repository *repository = NULL; git2r_tree_walk_cb_data cb_data = {0, R_NilValue}; SEXP repo = R_NilValue, sha = R_NilValue; SEXP result = R_NilValue, names = R_NilValue; if (git2r_arg_check_tree(tree)) git2r_error(__func__, NULL, "'tree'", git2r_err_tree_arg); if (git2r_arg_check_logical(recursive)) git2r_error(__func__, NULL, "'recursive'", git2r_err_logical_arg); repo = git2r_get_list_element(tree, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = git2r_get_list_element(tree, "sha"); git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); error = git_tree_lookup(&tree_obj, repository, &oid); if (error) goto cleanup; /* Count number of entries before creating the list */ cb_data.repository = repository; if (LOGICAL(recursive)[0]) cb_data.recursive = 1; error = git_tree_walk(tree_obj, 0, &git2r_tree_walk_cb, &cb_data); if (error) goto cleanup; PROTECT(result = Rf_allocVector(VECSXP, 6)); nprotect++; Rf_setAttrib(result, R_NamesSymbol, names = Rf_allocVector(STRSXP, 6)); SET_VECTOR_ELT(result, 0, Rf_allocVector(STRSXP, cb_data.n)); SET_STRING_ELT(names, 0, Rf_mkChar("mode")); SET_VECTOR_ELT(result, 1, Rf_allocVector(STRSXP, cb_data.n)); SET_STRING_ELT(names, 1, Rf_mkChar("type")); SET_VECTOR_ELT(result, 2, Rf_allocVector(STRSXP, cb_data.n)); SET_STRING_ELT(names, 2, Rf_mkChar("sha")); SET_VECTOR_ELT(result, 3, Rf_allocVector(STRSXP, cb_data.n)); SET_STRING_ELT(names, 3, Rf_mkChar("path")); SET_VECTOR_ELT(result, 4, Rf_allocVector(STRSXP, cb_data.n)); SET_STRING_ELT(names, 4, Rf_mkChar("name")); SET_VECTOR_ELT(result, 5, Rf_allocVector(INTSXP, cb_data.n)); SET_STRING_ELT(names, 5, Rf_mkChar("len")); cb_data.list = result; cb_data.n = 0; error = git_tree_walk(tree_obj, 0, &git2r_tree_walk_cb, &cb_data); cleanup: git_repository_free(repository); git_tree_free(tree_obj); if (nprotect) UNPROTECT(nprotect); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return result; } git2r/src/git2r_reset.c0000644000175000017500000000733213671131056014627 0ustar nileshnilesh/* * git2r, R bindings to the libgit2 library. * Copyright (C) 2013-2020 The git2r contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * git2r is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "git2r_arg.h" #include "git2r_commit.h" #include "git2r_deprecated.h" #include "git2r_error.h" #include "git2r_repository.h" #include "git2r_reset.h" #include "git2r_S3.h" #include "git2r_signature.h" /** * Reset current HEAD to the specified state * * @param commit The commit to which the HEAD should be moved to. * @param reset_type Kind of reset operation to perform. 'soft' means * the Head will be moved to the commit. 'mixed' reset will trigger a * 'soft' reset, plus the index will be replaced with the content of * the commit tree. 'hard' reset will trigger a 'mixed' reset and the * working directory will be replaced with the content of the index. * @return R_NilValue */ SEXP attribute_hidden git2r_reset( SEXP commit, SEXP reset_type) { int error; SEXP repo; git_commit *target = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg); if (git2r_arg_check_integer(reset_type)) git2r_error(__func__, NULL, "'reset_type'", git2r_err_integer_arg); repo = git2r_get_list_element(commit, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_commit_lookup(&target, repository, commit); if (error) goto cleanup; error = git_reset( repository, (git_object*)target, INTEGER(reset_type)[0], NULL); cleanup: git_commit_free(target); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } /** * Updates some entries in the index from the HEAD commit tree. * * @param repo S3 class git_repository * @param path The paths to reset * @return R_NilValue */ SEXP attribute_hidden git2r_reset_default( SEXP repo, SEXP path) { int error = 0; git_strarray pathspec = {0}; git_reference *head = NULL; git_object *head_commit = NULL; git_repository *repository = NULL; if (git2r_arg_check_string_vec(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_vec_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_copy_string_vec(&pathspec, path); if (error || !pathspec.count) goto cleanup; error = git_repository_head(&head, repository); if (error) goto cleanup; error = git_reference_peel(&head_commit, head, GIT2R_OBJECT_COMMIT); if (error) goto cleanup; error = git_reset_default(repository, head_commit, &pathspec); cleanup: git_reference_free(head); git_object_free(head_commit); free(pathspec.strings); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; } git2r/tools/0000755000175000017500000000000014145550451012577 5ustar nileshnileshgit2r/tools/lib-link.m40000644000175000017500000010044313443371164014546 0ustar nileshnilesh# lib-link.m4 serial 26 (gettext-0.18.2) dnl Copyright (C) 2001-2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_PREREQ([2.54]) dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and dnl augments the CPPFLAGS variable. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[m4_translit([$1],[./+-], [____])]) pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ AC_LIB_LINKFLAGS_BODY([$1], [$2]) ac_cv_lib[]Name[]_libs="$LIB[]NAME" ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" ac_cv_lib[]Name[]_cppflags="$INC[]NAME" ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX" ]) LIB[]NAME="$ac_cv_lib[]Name[]_libs" LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" INC[]NAME="$ac_cv_lib[]Name[]_cppflags" LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the dnl results of this search when this library appears as a dependency. HAVE_LIB[]NAME=yes popdef([NAME]) popdef([Name]) ]) dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode, [missing-message]) dnl searches for libname and the libraries corresponding to explicit and dnl implicit dependencies, together with the specified include files and dnl the ability to compile and link the specified testcode. The missing-message dnl defaults to 'no' and may contain additional hints for the user. dnl If found, it sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} dnl and LTLIB${NAME} variables and augments the CPPFLAGS variable, and dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[m4_translit([$1],[./+-], [____])]) pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME dnl accordingly. AC_LIB_LINKFLAGS_BODY([$1], [$2]) dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, dnl because if the user has installed lib[]Name and not disabled its use dnl via --without-lib[]Name-prefix, he wants to use it. ac_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ ac_save_LIBS="$LIBS" dnl If $LIB[]NAME contains some -l options, add it to the end of LIBS, dnl because these -l options might require -L options that are present in dnl LIBS. -l options benefit only from the -L options listed before it. dnl Otherwise, add it to the front of LIBS, because it may be a static dnl library that depends on another static library that is present in LIBS. dnl Static libraries benefit only from the static libraries listed after dnl it. case " $LIB[]NAME" in *" -l"*) LIBS="$LIBS $LIB[]NAME" ;; *) LIBS="$LIB[]NAME $LIBS" ;; esac AC_LINK_IFELSE( [AC_LANG_PROGRAM([[$3]], [[$4]])], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name='m4_if([$5], [], [no], [[$5]])']) LIBS="$ac_save_LIBS" ]) if test "$ac_cv_lib[]Name" = yes; then HAVE_LIB[]NAME=yes AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the lib][$1 library.]) AC_MSG_CHECKING([how to link with lib[]$1]) AC_MSG_RESULT([$LIB[]NAME]) else HAVE_LIB[]NAME=no dnl If $LIB[]NAME didn't lead to a usable library, we don't need dnl $INC[]NAME either. CPPFLAGS="$ac_save_CPPFLAGS" LIB[]NAME= LTLIB[]NAME= LIB[]NAME[]_PREFIX= fi AC_SUBST([HAVE_LIB]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) popdef([NAME]) popdef([Name]) ]) dnl Determine the platform dependent parameters needed to use rpath: dnl acl_libext, dnl acl_shlibext, dnl acl_libname_spec, dnl acl_library_names_spec, dnl acl_hardcode_libdir_flag_spec, dnl acl_hardcode_libdir_separator, dnl acl_hardcode_direct, dnl acl_hardcode_minus_L. AC_DEFUN([AC_LIB_RPATH], [ dnl Tell automake >= 1.10 to complain if config.rpath is missing. m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done ]) wl="$acl_cv_wl" acl_libext="$acl_cv_libext" acl_shlibext="$acl_cv_shlibext" acl_libname_spec="$acl_cv_libname_spec" acl_library_names_spec="$acl_cv_library_names_spec" acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" acl_hardcode_direct="$acl_cv_hardcode_direct" acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" dnl Determine whether the user wants rpath handling at all. AC_ARG_ENABLE([rpath], [ --disable-rpath do not hardcode runtime library paths], :, enable_rpath=yes) ]) dnl AC_LIB_FROMPACKAGE(name, package) dnl declares that libname comes from the given package. The configure file dnl will then not have a --with-libname-prefix option but a dnl --with-package-prefix option. Several libraries can come from the same dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar dnl macro call that searches for libname. AC_DEFUN([AC_LIB_FROMPACKAGE], [ pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) define([acl_frompackage_]NAME, [$2]) popdef([NAME]) pushdef([PACK],[$2]) pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) define([acl_libsinpackage_]PACKUP, m4_ifdef([acl_libsinpackage_]PACKUP, [m4_defn([acl_libsinpackage_]PACKUP)[, ]],)[lib$1]) popdef([PACKUP]) popdef([PACK]) ]) dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) dnl Autoconf >= 2.61 supports dots in --with options. pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[m4_translit(PACK,[.],[_])],PACK)]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_ARG_WITH(P_A_C_K[-prefix], [[ --with-]]P_A_C_K[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib --without-]]P_A_C_K[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" if test "$acl_libdirstem2" != "$acl_libdirstem" \ && ! test -d "$withval/$acl_libdirstem"; then additional_libdir="$withval/$acl_libdirstem2" fi fi fi ]) dnl Search the library and its dependencies in $additional_libdir and dnl $LDFLAGS. Using breadth-first-seach. LIB[]NAME= LTLIB[]NAME= INC[]NAME= LIB[]NAME[]_PREFIX= dnl HAVE_LIB${NAME} is an indicator that LIB${NAME}, LTLIB${NAME} have been dnl computed. So it has to be reset here. HAVE_LIB[]NAME= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='$1 $2' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" dnl See if it was already located by an earlier AC_LIB_LINKFLAGS dnl or AC_LIB_HAVE_LINKFLAGS call. uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./+-|ABCDEFGHIJKLMNOPQRSTUVWXYZ____|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" else dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined dnl that this library doesn't exist. So just drop it. : fi else dnl Search the library lib$name in $additional_libdir and $LDFLAGS dnl and the already constructed $LIBNAME/$LTLIBNAME. found_dir= found_la= found_so= found_a= eval libname=\"$acl_libname_spec\" # typically: libname=lib$name if test -n "$acl_shlibext"; then shrext=".$acl_shlibext" # typically: shrext=.so else shrext= fi if test $use_additional = yes; then dir="$additional_libdir" dnl The same code as in the loop below: dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then dnl Found the library. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then dnl Linking with a shared library. We attempt to hardcode its dnl directory into the executable's runpath, unless it's the dnl standard /usr/lib. if test "$enable_rpath" = no \ || test "X$found_dir" = "X/usr/$acl_libdirstem" \ || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then dnl No hardcoding is needed. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl Use an explicit option to hardcode DIR into the resulting dnl binary. dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi dnl The hardcoding into $LIBNAME is system dependent. if test "$acl_hardcode_direct" = yes; then dnl Using DIR/libNAME.so during linking hardcodes DIR into the dnl resulting binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode DIR into the resulting dnl binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else dnl Rely on "-L$found_dir". dnl But don't add it if it's already contained in the LDFLAGS dnl or the already constructed $LIBNAME haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" fi if test "$acl_hardcode_minus_L" != no; then dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH dnl here, because this doesn't fit in flags passed to the dnl compiler. So give up. No hardcoding. This affects only dnl very old systems. dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then dnl Linking with a static library. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" else dnl We shouldn't come here, but anyway it's good to have a dnl fallback. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" fi fi dnl Assume the include files are nearby. additional_includedir= case "$found_dir" in */$acl_libdirstem | */$acl_libdirstem/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; */$acl_libdirstem2 | */$acl_libdirstem2/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then dnl Potentially add $additional_includedir to $INCNAME. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's /usr/local/include and we are using GCC on Linux, dnl 3. if it's already present in $CPPFLAGS or the already dnl constructed $INCNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INC[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $INCNAME. INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" fi fi fi fi fi dnl Look for dependencies. if test -n "$found_la"; then dnl Read the .la file. It defines the variables dnl dlname, library_names, old_library, dependency_libs, current, dnl age, revision, installed, dlopen, dlpreopen, libdir. save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" dnl We use only dependency_libs. for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's /usr/local/lib and we are using GCC on Linux, dnl 3. if it's already present in $LDFLAGS or the already dnl constructed $LIBNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then haveit= if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LIBNAME. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LTLIBNAME. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) dnl Handle this in the next round. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) dnl Handle this in the next round. Throw away the .la's dnl directory; it is already contained in a preceding -L dnl option. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) dnl Most likely an immediate library name. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" ;; esac done fi else dnl Didn't find the library; assume it is in the system directories dnl known to the linker and runtime loader. (All the system dnl directories known to the linker should also be known to the dnl runtime loader, otherwise the system is severely misconfigured.) LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user must dnl pass all path elements in one option. We can arrange that for a dnl single library, but not when more than one $LIBNAMEs are used. alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" done dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" else dnl The -rpath options are cumulative. for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then dnl When using libtool, the option that works for both libraries and dnl executables is -R. The -R options are cumulative. for found_dir in $ltrpathdirs; do LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" done fi popdef([P_A_C_K]) popdef([PACKLIBS]) popdef([PACKUP]) popdef([PACK]) popdef([NAME]) ]) dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, dnl unless already present in VAR. dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes dnl contains two or three consecutive elements that belong together. AC_DEFUN([AC_LIB_APPENDTOVAR], [ for element in [$2]; do haveit= for x in $[$1]; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then [$1]="${[$1]}${[$1]:+ }$element" fi done ]) dnl For those cases where a variable contains several -L and -l options dnl referring to unknown libraries and directories, this macro determines the dnl necessary additional linker options for the runtime path. dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) dnl sets LDADDVAR to linker options needed together with LIBSVALUE. dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, dnl otherwise linking without libtool is assumed. AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], [ AC_REQUIRE([AC_LIB_RPATH]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) $1= if test "$enable_rpath" != no; then if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode directories into the resulting dnl binary. rpathdirs= next= for opt in $2; do if test -n "$next"; then dir="$next" dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2"; then rpathdirs="$rpathdirs $dir" fi next= else case $opt in -L) next=yes ;; -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2"; then rpathdirs="$rpathdirs $dir" fi next= ;; *) next= ;; esac fi done if test "X$rpathdirs" != "X"; then if test -n ""$3""; then dnl libtool is used for linking. Use -R options. for dir in $rpathdirs; do $1="${$1}${$1:+ }-R$dir" done else dnl The linker is used for linking directly. if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user dnl must pass all path elements in one option. alldirs= for dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" done acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="$flag" else dnl The -rpath options are cumulative. for dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="${$1}${$1:+ }$flag" done fi fi fi fi fi AC_SUBST([$1]) ]) git2r/tools/pkg.m40000644000175000017500000003057613443371164013637 0ustar nileshnilesh# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 11 (pkg-config-0.29.1) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------ dnl dnl Prepare a "--with-" configure option using the lowercase dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and dnl PKG_CHECK_MODULES in a single macro. AC_DEFUN([PKG_WITH_MODULES], [ m4_pushdef([with_arg], m4_tolower([$1])) m4_pushdef([description], [m4_default([$5], [build with ]with_arg[ support])]) m4_pushdef([def_arg], [m4_default([$6], [auto])]) m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) m4_case(def_arg, [yes],[m4_pushdef([with_without], [--without-]with_arg)], [m4_pushdef([with_without],[--with-]with_arg)]) AC_ARG_WITH(with_arg, AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, [AS_TR_SH([with_]with_arg)=def_arg]) AS_CASE([$AS_TR_SH([with_]with_arg)], [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], [auto],[PKG_CHECK_MODULES([$1],[$2], [m4_n([def_action_if_found]) $3], [m4_n([def_action_if_not_found]) $4])]) m4_popdef([with_arg]) m4_popdef([description]) m4_popdef([def_arg]) ])dnl PKG_WITH_MODULES dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ----------------------------------------------- dnl dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES dnl check._[VARIABLE-PREFIX] is exported as make variable. AC_DEFUN([PKG_HAVE_WITH_MODULES], [ PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) AM_CONDITIONAL([HAVE_][$1], [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) ])dnl PKG_HAVE_WITH_MODULES dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------------------ dnl dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make dnl and preprocessor variable. AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES git2r/tools/install-sh0000755000175000017500000003452413137360640014612 0ustar nileshnilesh#!/bin/sh # install - install a program, script, or datafile scriptversion=2016-01-11.22; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: git2r/tools/lib-ld.m40000644000175000017500000000714313443371164014213 0ustar nileshnilesh# lib-ld.m4 serial 6 dnl Copyright (C) 1996-2003, 2009-2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Subroutines of libtool.m4, dnl with replacements s/_*LT_PATH/AC_LIB_PROG/ and s/lt_/acl_/ to avoid dnl collision with libtool.m4. dnl From libtool-2.4. Sets the variable with_gnu_ld to yes or no. AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld], [# I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 /dev/null 2>&1 \ && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ || PATH_SEPARATOR=';' } fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`echo "$ac_prog"| sed 's%\\\\%/%g'` while echo "$ac_prog" | grep "$re_direlt" > /dev/null 2>&1; do ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL([acl_cv_path_LD], [if test -z "$LD"; then acl_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$acl_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=$(echo "$0" | sed -e 's,.*/,,') usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type # shellcheck disable=SC2162 IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ | unicom* | ibm* | next | hp | isi* | apollo | altos* \ | convergent* | ncr* | news | 32* | 3600* | 3100* \ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ | ultra | tti* | harris | dolphin | highlevel | gould \ | cbm | ns | masscomp | apple | axis | knuth | cray \ | microblaze* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 basic_os= ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; convex-c1) basic_machine=c1-convex basic_os=bsd ;; convex-c2) basic_machine=c2-convex basic_os=bsd ;; convex-c32) basic_machine=c32-convex basic_os=bsd ;; convex-c34) basic_machine=c34-convex basic_os=bsd ;; convex-c38) basic_machine=c38-convex basic_os=bsd ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc basic_os=sysv4 ;; i*86v) cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc basic_os=sysv ;; i*86sol2) cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next case $basic_os in openstep*) ;; nextstep*) ;; ns2*) basic_os=nextstep2 ;; *) basic_os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=$(echo "$basic_machine" | sed 's/-.*//') ;; *-*) # shellcheck disable=SC2162 IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x$basic_os != x then # First recognize some ad-hoc caes, or perhaps split kernel-os, or else just # set os. case $basic_os in gnu/linux*) kernel=linux os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|') ;; os2-emx) kernel=os2 os=$(echo $basic_os | sed -e 's|os2-emx|emx|') ;; nto-qnx*) kernel=nto os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|') ;; *-*) # shellcheck disable=SC2162 IFS="-" read kernel os <&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os in linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) ;; uclinux-uclibc* ) ;; -dietlibc* | -newlib* | -musl* | -uclibc* ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 exit 1 ;; kfreebsd*-gnu* | kopensolaris*-gnu*) ;; nto-qnx*) ;; os2-emx) ;; *-eabi* | *-gnueabi*) ;; -*) # Blank kernel with real OS is always fine. ;; *-*) echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be ;; *-hpux*) vendor=hp ;; *-mpeix*) vendor=hp ;; *-hiux*) vendor=hitachi ;; *-unos*) vendor=crds ;; *-dgux*) vendor=dg ;; *-luna*) vendor=omron ;; *-genix*) vendor=ns ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) vendor=ibm ;; *-ptx*) vendor=sequent ;; *-tpf*) vendor=ibm ;; *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; *-aux*) vendor=apple ;; *-hms*) vendor=hitachi ;; *-mpw* | *-macos*) vendor=apple ;; *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; *-vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: git2r/tools/lib-prefix.m40000644000175000017500000002042213443371164015104 0ustar nileshnilesh# lib-prefix.m4 serial 7 (gettext-0.18) dnl Copyright (C) 2001-2005, 2008-2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't dnl require excessive bracketing. ifdef([AC_HELP_STRING], [AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], [AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed dnl to access previously installed libraries. The basic assumption is that dnl a user will want packages to use other packages he previously installed dnl with the same --prefix option. dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate dnl libraries, but is otherwise very convenient. AC_DEFUN([AC_LIB_PREFIX], [ AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_LIB_ARG_WITH([lib-prefix], [ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib --without-lib-prefix don't search for libraries in includedir and libdir], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" fi fi ]) if test $use_additional = yes; then dnl Potentially add $additional_includedir to $CPPFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's already present in $CPPFLAGS, dnl 3. if it's /usr/local/include and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= for x in $CPPFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $CPPFLAGS. CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" fi fi fi fi dnl Potentially add $additional_libdir to $LDFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's already present in $LDFLAGS, dnl 3. if it's /usr/local/lib and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then haveit= for x in $LDFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LDFLAGS. LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" fi fi fi fi fi ]) dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, dnl acl_final_exec_prefix, containing the values to which $prefix and dnl $exec_prefix will expand at the end of the configure script. AC_DEFUN([AC_LIB_PREPARE_PREFIX], [ dnl Unfortunately, prefix and exec_prefix get only finally determined dnl at the end of configure. if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" ]) dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the dnl variables prefix and exec_prefix bound to the values they will have dnl at the end of the configure script. AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [ acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" $1 exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" ]) dnl AC_LIB_PREPARE_MULTILIB creates dnl - a variable acl_libdirstem, containing the basename of the libdir, either dnl "lib" or "lib64" or "lib/64", dnl - a variable acl_libdirstem2, as a secondary possible value for dnl acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or dnl "lib/amd64". AC_DEFUN([AC_LIB_PREPARE_MULTILIB], [ dnl There is no formal standard regarding lib and lib64. dnl On glibc systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine dnl the compiler's default mode by looking at the compiler's library search dnl path. If at least one of its elements ends in /lib64 or points to a dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI. dnl Otherwise we use the default, namely "lib". dnl On Solaris systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. AC_REQUIRE([AC_CANONICAL_HOST]) acl_libdirstem=lib acl_libdirstem2= case "$host_os" in solaris*) dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment dnl . dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the dnl symlink is missing, so we set acl_libdirstem2 too. AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit], [AC_EGREP_CPP([sixtyfour bits], [ #ifdef _LP64 sixtyfour bits #endif ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no]) ]) if test $gl_cv_solaris_64bit = yes; then acl_libdirstem=lib/64 case "$host_cpu" in sparc*) acl_libdirstem2=lib/sparcv9 ;; i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; esac fi ;; *) searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` if test -n "$searchpath"; then acl_save_IFS="${IFS= }"; IFS=":" for searchdir in $searchpath; do if test -d "$searchdir"; then case "$searchdir" in */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; */../ | */.. ) # Better ignore directories of this form. They are misleading. ;; *) searchdir=`cd "$searchdir" && pwd` case "$searchdir" in */lib64 ) acl_libdirstem=lib64 ;; esac ;; esac fi done IFS="$acl_save_IFS" fi ;; esac test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" ]) git2r/tools/version.c0000644000175000017500000000033613651043437014434 0ustar nileshnilesh#include #if LIBGIT2_VER_MAJOR == 0 # if LIBGIT2_VER_MINOR < 26 # error libgit2 version too old # endif #elif LIBGIT2_VER_MAJOR > 1 # error the libgit2 version is not compatible with git2r #endif git2r/tools/config.guess0000755000175000017500000014045014075750704015130 0ustar nileshnilesh#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2021 Free Software Foundation, Inc. timestamp='2021-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . me=$(echo "$0" | sed -e 's,.*/,,') usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039 { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$driver" break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif EOF eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)) case "$UNAME_MACHINE_ARCH" in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,') endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p') machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr") ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2) ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "$machine-${os}${release}${abi-}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//') echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//') echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//') echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; *:OS108:*:*) echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Twizzler:*:*) echo "$UNAME_MACHINE"-unknown-twizzler exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}') ;; *5.*) UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}') ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1) case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "$( (/bin/universe) 2>/dev/null)" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case $(/usr/bin/uname -p) in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:*:*) case "$(/usr/bin/arch -k)" in Series*|S4*) UNAME_RELEASE=$(uname -v) ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null) test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "$(/bin/arch)" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') && SYSTEM_NAME=$("$dummy" "$dummyarg") && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=$(/usr/bin/uname -p) if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then echo m88k-dg-dgux"$UNAME_RELEASE" else echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=$(/usr/bin/oslevel) else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }') if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/) else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null) sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null) case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy") test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz) FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/') echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/') echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=$(uname -p) set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi else echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf fi exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=$(/usr/bin/uname -p) case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-pc-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; *:GNU:*:*) # the GNU system echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC" exit ;; *:Minix:*:*) echo "$UNAME_MACHINE"-unknown-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) set_cc_for_build LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_X32 >/dev/null then LIBCABI="$LIBC"x32 fi fi echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI" exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//') if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case $(/bin/uname -X | grep "^Machine") in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=$(sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //')) (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=$( (uname -p) 2>/dev/null) echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; arm64:Darwin:*:*) echo aarch64-apple-darwin"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=$(uname -p) case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=$(uname -p) if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. # shellcheck disable=SC2154 if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" exit ;; *:*VMS:*:*) UNAME_MACHINE=$( (uname -p) 2>/dev/null) case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; *:Unleashed:*:*) echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" exit ;; esac # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null); if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown) uname -r = $( (uname -r) 2>/dev/null || echo unknown) uname -s = $( (uname -s) 2>/dev/null || echo unknown) uname -v = $( (uname -v) 2>/dev/null || echo unknown) /usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null) /bin/uname -X = $( (/bin/uname -X) 2>/dev/null) hostinfo = $( (hostinfo) 2>/dev/null) /bin/universe = $( (/bin/universe) 2>/dev/null) /usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null) /bin/arch = $( (/bin/arch) 2>/dev/null) /usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null) /usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null) UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: git2r/tools/winlibs.R0000644000175000017500000000065113301723127014366 0ustar nileshnilesh# We need https if(getRversion() < "3.3.0") setInternet2() VERSION <- commandArgs(TRUE) # Downloads libssh2 + dependencies if(!file.exists(sprintf("../windows/libgit2-%s/include/git2.h", VERSION))){ download.file(sprintf("https://github.com/rwinlib/libgit2/archive/v%s.zip", VERSION), "lib.zip", quiet = TRUE) dir.create("../windows", showWarnings = FALSE) unzip("lib.zip", exdir = "../windows") unlink("lib.zip") } git2r/tools/missing0000755000175000017500000001533113137360640014200 0ustar nileshnilesh#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2016-01-11.22; # UTC # Copyright (C) 1996-2015 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: git2r/tools/iconv.m40000644000175000017500000002207213443371164014164 0ustar nileshnilesh# iconv.m4 serial 19 (gettext-0.18.2) dnl Copyright (C) 2000-2002, 2007-2014, 2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], [ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_LIB_LINKFLAGS_BODY([iconv]) ]) AC_DEFUN([AM_ICONV_LINK], [ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and dnl those with the standalone portable GNU libiconv installed). AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) dnl Add $INCICONV to CPPFLAGS before performing the following checks, dnl because if the user has installed libiconv and not disabled its use dnl via --without-libiconv-prefix, he wants to use it. The first dnl AC_LINK_IFELSE will then fail, the second AC_LINK_IFELSE will succeed. am_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[ #include #include ]], [[iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);]])], [am_cv_func_iconv=yes]) if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[ #include #include ]], [[iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);]])], [am_cv_lib_iconv=yes] [am_cv_func_iconv=yes]) LIBS="$am_save_LIBS" fi ]) if test "$am_cv_func_iconv" = yes; then AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ dnl This tests against bugs in AIX 5.1, AIX 6.1..7.1, HP-UX 11.11, dnl Solaris 10. am_save_LIBS="$LIBS" if test $am_cv_lib_iconv = yes; then LIBS="$LIBS $LIBICONV" fi am_cv_func_iconv_works=no for ac_iconv_const in '' 'const'; do AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[ #include #include #ifndef ICONV_CONST # define ICONV_CONST $ac_iconv_const #endif ]], [[int result = 0; /* Test against AIX 5.1 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); if (cd_utf8_to_88591 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\342\202\254"; /* EURO SIGN */ char buf[10]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_utf8_to_88591, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) result |= 1; iconv_close (cd_utf8_to_88591); } } /* Test against Solaris 10 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); if (cd_ascii_to_88591 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\263"; char buf[10]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_ascii_to_88591, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) result |= 2; iconv_close (cd_ascii_to_88591); } } /* Test against AIX 6.1..7.1 bug: Buffer overrun. */ { iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\304"; static char buf[2] = { (char)0xDE, (char)0xAD }; ICONV_CONST char *inptr = input; size_t inbytesleft = 1; char *outptr = buf; size_t outbytesleft = 1; size_t res = iconv (cd_88591_to_utf8, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD) result |= 4; iconv_close (cd_88591_to_utf8); } } #if 0 /* This bug could be worked around by the caller. */ /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ { iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; char buf[50]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_88591_to_utf8, &inptr, &inbytesleft, &outptr, &outbytesleft); if ((int)res > 0) result |= 8; iconv_close (cd_88591_to_utf8); } } #endif /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is provided. */ if (/* Try standardized names. */ iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) /* Try IRIX, OSF/1 names. */ && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) /* Try AIX names. */ && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) /* Try HP-UX names. */ && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) result |= 16; return result; ]])], [am_cv_func_iconv_works=yes], , [case "$host_os" in aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; *) am_cv_func_iconv_works="guessing yes" ;; esac]) test "$am_cv_func_iconv_works" = no || break done LIBS="$am_save_LIBS" ]) case "$am_cv_func_iconv_works" in *no) am_func_iconv=no am_cv_lib_iconv=no ;; *) am_func_iconv=yes ;; esac else am_func_iconv=no am_cv_lib_iconv=no fi if test "$am_func_iconv" = yes; then AC_DEFINE([HAVE_ICONV], [1], [Define if you have the iconv() function and it works.]) fi if test "$am_cv_lib_iconv" = yes; then AC_MSG_CHECKING([how to link with libiconv]) AC_MSG_RESULT([$LIBICONV]) else dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV dnl either. CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi AC_SUBST([LIBICONV]) AC_SUBST([LTLIBICONV]) ]) dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to dnl avoid warnings like dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". dnl This is tricky because of the way 'aclocal' is implemented: dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. dnl Otherwise aclocal's initial scan pass would miss the macro definition. dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. dnl Otherwise aclocal would emit many "Use of uninitialized value $1" dnl warnings. m4_define([gl_iconv_AC_DEFUN], m4_version_prereq([2.64], [[AC_DEFUN_ONCE( [$1], [$2])]], [m4_ifdef([gl_00GNULIB], [[AC_DEFUN_ONCE( [$1], [$2])]], [[AC_DEFUN( [$1], [$2])]])])) gl_iconv_AC_DEFUN([AM_ICONV], [ AM_ICONV_LINK if test "$am_cv_func_iconv" = yes; then AC_MSG_CHECKING([for iconv declaration]) AC_CACHE_VAL([am_cv_proto_iconv], [ AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif ]], [[]])], [am_cv_proto_iconv_arg1=""], [am_cv_proto_iconv_arg1="const"]) am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` AC_MSG_RESULT([ $am_cv_proto_iconv]) AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], [Define as const if the declaration of iconv() needs const.]) dnl Also substitute ICONV_CONST in the gnulib generated . m4_ifdef([gl_ICONV_H_DEFAULTS], [AC_REQUIRE([gl_ICONV_H_DEFAULTS]) if test -n "$am_cv_proto_iconv_arg1"; then ICONV_CONST="const" fi ]) fi ]) git2r/tools/config.rpath0000644000175000017500000004425414141437067015120 0ustar nileshnilesh#! /bin/sh # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # # Copyright 1996-2021 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # The first argument passed to this file is the canonical host specification, # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld # should be set by the caller. # # The set of defined variables is at the end of this script. # Known limitations: # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer # than 256 bytes, otherwise the compiler driver will dump core. The only # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a shrext=.so host="$1" host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # Code taken from libtool.m4's _LT_CC_BASENAME. for cc_temp in $CC""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` # Code taken from libtool.m4's _LT_COMPILER_PIC. wl= if test "$GCC" = yes; then wl='-Wl,' else case "$host_os" in aix*) wl='-Wl,' ;; mingw* | cygwin* | pw32* | os2* | cegcc*) ;; hpux9* | hpux10* | hpux11*) wl='-Wl,' ;; irix5* | irix6* | nonstopux*) wl='-Wl,' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in ecc*) wl='-Wl,' ;; icc* | ifort*) wl='-Wl,' ;; lf95*) wl='-Wl,' ;; nagfor*) wl='-Wl,-Wl,,' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) wl='-Wl,' ;; ccc*) wl='-Wl,' ;; xl* | bgxl* | bgf* | mpixl*) wl='-Wl,' ;; como) wl='-lopt=' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ F* | *Sun*Fortran*) wl= ;; *Sun\ C*) wl='-Wl,' ;; esac ;; esac ;; newsos6) ;; *nto* | *qnx*) ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; rdos*) ;; solaris*) case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) wl='-Qoption ld ' ;; *) wl='-Wl,' ;; esac ;; sunos4*) wl='-Qoption ld ' ;; sysv4 | sysv4.2uw2* | sysv4.3*) wl='-Wl,' ;; sysv4*MP*) ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) wl='-Wl,' ;; unicos*) wl='-Wl,' ;; uts4*) ;; esac fi # Code taken from libtool.m4's _LT_LINKER_SHLIBS. hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_direct=no hardcode_minus_L=no case "$host_os" in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. # Unlike libtool, we use -rpath here, not --rpath, since the documented # option of GNU ld is called -rpath, not --rpath. hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' case "$host_os" in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no fi ;; amigaos*) case "$host_cpu" in powerpc) ;; m68k) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then : else ld_shlibs=no fi ;; haiku*) ;; interix[3-9]*) hardcode_direct=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; netbsd*) ;; solaris*) if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then ld_shlibs=no elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' else ld_shlibs=no fi ;; esac ;; sunos4*) hardcode_direct=yes ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then hardcode_libdir_flag_spec= fi else case "$host_os" in aix3*) # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac fi hardcode_direct=yes hardcode_libdir_separator=':' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && \ strings "$collect2name" | grep resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac fi # Begin _LT_AC_SYS_LIBPATH_AIX. echo 'int main () { return 0; }' > conftest.c ${CC} ${LDFLAGS} conftest.c -o conftest aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` fi if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib" fi rm -f conftest.c conftest # End _LT_AC_SYS_LIBPATH_AIX. if test "$aix_use_runtimelinking" = yes; then hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' else hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" fi fi ;; amigaos*) case "$host_cpu" in powerpc) ;; m68k) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' libext=lib ;; darwin* | rhapsody*) hardcode_direct=no if { case $cc_basename in ifort*) true;; *) test "$GCC" = yes;; esac; }; then : else ld_shlibs=no fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; freebsd2.[01]*) hardcode_direct=yes hardcode_minus_L=yes ;; freebsd* | dragonfly* | midnightbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; hpux9*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; hpux10*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no ;; *) hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; netbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; newsos6) hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then hardcode_libdir_flag_spec='${wl}-rpath,$libdir' else case "$host_os" in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) hardcode_libdir_flag_spec='-R$libdir' ;; *) hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; osf3*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) if test "$GCC" = yes; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else # Both cc and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi hardcode_libdir_separator=: ;; solaris*) hardcode_libdir_flag_spec='-R$libdir' ;; sunos4*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes ;; sysv4) case $host_vendor in sni) hardcode_direct=yes # is this really true??? ;; siemens) hardcode_direct=no ;; motorola) hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac ;; sysv4.3*) ;; sysv4*MP*) if test -d /usr/nec; then ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) ;; sysv5* | sco3.2v5* | sco5v6*) hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' hardcode_libdir_separator=':' ;; uts4*) hardcode_libdir_flag_spec='-L$libdir' ;; *) ld_shlibs=no ;; esac fi # Check dynamic linker characteristics # Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. # Unlike libtool.m4, here we don't care about _all_ names of the library, but # only about the one the linker finds when passed -lNAME. This is the last # element of library_names_spec in libtool.m4, or possibly two of them if the # linker has special search rules. library_names_spec= # the last element of library_names_spec in libtool.m4 libname_spec='lib$name' case "$host_os" in aix3*) library_names_spec='$libname.a' ;; aix[4-9]*) library_names_spec='$libname$shrext' ;; amigaos*) case "$host_cpu" in powerpc*) library_names_spec='$libname$shrext' ;; m68k) library_names_spec='$libname.a' ;; esac ;; beos*) library_names_spec='$libname$shrext' ;; bsdi[45]*) library_names_spec='$libname$shrext' ;; cygwin* | mingw* | pw32* | cegcc*) shrext=.dll library_names_spec='$libname.dll.a $libname.lib' ;; darwin* | rhapsody*) shrext=.dylib library_names_spec='$libname$shrext' ;; dgux*) library_names_spec='$libname$shrext' ;; freebsd[23].*) library_names_spec='$libname$shrext$versuffix' ;; freebsd* | dragonfly* | midnightbsd*) library_names_spec='$libname$shrext' ;; gnu*) library_names_spec='$libname$shrext' ;; haiku*) library_names_spec='$libname$shrext' ;; hpux9* | hpux10* | hpux11*) case $host_cpu in ia64*) shrext=.so ;; hppa*64*) shrext=.sl ;; *) shrext=.sl ;; esac library_names_spec='$libname$shrext' ;; interix[3-9]*) library_names_spec='$libname$shrext' ;; irix5* | irix6* | nonstopux*) library_names_spec='$libname$shrext' case "$host_os" in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; *) libsuff= shlibsuff= ;; esac ;; esac ;; linux*oldld* | linux*aout* | linux*coff*) ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) library_names_spec='$libname$shrext' ;; knetbsd*-gnu) library_names_spec='$libname$shrext' ;; netbsd*) library_names_spec='$libname$shrext' ;; newsos6) library_names_spec='$libname$shrext' ;; *nto* | *qnx*) library_names_spec='$libname$shrext' ;; openbsd*) library_names_spec='$libname$shrext$versuffix' ;; os2*) libname_spec='$name' shrext=.dll library_names_spec='$libname.a' ;; osf3* | osf4* | osf5*) library_names_spec='$libname$shrext' ;; rdos*) ;; solaris*) library_names_spec='$libname$shrext' ;; sunos4*) library_names_spec='$libname$shrext$versuffix' ;; sysv4 | sysv4.3*) library_names_spec='$libname$shrext' ;; sysv4*MP*) library_names_spec='$libname$shrext' ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) library_names_spec='$libname$shrext' ;; tpf*) library_names_spec='$libname$shrext' ;; uts4*) library_names_spec='$libname$shrext' ;; esac sed_quote_subst='s/\(["`$\\]\)/\\\1/g' escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` shlibext=`echo "$shrext" | sed -e 's,^\.,,'` escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' < 0) ## Check branch name argument res <- tools::assertError(branch_set_upstream()) stopifnot(length(grep("Missing argument name", res[[1]]$message)) > 0) ## Print branch b <- repository_head(repo) stopifnot(identical(print(b), b)) ## Create a branch b <- branch_create(commit_1, name = "test") stopifnot(identical(b$name, "test")) stopifnot(identical(b$type, 1L)) stopifnot(identical(sha(b), branch_target(b))) stopifnot(identical(length(branches(repo)), 2L)) stopifnot(identical(branch_target(branches(repo)[[1]]), branch_target(branches(repo)[[2]]))) ## Check is_branch stopifnot(identical(is_branch(b), TRUE)) stopifnot(identical(is_branch(5), FALSE)) ## Add one more commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Another commit message") ## Now the first branch should have moved on stopifnot(!identical(branch_target(branches(repo)[[1]]), branch_target(branches(repo)[[2]]))) ## Create a branch with the same name should fail tools::assertError(branch_create(commit_2, name = "test")) ## Force it and check the branches are identical again b <- branch_create(commit_2, name = "test", force = TRUE) stopifnot(identical(branch_target(branches(repo)[[1]]), branch_target(branches(repo)[[2]]))) ## Test arguments res <- tools::assertError(.Call(git2r:::git2r_branch_delete, NULL)) stopifnot(length(grep("'branch' must be an S3 class git_branch", res[[1]]$message)) > 0) res <- tools::assertError(.Call(git2r:::git2r_branch_delete, 3)) stopifnot(length(grep("'branch' must be an S3 class git_branch", res[[1]]$message)) > 0) res <- tools::assertError(.Call(git2r:::git2r_branch_delete, repo)) stopifnot(length(grep("'branch' must be an S3 class git_branch", res[[1]]$message)) > 0) b_tmp <- b b_tmp$name <- NA_character_ res <- tools::assertError(.Call(git2r:::git2r_branch_delete, b_tmp)) stopifnot(length(grep("'branch' must be an S3 class git_branch", res[[1]]$message)) > 0) b_tmp <- b b_tmp$type <- NA_integer_ res <- tools::assertError(.Call(git2r:::git2r_branch_delete, b_tmp)) stopifnot(length(grep("'branch' must be an S3 class git_branch", res[[1]]$message)) > 0) b_tmp$type <- 3L res <- tools::assertError(.Call(git2r:::git2r_branch_delete, b_tmp)) stopifnot(length(grep("'branch' must be an S3 class git_branch", res[[1]]$message)) > 0) ## Delete branch branch_delete(b) stopifnot(identical(length(branches(repo)), 1L)) ## Add one more commit writeLines(c("Hello world!", "HELLO WORLD!", "hello world"), file.path(path, "test.txt")) add(repo, "test.txt") commit_3 <- commit(repo, "Another third commit message") ## Create and test renaming of branches b_1 <- branch_create(commit_1, name = "test-1") b_2 <- branch_create(commit_2, name = "test-2") b_3 <- branch_create(commit_3, name = "test-3") stopifnot(identical(length(branches(repo)), 4L)) b_1 <- branch_rename(b_1, name = "test-1-new-name") stopifnot(identical(length(branches(repo)), 4L)) stopifnot(identical(b_1$name, "test-1-new-name")) tools::assertError(branch_rename(b_1, name = "test-2")) branch_rename(b_1, name = "test-2", force = TRUE) stopifnot(identical(length(branches(repo)), 3L)) ## Check branches method with missing repo argument wd <- setwd(path) stopifnot(identical(length(branches()), 3L)) if (!is.null(wd)) setwd(wd) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/status.R0000644000175000017500000001201113526007213014235 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Status case 1 status_exp_1 <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = empty_named_list()), class = "git_status") status_obs_1 <- status(repo) stopifnot(identical(print(status_obs_1), status_obs_1)) str(status_exp_1) str(status_obs_1) stopifnot(identical(status_obs_1, status_exp_1)) stopifnot(identical(capture.output(status(repo)), "working directory clean")) ## Status case 2, include ignored files status_exp_2 <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = empty_named_list(), ignored = empty_named_list()), class = "git_status") status_obs_2 <- status(repo, ignored = TRUE) status_obs_2 str(status_exp_2) str(status_obs_2) stopifnot(identical(status_obs_2, status_exp_2)) stopifnot(identical(capture.output(status(repo, ignored = TRUE)), "working directory clean")) ## Create 4 files writeLines("File-1", file.path(path, "test-1.txt")) writeLines("File-2", file.path(path, "test-2.txt")) writeLines("File-3", file.path(path, "test-3.txt")) writeLines("File-4", file.path(path, "test-4.txt")) ## Status case 3: 4 untracked files status_exp_3 <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = list(untracked = "test-1.txt", untracked = "test-2.txt", untracked = "test-3.txt", untracked = "test-4.txt")), class = "git_status") status_obs_3 <- status(repo) status_obs_3 str(status_exp_3) str(status_obs_3) stopifnot(identical(status_obs_3, status_exp_3)) ## Add file 1 and 2 to the repository and commit add(repo, c("test-1.txt", "test-2.txt")) commit(repo, "Commit message") ## Status case 4: 2 untracked files status_exp_4 <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = list(untracked = "test-3.txt", untracked = "test-4.txt")), class = "git_status") status_obs_4 <- status(repo) status_obs_4 str(status_exp_4) str(status_obs_4) stopifnot(identical(status_obs_4, status_exp_4)) ## Update file 1 & 2 writeLines(c("File-1", "Hello world"), file.path(path, "test-1.txt")) writeLines(c("File-2", "Hello world"), file.path(path, "test-2.txt")) ## Add file 1 add(repo, "test-1.txt") ## Status case 5: 1 staged file, 1 unstaged file and 2 untracked files status_exp_5 <- structure(list(staged = list(modified = "test-1.txt"), unstaged = list(modified = "test-2.txt"), untracked = list(untracked = "test-3.txt", untracked = "test-4.txt")), class = "git_status") status_obs_5 <- status(repo) status_obs_5 str(status_exp_5) str(status_obs_5) stopifnot(identical(status_obs_5, status_exp_5)) ## Add .gitignore file with file test-4.txt writeLines("test-4.txt", file.path(path, ".gitignore")) ## Status case 6: 1 staged file, 1 unstaged file, 2 untracked files ## and 1 ignored file status_exp_6 <- structure(list(staged = list(modified = "test-1.txt"), unstaged = list(modified = "test-2.txt"), untracked = list(untracked = ".gitignore", untracked = "test-3.txt"), ignored = list(ignored = "test-4.txt")), class = "git_status") status_obs_6 <- status(repo, ignore = TRUE) status_obs_6 str(status_exp_6) str(status_obs_6) stopifnot(identical(status_obs_6, status_exp_6)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/commit.R0000644000175000017500000001726313526011363014221 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Commit without adding changes should produce an error tools::assertError(commit(repo, "Test to commit")) ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) ## Commit without adding changes should produce an error tools::assertError(commit(repo, "Test to commit")) ## Add add(repo, "test.txt") ## Commit with empty message should produce an error tools::assertError(commit(repo, "")) ## Commit commit_1 <- commit(repo, "Commit message", session = TRUE) summary(commit_1) tag_1 <- tag(repo, "Tagname1", "Tag message 1") ## Check commit stopifnot(identical(commit_1$author$name, "Alice")) stopifnot(identical(commit_1$author$email, "alice@example.org")) stopifnot(identical(lookup(repo, sha(commit_1)), commit_1)) stopifnot(identical(length(commits(repo)), 1L)) stopifnot(identical(commits(repo)[[1]]$author$name, "Alice")) stopifnot(identical(commits(repo)[[1]]$author$email, "alice@example.org")) stopifnot(identical(parents(commit_1), list())) stopifnot(identical(print(commit_1), commit_1)) ## Check is_commit stopifnot(identical(is_commit(commit_1), TRUE)) stopifnot(identical(is_commit(5), FALSE)) ## Commit without adding changes should produce an error tools::assertError(commit(repo, "Test to commit")) ## Add another commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Commit message 2") summary(commit_2) tag_2 <- tag(repo, "Tagname2", "Tag message 2") ## Check relationship stopifnot(identical(descendant_of(commit_2, commit_1), TRUE)) stopifnot(identical(descendant_of(commit_1, commit_2), FALSE)) stopifnot(identical(descendant_of(tag_2, tag_1), TRUE)) stopifnot(identical(descendant_of(tag_1, tag_2), FALSE)) stopifnot(identical(descendant_of(branches(repo)[[1]], commit_1), TRUE)) stopifnot(identical(descendant_of(commit_1, branches(repo)[[1]]), FALSE)) stopifnot(identical(length(parents(commit_2)), 1L)) stopifnot(identical(parents(commit_2)[[1]], commit_1)) ## Check contributions stopifnot(identical( colnames(contributions(repo, by = "author", breaks = "day")), c("when", "author", "n"))) stopifnot(identical(colnames(contributions(repo)), c("when", "n"))) stopifnot(identical(nrow(contributions(repo)), 1L)) stopifnot(identical(contributions(repo)$n, 2L)) stopifnot(identical(contributions(repo, by = "author", breaks = "day")$n, 2L)) ## Add another commit with 'all' argument writeLines(c("Hello world!", "HELLO WORLD!", "HeLlO wOrLd!"), file.path(path, "test.txt")) commit(repo, "Commit message 3", all = TRUE) status_clean <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = empty_named_list()), class = "git_status") stopifnot(identical(status(repo), status_clean)) ## Delete file and commit with 'all' argument file.remove(file.path(path, "test.txt")) commit(repo, "Commit message 4", all = TRUE) stopifnot(identical(status(repo), status_clean)) ## Add and commit multiple tracked files with 'all' argument writeLines(sample(letters, 3), file.path(path, "test2.txt")) add(repo, "test2.txt") writeLines(sample(letters, 3), file.path(path, "test3.txt")) add(repo, "test3.txt") writeLines(sample(letters, 3), file.path(path, "test4.txt")) add(repo, "test4.txt") commit(repo, "Commit message 5") stopifnot(identical(status(repo), status_clean)) writeLines(sample(letters, 3), file.path(path, "test2.txt")) writeLines(sample(letters, 3), file.path(path, "test3.txt")) writeLines(sample(letters, 3), file.path(path, "test4.txt")) commit(repo, "Commit message 6", all = TRUE) stopifnot(identical(status(repo), status_clean)) ## Add one tracked file and delete another with 'all' argument writeLines(sample(letters, 3), file.path(path, "test2.txt")) file.remove(file.path(path, "test4.txt")) commit(repo, "Commit message 7", all = TRUE) stopifnot(identical(status(repo), status_clean)) ## Delete multiple tracked files with 'all' argument file.remove(file.path(path, "test2.txt")) file.remove(file.path(path, "test3.txt")) commit(repo, "Commit message 8", all = TRUE) stopifnot(identical(status(repo), status_clean)) ## Check max number of commits in output stopifnot(identical(length(commits(repo)), 8L)) stopifnot(identical(length(commits(repo, n = -1)), 8L)) stopifnot(identical(length(commits(repo, n = 2)), 2L)) tools::assertError(commits(repo, n = 2.2)) tools::assertError(commits(repo, n = "2")) tools::assertError(commits(repo, n = 1:2)) ## Check to coerce repository to data.frame df <- as.data.frame(repo) stopifnot(identical(dim(df), c(8L, 6L))) stopifnot(identical(names(df), c("sha", "summary", "message", "author", "email", "when"))) ## Set working directory to path and check commits setwd(path) stopifnot(identical(sha(last_commit()), sha(commits(repo, n = 1)[[1]]))) stopifnot(identical(length(commits()), 8L)) stopifnot(identical(length(commits(n = -1)), 8L)) stopifnot(identical(length(commits(n = 2)), 2L)) tools::assertError(commits(n = 2.2)) tools::assertError(commits(n = "2")) ## Check plot method plot_file <- tempfile(fileext = ".pdf") pdf(plot_file) plot(repo) dev.off() stopifnot(file.exists(plot_file)) unlink(plot_file) ## Check punch card plot method punch_card_plot_file <- tempfile(fileext = ".pdf") pdf(punch_card_plot_file) punch_card(repo) dev.off() stopifnot(file.exists(punch_card_plot_file)) unlink(punch_card_plot_file) ## Check that 'git2r_arg_check_commit' raise error res <- tools::assertError(.Call(git2r:::git2r_commit_tree, NULL)) stopifnot(length(grep("'commit' must be an S3 class git_commit", res[[1]]$message)) > 0) res <- tools::assertError(.Call(git2r:::git2r_commit_tree, 3)) stopifnot(length(grep("'commit' must be an S3 class git_commit", res[[1]]$message)) > 0) res <- tools::assertError(.Call(git2r:::git2r_commit_tree, repo)) stopifnot(length(grep("'commit' must be an S3 class git_commit", res[[1]]$message)) > 0) commit_1$sha <- NA_character_ res <- tools::assertError(.Call(git2r:::git2r_commit_tree, commit_1)) stopifnot(length(grep("'commit' must be an S3 class git_commit", res[[1]]$message)) > 0) ## Cleanup unlink(path, recursive = TRUE) if (identical(Sys.getenv("NOT_CRAN"), "true") || identical(Sys.getenv("R_COVR"), "true")) { path <- tempfile(pattern = "git2r-") dir.create(path) setwd(path) system("git clone --depth 2 https://github.com/ropensci/git2r.git") ## Check the number of commits in the shallow clone. stopifnot(identical(length(commits(repository("git2r"))), 2L)) stopifnot(identical(length(commits(repository("git2r"), n = 1)), 1L)) ## Cleanup unlink(path, recursive = TRUE) } git2r/tests/push-force.R0000644000175000017500000000570014076231103014772 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create directories for repositories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo_1 <- tempfile(pattern = "git2r-") path_repo_2 <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) ## Create bare repository bare_repo <- init(path_bare, bare = TRUE) ## Clone to repo 1 repo_1 <- clone(path_bare, path_repo_1) config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo 1 and push to bare writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit_1 <- commit(repo_1, "First commit message") branch_name <- branches(repo_1)[[1]]$name push(repo_1, "origin", paste0("refs/heads/", branch_name)) ## Clone to repo 2 repo_2 <- clone(path_bare, path_repo_2) config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Add more changes to repo 1 and push to bare writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit_2 <- commit(repo_1, "Second commit message") push(repo_1, "origin", paste0("refs/heads/", branch_name)) ## Add changes to repo 2 and push to bare writeLines( c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut"), con = file.path(path_repo_2, "test-1.txt")) add(repo_2, "test-1.txt") commit_3 <- commit(repo_2, "Third commit message") ## We are behind, so this should fail tools::assertError(push(repo_2)) ## Push with force = TRUE push(repo_2, force = TRUE) ## Check commits stopifnot(identical(length(commits(bare_repo)), 2L)) stopifnot(identical(sha(commits(repo_2)[[1]]), sha(commits(bare_repo)[[1]]))) stopifnot(identical(sha(commits(repo_2)[[2]]), sha(commits(bare_repo)[[2]]))) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo_1, recursive = TRUE) unlink(path_repo_2, recursive = TRUE) git2r/tests/push.R0000644000175000017500000001102614076232725013707 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create 2 directories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo) ## Create repositories bare_repo <- init(path_bare, bare = TRUE) repo <- clone(path_bare, path_repo) ## Check the repositores stopifnot(identical(is_bare(bare_repo), TRUE)) stopifnot(identical(is_bare(repo), FALSE)) ## Config repositories config(repo, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo writeLines("Hello world", con = file.path(path_repo, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message") branch_name <- branches(repo)[[1]]$name ## Check commit stopifnot(identical(commit_1$author$name, "Alice")) stopifnot(identical(commit_1$author$email, "alice@example.org")) stopifnot(identical(length(commits(repo)), 1L)) stopifnot(identical(commits(repo)[[1]]$author$name, "Alice")) stopifnot(identical(commits(repo)[[1]]$author$email, "alice@example.org")) ## Check push arguments tools::assertError(push(repo, character(0), paste0("refs/heads/", branch_name))) tools::assertError(push(repo, NA_character_, paste0("refs/heads/", branch_name))) tools::assertError(push(repo, c("origin", "origin"), paste0("refs/heads/", branch_name))) tools::assertError(push(repo, "origin")) tools::assertError(push(repo, name = "origin")) push(repo, "origin", character(0)) push(repo, "origin", NA_character_) push(repo, "origin", c(NA_character_, NA_character_)) stopifnot(identical(reflog(repo, paste0("refs/remotes/origin/", branch_name)), structure(list(), class = "git_reflog"))) ## No tracking branch assigned to master tools::assertError(push(branches(repo)[[1]])) ## Push changes from repo to origin push(repo, "origin", paste0("refs/heads/", branch_name)) r <- reflog(repo, paste0("refs/remotes/origin/", branch_name)) stopifnot(identical(length(r), 1L)) r <- r[[1]] stopifnot(identical(sha(r), sha(commit_1))) stopifnot(identical(r$message, "update by push")) stopifnot(identical(r$index, 0L)) stopifnot(identical(r$committer$name, "Alice")) stopifnot(identical(r$committer$email, "alice@example.org")) stopifnot(identical(r$refname, paste0("refs/remotes/origin/", branch_name))) stopifnot(identical(r$repo$path, repo$path)) push(branches(repo)[[1]]) ## Check result in bare repository stopifnot(identical(length(commits(bare_repo)), 1L)) bare_commit_1 <- commits(bare_repo)[[1]] stopifnot(identical(sha(commit_1), sha(bare_commit_1))) stopifnot(identical(commit_1$author, bare_commit_1$author)) stopifnot(identical(commit_1$committer, bare_commit_1$committer)) stopifnot(identical(commit_1$summary, bare_commit_1$summary)) stopifnot(identical(commit_1$message, bare_commit_1$message)) stopifnot(!identical(commit_1$repo, bare_commit_1$repo)) ## Add changes to repo and push head writeLines(c("Hello world", "HELLO WORLD"), con = file.path(path_repo, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Commit message 2") push(repo) bare_commit_2 <- lookup(bare_repo, sha(commit_2)) stopifnot(identical(sha(commit_2), sha(bare_commit_2))) stopifnot(identical(commit_2$author, bare_commit_2$author)) stopifnot(identical(commit_2$committer, bare_commit_2$committer)) stopifnot(identical(commit_2$summary, bare_commit_2$summary)) stopifnot(identical(commit_2$message, bare_commit_2$message)) stopifnot(!identical(commit_2$repo, bare_commit_2$repo)) ## Check 'set_upstream' branch_set_upstream(repository_head(repo), NULL) push(repo, "origin", paste0("refs/heads/", branch_name)) stopifnot(is.null(branch_get_upstream(repository_head(repo)))) push(repo, "origin", paste0("refs/heads/", branch_name), set_upstream = TRUE) stopifnot(!is.null(branch_get_upstream(repository_head(repo)))) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo, recursive = TRUE) git2r/tests/clone_bare.R0000644000175000017500000000436013525743542015026 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create 2 directories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo) ## Initialize a repository repo <- init(path_repo) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Add commit to repo writeLines("Hello world", con = file.path(path_repo, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message") ## Check bare argument tools::assertError(clone(path_repo, path_bare, bare = c(TRUE, TRUE))) tools::assertError(clone(path_repo, path_bare, bare = 1)) tools::assertError(clone(path_repo, path_bare, bare = 1L)) tools::assertError(clone(path_repo, path_bare, bare = "test")) ## Clone repo to bare repository bare_repo <- clone(path_repo, path_bare, bare = TRUE) ## Check the repositores stopifnot(identical(is_bare(bare_repo), TRUE)) stopifnot(identical(is_bare(repo), FALSE)) ## Check result in bare repository stopifnot(identical(length(commits(bare_repo)), 1L)) bare_commit_1 <- commits(bare_repo)[[1]] stopifnot(identical(sha(commit_1), sha(bare_commit_1))) stopifnot(identical(commit_1$author, bare_commit_1$author)) stopifnot(identical(commit_1$committer, bare_commit_1$committer)) stopifnot(identical(commit_1$summary, bare_commit_1$summary)) stopifnot(identical(commit_1$message, bare_commit_1$message)) stopifnot(!identical(commit_1$repo, bare_commit_1$repo)) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo, recursive = TRUE) git2r/tests/revparse.R0000644000175000017500000000300213525743533014554 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) ## Add and commit add(repo, "test.txt") commit_1 <- commit(repo, "First commit message") tools::assertError(revparse_single(repo, "HEAD^")) ## Add another commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Second commit message") stopifnot(identical(revparse_single(repo, "HEAD^"), commit_1)) stopifnot(is_blob(revparse_single(repo, "HEAD:test.txt"))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/checkout_tag.R0000644000175000017500000000366214076204114015366 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create first commit writeLines("Hello world!", file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit_1 <- commit(repo, "First commit message") tag_1 <- tag(repo, "Tag1", "First tag message") ## Create and checkout dev branch in repo checkout(repo, "dev", create = TRUE) ## Create second commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-2.txt")) add(repo, "test-2.txt") commit_2 <- commit(repo, "Second commit message") tag_2 <- tag(repo, "Tag2", "Second tag message") ## Check files stopifnot(identical(list.files(path), c("test-1.txt", "test-2.txt"))) ## Checkout tag_1 and check files checkout(tag_1) stopifnot(identical(list.files(path), "test-1.txt")) ## Checkout tag_2 and check files checkout(tag_2) stopifnot(identical(list.files(path), c("test-1.txt", "test-2.txt"))) ## Checkout "Tag1" and check files checkout(repo, "Tag1") stopifnot(identical(list.files(path), "test-1.txt")) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/commits_path.R0000644000175000017500000002237614076204367015432 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path, branch = "main") config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create two files and alternate commits writeLines("1", file.path(path, "odd.txt")) add(repo, "odd.txt") c1 <- commit(repo, "commit 1") writeLines("2", file.path(path, "even.txt")) add(repo, "even.txt") c2 <- commit(repo, "commit 2") writeLines("3", file.path(path, "odd.txt")) add(repo, "odd.txt") c3 <- commit(repo, "commit 3") writeLines("4", file.path(path, "even.txt")) add(repo, "even.txt") c4 <- commit(repo, "commit 4") writeLines("5", file.path(path, "odd.txt")) add(repo, "odd.txt") c5 <- commit(repo, "commit 5") writeLines("6", file.path(path, "even.txt")) add(repo, "even.txt") c6 <- commit(repo, "commit 6") commits_all <- commits(repo) stopifnot(length(commits_all) == 6) ## Test path commits_odd <- commits(repo, path = "odd.txt") stopifnot(length(commits_odd) == 3) stopifnot(commits_odd[[1]]$sha == c5$sha) stopifnot(commits_odd[[2]]$sha == c3$sha) stopifnot(commits_odd[[3]]$sha == c1$sha) commits_even <- commits(repo, path = "even.txt") stopifnot(length(commits_even) == 3) stopifnot(commits_even[[1]]$sha == c6$sha) stopifnot(commits_even[[2]]$sha == c4$sha) stopifnot(commits_even[[3]]$sha == c2$sha) ## Test reverse commits_odd_rev <- commits(repo, reverse = TRUE, path = "odd.txt") stopifnot(length(commits_odd_rev) == 3) stopifnot(commits_odd_rev[[1]]$sha == c1$sha) stopifnot(commits_odd_rev[[2]]$sha == c3$sha) stopifnot(commits_odd_rev[[3]]$sha == c5$sha) commits_even_rev <- commits(repo, reverse = TRUE, path = "even.txt") stopifnot(length(commits_even_rev) == 3) stopifnot(commits_even_rev[[1]]$sha == c2$sha) stopifnot(commits_even_rev[[2]]$sha == c4$sha) stopifnot(commits_even_rev[[3]]$sha == c6$sha) ## Test n commits_odd_n <- commits(repo, n = 2, path = "odd.txt") stopifnot(length(commits_odd_n) == 2) stopifnot(commits_odd_n[[1]]$sha == c5$sha) stopifnot(commits_odd_n[[2]]$sha == c3$sha) commits_even_n <- commits(repo, n = 2, path = "even.txt") stopifnot(length(commits_even_n) == 2) stopifnot(commits_even_n[[1]]$sha == c6$sha) stopifnot(commits_even_n[[2]]$sha == c4$sha) commits_odd_0 <- commits(repo, n = 0, path = "odd.txt") stopifnot(length(commits_odd_0) == 0) stopifnot(identical(commits_odd_0, list())) commits_even_0 <- commits(repo, n = 0, path = "even.txt") stopifnot(length(commits_even_0) == 0) stopifnot(identical(commits_even_0, list())) ## Test ref checkout(repo, branch = "test-ref", create = TRUE) writeLines("7", file.path(path, "odd.txt")) add(repo, "odd.txt") c7 <- commit(repo, "commit 7") writeLines("8", file.path(path, "even.txt")) add(repo, "even.txt") c8 <- commit(repo, "commit 8") commits_odd_ref <- commits(repo, ref = "main", path = "odd.txt") stopifnot(length(commits_odd_ref) == 3) stopifnot(commits_odd_ref[[1]]$sha == c5$sha) stopifnot(commits_odd_ref[[2]]$sha == c3$sha) stopifnot(commits_odd_ref[[3]]$sha == c1$sha) commits_even_ref <- commits(repo, ref = "main", path = "even.txt") stopifnot(length(commits_even_ref) == 3) stopifnot(commits_even_ref[[1]]$sha == c6$sha) stopifnot(commits_even_ref[[2]]$sha == c4$sha) stopifnot(commits_even_ref[[3]]$sha == c2$sha) checkout(repo, branch = "main") ## Test renaming a file (path does not support --follow) writeLines("a file to be renamed", file.path(path, "original.txt")) add(repo, "original.txt") c_original <- commit(repo, "commit original") commits_original <- commits(repo, path = "original.txt") stopifnot(length(commits_original) == 1) stopifnot(commits_original[[1]]$sha == c_original$sha) file.rename(file.path(path, "original.txt"), file.path(path, "new.txt")) add(repo, c("original.txt", "new.txt")) c_new <- commit(repo, "commit new") commits_new <- commits(repo, path = "new.txt") stopifnot(length(commits_new) == 1) stopifnot(commits_new[[1]]$sha == c_new$sha) ## Test merge commits writeLines(letters[1:5], file.path(path, "merge.txt")) add(repo, "merge.txt") c_merge_1 <- commit(repo, "commit merge 1") checkout(repo, branch = "test-merge", create = TRUE) cat("z", file = file.path(path, "merge.txt"), append = TRUE) add(repo, "merge.txt") c_merge_2 <- commit(repo, "commit merge 2") checkout(repo, branch = "main") writeLines(c("A", letters[2:5]), file.path(path, "merge.txt")) add(repo, "merge.txt") c_merge_3 <- commit(repo, "commit merge 3") c_merge_4 <- merge(repo, "test-merge") stopifnot(class(c_merge_4) == "git_merge_result") commits_merge <- commits(repo, path = "merge.txt") stopifnot(length(commits_merge) == 4) stopifnot(commits_merge[[1]]$sha == c_merge_4$sha) stopifnot(commits_merge[[2]]$sha == c_merge_3$sha) stopifnot(commits_merge[[3]]$sha == c_merge_2$sha) stopifnot(commits_merge[[4]]$sha == c_merge_1$sha) ## Test absolute path writeLines("absolute", file.path(path, "abs.txt")) add(repo, "abs.txt") c_abs <- commit(repo, "commit absolute") commits_abs <- commits(repo, path = file.path(path, "abs.txt")) stopifnot(length(commits_abs) == 1) stopifnot(commits_abs[[1]]$sha == c_abs$sha) ## Test topological and time ## Strategy: ## - Commit a new file test-time.txt ## - Commit a change on branch test-time-1 (a) ## - Commit a change on branch test-time-2 (c) ## - Commit a change on branch test-time-1 (b) ## - Commit a change on branch test-time-2 (d) ## - Merge branch test-time-2 into main (fast-forward) ## - Merge branch test-time-1 into main (merge commit) ## ## $ git log --all --decorate --oneline --graph -n 6 ## * 79e6880 (HEAD -> main) merge test-time-1 ## |\ ## | * e2f18f1 (test-time-1) commit b ## | * 5f34820 commit a ## * | b954ec9 (test-time-2) commit d ## * | 7ae2fd5 commit c ## |/ ## * 923f3ea commit base Sys.sleep(1) writeLines(as.character(1:100), file.path(path, "test-time.txt")) add(repo, "test-time.txt") c_base <- commit(repo, "commit base") Sys.sleep(1) branch_create(commit = c_base, name = "test-time-1") branch_create(commit = c_base, name = "test-time-2") checkout(repo, branch = "test-time-1") writeLines(c("edit", 2:100), file.path(path, "test-time.txt")) add(repo, "test-time.txt") c_a <- commit(repo, "commit a") Sys.sleep(1) checkout(repo, branch = "test-time-2") writeLines(c(1:25, "edit", 27:100), file.path(path, "test-time.txt")) add(repo, "test-time.txt") c_c <- commit(repo, "commit c") Sys.sleep(1) checkout(repo, branch = "test-time-1") writeLines(c(1:50, "edit", 52:100), file.path(path, "test-time.txt")) add(repo, "test-time.txt") c_b <- commit(repo, "commit b") Sys.sleep(1) checkout(repo, branch = "test-time-2") writeLines(c(1:75, "edit", 77:100), file.path(path, "test-time.txt")) add(repo, "test-time.txt") c_d <- commit(repo, "commit d") Sys.sleep(1) checkout(repo, branch = "main") merge(repo, "test-time-2") # Fast-forward merge(repo, "test-time-1") # Merge commit c_merge_time <- commits(repo, n = 1)[[1]] ## topological - commits in test-time-2 come first because it was ## merged first stopifnot(identical( commits(repo, topological = TRUE, time = FALSE, path = "test-time.txt"), list(c_merge_time, c_b, c_a, c_d, c_c, c_base) )) stopifnot(identical( commits(repo, topological = TRUE, time = FALSE, path = "test-time.txt"), commits(repo, topological = TRUE, time = FALSE)[1:6] )) ## time - commits ordered by time they were created, not merged into ## main stopifnot(identical( commits(repo, topological = FALSE, time = TRUE, path = "test-time.txt"), list(c_merge_time, c_d, c_b, c_c, c_a, c_base) )) stopifnot(identical( commits(repo, topological = FALSE, time = TRUE, path = "test-time.txt"), commits(repo, topological = FALSE, time = TRUE)[1:6] )) ## topological and time - dominated by time stopifnot(identical( commits(repo, topological = TRUE, time = TRUE, path = "test-time.txt"), list(c_merge_time, c_d, c_b, c_c, c_a, c_base) )) stopifnot(identical( commits(repo, topological = TRUE, time = TRUE, path = "test-time.txt"), commits(repo, topological = TRUE, time = TRUE)[1:6] )) ## reverse with topological and/or time stopifnot(identical( commits(repo, topological = TRUE, time = FALSE, reverse = TRUE, path = "test-time.txt"), rev(list(c_merge_time, c_b, c_a, c_d, c_c, c_base)) )) stopifnot(identical( commits(repo, topological = FALSE, time = TRUE, reverse = TRUE, path = "test-time.txt"), rev(list(c_merge_time, c_d, c_b, c_c, c_a, c_base)) )) stopifnot(identical( commits(repo, topological = TRUE, time = TRUE, reverse = TRUE, path = "test-time.txt"), rev(list(c_merge_time, c_d, c_b, c_c, c_a, c_base)) )) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/libgit2.R0000644000175000017500000000245413525743536014276 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() stopifnot(identical(names(libgit2_features()), c("threads", "https", "ssh"))) stopifnot(identical(names(libgit2_version()), c("major", "minor", "rev"))) tools::assertError(ssl_cert_locations()) if (identical(Sys.getenv("R_COVR"), "true")) { if (isTRUE(libgit2_features()$https)) { ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) stopifnot(is.null(ssl_cert_locations(path = path))) unlink(path) } } git2r/tests/signature.R0000644000175000017500000000222713415432334014726 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Check printing of a class git_signature when <- structure(list(time = 1395567947, offset = 60), class = "git_time") signature <- structure(list(name = "Alice", email = "alice@example.org", when = when), class = "git_signature") stopifnot(identical(print(signature), signature)) git2r/tests/reference.R0000644000175000017500000000325014076203176014664 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path, branch = "main") config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) ## add and commit add(repo, "test.txt") commit(repo, "Commit message") ## Check dwim of reference shorthand stopifnot(identical(.Call(git2r:::git2r_reference_dwim, repo, "")$name, "refs/heads/main")) stopifnot(identical(.Call(git2r:::git2r_reference_dwim, repo, "main")$name, "refs/heads/main")) stopifnot(identical( .Call(git2r:::git2r_reference_dwim, repo, "refs/heads/main")$name, "refs/heads/main")) ## print reference r <- .Call(git2r:::git2r_reference_dwim, repo, "refs/heads/main") stopifnot(identical(print(r), r)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/merge_named_branch.R0000644000175000017500000000761514076226150016514 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path, branch = "main") config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message 1") ## Create first branch, checkout, add file and commit checkout(repo, "branch1", create = TRUE) writeLines("Branch 1", file.path(path, "branch-1.txt")) add(repo, "branch-1.txt") commit_2 <- commit(repo, "Commit message branch 1") ## Create second branch, checkout, add file and commit b_2 <- branch_create(commit_1, "branch2") checkout(b_2) writeLines("Branch 2", file.path(path, "branch-2.txt")) add(repo, "branch-2.txt") commit_3 <- commit(repo, "Commit message branch 2") writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit_4 <- commit(repo, "Second commit message branch 2") ## Check that merge base equals commit_1 stopifnot(identical(merge_base(commit_2, commit_3), commit_1)) ## Checkout main b <- branches(repo) checkout(b[sapply(b, "[", "name") == "main"][[1]], force = TRUE) ## Merge branch 1 m_1 <- merge(repo, "branch1") stopifnot(identical(m_1$fast_forward, TRUE)) stopifnot(identical(m_1$conflicts, FALSE)) stopifnot(identical(sha(m_1), NA_character_)) ## Merge branch 2 m_2 <- merge(path, "branch2") stopifnot(identical(m_2$fast_forward, FALSE)) stopifnot(identical(m_2$conflicts, FALSE)) stopifnot(identical(sha(m_2), sha(commits(repo)[[1]]))) ## Create third branch, checkout, change file and commit checkout(repo, "branch3", create = TRUE) writeLines(c("Lorem ipsum dolor amet sit, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message branch 3") ## Checkout main and create a change that creates a merge conflict checkout(repo, "main", force = TRUE) writeLines(c("Lorem ipsum dolor sit amet, adipisicing consectetur elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Some commit message branch 1") ## Merge branch 3 m_3 <- merge(repo, "branch3") stopifnot(identical(m_3$up_to_date, FALSE)) stopifnot(identical(m_3$fast_forward, FALSE)) stopifnot(identical(m_3$conflicts, TRUE)) stopifnot(identical(sha(m_3), NA_character_)) ## Check status; Expect to have one unstaged unmerged conflict. stopifnot(identical(status(repo), structure(list(staged = empty_named_list(), unstaged = list(conflicted = "test.txt"), untracked = empty_named_list()), class = "git_status"))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/fast_forward_merge.R0000644000175000017500000000425214076204436016571 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create directory for repository in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Create repository repo <- init(path, branch = "main") config(repo, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit_1 <- commit(repo, "First commit message") ## Create branch and checkout checkout(branch_create(commit_1, name = "test")) ## Add changes to test branch writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit_2 <- commit(repo, "Second commit message") # Checkout main and merge b <- branches(repo) checkout(b[sapply(b, "[", "name") == "main"][[1]], force = TRUE) m <- merge(b[sapply(b, "[", "name") == "test"][[1]]) # Check merge stopifnot(inherits(m, "git_merge_result")) stopifnot(identical(m$up_to_date, FALSE)) stopifnot(identical(m$fast_forward, TRUE)) stopifnot(identical(m$conflicts, FALSE)) stopifnot(identical(sha(m), NA_character_)) stopifnot(identical(length(commits(repo)), 2L)) # Check reflog r <- reflog(repo) stopifnot(identical(r[[1]]$message, "merge test: Fast-forward")) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/stash.R0000644000175000017500000000643213526015244014051 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test-1.txt")) ## add and commit add(repo, "test-1.txt") commit(repo, "Commit message") ## Pop stash writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-1.txt")) stash(repo) stopifnot(identical("Hello world!", readLines(file.path(path, "test-1.txt")))) stash_pop(repo) stopifnot(identical(c("Hello world!", "HELLO WORLD!"), readLines(file.path(path, "test-1.txt")))) ## Make one more commit add(repo, "test-1.txt") commit(repo, "Next commit message") ## Check that there are no stashes stopifnot(identical(stash_list(repo), list())) ## Apply stash writeLines(c("Hello world!", "HELLO WORLD!", "hello world!"), file.path(path, "test-1.txt")) stash(repo) stopifnot(identical(c("Hello world!", "HELLO WORLD!"), readLines(file.path(path, "test-1.txt")))) stash_apply(repo) stopifnot(identical(c("Hello world!", "HELLO WORLD!", "hello world!"), readLines(file.path(path, "test-1.txt")))) stopifnot(identical(length(stash_list(repo)), 1L)) stash_drop(repo, 1) stopifnot(identical(stash_list(repo), list())) ## Make one more commit add(repo, "test-1.txt") commit(repo, "Apply stash commit message") ## Create one more file writeLines("Hello world!", file.path(path, "test-2.txt")) ## Check that there are no stashes stopifnot(identical(stash_list(repo), list())) ## Stash stash(repo) stopifnot(identical(stash_list(repo), list())) s <- stash(repo, untracked = TRUE) stopifnot(identical(print(s), s)) summary(s) stopifnot(identical(length(stash_list(repo)), 1L)) tree(stash_list(repo)[[1]]) ## Drop stash stash_drop(repo, 1) stopifnot(identical(stash_list(repo), list())) ## Check stash_drop argument tools::assertError(stash_drop(repo)) tools::assertError(stash_drop(repo, -1)) tools::assertError(stash_drop(repo, 0.5)) ## Create one more file writeLines("Hello world!", file.path(path, "test-3.txt")) ## Create stash in repository stash(repo, untracked = TRUE) stopifnot(identical(length(stash_list(repo)), 1L)) ## Check stash_list method with missing repo argument wd <- setwd(path) stopifnot(identical(length(stash_list()), 1L)) if (!is.null(wd)) setwd(wd) ## Drop git_stash object in repository stash_drop(stash_list(repo)[[1]]) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/clone_checkout.R0000644000175000017500000000460313526015642015714 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create 2 directories in tempdir path_src <- tempfile(pattern = "git2r-") path_tgt <- tempfile(pattern = "git2r-") dir.create(path_tgt) dir.create(path_src) ## Initialize a repository repo_src <- init(path_src) config(repo_src, user.name = "Alice", user.email = "alice@example.org") ## Add commit to repo filename <- "test.txt" writeLines("Hello world", con = file.path(path_src, filename)) add(repo_src, "test.txt") commit_src <- commit(repo_src, "Commit message") ## Check checkout argument tools::assertError(clone(path_src, path_tgt, checkout = c(FALSE, TRUE))) tools::assertError(clone(path_src, path_tgt, checkout = 1)) tools::assertError(clone(path_src, path_tgt, checkout = 1L)) tools::assertError(clone(path_src, path_tgt, checkout = "test")) ## Clone source to target repository without checking out any files repo_tgt <- clone(path_src, path_tgt, checkout = FALSE) ## List files in the repositores stopifnot(identical(list.files(path_src), filename)) stopifnot(identical(list.files(path_tgt), character(0))) ## Compare commits stopifnot(identical(length(commits(repo_tgt)), 1L)) commit_tgt <- last_commit(repo_tgt) stopifnot(identical(sha(last_commit(path_tgt)), sha(commit_tgt))) stopifnot(identical(sha(commit_src), sha(commit_tgt))) stopifnot(identical(commit_src$author, commit_tgt$author)) stopifnot(identical(commit_src$committer, commit_tgt$committer)) stopifnot(identical(commit_src$summary, commit_tgt$summary)) stopifnot(identical(commit_src$message, commit_tgt$message)) stopifnot(!identical(commit_src$repo, commit_tgt$repo)) ## Cleanup unlink(path_tgt, recursive = TRUE) unlink(path_src, recursive = TRUE) git2r/tests/refspec.R0000644000175000017500000000366113526016314014356 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create directories for repositories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo) ## Create bare repository bare_repo <- init(path_bare, bare = TRUE) ## Clone to repo repo <- clone(path_bare, path_repo) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path_repo, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "First commit message") ## Add more changes to repo writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path_repo, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Second commit message") ## Check remote stopifnot(identical( git2r:::get_refspec(repo, spec = "master")$remote, "origin")) ## Detach checkout(commit_1) tools::assertError(git2r:::get_refspec(repo)) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo, recursive = TRUE) git2r/tests/remove.R0000644000175000017500000000266313526006350014224 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Add files invisible(lapply(file.path(path, letters[1:4]), writeLines, text = "")) add(repo, letters) commit(repo, "init") ## Remove one file rm_file(repo, letters[1]) commit(repo, "remove") ## Remove two files. Don't raise warnings withCallingHandlers(rm_file(repo, letters[2:3]), warning = function(w) stop(w)) ## Remove one file using the absolute path to the file. rm_file(repo, file.path(path, letters[4])) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/tag.R0000644000175000017500000000541113526004772013502 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) ## add and commit add(repo, "test.txt") commit(repo, "Commit message") ## Check tags, no tag added stopifnot(identical(tags(repo), empty_named_list())) ## Create tag new_tag <- tag(repo, "Tagname", "Tag message") stopifnot(identical(print(new_tag), new_tag)) summary(new_tag) ## Check tag stopifnot(identical(lookup(repo, sha(new_tag)), new_tag)) stopifnot(identical(new_tag$name, "Tagname")) stopifnot(identical(new_tag$message, "Tag message")) stopifnot(identical(new_tag$tagger$name, "Alice")) stopifnot(identical(new_tag$tagger$email, "alice@example.org")) stopifnot(identical(length(tags(repo)), 1L)) stopifnot(identical(tags(repo)[[1]]$name, "Tagname")) stopifnot(identical(tags(repo)[[1]]$message, "Tag message")) stopifnot(identical(tags(repo)[[1]]$tagger$name, "Alice")) stopifnot(identical(tags(repo)[[1]]$tagger$email, "alice@example.org")) ## Check objects in object database stopifnot(identical(table(odb_objects(repo)$type), structure(c(1L, 1L, 1L, 1L), .Dim = 4L, .Dimnames = structure(list( c("blob", "commit", "tag", "tree")), .Names = ""), class = "table"))) ## Delete tag tag_delete(new_tag) stopifnot(identical(length(tags(repo)), 0L)) ## Create tag with session info tag(repo, "Tagname", "Tag message", session = TRUE) stopifnot(grep("git2r", tags(repo)[[1]]$message) > 0) ## Check tags method with default repo argument wd <- setwd(path) stopifnot(identical(length(tags()), 1L)) tag_delete(name = "Tagname") stopifnot(identical(length(tags()), 0L)) if (!is.null(wd)) setwd(wd) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/reset.R0000644000175000017500000001025413526005144014044 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test-1.txt")) ## Add and reset an empty repository using a path add(repo, "test-1.txt") stopifnot(identical( status(repo), structure(list(staged = list(new = "test-1.txt"), unstaged = empty_named_list(), untracked = empty_named_list()), class = "git_status"))) reset(repo, path = "test-1.txt") stopifnot(identical( status(repo), structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = list(untracked = "test-1.txt")), class = "git_status"))) ## Add and reset a non-empty repository using a path add(repo, "test-1.txt") commit(repo, "First commit") writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-1.txt")) add(repo, "test-1.txt") stopifnot(identical( status(repo), structure(list(staged = list(modified = "test-1.txt"), unstaged = empty_named_list(), untracked = empty_named_list()), class = "git_status"))) reset(repo, path = "test-1.txt") stopifnot(identical( status(repo), structure(list(staged = empty_named_list(), unstaged = list(modified = "test-1.txt"), untracked = empty_named_list()), class = "git_status"))) ## add and commit add(repo, "test-1.txt") commit_1 <- commit(repo, "Commit message") ## Make one more commit writeLines(c("Hello world!", "HELLO WORLD!", "hello world!"), file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit(repo, "Next commit message") ## Create one more file writeLines("Hello world!", file.path(path, "test-2.txt")) ## 'soft' reset to first commit reset(commit_1) soft_exp <- structure(list(staged = list(modified = "test-1.txt"), unstaged = empty_named_list(), untracked = list(untracked = "test-2.txt")), class = "git_status") soft_obs <- status(repo) stopifnot(identical(soft_obs, soft_exp)) stopifnot(identical(length(commits(repo)), 2L)) stopifnot(identical(commits(repo)[[1]], commit_1)) ## 'mixed' reset to first commit commit(repo, "Next commit message") reset(commit_1, "mixed") mixed_exp <- structure(list(staged = empty_named_list(), unstaged = list(modified = "test-1.txt"), untracked = list(untracked = "test-2.txt")), class = "git_status") mixed_obs <- status(repo) stopifnot(identical(mixed_obs, mixed_exp)) stopifnot(identical(length(commits(repo)), 2L)) stopifnot(identical(commits(repo)[[1]], commit_1)) ## 'hard' reset to first commit add(repo, "test-1.txt") commit(repo, "Next commit message") reset(commit_1, "hard") hard_exp <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = list(untracked = "test-2.txt")), class = "git_status") hard_obs <- status(repo) stopifnot(identical(hard_obs, hard_exp)) stopifnot(identical(length(commits(repo)), 2L)) stopifnot(identical(commits(repo)[[1]], commit_1)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/diff.R0000644000175000017500000001501313765412241013635 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add, commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message") ## Change the file, diff between index and workdir writeLines("Hello again!\nHere is a second line\nAnd a third", file.path(path, "test.txt")) diff_1 <- diff(repo) diff(repo, as_char = TRUE) diff(repo, as_char = TRUE, filename = file.path(path, "test.diff")) stopifnot(identical(diff_1$old, "index")) stopifnot(identical(diff_1$new, "workdir")) stopifnot(identical(length(diff_1$files), 1L)) stopifnot(identical(diff_1$files[[1]]$old_file, "test.txt")) stopifnot(identical(diff_1$files[[1]]$new_file, "test.txt")) stopifnot(identical(length(diff_1$files[[1]]$hunks), 1L)) stopifnot(identical(length(diff_1$files[[1]]$hunks[[1]]$lines), 4L)) ## TODO: check actual diff ## Diff between index and HEAD is empty diff_2 <- diff(repo, index = TRUE) diff(repo, as_char = TRUE) diff(repo, as_char = TRUE, filename = file.path(path, "test.diff")) stopifnot(identical(diff_2$old, "HEAD")) stopifnot(identical(diff_2$new, "index")) stopifnot(identical(diff_2$files, list())) ## Diff between tree and working dir, same as diff_1 diff_3 <- diff(tree(commits(repo)[[1]])) diff(repo, as_char = TRUE) diff(repo, as_char = TRUE, filename = file.path(path, "test.diff")) stopifnot(identical(diff_3$old, tree(commits(repo)[[1]]))) stopifnot(identical(diff_3$new, "workdir")) stopifnot(identical(diff_3$files, diff_1$files)) stopifnot(identical(print(diff_3), diff_3)) ## Add changes, diff between index and HEAD is the same as diff_1 add(repo, "test.txt") diff_4 <- diff(repo, index = TRUE) diff(repo, as_char = TRUE) diff(repo, as_char = TRUE, filename = file.path(path, "test.diff")) stopifnot(identical(diff_4$old, "HEAD")) stopifnot(identical(diff_4$new, "index")) stopifnot(identical(diff_4$files, diff_1$files)) ## Diff between tree and index diff_5 <- diff(tree(commits(repo)[[1]]), index = TRUE) diff(repo, as_char = TRUE) diff(repo, as_char = TRUE, filename = file.path(path, "test.diff")) stopifnot(identical(diff_5$old, tree(commits(repo)[[1]]))) stopifnot(identical(diff_5$new, "index")) stopifnot(identical(diff_5$files, diff_1$files)) ## Diff between two trees commit(repo, "Second commit") tree_1 <- tree(commits(repo)[[2]]) tree_2 <- tree(commits(repo)[[1]]) diff_6 <- diff(tree_1, tree_2) diff(repo, as_char = TRUE) diff(repo, as_char = TRUE, filename = file.path(path, "test.diff")) stopifnot(identical(diff_6$old, tree_1)) stopifnot(identical(diff_6$new, tree_2)) stopifnot(identical(diff_6$files, diff_1$files)) ## Length of a diff stopifnot(identical(length(diff_1), 1L)) stopifnot(identical(length(diff_2), 0L)) stopifnot(identical(length(diff_3), 1L)) stopifnot(identical(length(diff_4), 1L)) stopifnot(identical(length(diff_5), 1L)) stopifnot(identical(length(diff_6), 1L)) ## Binary files set.seed(42) writeBin(as.raw((sample(0:255, 1000, replace = TRUE))), con = file.path(path, "test.bin")) add(repo, "test.bin") diff_7 <- diff(repo, index = TRUE) diff(repo, as_char = TRUE) diff(repo, as_char = TRUE, filename = file.path(path, "test.diff")) stopifnot(any(grepl("binary file", capture.output(summary(diff_7))))) ## TODO: errors ## Check non-logical index argument res <- tools::assertError( .Call(git2r:::git2r_diff, NULL, NULL, NULL, "FALSE", NULL, 3L, 0L, "a", "b", NULL, NULL, NULL)) stopifnot(length(grep(paste0("Error in 'git2r_diff': 'index' must be logical ", "vector of length one with non NA value\n"), res[[1]]$message)) > 0) ## Check various combinations of diff arguments res <- tools::assertError( .Call(git2r:::git2r_diff, NULL, NULL, tree(commits(repo)[[1]]), FALSE, NULL, 3L, 0L, "a", "b", NULL, NULL, NULL)) stopifnot(length(grep("Error in 'git2r_diff': Invalid diff parameters", res[[1]]$message)) > 0) res <- tools::assertError( .Call(git2r:::git2r_diff, NULL, NULL, tree(commits(repo)[[1]]), TRUE, NULL, 3L, 0L, "a", "b", NULL, NULL, NULL)) stopifnot(length(grep("Error in 'git2r_diff': Invalid diff parameters", res[[1]]$message)) > 0) res <- tools::assertError( .Call(git2r:::git2r_diff, repo, tree(commits(repo)[[1]]), NULL, FALSE, NULL, 3L, 0L, "a", "b", NULL, NULL, NULL)) stopifnot(length(grep("Error in 'git2r_diff': Invalid diff parameters", res[[1]]$message)) > 0) res <- tools::assertError( .Call(git2r:::git2r_diff, repo, tree(commits(repo)[[1]]), NULL, TRUE, NULL, 3L, 0L, "a", "b", NULL, NULL, NULL)) stopifnot(length(grep("Error in 'git2r_diff': Invalid diff parameters", res[[1]]$message)) > 0) res <- tools::assertError( .Call(git2r:::git2r_diff, repo, tree(commits(repo)[[1]]), tree(commits(repo)[[2]]), FALSE, NULL, 3L, 0L, "a", "b", NULL, NULL, NULL)) stopifnot(length(grep("Error in 'git2r_diff': Invalid diff parameters", res[[1]]$message)) > 0) res <- tools::assertError( .Call(git2r:::git2r_diff, repo, tree(commits(repo)[[1]]), tree(commits(repo)[[2]]), TRUE, NULL, 3L, 0L, "a", "b", NULL, NULL, NULL)) stopifnot(length(grep("Error in 'git2r_diff': Invalid diff parameters", res[[1]]$message)) > 0) ## TODO: printing ## Cleanup unlink(path, recursive = TRUE) git2r/tests/bundle.R0000644000175000017500000000355713526015521014203 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(file.path(path, "bundle", "R"), recursive = TRUE) ## Initialize a repository repo <- init(file.path(path, "bundle")) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a DESCRIPTION file writeLines(c( "package: bundle", "Title: Bundle Git Repository", "Description: Bundle a bare repository of the code in the 'inst' folder.", "Version: 0.1", "License: GPL-2", "Authors@R: person('Alice', role = c('aut', 'cre'),", " email = 'alice@example.org')"), con = file.path(path, "bundle", "DESCRIPTION")) add(repo, file.path(path, "bundle", "DESCRIPTION")) commit(repo, "Add DESCRIPTION file") ## Create R file writeLines("f <- function(x, y) x+y", con = file.path(path, "bundle", "R", "bundle.R")) add(repo, file.path(path, "bundle", "R", "bundle.R")) commit(repo, "Add R file") ## Bundle package bundle_r_package(repo) ## Fails if bundled package exists tools::assertError(bundle_r_package(repo)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/when.R0000644000175000017500000000543113565056621013675 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Check when method w1 <- structure(list(time = 1395567947, offset = 60), class = "git_time") stopifnot(identical(when(w1), "2014-03-23 09:45:47 GMT")) stopifnot(identical(when(w1, usetz = FALSE), "2014-03-23 09:45:47")) stopifnot(identical(when(w1, tz = "Europe/Stockholm", origin = "1980-02-02"), "2024-04-23 11:45:47 CEST")) s1 <- structure(list(name = "Alice", email = "alice@example.org", when = w1), class = "git_signature") stopifnot(identical(when(s1), "2014-03-23 09:45:47 GMT")) stopifnot(identical(when(s1, usetz = FALSE), "2014-03-23 09:45:47")) stopifnot(identical(when(s1, tz = "Europe/Stockholm", origin = "1980-02-02"), "2024-04-23 11:45:47 CEST")) w2 <- structure(list(time = 1395567950, offset = 60), class = "git_time") s2 <- structure(list(name = "Alice", email = "alice@example.org", when = w2), class = "git_signature") c1 <- structure(list(sha = "166f3f779fd7e4165aaa43f2828050ce040052b0", author = s1, committer = s2, summary = "A commit summary", message = "A commit message"), class = "git_commit") stopifnot(identical(when(c1), "2014-03-23 09:45:47 GMT")) stopifnot(identical(when(c1, usetz = FALSE), "2014-03-23 09:45:47")) stopifnot(identical(when(c1, tz = "Europe/Stockholm", origin = "1980-02-02"), "2024-04-23 11:45:47 CEST")) t1 <- structure(list(sha = "166f3f779fd7e4165aaa43f2828050ce040052b0", message = "A tag message", name = "A tage name", tagger = s1, target = "166f3f779fd7e4165aaa43f2828050ce040052b0"), class = "git_tag") stopifnot(identical(when(t1), "2014-03-23 09:45:47 GMT")) stopifnot(identical(when(t1, usetz = FALSE), "2014-03-23 09:45:47")) stopifnot(identical(when(t1, tz = "Europe/Stockholm", origin = "1980-02-02"), "2024-04-23 11:45:47 CEST")) git2r/tests/bare_repository.R0000644000175000017500000000241513526015405016133 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a bare repository repo <- init(path, bare = TRUE) ## Check that the state of the repository stopifnot(identical(is_bare(repo), TRUE)) stopifnot(identical(is_empty(repo), TRUE)) ## Check that workdir is NULL for a bare repository stopifnot(is.null(workdir(repo))) ## Check with missing repo argument setwd(path) stopifnot(identical(is_bare(), TRUE)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/normal_merge.R0000644000175000017500000000526214076230727015404 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Initialize a temporary repository path <- tempfile(pattern = "git2r-") dir.create(path) repo <- init(path, branch = "main") ## Create a user and commit a file config(repo, user.name = "Author", user.email = "author@example.org") writeLines(c("First line in file 1.", "Second line in file 1."), file.path(path, "example-1.txt")) add(repo, "example-1.txt") commit(repo, "First commit message") ## Create and add one more file writeLines(c("First line in file 2.", "Second line in file 2."), file.path(path, "example-2.txt")) add(repo, "example-2.txt") commit(repo, "Second commit message") ## Create a new branch 'fix' checkout(repo, "fix", create = TRUE) ## Update 'example-1.txt' (swap words in first line) and commit writeLines(c("line First in file 1.", "Second line in file 1."), file.path(path, "example-1.txt")) add(repo, "example-1.txt") commit(repo, "Third commit message") checkout(repo, "main") ## Update 'example-2.txt' (swap words in second line) and commit writeLines(c("First line in file 2.", "line Second in file 2."), file.path(path, "example-2.txt")) add(repo, "example-2.txt") commit(repo, "Fourth commit message") # Missing branch to merge with should throw an error tools::assertError(merge(repo)) ## Merge 'fix' m <- merge(repo, "fix", TRUE, default_signature(repo)) stopifnot(identical(format(m), "Merge")) ## Merge 'fix' again m <- merge(repo, "fix", TRUE, default_signature(repo)) stopifnot(identical(format(m), "Already up-to-date")) ## Check number of parents of each commit stopifnot(identical(sapply(commits(repo), function(x) length(parents(x))), c(2L, 1L, 1L, 1L, 0L))) ## Check that last commit is a merge stopifnot(is_merge(last_commit(repo))) summary(last_commit(repo)) ## Check that metadata associated with merge is removed stopifnot(!file.exists(file.path(path, ".git", "MERGE_HEAD"))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/merge.R0000644000175000017500000001172514076226412014031 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path, branch = "main") config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message 1") ## Create first branch, checkout, add file and commit b_1 <- branch_create(commit_1, "branch1") checkout(b_1) writeLines("Branch 1", file.path(path, "branch-1.txt")) add(repo, "branch-1.txt") commit_2 <- commit(repo, "Commit message branch 1") ## Create second branch, checkout, add file and commit b_2 <- branch_create(commit_1, "branch2") checkout(b_2) writeLines("Branch 2", file.path(path, "branch-2.txt")) add(repo, "branch-2.txt") commit_3 <- commit(repo, "Commit message branch 2") writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit_4 <- commit(repo, "Second commit message branch 2") ## Check that merge base equals commit_1 stopifnot(identical(merge_base(commit_2, commit_3), commit_1)) ## Checkout main b <- branches(repo) checkout(b[sapply(b, "[", "name") == "main"][[1]], force = TRUE) ## Merge branch 1 m_1 <- merge(b[sapply(b, "[", "name") == "branch1"][[1]]) stopifnot(identical(m_1$fast_forward, TRUE)) stopifnot(identical(m_1$conflicts, FALSE)) stopifnot(identical(sha(m_1), NA_character_)) stopifnot(identical(print(m_1), m_1)) ## Merge branch 1 again m_1_again <- merge(b[sapply(b, "[", "name") == "branch1"][[1]]) stopifnot(identical(m_1_again$up_to_date, TRUE)) stopifnot(identical(m_1_again$fast_forward, FALSE)) stopifnot(identical(m_1_again$conflicts, FALSE)) stopifnot(identical(sha(m_1_again), NA_character_)) ## Merge branch 2 m_2 <- merge(b[sapply(b, "[", "name") == "branch2"][[1]]) stopifnot(identical(m_2$fast_forward, FALSE)) stopifnot(identical(m_2$conflicts, FALSE)) stopifnot(identical(sha(m_2), sha(commits(repo)[[1]]))) ## Create third branch, checkout, change file and commit b_3 <- branch_create(lookup(repo, sha(m_2)), "branch3") checkout(b_3) writeLines(c("Lorem ipsum dolor amet sit, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message branch 3") ## Checkout main and create a change that creates a conflict on ## merge b <- branches(repo) checkout(b[sapply(b, "[", "name") == "main"][[1]], force = TRUE) writeLines(c("Lorem ipsum dolor sit amet, adipisicing consectetur elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Some commit message branch 1") ## Merge branch 3 with fail = TRUE m_3 <- merge(b[sapply(b, "[", "name") == "branch3"][[1]], fail = TRUE) stopifnot(identical(m_3$up_to_date, FALSE)) stopifnot(identical(m_3$fast_forward, FALSE)) stopifnot(identical(m_3$conflicts, TRUE)) stopifnot(identical(sha(m_3), NA_character_)) m_3 ## Check status; Expect to have a clean working directory wd <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = empty_named_list()), class = "git_status") stopifnot(identical(status(repo), wd)) ## Merge branch 3 m_3 <- merge(b[sapply(b, "[", "name") == "branch3"][[1]]) stopifnot(identical(m_3$up_to_date, FALSE)) stopifnot(identical(m_3$fast_forward, FALSE)) stopifnot(identical(m_3$conflicts, TRUE)) stopifnot(identical(sha(m_3), NA_character_)) m_3 ## Check status; Expect to have one unstaged unmerged conflict. stopifnot(identical(status(repo), structure(list(staged = empty_named_list(), unstaged = list(conflicted = "test.txt"), untracked = empty_named_list()), class = "git_status"))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/remotes.R0000644000175000017500000000463413525743534014420 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) ## Add and commit add(repo, "test.txt") commit_1 <- commit(repo, "Commit message") ## Add a remote remote_add(repo, "playground", "https://github.com/gaborcsardi/playground") stopifnot(identical(remotes(repo), "playground")) stopifnot(identical(remote_url(repo, "playground"), "https://github.com/gaborcsardi/playground")) stopifnot(identical(remote_url(repo), "https://github.com/gaborcsardi/playground")) ## Rename a remote remote_rename(repo, "playground", "foobar") stopifnot(identical(remotes(repo), "foobar")) stopifnot(identical(remote_url(repo, "foobar"), "https://github.com/gaborcsardi/playground")) ## Set remote url remote_set_url(repo, "foobar", "https://github.com/stewid/playground") stopifnot(identical(remote_url(repo, "foobar"), "https://github.com/stewid/playground")) ## Remove a remote remote_remove(repo, "foobar") stopifnot(identical(remotes(repo), character(0))) if (identical(Sys.getenv("NOT_CRAN"), "true")) { if (isTRUE(libgit2_features()$https)) { refs <- remote_ls("https://github.com/ropensci/git2r") stopifnot(length(refs) > 0) stopifnot(names(refs) > 0) stopifnot(any(names(refs) == "HEAD")) } } # an invalid URL should throw an error tools::assertError(remote_ls("bad")) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/clone_branch.R0000644000175000017500000000552014076227744015354 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create directories for repositories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo_1 <- tempfile(pattern = "git2r-") path_repo_2 <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) ## Create bare repository bare_repo <- init(path_bare, bare = TRUE) ## Clone to repo 1 repo_1 <- clone(path_bare, path_repo_1) config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo 1 writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit_1 <- commit(repo_1, "First commit message") branch_name <- branches(repo_1)[[1]]$name ## Create 'dev' branch checkout(branch_create(commit_1, name = "dev")) ## Add more changes to repo 1 writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit(repo_1, "Second commit message") ## Add more changes to repo 1 writeLines( c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut"), con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit(repo_1, "Third commit message") ## Push to bare push(repo_1, "origin", paste0("refs/heads/", branch_name)) push(repo_1, "origin", "refs/heads/dev") ## Print branch branches(repo_1)[[paste0("origin/", branch_name)]] ## Clone to repo 2 repo_2 <- clone(url = path_bare, local_path = path_repo_2, branch = "dev") config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Check branch and commits stopifnot(identical(length(commits(repo_2)), 3L)) stopifnot(identical(repository_head(repo_2)$name, "dev")) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo_1, recursive = TRUE) unlink(path_repo_2, recursive = TRUE) git2r/tests/pull.R0000644000175000017500000001143214076212045013676 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create directories for repositories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo_1 <- tempfile(pattern = "git2r-") path_repo_2 <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) ## Create bare repository bare_repo <- init(path_bare, bare = TRUE) ## Clone to repo 1 repo_1 <- clone(path_bare, path_repo_1) config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo 1 and push to bare writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit_1 <- commit(repo_1, "First commit message") branch_name <- branches(repo_1)[[1]]$name push(repo_1, "origin", paste0("refs/heads/", branch_name)) ## Clone to repo 2 repo_2 <- clone(path_bare, path_repo_2) config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Add more changes to repo 1 and push to bare writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit_2 <- commit(repo_1, "Second commit message") push(repo_1, "origin", paste0("refs/heads/", branch_name)) ## Pull changes to repo_2 pull(repo_2) stopifnot(identical(length(commits(repo_2)), 2L)) ## Check remote url of repo_2 stopifnot(identical( branch_remote_url(branch_get_upstream(repository_head(repo_2))), path_bare)) ## Unset remote remote tracking branch branch_set_upstream(repository_head(repo_2), NULL) stopifnot(is.null(branch_get_upstream(repository_head(repo_2)))) tools::assertError(pull(repo_2)) tools::assertError(branch_set_upstream(repository_head(repo_2), NULL)) ## Add more changes to repo 1 and push to bare writeLines( c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", "minim veniam, quis nostrud exercitation ullamco laboris nisi ut"), con = file.path(path_repo_1, "test-1.txt")) add(repo_1, "test-1.txt") commit_3 <- commit(repo_1, "Third commit message") push(repo_1) ## Set remote tracking branch branch_set_upstream(repository_head(repo_2), paste0("origin/", branch_name)) stopifnot(identical( branch_remote_url(branch_get_upstream(repository_head(repo_2))), path_bare)) ## Pull changes to repo_2 pull(repo_2) stopifnot(identical(length(commits(repo_2)), 3L)) ## Check references in repo_1 and repo_2. Must clear the repo item ## since the repositories have different paths. stopifnot(identical(length(references(repo_1)), 2L)) ref_1 <- references(repo_1) lapply(seq_len(length(ref_1)), function(i) { ref_1[[i]]$repo <<- NULL }) ref_2 <- references(repo_2) lapply(seq_len(length(ref_2)), function(i) { ref_2[[i]]$repo <<- NULL }) name <- paste0("refs/heads/", branch_name) stopifnot(identical(ref_1[[name]], ref_2[[name]])) name <- paste0("refs/remotes/", branch_name) stopifnot(identical(ref_1[[name]], ref_2[[name]])) ref_1 <- references(repo_1)[[paste0("refs/heads/", branch_name)]] stopifnot(identical(ref_1$name, paste0("refs/heads/", branch_name))) stopifnot(identical(ref_1$type, 1L)) stopifnot(identical(sha(ref_1), sha(commit_3))) stopifnot(identical(ref_1$target, NA_character_)) stopifnot(identical(ref_1$shorthand, branch_name)) ref_2 <- references(repo_1)[[paste0("refs/remotes/origin/", branch_name)]] stopifnot(identical(ref_2$name, paste0("refs/remotes/origin/", branch_name))) stopifnot(identical(ref_2$type, 1L)) stopifnot(identical(sha(ref_2), sha(commit_3))) stopifnot(identical(ref_2$target, NA_character_)) stopifnot(identical(ref_2$shorthand, paste0("origin/", branch_name))) ## Check references with missing repo argument wd <- setwd(path_repo_1) stopifnot(identical(length(references()), 2L)) if (!is.null(wd)) setwd(wd) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo_1, recursive = TRUE) unlink(path_repo_2, recursive = TRUE) git2r/tests/fetch.R0000644000175000017500000001456214076227036014030 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create 2 directories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo_1 <- tempfile(pattern = "git2r-") path_repo_2 <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) ## Create repositories bare_repo <- init(path_bare, bare = TRUE) repo_1 <- clone(path_bare, path_repo_1) repo_2 <- clone(path_bare, path_repo_2) ## Config repositories config(repo_1, user.name = "Alice", user.email = "alice@example.org") config(repo_2, user.name = "Bob", user.email = "bob@example.org") ## Add changes to repo 1 writeLines("Hello world", con = file.path(path_repo_1, "test.txt")) add(repo_1, "test.txt") commit_1 <- commit(repo_1, "Commit message") branch_name <- branches(repo_1)[[1]]$name ## Push changes from repo 1 to origin push(repo_1, "origin", paste0("refs/heads/", branch_name)) ## Check result in bare repository stopifnot(identical(length(commits(bare_repo)), 1L)) bare_commit_1 <- commits(bare_repo)[[1]] stopifnot(identical(sha(commit_1), sha(bare_commit_1))) stopifnot(identical(commit_1$author, bare_commit_1$author)) stopifnot(identical(commit_1$committer, bare_commit_1$committer)) stopifnot(identical(commit_1$summary, bare_commit_1$summary)) stopifnot(identical(commit_1$message, bare_commit_1$message)) stopifnot(!identical(commit_1$repo, bare_commit_1$repo)) ## Fetch fetch(repo_2, "origin") fh <- fetch_heads(repo_2)[[1]] stopifnot(identical(sha(fh), fh$sha)) ## Test show method of non-empty repository where head is null show(repo_2) ## Check that 'git2r_arg_check_credentials' raise error res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", 3, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", repo_1, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- cred_env(c("username", "username"), "password") res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- cred_env("username", c("password", "passowrd")) res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- cred_user_pass(c("username", "username"), "password") res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- cred_user_pass("username", c("password", "passowrd")) res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- cred_token(c("GITHUB_PAT", "GITHUB_PAT")) res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- structure(list(publickey = c("id_rsa.pub", "id_rsa.pub"), privatekey = "id_rsa", passphrase = character(0)), class = "cred_ssh_key") res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- structure(list(publickey = "id_rsa.pub", privatekey = c("id_rsa", "id_rsa"), passphrase = character(0)), class = "cred_ssh_key") res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- structure(list(publickey = "id_rsa.pub", privatekey = "id_rsa", passphrase = NA_character_), class = "cred_ssh_key") res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) credentials <- structure(list(publickey = "id_rsa.pub", privatekey = "id_rsa", passphrase = c("passphrase", "passphrase")), class = "cred_ssh_key") res <- tools::assertError( .Call(git2r:::git2r_remote_fetch, repo_1, "origin", credentials, "fetch", FALSE, NULL)) stopifnot(length(grep("'credentials' must be an S3 class with credentials", res[[1]]$message)) > 0) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo_1, recursive = TRUE) unlink(path_repo_2, recursive = TRUE) git2r/tests/time.R0000644000175000017500000000452213565057326013675 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Test to coerce git_t <- structure(list(time = 1395567947, offset = 60), class = "git_time") stopifnot(identical(as.character(git_t), "2014-03-23 09:45:47 GMT")) stopifnot(identical(as.character(git_t, usetz = FALSE), "2014-03-23 09:45:47")) stopifnot(identical(as.POSIXct(git_t), as.POSIXct(1395567947, tz = "GMT", origin = "1970-01-01"))) stopifnot(identical(print(git_t), git_t)) as.POSIXct(1395567947, origin = "1970-01-01", tz = "-03") ## Test that origin/tz can be passed to as.POSIXct stopifnot(identical(as.POSIXct(git_t, tz = "Europe/Stockholm", origin = "1980-02-02"), as.POSIXct(1395567947, tz = "Europe/Stockholm", origin = "1980-02-02"))) ## Test that origin/tz can be passed to as.character stopifnot(identical(as.character(git_t, tz = "Europe/Stockholm", origin = "1980-02-02"), "2024-04-23 11:45:47 CEST")) stopifnot(identical(as.character(git_t, tz = "Europe/Stockholm", origin = "1980-02-02", usetz = FALSE), "2024-04-23 11:45:47")) ## Test that origin/tz can be passed to print stopifnot(identical( utils::capture.output(print(git_t, tz = "Europe/Stockholm", origin = "1980-02-02")), "2024-04-23 11:45:47 CEST" )) stopifnot(identical( utils::capture.output(print(git_t, tz = "Europe/Stockholm", origin = "1980-02-02", usetz = FALSE)), "2024-04-23 11:45:47" )) git2r/tests/blame.R0000644000175000017500000000562513526013301014002 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "First commit message") ## Create new user and change file config(repo, user.name = "Bob", user.email = "bob@example.org") writeLines(c("Hello world!", "HELLO WORLD!", "HOLA"), file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Second commit message") ## Check blame b <- blame(repo, "test.txt") stopifnot(identical(length(b$hunks), 2L)) ## Hunk: 1 stopifnot(identical(b$hunks[[1]]$lines_in_hunk, 1L)) stopifnot(identical(b$hunks[[1]]$final_commit_id, sha(commit_1))) stopifnot(identical(b$hunks[[1]]$final_start_line_number, 1L)) stopifnot(identical(b$hunks[[1]]$final_signature$name, "Alice")) stopifnot(identical(b$hunks[[1]]$final_signature$email, "alice@example.org")) stopifnot(identical(b$hunks[[1]]$orig_commit_id, sha(commit_1))) stopifnot(identical(b$hunks[[1]]$orig_start_line_number, 1L)) stopifnot(identical(b$hunks[[1]]$orig_signature$name, "Alice")) stopifnot(identical(b$hunks[[1]]$orig_signature$email, "alice@example.org")) stopifnot(identical(b$hunks[[1]]$orig_path, "test.txt")) stopifnot(identical(b$hunks[[1]]$boundary, TRUE)) ## Hunk: 2 stopifnot(identical(b$hunks[[2]]$lines_in_hunk, 2L)) stopifnot(identical(b$hunks[[2]]$final_commit_id, sha(commit_2))) stopifnot(identical(b$hunks[[2]]$final_start_line_number, 2L)) stopifnot(identical(b$hunks[[2]]$final_signature$name, "Bob")) stopifnot(identical(b$hunks[[2]]$final_signature$email, "bob@example.org")) stopifnot(identical(b$hunks[[2]]$orig_commit_id, sha(commit_2))) stopifnot(identical(b$hunks[[2]]$orig_start_line_number, 2L)) stopifnot(identical(b$hunks[[2]]$orig_signature$name, "Bob")) stopifnot(identical(b$hunks[[2]]$orig_signature$email, "bob@example.org")) stopifnot(identical(b$hunks[[2]]$orig_path, "test.txt")) stopifnot(identical(b$hunks[[2]]$boundary, FALSE)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/blob.R0000644000175000017500000001325513526236163013653 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) library(tools) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file f <- file(file.path(path, "test.txt"), "wb") writeChar("Hello world!\n", f, eos = NULL) close(f) ## add and commit add(repo, "test.txt") new_commit <- commit(repo, "Commit message") ## Lookup blob blob <- lookup(repo, "cd0875583aabe89ee197ea133980a9085d08e497") stopifnot(isTRUE(is_blob(blob))) stopifnot(identical(sha(blob), "cd0875583aabe89ee197ea133980a9085d08e497")) stopifnot(identical(is_binary(blob), FALSE)) stopifnot(identical(blob, lookup(repo, "cd0875"))) stopifnot(identical(length(blob), 13L)) stopifnot(identical(content(blob), "Hello world!")) stopifnot(identical(print(blob), blob)) ## Add one more commit f <- file(file.path(path, "test.txt"), "wb") writeChar("Hello world!\nHELLO WORLD!\nHeLlO wOrLd!\n", f, eos = NULL) close(f) add(repo, "test.txt") blob <- lookup(repo, tree(commit(repo, "New commit message"))$id[1]) stopifnot(identical(content(blob), c("Hello world!", "HELLO WORLD!", "HeLlO wOrLd!"))) ## Check content of binary file set.seed(42) writeBin(as.raw((sample(0:255, 1000, replace = TRUE))), con = file.path(path, "test.bin")) add(repo, "test.bin") commit(repo, "Add binary file") blob <- tree(last_commit(repo))["test.bin"] stopifnot(identical(content(blob), NA_character_)) ## Hash stopifnot(identical(hash("Hello, world!\n"), "af5626b4a114abcb82d63db7c8082c3c4756e51b")) stopifnot(identical(hash("test content\n"), "d670460b4b4aece5915caf5c68d12f560a9fe3e4")) stopifnot(identical(hash(c("Hello, world!\n", "test content\n")), c("af5626b4a114abcb82d63db7c8082c3c4756e51b", "d670460b4b4aece5915caf5c68d12f560a9fe3e4"))) stopifnot(identical(hash(c("Hello, world!\n", NA_character_, "test content\n")), c("af5626b4a114abcb82d63db7c8082c3c4756e51b", NA_character_, "d670460b4b4aece5915caf5c68d12f560a9fe3e4"))) stopifnot(identical(hash(character(0)), character(0))) ## Hash file test_1_txt <- file(file.path(path, "test-1.txt"), "wb") writeChar("Hello, world!\n", test_1_txt, eos = NULL) close(test_1_txt) test_2_txt <- file(file.path(path, "test-2.txt"), "wb") writeChar("test content\n", test_2_txt, eos = NULL) close(test_2_txt) stopifnot(identical(hash("Hello, world!\n"), hashfile(file.path(path, "test-1.txt")))) stopifnot(identical(hash("test content\n"), hashfile(file.path(path, "test-2.txt")))) stopifnot(identical(hash(c("Hello, world!\n", "test content\n")), hashfile(c(file.path(path, "test-1.txt"), file.path(path, "test-2.txt"))))) assertError(hashfile(c(file.path(path, "test-1.txt"), NA_character_, file.path(path, "test-2.txt")))) stopifnot(identical(hashfile(character(0)), character(0))) ## Create blob from disk tmp_file_1 <- tempfile() tmp_file_2 <- tempfile() f1 <- file(tmp_file_1, "wb") writeChar("Hello, world!\n", f1, eos = NULL) close(f1) f2 <- file(tmp_file_2, "wb") writeChar("test content\n", f2, eos = NULL) close(f2) blob_list_1 <- blob_create(repo, c(tmp_file_1, tmp_file_2), relative = FALSE) unlink(tmp_file_1) unlink(tmp_file_2) stopifnot(identical(sapply(blob_list_1, "[[", "sha"), c("af5626b4a114abcb82d63db7c8082c3c4756e51b", "d670460b4b4aece5915caf5c68d12f560a9fe3e4"))) ## Create blob from workdir tmp_file_3 <- file.path(path, "test-workdir-1.txt") tmp_file_4 <- file.path(path, "test-workdir-2.txt") f3 <- file(tmp_file_3, "wb") writeChar("Hello, world!\n", f3, eos = NULL) close(f3) f4 <- file(tmp_file_4, "wb") writeChar("test content\n", f4, eos = NULL) close(f4) blob_list_2 <- blob_create(repo, c("test-workdir-1.txt", "test-workdir-2.txt")) stopifnot(identical(sapply(blob_list_2, "[[", "sha"), c("af5626b4a114abcb82d63db7c8082c3c4756e51b", "d670460b4b4aece5915caf5c68d12f560a9fe3e4"))) ## Test arguments check_error(assertError(.Call(git2r:::git2r_blob_content, NULL)), "'blob' must be an S3 class git_blob") check_error(assertError(.Call(git2r:::git2r_blob_content, 3)), "'blob' must be an S3 class git_blob") check_error(assertError(.Call(git2r:::git2r_blob_content, repo)), "'blob' must be an S3 class git_blob") b <- blob_list_1[[1]] b$sha <- NA_character_ check_error(assertError(.Call(git2r:::git2r_blob_content, b)), "'blob' must be an S3 class git_blob") check_error(assertError(hashfile(NA)), "invalid 'path' argument") ## Cleanup unlink(path, recursive = TRUE) git2r/tests/pre-process-path.R0000644000175000017500000001136613526005173016125 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) setwd(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Test to add file with a leading './' writeLines("foo-1", file.path(path, "foo-1")) add(repo, "./foo-1") status_exp <- structure(list(staged = list(new = "foo-1"), unstaged = empty_named_list(), untracked = empty_named_list()), class = "git_status") status_obs <- status(repo) str(status_exp) str(status_obs) stopifnot(identical(status_obs, status_exp)) ## Test to add file in sub-folder with sub-folder as working directory writeLines("foo-2", file.path(path, "foo-2")) dir.create(file.path(path, "foo_dir")) writeLines("foo-2", file.path(path, "foo_dir/foo-2")) setwd("./foo_dir") add(repo, "foo-2") status_exp <- structure(list(staged = list(new = "foo-1", new = "foo_dir/foo-2"), unstaged = empty_named_list(), untracked = list(untracked = "foo-2")), class = "git_status") status_obs <- status(repo) str(status_exp) str(status_obs) stopifnot(identical(status_obs, status_exp)) ## Test glob expansion setwd(tempdir()) dir.create(file.path(path, "glob_dir")) writeLines("a", file.path(path, "glob_dir/a.txt")) writeLines("b", file.path(path, "glob_dir/b.txt")) writeLines("c", file.path(path, "glob_dir/c.txt")) writeLines("d", file.path(path, "glob_dir/d.md")) add(repo, "glob_dir/*txt") status_exp <- structure(list(staged = list(new = "foo-1", new = "foo_dir/foo-2", new = "glob_dir/a.txt", new = "glob_dir/b.txt", new = "glob_dir/c.txt"), unstaged = empty_named_list(), untracked = list(untracked = "foo-2", untracked = "glob_dir/d.md")), class = "git_status") status_obs <- status(repo) str(status_exp) str(status_obs) stopifnot(identical(status_obs, status_exp)) ## Test glob expansion with relative path setwd(path) add(repo, "./glob_dir/*md") status_exp <- structure(list(staged = list(new = "foo-1", new = "foo_dir/foo-2", new = "glob_dir/a.txt", new = "glob_dir/b.txt", new = "glob_dir/c.txt", new = "glob_dir/d.md"), unstaged = empty_named_list(), untracked = list(untracked = "foo-2")), class = "git_status") status_obs <- status(repo) str(status_exp) str(status_obs) stopifnot(identical(status_obs, status_exp)) ## Test to add file in root of workdir when the file also exists in ## current workdir. setwd(tempdir()) writeLines("e", file.path(path, "e.txt")) writeLines("e", file.path(tempdir(), "e.txt")) add(repo, "e.txt") status_exp <- structure(list(staged = list(new = "e.txt", new = "foo-1", new = "foo_dir/foo-2", new = "glob_dir/a.txt", new = "glob_dir/b.txt", new = "glob_dir/c.txt", new = "glob_dir/d.md"), unstaged = empty_named_list(), untracked = list(untracked = "foo-2")), class = "git_status") status_obs <- status(repo) str(status_exp) str(status_obs) stopifnot(identical(status_obs, status_exp)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/util/0000755000175000017500000000000013526235427013563 5ustar nileshnileshgit2r/tests/util/check.R0000644000175000017500000000215313526235427014764 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. empty_named_list <- function() { structure(list(), .Names = character(0)) } ## Raise an error if the error message doesn't match. check_error <- function(current, target, exact = FALSE) { if (isTRUE(exact)) { stopifnot(identical(current[[1]]$message, target)) } else { stopifnot(length(grep(target, current[[1]]$message)) > 0) } invisible(NULL) } git2r/tests/index.R0000644000175000017500000001025013526005210014017 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create directories dir.create(file.path(path, "sub-folder")); dir.create(file.path(path, "sub-folder", "sub-sub-folder")); ## Create files writeLines("Hello world!", file.path(path, "file-1.txt")) writeLines("Hello world!", file.path(path, "sub-folder", "file-2.txt")) writeLines("Hello world!", file.path(path, "sub-folder", "file-3.txt")) writeLines("Hello world!", file.path(path, "sub-folder", "sub-sub-folder", "file-4.txt")) writeLines("Hello world!", file.path(path, "sub-folder", "sub-sub-folder", "file-5.txt")) ## Add add(repo, "file-1.txt") status_exp <- structure(list(staged = list(new = "file-1.txt"), unstaged = empty_named_list(), untracked = list(untracked = "sub-folder/")), class = "git_status") status_obs <- status(repo) stopifnot(identical(status_obs, status_exp)) ## Index remove by path index_remove_bypath(repo, "file-1.txt") status_exp <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = list(untracked = "file-1.txt", untracked = "sub-folder/")), class = "git_status") status_obs <- status(repo) stopifnot(identical(status_obs, status_exp)) ## Add add(repo, "sub-folder") status_exp <- structure(list(staged = list( new = "sub-folder/file-2.txt", new = "sub-folder/file-3.txt", new = "sub-folder/sub-sub-folder/file-4.txt", new = "sub-folder/sub-sub-folder/file-5.txt"), unstaged = empty_named_list(), untracked = list(untracked = "file-1.txt")), class = "git_status") status_obs <- status(repo) stopifnot(identical(status_obs, status_exp)) ## Commit commit(repo, "First commit message") ## It should fail to remove non-existing, untracked and ignored files tools::assertError(rm_file(repo, c("file-1.txt", "file-2.txt"))) tools::assertError(rm_file(repo, c("file-1.txt", ""))) tools::assertError(rm_file(repo, c("file-1.txt"))) writeLines("/file-1.txt", file.path(path, ".gitignore")) tools::assertError(rm_file(repo, "file-1.txt")) ## It should fail to remove files with staged changes file.remove(file.path(path, ".gitignore")) add(repo, "file-1.txt") tools::assertError(rm_file(repo, "file-1.txt")) ## It should fail to remove files with unstaged changes commit(repo, "Second commit message") writeLines(c("Hello world!", "Hello world!"), file.path(path, "file-1.txt")) tools::assertError(rm_file(repo, "file-1.txt")) ## Remove file add(repo, "file-1.txt") commit(repo, "Third commit message") rm_file(repo, "file-1.txt") status_exp <- structure(list(staged = list(deleted = "file-1.txt"), unstaged = empty_named_list(), untracked = empty_named_list()), class = "git_status") status_obs <- status(repo) stopifnot(identical(status_obs, status_exp)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/checkout-named-branch.R0000644000175000017500000000567614076230141017056 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create directories for repositories in tempdir path_bare <- tempfile(pattern = "git2r-") path_repo_1 <- tempfile(pattern = "git2r-") path_repo_2 <- tempfile(pattern = "git2r-") dir.create(path_bare) dir.create(path_repo_1) dir.create(path_repo_2) ## Create bare repository bare_repo <- init(path_bare, bare = TRUE) ## Clone to repo 1 repo_1 <- clone(path_bare, path_repo_1) config(repo_1, user.name = "Alice", user.email = "alice@example.org") ## Add changes to repo 1 and push to bare writeLines( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path_repo_1, "test.txt")) add(repo_1, "test.txt") commit(repo_1, "First commit message") branch_name <- branches(repo_1)[[1]]$name push(repo_1, "origin", paste0("refs/heads/", branch_name)) ## Test checkout branch argument tools::assertError(checkout(repo_1)) tools::assertError(checkout(repo_1, c("master", "master"))) tools::assertError(checkout(repo_1, "dev")) ## Create and checkout dev branch in repo 1 checkout(repo_1, "dev", create = TRUE) ## Add changes to dev branch in repo 1 and push to bare writeLines( c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path_repo_1, "test.txt")) add(repo_1, "test.txt") commit(repo_1, "Second commit message") push(repo_1, "origin", "refs/heads/dev") ## Clone to repo 2 repo_2 <- clone(path_bare, path_repo_2) config(repo_2, user.name = "Bob", user.email = "bob@example.org") stopifnot(identical( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", readLines(file.path(path_repo_2, "test.txt")))) ## Checkout dev branch checkout(repo_2, "dev") ## Check content of file stopifnot(identical( c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), readLines(file.path(path_repo_2, "test.txt")))) ## Checkout previous branch checkout(repo_2, "-") stopifnot(identical(repository_head(repo_2)$name, branch_name)) ## Cleanup unlink(path_bare, recursive = TRUE) unlink(path_repo_1, recursive = TRUE) unlink(path_repo_2, recursive = TRUE) git2r/tests/tree.R0000644000175000017500000000477113526015121013664 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file f <- file(file.path(path, "test.txt"), "wb") writeChar("Hello world!\n", f, eos = NULL) close(f) ## add and commit add(repo, "test.txt") commit(repo, "Commit message") ## Check tree stopifnot(is_tree(lookup(repo, "a0b0b9e615e9e433eb5f11859e9feac4564c58c5"))) stopifnot(identical( sha(lookup(repo, "a0b0b9e615e9e433eb5f11859e9feac4564c58c5")), "a0b0b9e615e9e433eb5f11859e9feac4564c58c5")) stopifnot(is_tree(tree(commits(repo)[[1]]))) stopifnot(identical(lookup(repo, "a0b0b9e615e9e433eb5f11859e9feac4564c58c5"), tree(commits(repo)[[1]]))) stopifnot(identical(length(tree(commits(repo)[[1]])), 1L)) ## Coerce to a data.frame and check column names stopifnot(identical(names(as.data.frame(tree(commits(repo)[[1]]))), c("mode", "type", "sha", "name"))) ## Coerce to list and check length stopifnot(identical(length(as.list(tree(last_commit(repo)))), 1L)) ## Print and summary stopifnot(identical(print(tree(last_commit(repo))), tree(last_commit(repo)))) summary(tree(last_commit(repo))) ## Check indexing stopifnot(is_blob(tree(last_commit(repo))[TRUE])) stopifnot(is_blob(tree(last_commit(repo))["test.txt"])) res <- tools::assertError(tree(last_commit(repo))[data.frame()]) stopifnot(length(grep("Invalid index", res[[1]]$message)) > 0) ## Check ls_tree stopifnot(identical(ls_tree(repo = repo), ls_tree(repo = path))) stopifnot(identical(ls_tree(tree = sha(tree(last_commit(repo))), repo = repo), ls_tree(repo = repo))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/checkout_branch.R0000644000175000017500000000432514076230471016052 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path, branch = "main") config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create first commit writeLines("Hello world!", file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit(repo, "First commit message") ## Create and checkout dev branch in repo checkout(repo, "dev", create = TRUE) ## Create second commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-2.txt")) add(repo, "test-2.txt") commit(repo, "Second commit message") ## Check files stopifnot(identical(list.files(path), c("test-1.txt", "test-2.txt"))) ## Checkout master branch and check files checkout(repo, "main") stopifnot(identical(list.files(path), "test-1.txt")) ## Cleanup unlink(path, recursive = TRUE) ## Checkout branch in empty repository ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create and checkout dev branch in repo checkout(repo, "dev", create = TRUE) ## Create first commit writeLines("Hello world!", file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit(repo, "First commit message") stopifnot(identical(length(branches(repo)), 1L)) stopifnot(identical(branches(repo)[[1]]$name, "dev")) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/checkout_commit.R0000644000175000017500000000335713525743543016120 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create first commit writeLines("Hello world!", file.path(path, "test-1.txt")) add(repo, "test-1.txt") commit_1 <- commit(repo, "First commit message") ## Create and checkout dev branch in repo checkout(repo, "dev", create = TRUE) ## Create second commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-2.txt")) add(repo, "test-2.txt") commit_2 <- commit(repo, "Second commit message") ## Check files stopifnot(identical(list.files(path), c("test-1.txt", "test-2.txt"))) ## Checkout commit_1 and check files checkout(commit_1) stopifnot(identical(list.files(path), "test-1.txt")) ## Checkout commit_2 and check files checkout(commit_2) stopifnot(identical(list.files(path), c("test-1.txt", "test-2.txt"))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/invalid-conf-var.R0000644000175000017500000000325113526016051016057 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) ## Config repository config(repo, user.name = "Alice") ## Let's set one valid and one with variable with invalid format res <- tools::assertWarning(config(repo, user.email = "alice@example.org", lol = "wut")) stopifnot(length(grep("Variable was not in a valid format: 'lol'", res[[1]]$message)) > 0) cfg_exp <- structure(list(user.name = "Alice", user.email = "alice@example.org", "NA" = NULL), .Names = c("user.name", "user.email", NA)) cfg_obs <- config(repo)$local cfg_obs <- cfg_obs[c("user.name", "user.email", "lol")] stopifnot(identical(cfg_obs, cfg_exp)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/graph.R0000644000175000017500000000346113525743537014043 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "First commit message") tag_1 <- tag(repo, "Tagname1", "Tag message 1") ## Change file and commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Second commit message") tag_2 <- tag(repo, "Tagname2", "Tag message 2") ## Check ahead behind stopifnot(identical(ahead_behind(commit_1, commit_2), c(0L, 1L))) stopifnot(identical(ahead_behind(tag_1, tag_2), c(0L, 1L))) stopifnot(identical(ahead_behind(tag_2, tag_1), c(1L, 0L))) stopifnot(identical(ahead_behind(commit_1, branches(repo)[[1]]), c(0L, 1L))) stopifnot(identical(ahead_behind(branches(repo)[[1]], commit_1), c(1L, 0L))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/add-force.R0000644000175000017500000000472213526002357014554 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a '.gitignore' file writeLines("test.txt", file.path(path, ".gitignore")) add(repo, ".gitignore") commit(repo, "First commit message") ## Create a file writeLines("Hello world!", file.path(path, "test.txt")) ## Check status s_1 <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = empty_named_list(), ignored = list(ignored = "test.txt")), class = "git_status") stopifnot(identical(status(repo, ignored = TRUE), s_1)) ## The file is ignored and should not be added add(repo, "test.txt") stopifnot(identical(status(repo, ignored = TRUE), s_1)) ## The file is ignored but should be added with force s_2 <- structure(list(staged = list(new = "test.txt"), unstaged = empty_named_list(), untracked = empty_named_list(), ignored = empty_named_list()), class = "git_status") add(repo, "test.txt", force = TRUE) stopifnot(identical(status(repo, ignored = TRUE), s_2)) ## Commit and check status s_3 <- structure(list(staged = empty_named_list(), unstaged = empty_named_list(), untracked = empty_named_list(), ignored = empty_named_list()), class = "git_status") commit(repo, "Second commit message") stopifnot(identical(status(repo, ignored = TRUE), s_3)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/note.R0000644000175000017500000000640613526016146013677 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create a file, add and commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "Commit message 1") ## Create another commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Commit message 2") ## Check default ref stopifnot(identical(note_default_ref(repo), "refs/notes/commits")) ## Check that an invalid object argument in note_create produce an ## error. tools::assertError(note_create(object = NULL, message = "test")) tools::assertError(note_create(object = 1, message = "test")) ## Check that notes is an empty list stopifnot(identical(notes(repo), list())) ## Create note in default namespace note_1 <- note_create(commit_1, "Note-1") stopifnot(identical(print(note_1), note_1)) stopifnot(identical(length(notes(repo)), 1L)) stopifnot(identical(sha(note_1), note_1$sha)) tools::assertError(note_create(commit_1, "Note-2")) note_2 <- note_create(commit_1, "Note-2", force = TRUE) stopifnot(identical(length(notes(repo)), 1L)) ## Check that an invalid note argument in note_remove produce an ## error. tools::assertError(note_remove(note = 1)) ## Create note in named (review) namespace note_3 <- note_create(commit_1, "Note-3", ref = "refs/notes/review") note_4 <- note_create(commit_2, "Note-4", ref = "refs/notes/review") stopifnot(identical(length(notes(repo, ref = "refs/notes/review")), 2L)) note_remove(note_3) note_remove(note_4) stopifnot(identical(notes(repo, ref = "refs/notes/review"), list())) note_5 <- note_create(commit_1, "Note-5", ref = "review") note_6 <- note_create(commit_2, "Note-6", ref = "review") stopifnot(identical(length(notes(repo, ref = "review")), 2L)) note_remove(note_5) note_remove(note_6) stopifnot(identical(length(notes(repo, ref = "review")), 0L)) ## Create note on blob and tree tree_1 <- tree(commit_1) note_7 <- note_create(tree_1, "Note-7") stopifnot(is(object = lookup(repo, note_7$annotated), class2 = "git_tree")) stopifnot(identical(length(notes(repo)), 2L)) blob_1 <- lookup(repo, tree_1$id[1]) note_8 <- note_create(blob_1, "Note-8") stopifnot(is(object = lookup(repo, note_8$annotated), class2 = "git_blob")) stopifnot(identical(length(notes(repo)), 3L)) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/ls_tree.R0000644000175000017500000000465513556640753014405 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Initialize a temporary repository path <- tempfile(pattern = "git2r-") dir.create(path) dir.create(file.path(path, "subfolder")) repo <- init(path) ## Create a user config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create three files and commit writeLines("First file", file.path(path, "example-1.txt")) writeLines("Second file", file.path(path, "subfolder/example-2.txt")) writeLines("Third file", file.path(path, "example-3.txt")) add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) commit(repo, "Commit message") ## Traverse tree entries and its subtrees. ## Various approaches that give identical result. stopifnot(identical(ls_tree(tree = tree(last_commit(path))), ls_tree(tree = tree(last_commit(repo))))) stopifnot(identical(ls_tree(repo = path), ls_tree(repo = repo))) ## ls_tree(repo = repo) should match `git ls-tree -lr HEAD` ls_tree_result <- ls_tree(repo = repo) stopifnot(identical(ls_tree_result$name, c("example-1.txt", "example-3.txt", "example-2.txt"))) # Argument `tree` can be a 'character that identifies a tree in the repository' ls_tree(tree = tree(last_commit(path))$sha, repo = repo) ## Skip content in subfolder ls_tree_toplevel <- ls_tree(repo = repo, recursive = FALSE) stopifnot(nrow(ls_tree_toplevel) == 3) stopifnot(identical(ls_tree_toplevel$name, c("example-1.txt", "example-3.txt", "subfolder"))) ## Start in subfolder ls_tree_subfolder <- ls_tree(tree = "HEAD:subfolder", repo = repo) stopifnot(nrow(ls_tree_subfolder) == 1) stopifnot(identical(ls_tree_subfolder$name, "example-2.txt")) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/odb_blobs.R0000644000175000017500000000432213526016167014655 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library("git2r") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path) config(repo, user.name = "Alice", user.email = "alice@@example.org") ## Create a file, add and commit writeLines("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message 1") ## Change file and commit writeLines(c("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", "eiusmod tempor incididunt ut labore et dolore magna aliqua."), con = file.path(path, "test.txt")) add(repo, "test.txt") commit(repo, "Commit message 2") ## Commit same content under different name in a sub-directory dir.create(file.path(path, "sub-directory")) file.copy(file.path(path, "test.txt"), file.path(path, "sub-directory", "copy.txt")) add(repo, "sub-directory/copy.txt") commit(repo, "Commit message 3") ## List blobs b <- odb_blobs(repo) ## Order the data.frame before checking b <- b[order(b$name), ] ## Check blobs stopifnot(identical(nrow(b), 3L)) stopifnot(identical( colnames(b), c("sha", "path", "name", "len", "commit", "author", "when"))) stopifnot(identical(b$path, c("sub-directory", "", ""))) stopifnot(identical(b$name, c("copy.txt", "test.txt", "test.txt"))) stopifnot(identical(b$author, c("Alice", "Alice", "Alice"))) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/checkout.R0000644000175000017500000000723714076204056014542 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## Initialize a repository repo <- init(path, branch = "main") config(repo, user.name = "Alice", user.email = "alice@example.org") ## Create first commit writeLines("Hello world!", file.path(path, "test.txt")) add(repo, "test.txt") commit_1 <- commit(repo, "First commit message") ## Edit file and checkout writeLines(c("Hello world!", "Hello world!"), file.path(path, "test.txt")) status_exp_1 <- structure(list(staged = structure(list(), .Names = character(0)), unstaged = structure(list(modified = "test.txt"), .Names = "modified"), untracked = structure(list(), .Names = character(0))), .Names = c("staged", "unstaged", "untracked"), class = "git_status") status_obs_1 <- status(repo) str(status_exp_1) str(status_obs_1) stopifnot(identical(status_obs_1, status_exp_1)) checkout(repo, path = "test.txt") status_exp_2 <- structure(list(staged = structure(list(), .Names = character(0)), unstaged = structure(list(), .Names = character(0)), untracked = structure(list(), .Names = character(0))), .Names = c("staged", "unstaged", "untracked"), class = "git_status") status_obs_2 <- status(repo) str(status_exp_2) str(status_obs_2) stopifnot(identical(status_obs_2, status_exp_2)) ## Create second commit writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) add(repo, "test.txt") commit_2 <- commit(repo, "Second commit message") tag(repo, "commit_2", "Tag message") ## Create third commit writeLines(c("Hello world!", "HELLO WORLD!", "HeLlO wOrLd!"), file.path(path, "test.txt")) add(repo, "test.txt") commit_3 <- commit(repo, "Third commit message") ## Check HEAD stopifnot(identical(is_detached(repo), FALSE)) stopifnot(identical(repository_head(repo)$name, "main")) ## Check show and summary repo summary(repo) ## Checkout first commit checkout(commit_1, TRUE) stopifnot(identical(is_detached(repo), TRUE)) stopifnot(identical(repository_head(repo), commit_1)) stopifnot(identical(readLines(file.path(path, "test.txt")), "Hello world!")) ## Check show and summary repo summary(repo) ## Checkout tag checkout(tags(repo)[[1]], TRUE) stopifnot(identical(is_detached(repo), TRUE)) stopifnot(identical(readLines(file.path(path, "test.txt")), c("Hello world!", "HELLO WORLD!"))) ## Check is_detached with missing repo argument wd <- setwd(path) stopifnot(identical(is_detached(), TRUE)) if (!is.null(wd)) setwd(wd) ## Cleanup unlink(path, recursive = TRUE) git2r/tests/repository.R0000644000175000017500000000710613526005153015143 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013 - 2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. library(git2r) source("util/check.R") ## For debugging sessionInfo() ## Create a directory in tempdir path <- tempfile(pattern = "git2r-") dir.create(path) ## is_bare: "Invalid repository" tools::assertError(is_bare(new("git_repository"))) ## is_empty: "Invalid repository" tools::assertError(is_empty(new("git_repository"))) ## Check that open an invalid repository fails tools::assertError(repository(path)) tools::assertError(repository(path, discover = FALSE)) ## Check that it fails to open/init a repository with a path to a ## file. writeLines("test", file.path(path, "test.txt")) tools::assertError(repository(file.path(path, "test.txt"), discover = FALSE)) tools::assertError(init(file.path(path, "test.txt"))) unlink(file.path(path, "test.txt")) ## Initialize a repository repo <- init(path) stopifnot(identical(print(repo), repo)) ## Check the state of the repository stopifnot(identical(is_bare(repo), FALSE)) stopifnot(identical(is_empty(repo), TRUE)) stopifnot(identical(is_shallow(repo), FALSE)) stopifnot(identical(branches(repo), empty_named_list())) stopifnot(identical(references(repo), empty_named_list())) stopifnot(identical(commits(repo), list())) stopifnot(identical(repository_head(repo), NULL)) # check that we can find repository from a path wd <- sub(paste0("[", .Platform$file.sep, "]$"), "", workdir(repo)) writeLines("test file", con = file.path(wd, "myfile.txt")) stopifnot(identical(discover_repository(file.path(wd, "myfile.txt")), file.path(wd, ".git"))) stopifnot(identical(discover_repository(file.path(wd, "doesntexist.txt")), NULL)) # Check that we can use ceiling in discover repostiory dir.create(file.path(wd, "temp")) stopifnot(identical(discover_repository(file.path(wd, "temp"), 0), NULL)) stopifnot(identical(discover_repository(file.path(wd, "temp"), 1), file.path(wd, ".git"))) tools::assertError(discover_repository(file.path(wd, "temp"), 2)) ## Check that lookup with a sha of less than 4 characters or more than ## 40 characters fail. tools::assertError(lookup(repo, paste0(rep("a", 3), collapse = ""))) tools::assertError(lookup(repo, paste0(rep("a", 41), collapse = ""))) ## Check in_repository stopifnot(identical(in_repository(path), TRUE)) ## Check: ## - in_repository method with missing path argument ## - repository method with missing path argument ## - workdir method with missing path argument ## - is_empty method with missing repo argument ## - is_shallow method with missing repo argument wd <- setwd(path) stopifnot(identical(in_repository(), TRUE)) stopifnot(identical(workdir(repository(path)), workdir(repository()))) stopifnot(identical(workdir(repository(path)), workdir())) stopifnot(identical(is_empty(), TRUE)) stopifnot(identical(is_shallow(), FALSE)) if (!is.null(wd)) setwd(wd) ## Cleanup unlink(path, recursive = TRUE) git2r/configure0000755000175000017500000072364714145550451013371 0ustar nileshnilesh#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for git2r see.DESCRIPTION.file. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: https://github.com/ropensci/git2r/issues about your $0: system, including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='git2r' PACKAGE_TARNAME='git2r' PACKAGE_VERSION='see.DESCRIPTION.file' PACKAGE_STRING='git2r see.DESCRIPTION.file' PACKAGE_BUGREPORT='https://github.com/ropensci/git2r/issues' PACKAGE_URL='' ac_unique_file="src/git2r.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='PKG_CPPFLAGS GIT2R_SRC_SHA1 GIT2R_SRC_REGEX LTLIBICONV LIBICONV EGREP GREP CPP libssh2_LIBS libssh2_CFLAGS openssl_LIBS openssl_CFLAGS zlib_LIBS zlib_CFLAGS LTLIBOBJS LIBOBJS PKG_LIBS PKG_CFLAGS BREW PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC host_os host_vendor host_cpu host build_os build_vendor build_cpu build target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_libgit2 with_gnu_ld enable_rpath with_libiconv_prefix ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR zlib_CFLAGS zlib_LIBS openssl_CFLAGS openssl_LIBS libssh2_CFLAGS libssh2_LIBS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures git2r see.DESCRIPTION.file to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/git2r] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of git2r see.DESCRIPTION.file:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-rpath do not hardcode runtime library paths Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-libgit2 Ignore presence of a system libgit2 library and instead use the internal git2r libgit2 library --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib --without-libiconv-prefix don't search for libiconv in includedir and libdir Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path zlib_CFLAGS C compiler flags for zlib, overriding pkg-config zlib_LIBS linker flags for zlib, overriding pkg-config openssl_CFLAGS C compiler flags for openssl, overriding pkg-config openssl_LIBS linker flags for openssl, overriding pkg-config libssh2_CFLAGS C compiler flags for libssh2, overriding pkg-config libssh2_LIBS linker flags for libssh2, overriding pkg-config CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF git2r configure see.DESCRIPTION.file generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } if eval \${$4+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$4 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by git2r $as_me see.DESCRIPTION.file, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in tools "$srcdir"/tools; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in tools \"$srcdir\"/tools" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # iconv.m4 serial 19 (gettext-0.18.2) # lib-ld.m4 serial 6 # lib-link.m4 serial 26 (gettext-0.18.2) # lib-prefix.m4 serial 7 (gettext-0.18) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 11 (pkg-config-0.29.1) # System libgit2 # Check whether --with-libgit2 was given. if test "${with_libgit2+set}" = set; then : withval=$with_libgit2; fi # Checks for programs. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ## Check for brew on macOS # Check for pkg-config if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi # Check for R : ${R_HOME=`R RHOME`} if test -z "${R_HOME}"; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Could not determine R_HOME See \`config.log' for more details" "$LINENO" 5; } fi RBIN="${R_HOME}/bin/R" # Library settings PKG_CONFIG_NAME="libgit2" PKG_BREW_NAME="libgit2" PKG_LIBS="-lgit2" PKG_CFLAGS="" # The minimum version of libgit2 that is compatible with git2r. The # version 0.26 is in all Fedora releases and at least the latest # Ubuntu. LIBGIT2_MIN_VERSION=0.26.0 # Check if building against bundled libgit2 or system libgit2 USE_BUNDLED_LIBGIT2=yes if test "x$with_libgit2" = "xno"; then { $as_echo "$as_me:${as_lineno-$LINENO}: ignore presence of a system libgit2 library" >&5 $as_echo "$as_me: ignore presence of a system libgit2 library" >&6;} elif test "x${INCLUDE_DIR}${LIB_DIR}" = x; then if test -n "$PKG_CONFIG" ; then # Check if libgit2 is installed and have a version that is # compatible with git2r. if $PKG_CONFIG ${PKG_CONFIG_NAME} --atleast-version=${LIBGIT2_MIN_VERSION}; then PKGCONFIG_CFLAGS=`"${PKG_CONFIG}" --cflags "${PKG_CONFIG_NAME}"` PKGCONFIG_LIBS=`"${PKG_CONFIG}" --libs "${PKG_CONFIG_NAME}"` fi fi if test "x${PKGCONFIG_CFLAGS}${PKGCONFIG_LIBS}" = x; then case "${host_os}" in darwin*) # Extract the first word of "brew", so it can be a program name with args. set dummy brew; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_BREW+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$BREW"; then ac_cv_prog_BREW="$BREW" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_BREW="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi BREW=$ac_cv_prog_BREW if test -n "$BREW"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BREW" >&5 $as_echo "$BREW" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x${BREW}" = xyes; then BREWDIR=`brew --prefix` USE_BUNDLED_LIBGIT2=no else curl -sfL "https://autobrew.github.io/scripts/libgit2" > autobrew . autobrew USE_BUNDLED_LIBGIT2=no fi ;; esac else echo "Found pkg-config cflags and libs!" PKG_CFLAGS="${PKGCONFIG_CFLAGS}" PKG_LIBS="${PKGCONFIG_LIBS}" USE_BUNDLED_LIBGIT2=no fi else echo "Found INCLUDE_DIR and/or LIB_DIR!" PKG_CFLAGS="-I${INCLUDE_DIR} ${PKG_CFLAGS}" PKG_LIBS="-L${LIB_DIR} ${PKG_LIBS}" USE_BUNDLED_LIBGIT2=no fi # Find the compiler and compiler flags to use CC=`"${RBIN}" CMD config CC` CFLAGS=`"${RBIN}" CMD config CFLAGS` CPPFLAGS=`"${RBIN}" CMD config CPPFLAGS` # If a system installation of libgit2 is available, check that the version # works with git2r. if test "x${USE_BUNDLED_LIBGIT2}" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the libgit2 version will work in git2r" >&5 $as_echo_n "checking whether the libgit2 version will work in git2r... " >&6; } libgit2_ver_ok=no ${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E tools/version.c >/dev/null 2>&1 && libgit2_ver_ok=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${libgit2_ver_ok}" >&5 $as_echo "${libgit2_ver_ok}" >&6; } if test "x${libgit2_ver_ok}" = xno; then USE_BUNDLED_LIBGIT2=yes fi fi ################# Begin configuration to use system libgit2 ################## if test "x${USE_BUNDLED_LIBGIT2}" = xno; then # The function 'git_buf_free' is deprecated in libgit2 # v0.28.0. Use 'git_buf_dispose', if available, instead. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the libgit2 function git_buf_dispose is available" >&5 $as_echo_n "checking whether the libgit2 function git_buf_dispose is available... " >&6; } have_buf_dispose=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { git_buf_dispose(NULL); ; return 0; } _ACEOF PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&5 2>&5 && have_buf_dispose=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${have_buf_dispose}" >&5 $as_echo "${have_buf_dispose}" >&6; } if test "x${have_buf_dispose}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_HAVE_BUF_DISPOSE" fi # The constants GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, # GIT_OBJ_TAG_GIT_OBJ_TREE and GIT_REF_OID are deprecated in # libgit2 v0.28.0. Use GIT_OBJECT_ANY, GIT_OBJECT_BLOB, # GIT_OBJECT_COMMIT, GIT_OBJECT_TAG_GIT_OBJECT_TREE and # GIT_REFERENCE_DIRECT, if available, instead. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the libgit2 constant GIT_OBJECT_ANY is available" >&5 $as_echo_n "checking whether the libgit2 constant GIT_OBJECT_ANY is available... " >&6; } have_git_object_any=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { git_object_typeisloose(GIT_OBJECT_ANY); ; return 0; } _ACEOF PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&5 2>&5 && have_git_object_any=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${have_git_object_any}" >&5 $as_echo "${have_git_object_any}" >&6; } if test "x${have_git_object_any}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_HAVE_OBJECT_ANY" fi # Several libgit2 error functions and enumaration values have been # deprecated, use newer versions. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the libgit2 function git_error_last is available" >&5 $as_echo_n "checking whether the libgit2 function git_error_last is available... " >&6; } have_git_error_last=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { git_error_last(); ; return 0; } _ACEOF PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&5 2>&5 && have_git_error_last=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${have_git_error_last}" >&5 $as_echo "${have_git_error_last}" >&6; } if test "x${have_git_error_last}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_HAVE_GIT_ERROR" fi # libgit v0.99.0: Several structures, enums and values have been # renamed in libgit version 0.99.0. The former names are # deprecated. See # https://github.com/libgit2/libgit2/releases/tag/v0.99.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the libgit2 function git_oid_is_zero is available" >&5 $as_echo_n "checking whether the libgit2 function git_oid_is_zero is available... " >&6; } have_git_oid_is_zero=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { git_oid_is_zero(NULL); ; return 0; } _ACEOF PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&5 2>&5 && have_git_oid_is_zero=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${have_git_oid_is_zero}" >&5 $as_echo "${have_git_oid_is_zero}" >&6; } if test "x${have_git_oid_is_zero}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_LIBGIT2_V0_99_0_RENAMES" fi # For debugging echo "----- Results of the git2r package configure -----" echo "" echo " PKG_CFLAGS: ${PKG_CFLAGS}" echo " PKG_LIBS: ${PKG_LIBS}" echo "" echo "--------------------------------------------------" PKG_CFLAGS="${PKG_CFLAGS}" PKG_LIBS="${PKG_LIBS}" ac_config_files="$ac_config_files src/Makevars" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by git2r $as_me see.DESCRIPTION.file, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ git2r config.status see.DESCRIPTION.file configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/Makevars") CONFIG_FILES="$CONFIG_FILES src/Makevars" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi fi ################# End configuration to use system libgit2 #################### ################# Begin configuration to build bundled libgit2 ############### if test "x${USE_BUNDLED_LIBGIT2}" = xyes; then if test "x$with_libgit2" = "xyes"; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "system libgit2 requested but not found See \`config.log' for more details" "$LINENO" 5; } elif test "x$with_libgit2" = "xno"; then { $as_echo "$as_me:${as_lineno-$LINENO}: attempting configuration of bundled libgit2" >&5 $as_echo "$as_me: attempting configuration of bundled libgit2" >&6;} else { $as_echo "$as_me:${as_lineno-$LINENO}: package dependency requirement 'libgit2 >= ${LIBGIT2_MIN_VERSION}' could not be satisfied." >&5 $as_echo "$as_me: package dependency requirement 'libgit2 >= ${LIBGIT2_MIN_VERSION}' could not be satisfied." >&6;} echo " ----------------------------------------------------------------------- Unable to find the libgit2 library on this system. Building 'git2r' using the bundled source of the libgit2 library. To build git2r with a system installation of libgit2, please install: libgit2-dev (package on e.g. Debian and Ubuntu) libgit2-devel (package on e.g. Fedora, CentOS and RHEL) libgit2 (Homebrew package on OS X) and try again. If the libgit2 library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to libgit2 with: given you downloaded a tar-gz archive: R CMD INSTALL git2r-.tar.gz --configure-vars='INCLUDE_DIR=/path/to/include LIB_DIR=/path/to/lib' or cloned the GitHub git2r repository into a directory: R CMD INSTALL git2r/ --configure-vars='INCLUDE_DIR=/path/to/include LIB_DIR=/path/to/lib' or download and install git2r in R using install.packages('git2r', type='source', configure.vars='LIB_DIR=-L/path/to/libs INCLUDE_DIR=-I/path/to/headers') On macOS, another possibility is to let the configuration automatically download the libgit2 library from the Homebrew package manager with: R CMD INSTALL git2r-.tar.gz --configure-vars='autobrew=yes' or R CMD INSTALL git2r/ --configure-vars='autobrew=yes' or install.packages('git2r', type='source', configure.vars='autobrew=yes') ----------------------------------------------------------------------- " { $as_echo "$as_me:${as_lineno-$LINENO}: attempting configuration of bundled libgit2" >&5 $as_echo "$as_me: attempting configuration of bundled libgit2" >&6;} fi # Use R to determine architecture of the machine { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void*" >&5 $as_echo_n "checking size of void*... " >&6; } sizeof_voidp=`"${RBIN}" --slave --vanilla -e "cat(.Machine\\$sizeof.pointer)"` { $as_echo "$as_me:${as_lineno-$LINENO}: result: $sizeof_voidp" >&5 $as_echo "$sizeof_voidp" >&6; } if test "x$sizeof_voidp" = "x8"; then CPPFLAGS="${CPPFLAGS} -DGIT_ARCH_64" elif test "x$sizeof_voidp" = "x4"; then CPPFLAGS="${CPPFLAGS} -DGIT_ARCH_32" else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Unsupported architecture See \`config.log' for more details" "$LINENO" 5; } fi # Check for zlib have_zlib=no if test -n "$PKG_CONFIG" ; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for zlib" >&5 $as_echo_n "checking for zlib... " >&6; } if test -n "$zlib_CFLAGS"; then pkg_cv_zlib_CFLAGS="$zlib_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_zlib_CFLAGS=`$PKG_CONFIG --cflags "zlib" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$zlib_LIBS"; then pkg_cv_zlib_LIBS="$zlib_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_zlib_LIBS=`$PKG_CONFIG --libs "zlib" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then zlib_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib" 2>&1` else zlib_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$zlib_PKG_ERRORS" >&5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else zlib_CFLAGS=$pkg_cv_zlib_CFLAGS zlib_LIBS=$pkg_cv_zlib_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } CPPFLAGS="${zlib_CFLAGS} ${CPPFLAGS}" LIBS="${zlib_LIBS} ${LIBS}" have_zlib=yes fi fi if test "x${have_zlib}" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing inflate" >&5 $as_echo_n "checking for library containing inflate... " >&6; } if ${ac_cv_search_inflate+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inflate (); int main () { return inflate (); ; return 0; } _ACEOF for ac_lib in '' z; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_inflate=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_inflate+:} false; then : break fi done if ${ac_cv_search_inflate+:} false; then : else ac_cv_search_inflate=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inflate" >&5 $as_echo "$ac_cv_search_inflate" >&6; } ac_res=$ac_cv_search_inflate if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" have_zlib=yes fi fi if test "x${have_zlib}" = xno; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? " --------------------------------------------- The zlib library that is required to build git2r was not found. Please install: zlib1g-dev (package on e.g. Debian and Ubuntu) zlib-devel (package on e.g. Fedora, CentOS and RHEL) and try again. If the zlib library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to zlib with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' --------------------------------------------- See \`config.log' for more details" "$LINENO" 5; } fi # Check for SSL for https transport have_ssl=no case "${host_os}" in darwin*) # On macOS, use the Security and CoreFoundation framework have_ssl=yes CPPFLAGS="${CPPFLAGS} -DGIT_SECURE_TRANSPORT=1 -DGIT_HTTPS=1" LIBS="${LIBS} -framework Security -framework CoreFoundation" ;; *) if test "x${OPENSSL_INCLUDES}" = x; then :; else CPPFLAGS="${CPPFLAGS} -I${OPENSSL_INCLUDES}" fi if test -n "$PKG_CONFIG" ; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl" >&5 $as_echo_n "checking for openssl... " >&6; } if test -n "$openssl_CFLAGS"; then pkg_cv_openssl_CFLAGS="$openssl_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_openssl_CFLAGS=`$PKG_CONFIG --cflags "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$openssl_LIBS"; then pkg_cv_openssl_LIBS="$openssl_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_openssl_LIBS=`$PKG_CONFIG --libs "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then openssl_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl" 2>&1` else openssl_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$openssl_PKG_ERRORS" >&5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else openssl_CFLAGS=$pkg_cv_openssl_CFLAGS openssl_LIBS=$pkg_cv_openssl_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } CPPFLAGS="${openssl_CFLAGS} ${CPPFLAGS}" LIBS="${openssl_LIBS} ${LIBS}" have_ssl=yes fi fi if test "x${have_ssl}" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing EVP_EncryptInit" >&5 $as_echo_n "checking for library containing EVP_EncryptInit... " >&6; } if ${ac_cv_search_EVP_EncryptInit+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char EVP_EncryptInit (); int main () { return EVP_EncryptInit (); ; return 0; } _ACEOF for ac_lib in '' crypto; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_EVP_EncryptInit=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_EVP_EncryptInit+:} false; then : break fi done if ${ac_cv_search_EVP_EncryptInit+:} false; then : else ac_cv_search_EVP_EncryptInit=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_EVP_EncryptInit" >&5 $as_echo "$ac_cv_search_EVP_EncryptInit" >&6; } ac_res=$ac_cv_search_EVP_EncryptInit if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing SSL_library_init" >&5 $as_echo_n "checking for library containing SSL_library_init... " >&6; } if ${ac_cv_search_SSL_library_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char SSL_library_init (); int main () { return SSL_library_init (); ; return 0; } _ACEOF for ac_lib in '' ssl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_SSL_library_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_SSL_library_init+:} false; then : break fi done if ${ac_cv_search_SSL_library_init+:} false; then : else ac_cv_search_SSL_library_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_SSL_library_init" >&5 $as_echo "$ac_cv_search_SSL_library_init" >&6; } ac_res=$ac_cv_search_SSL_library_init if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" have_ssl=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing OPENSSL_init_ssl" >&5 $as_echo_n "checking for library containing OPENSSL_init_ssl... " >&6; } if ${ac_cv_search_OPENSSL_init_ssl+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char OPENSSL_init_ssl (); int main () { return OPENSSL_init_ssl (); ; return 0; } _ACEOF for ac_lib in '' ssl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_OPENSSL_init_ssl=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_OPENSSL_init_ssl+:} false; then : break fi done if ${ac_cv_search_OPENSSL_init_ssl+:} false; then : else ac_cv_search_OPENSSL_init_ssl=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_OPENSSL_init_ssl" >&5 $as_echo "$ac_cv_search_OPENSSL_init_ssl" >&6; } ac_res=$ac_cv_search_OPENSSL_init_ssl if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" have_ssl=yes fi fi fi if test "x${have_ssl}" = xyes; then CPPFLAGS="${CPPFLAGS} -DGIT_OPENSSL=1 -DGIT_HTTPS=1" else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --------------------------------------------- Unable to find the OpenSSL library on this system. Building a version without support for https transport. To build with https support, please install: libssl-dev (package on e.g. Debian and Ubuntu) openssl-devel (package on e.g. Fedora, CentOS and RHEL) and try again. If the OpenSSL library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to OpenSSL with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' ---------------------------------------------" >&5 $as_echo "$as_me: WARNING: --------------------------------------------- Unable to find the OpenSSL library on this system. Building a version without support for https transport. To build with https support, please install: libssl-dev (package on e.g. Debian and Ubuntu) openssl-devel (package on e.g. Fedora, CentOS and RHEL) and try again. If the OpenSSL library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to OpenSSL with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' ---------------------------------------------" >&2;} fi ;; esac # Check for LibSSH2 have_ssh2=no if test -n "$PKG_CONFIG" ; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libssh2" >&5 $as_echo_n "checking for libssh2... " >&6; } if test -n "$libssh2_CFLAGS"; then pkg_cv_libssh2_CFLAGS="$libssh2_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libssh2 >= 1.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "libssh2 >= 1.8") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_libssh2_CFLAGS=`$PKG_CONFIG --cflags "libssh2 >= 1.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$libssh2_LIBS"; then pkg_cv_libssh2_LIBS="$libssh2_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libssh2 >= 1.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "libssh2 >= 1.8") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_libssh2_LIBS=`$PKG_CONFIG --libs "libssh2 >= 1.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then libssh2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libssh2 >= 1.8" 2>&1` else libssh2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libssh2 >= 1.8" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$libssh2_PKG_ERRORS" >&5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else libssh2_CFLAGS=$pkg_cv_libssh2_CFLAGS libssh2_LIBS=$pkg_cv_libssh2_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } CPPFLAGS="${libssh2_CFLAGS} ${CPPFLAGS}" LIBS="${libssh2_LIBS} ${LIBS}" have_ssh2=yes fi fi if test "x${have_ssh2}" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --------------------------------------------- Unable to find the LibSSH2 (ver >= v1.8) library on this system. Building git2r without support for SSH transport. To build with SSH support, please install: libssh2-1-dev (package on e.g. Debian and Ubuntu) libssh2-devel (package on e.g. Fedora, CentOS and RHEL) libssh2 (Homebrew package on OS X) and try again. If the LibSSH2 library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to LibSSH2 with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' ---------------------------------------------" >&5 $as_echo "$as_me: WARNING: --------------------------------------------- Unable to find the LibSSH2 (ver >= v1.8) library on this system. Building git2r without support for SSH transport. To build with SSH support, please install: libssh2-1-dev (package on e.g. Debian and Ubuntu) libssh2-devel (package on e.g. Fedora, CentOS and RHEL) libssh2 (Homebrew package on OS X) and try again. If the LibSSH2 library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to LibSSH2 with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' ---------------------------------------------" >&2;} else CPPFLAGS="${CPPFLAGS} -DGIT_SSH" fi # Check for iconv case "${host_os}" in darwin*) if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then # Determine PATH_SEPARATOR by trying to find /bin/sh in a PATH which # contains only /bin. Note that ksh looks also at the FPATH variable, # so we have to set that as well for the test. PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ || PATH_SEPARATOR=';' } fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`echo "$ac_prog"| sed 's%\\\\%/%g'` while echo "$ac_prog" | grep "$re_direlt" > /dev/null 2>&1; do ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${acl_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then acl_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$acl_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${acl_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$acl_cv_prog_gnu_ld" >&6; } with_gnu_ld=$acl_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5 $as_echo_n "checking for shared library run path origin... " >&6; } if ${acl_cv_rpath+:} false; then : $as_echo_n "(cached) " >&6 else CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5 $as_echo "$acl_cv_rpath" >&6; } wl="$acl_cv_wl" acl_libext="$acl_cv_libext" acl_shlibext="$acl_cv_shlibext" acl_libname_spec="$acl_cv_libname_spec" acl_library_names_spec="$acl_cv_library_names_spec" acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" acl_hardcode_direct="$acl_cv_hardcode_direct" acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" # Check whether --enable-rpath was given. if test "${enable_rpath+set}" = set; then : enableval=$enable_rpath; : else enable_rpath=yes fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" acl_libdirstem=lib acl_libdirstem2= case "$host_os" in solaris*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit host" >&5 $as_echo_n "checking for 64-bit host... " >&6; } if ${gl_cv_solaris_64bit+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef _LP64 sixtyfour bits #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "sixtyfour bits" >/dev/null 2>&1; then : gl_cv_solaris_64bit=yes else gl_cv_solaris_64bit=no fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_solaris_64bit" >&5 $as_echo "$gl_cv_solaris_64bit" >&6; } if test $gl_cv_solaris_64bit = yes; then acl_libdirstem=lib/64 case "$host_cpu" in sparc*) acl_libdirstem2=lib/sparcv9 ;; i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; esac fi ;; *) searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` if test -n "$searchpath"; then acl_save_IFS="${IFS= }"; IFS=":" for searchdir in $searchpath; do if test -d "$searchdir"; then case "$searchdir" in */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; */../ | */.. ) # Better ignore directories of this form. They are misleading. ;; *) searchdir=`cd "$searchdir" && pwd` case "$searchdir" in */lib64 ) acl_libdirstem=lib64 ;; esac ;; esac fi done IFS="$acl_save_IFS" fi ;; esac test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" use_additional=yes acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" # Check whether --with-libiconv-prefix was given. if test "${with_libiconv_prefix+set}" = set; then : withval=$with_libiconv_prefix; if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" if test "$acl_libdirstem2" != "$acl_libdirstem" \ && ! test -d "$withval/$acl_libdirstem"; then additional_libdir="$withval/$acl_libdirstem2" fi fi fi fi LIBICONV= LTLIBICONV= INCICONV= LIBICONV_PREFIX= HAVE_LIBICONV= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='iconv ' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./+-|ABCDEFGHIJKLMNOPQRSTUVWXYZ____|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value" else : fi else found_dir= found_la= found_so= found_a= eval libname=\"$acl_libname_spec\" # typically: libname=lib$name if test -n "$acl_shlibext"; then shrext=".$acl_shlibext" # typically: shrext=.so else shrext= fi if test $use_additional = yes; then dir="$additional_libdir" if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then if test "$enable_rpath" = no \ || test "X$found_dir" = "X/usr/$acl_libdirstem" \ || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi if test "$acl_hardcode_direct" = yes; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else haveit= for x in $LDFLAGS $LIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir" fi if test "$acl_hardcode_minus_L" != no; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" else LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a" else LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name" fi fi additional_includedir= case "$found_dir" in */$acl_libdirstem | */$acl_libdirstem/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` if test "$name" = 'iconv'; then LIBICONV_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; */$acl_libdirstem2 | */$acl_libdirstem2/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` if test "$name" = 'iconv'; then LIBICONV_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INCICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir" fi fi fi fi fi if test -n "$found_la"; then save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then haveit= if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIBICONV; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) LIBICONV="${LIBICONV}${LIBICONV:+ }$dep" LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep" ;; esac done fi else LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$acl_hardcode_libdir_separator"; then alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" done acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" else for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then for found_dir in $ltrpathdirs; do LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir" done fi am_save_CPPFLAGS="$CPPFLAGS" for element in $INCICONV; do haveit= for x in $CPPFLAGS; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5 $as_echo_n "checking for iconv... " >&6; } if ${am_cv_func_iconv+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : am_cv_func_iconv=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : am_cv_lib_iconv=yes am_cv_func_iconv=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$am_save_LIBS" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5 $as_echo "$am_cv_func_iconv" >&6; } if test "$am_cv_func_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working iconv" >&5 $as_echo_n "checking for working iconv... " >&6; } if ${am_cv_func_iconv_works+:} false; then : $as_echo_n "(cached) " >&6 else am_save_LIBS="$LIBS" if test $am_cv_lib_iconv = yes; then LIBS="$LIBS $LIBICONV" fi am_cv_func_iconv_works=no for ac_iconv_const in '' 'const'; do if test "$cross_compiling" = yes; then : case "$host_os" in aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; *) am_cv_func_iconv_works="guessing yes" ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifndef ICONV_CONST # define ICONV_CONST $ac_iconv_const #endif int main () { int result = 0; /* Test against AIX 5.1 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); if (cd_utf8_to_88591 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\342\202\254"; /* EURO SIGN */ char buf[10]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_utf8_to_88591, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) result |= 1; iconv_close (cd_utf8_to_88591); } } /* Test against Solaris 10 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); if (cd_ascii_to_88591 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\263"; char buf[10]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_ascii_to_88591, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) result |= 2; iconv_close (cd_ascii_to_88591); } } /* Test against AIX 6.1..7.1 bug: Buffer overrun. */ { iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\304"; static char buf[2] = { (char)0xDE, (char)0xAD }; ICONV_CONST char *inptr = input; size_t inbytesleft = 1; char *outptr = buf; size_t outbytesleft = 1; size_t res = iconv (cd_88591_to_utf8, &inptr, &inbytesleft, &outptr, &outbytesleft); if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD) result |= 4; iconv_close (cd_88591_to_utf8); } } #if 0 /* This bug could be worked around by the caller. */ /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ { iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static ICONV_CONST char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; char buf[50]; ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_88591_to_utf8, &inptr, &inbytesleft, &outptr, &outbytesleft); if ((int)res > 0) result |= 8; iconv_close (cd_88591_to_utf8); } } #endif /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is provided. */ if (/* Try standardized names. */ iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) /* Try IRIX, OSF/1 names. */ && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) /* Try AIX names. */ && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) /* Try HP-UX names. */ && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) result |= 16; return result; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : am_cv_func_iconv_works=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi test "$am_cv_func_iconv_works" = no || break done LIBS="$am_save_LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv_works" >&5 $as_echo "$am_cv_func_iconv_works" >&6; } case "$am_cv_func_iconv_works" in *no) am_func_iconv=no am_cv_lib_iconv=no ;; *) am_func_iconv=yes ;; esac else am_func_iconv=no am_cv_lib_iconv=no fi if test "$am_func_iconv" = yes; then $as_echo "#define HAVE_ICONV 1" >>confdefs.h fi if test "$am_cv_lib_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5 $as_echo_n "checking how to link with libiconv... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5 $as_echo "$LIBICONV" >&6; } else CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi if test "$am_cv_func_iconv" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv declaration" >&5 $as_echo_n "checking for iconv declaration... " >&6; } if ${am_cv_proto_iconv+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : am_cv_proto_iconv_arg1="" else am_cv_proto_iconv_arg1="const" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);" fi am_cv_proto_iconv=`echo "$am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_proto_iconv" >&5 $as_echo " $am_cv_proto_iconv" >&6; } cat >>confdefs.h <<_ACEOF #define ICONV_CONST $am_cv_proto_iconv_arg1 _ACEOF fi if test "x${am_cv_func_iconv}" = xyes; then CPPFLAGS="${CPPFLAGS} -DGIT_USE_ICONV" fi ;; esac # Configuration specific for solaris case "${host_os}" in solaris*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing connect" >&5 $as_echo_n "checking for library containing connect... " >&6; } if ${ac_cv_search_connect+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char connect (); int main () { return connect (); ; return 0; } _ACEOF for ac_lib in '' socket; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_connect=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_connect+:} false; then : break fi done if ${ac_cv_search_connect+:} false; then : else ac_cv_search_connect=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_connect" >&5 $as_echo "$ac_cv_search_connect" >&6; } ac_res=$ac_cv_search_connect if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 $as_echo_n "checking for library containing gethostbyname... " >&6; } if ${ac_cv_search_gethostbyname+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gethostbyname (); int main () { return gethostbyname (); ; return 0; } _ACEOF for ac_lib in '' nsl socket; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_gethostbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_gethostbyname+:} false; then : break fi done if ${ac_cv_search_gethostbyname+:} false; then : else ac_cv_search_gethostbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 $as_echo "$ac_cv_search_gethostbyname" >&6; } ac_res=$ac_cv_search_gethostbyname if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # Include and use regex on solaris CPPFLAGS="-Ilibgit2/deps/regex ${CPPFLAGS}" GIT2R_SRC_REGEX=libgit2/deps/regex/regex.o ;; esac # Add include paths for git2r CPPFLAGS="-I. -Ilibgit2/src -Ilibgit2/include -Ilibgit2/deps/http-parser ${CPPFLAGS}" # Add definitions CPPFLAGS="${CPPFLAGS} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DLIBGIT2_NO_FEATURES_H -DR_NO_REMAP -DSTRICT_R_HEADERS" # Specify sha1 implementation case "${host_os}" in darwin*) CPPFLAGS="${CPPFLAGS} -DGIT_SHA1_COLLISIONDETECT=1 -DSHA1DC_NO_STANDARD_INCLUDES=1" CPPFLAGS="${CPPFLAGS} -DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\\\"common.h\\\" -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\\\"common.h\\\"" GIT2R_SRC_SHA1="libgit2/src/hash/sha1/collisiondetect.o libgit2/src/hash/sha1/sha1dc/sha1.o libgit2/src/hash/sha1/sha1dc/ubc_check.o" ;; *) if test "x${have_ssl}" = xyes; then CPPFLAGS="${CPPFLAGS} -DGIT_SHA1_OPENSSL=1" GIT2R_SRC_SHA1="libgit2/src/hash/sha1/openssl.o" else CPPFLAGS="${CPPFLAGS} -DGIT_SHA1_COLLISIONDETECT=1 -DSHA1DC_NO_STANDARD_INCLUDES=1" CPPFLAGS="${CPPFLAGS} -DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\\\"common.h\\\" -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\\\"common.h\\\"" GIT2R_SRC_SHA1="libgit2/src/hash/sha1/sha1dc/sha1.o libgit2/src/hash/sha1/sha1dc/ubc_check.o" fi ;; esac # Add definitions specific for solaris case "${host_os}" in solaris*) CPPFLAGS="${CPPFLAGS} -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS" ;; esac # Checks for structures { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim" "ac_cv_member_struct_stat_st_mtim" "$ac_includes_default " if test "x$ac_cv_member_struct_stat_st_mtim" = xyes; then : CPPFLAGS="${CPPFLAGS} -DGIT_USE_STAT_MTIM" fi ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimespec" "ac_cv_member_struct_stat_st_mtimespec" "$ac_includes_default " if test "x$ac_cv_member_struct_stat_st_mtimespec" = xyes; then : CPPFLAGS="${CPPFLAGS} -DGIT_USE_STAT_MTIMESPEC" fi ac_fn_c_check_member "$LINENO" "struct stat" "st_mtime_nsec" "ac_cv_member_struct_stat_st_mtime_nsec" "$ac_includes_default " if test "x$ac_cv_member_struct_stat_st_mtime_nsec" = xyes; then : CPPFLAGS="${CPPFLAGS} -DGIT_USE_STAT_MTIME_NSEC" fi if test "x$ac_cv_member_struct_stat_st_mtim" = "xyes"; then ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim.tv_nsec" "ac_cv_member_struct_stat_st_mtim_tv_nsec" "$ac_includes_default " if test "x$ac_cv_member_struct_stat_st_mtim_tv_nsec" = xyes; then : CPPFLAGS="${CPPFLAGS} -DGIT_USE_NSEC" fi elif test "x$ac_cv_member_struct_stat_st_mtimespec" = "xyes"; then ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimespec.tv_nsec" "ac_cv_member_struct_stat_st_mtimespec_tv_nsec" "$ac_includes_default " if test "x$ac_cv_member_struct_stat_st_mtimespec_tv_nsec" = xyes; then : CPPFLAGS="${CPPFLAGS} -DGIT_USE_NSEC" fi else CPPFLAGS="${CPPFLAGS} -DGIT_USE_NSEC" fi # Checks for library functions. for ac_func in futimens qsort_r qsort_s do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done if test $ac_cv_func_futimens = yes; then CPPFLAGS="${CPPFLAGS} -DHAVE_FUTIMENS" fi if test $ac_cv_func_qsort_r = yes; then CPPFLAGS="${CPPFLAGS} -DHAVE_QSORT_R" fi if test $ac_cv_func_qsort_s = yes; then CPPFLAGS="${CPPFLAGS} -DHAVE_QSORT_S" fi # The function 'git_buf_free' is deprecated in libgit2. Use # 'git_buf_dispose' instead. # # CPPFLAGS="${CPPFLAGS} -DGIT_DEPRECATE_HARD -DGIT2R_HAVE_BUF_DISPOSE" CPPFLAGS="${CPPFLAGS} -DGIT2R_HAVE_BUF_DISPOSE" # The constants GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, # GIT_OBJ_TAG_GIT_OBJ_TREE and GIT_REF_OID are deprecated in # libgit2. Use GIT_OBJECT_ANY, GIT_OBJECT_BLOB, GIT_OBJECT_COMMIT, # GIT_OBJECT_TAG_GIT_OBJECT_TREE and GIT_REFERENCE_DIRECT instead. CPPFLAGS="${CPPFLAGS} -DGIT2R_HAVE_OBJECT_ANY" # Several libgit2 error functions and enumaration values have been # deprecated, use newer versions. CPPFLAGS="${CPPFLAGS} -DGIT2R_HAVE_GIT_ERROR" # libgit v0.99.0: Several structures, enums and values have been # renamed in libgit version 0.99.0. The former names are # deprecated. See # https://github.com/libgit2/libgit2/releases/tag/v0.99.0 CPPFLAGS="${CPPFLAGS} -DGIT2R_LIBGIT2_V0_99_0_RENAMES" CPPFLAGS="${CPPFLAGS} -DGIT_REGEX_REGCOMP -DTHREADSAFE=OFF" PKG_CFLAGS="${PKG_CFLAGS}" PKG_CPPFLAGS="${CPPFLAGS}" PKG_LIBS="${LIBS} ${LIBICONV}" ac_config_files="$ac_config_files src/Makevars_libgit2" echo " ----- Results of the git2r package configure ----- HTTPS transport......................: ${have_ssl} LibSSH2 to enable the SSH transport..: ${have_ssh2} -------------------------------------------------- " cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by git2r $as_me see.DESCRIPTION.file, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ git2r config.status see.DESCRIPTION.file configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/Makevars") CONFIG_FILES="$CONFIG_FILES src/Makevars" ;; "src/Makevars_libgit2") CONFIG_FILES="$CONFIG_FILES src/Makevars_libgit2" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi cd src; mv Makevars_libgit2 Makevars fi ################# End configuration to build bundled libgit2 ################# git2r/R/0000755000175000017500000000000014076203020011626 5ustar nileshnileshgit2r/R/config.R0000644000175000017500000001465513526217230013240 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Config ##' ##' Config file management. To display the configuration variables, ##' call method \code{config} without the \code{user.name}, ##' \code{user.email} or \code{...} options. ##' ##' There are two ways git2r can find the local repository when ##' writing local options (1) Use the \code{repo} argument. (2) If the ##' \code{repo} argument is \code{NULL} but the current working ##' directory is inside the local repository, then \code{git2r} uses ##' that repository. ##' @param repo The \code{repository}. Default is NULL. ##' @param global Write option(s) to global configuration ##' file. Default is FALSE. ##' @param user.name The user name. Use NULL to delete the entry ##' @param user.email The e-mail address. Use NULL to delete the entry ##' @param ... Additional options to write or delete from the ##' configuration. ##' @return S3 class \code{git_config}. When writing options, the ##' configuration is returned invisible. ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern = "git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Set user name and email. ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Display configuration ##' config(repo) ##' ##' ## Delete user email. ##' config(repo, user.email = NULL) ##' ##' ## Display configuration ##' config(repo) ##' } config <- function(repo = NULL, global = FALSE, user.name, user.email, ...) { if (is.null(repo)) { repo <- discover_repository(getwd()) if (!is.null(repo)) repo <- repository(repo) } ## Check that 'global' is either TRUE or FALSE stopifnot(any(identical(global, TRUE), identical(global, FALSE))) variables <- list(...) if (!missing(user.name)) variables <- c(variables, list(user.name = user.name)) if (!missing(user.email)) variables <- c(variables, list(user.email = user.email)) if (length(variables)) { for (i in seq_len(length(variables))) { if (!is.null(variables[[i]])) { if (!is.character(variables[[i]])) { stop("'", names(variables)[i], "' must be a character vector") } } } if (isTRUE(global)) { repo <- NULL if (.Platform$OS.type == "windows") { ## Ensure that git2r writes the config file to the ## root of the user's home directory by first creating ## an empty file. Otherwise it may be written to the ## user's Documents/ directory. Only create the empty ## file if the user has specified configuration ## options to set and no global config file exists. if (is.na(git_config_files()[["path"]][3])) { if (length(variables) > 0) { file.create(file.path(home_dir(), ".gitconfig")) } } } } else if (is.null(repo)) { stop("Unable to locate local repository") } .Call(git2r_config_set, repo, variables) } cfg <- .Call(git2r_config_get, repo) ## Sort the variables within levels by name cfg <- structure(lapply(cfg, function(x) x[order(names(x))]), class = "git_config") if (length(variables)) { invisible(cfg) } else { return(cfg) } } ##' @export print.git_config <- function(x, ...) { lapply(names(x), function(level) { cat(sprintf("%s:\n", level)) lapply(names(x[[level]]), function(entry) { cat(sprintf(" %s=%s\n", entry, x[[level]][[entry]][1])) }) }) invisible(x) } ##' Locate the path to configuration files ##' ##' Potential configuration files: ##' \describe{ ##' \item{system}{ ##' Locate the path to the system configuration file. If ##' '/etc/gitconfig' doesn't exist, it will look for ##' '\%PROGRAMFILES\%'. ##' } ##' \item{xdg}{ ##' Locate the path to the global xdg compatible configuration ##' file. The xdg compatible configuration file is usually located ##' in '$HOME/.config/git/config'. This method will try to guess ##' the full path to that file, if the file exists. ##' } ##' \item{global}{ ##' The user or global configuration file is usually located in ##' '$HOME/.gitconfig'. This method will try to guess the full ##' path to that file, if the file exists. ##' } ##' \item{local}{ ##' Locate the path to the repository specific configuration file, ##' if the file exists. ##' } ##' } ##' @template repo-param ##' @return a \code{data.frame} with one row per potential ##' configuration file where \code{NA} means not found. ##' @export git_config_files <- function(repo = ".") { ## Lookup repository if (inherits(repo, "git_repository")) { repo <- repo$path } else if (is.null(repo)) { repo <- discover_repository(getwd()) } else if (is.character(repo) && (length(repo) == 1) && !is.na(repo) && isTRUE(file.info(repo)$isdir)) { repo <- discover_repository(repo) } else { repo <- NULL } ## Find local configuration file if (is.null(repo)) { path <- NA_character_ } else { path <- file.path(normalizePath(repo), "config") if (!isTRUE(!file.info(path)$isdir)) path <- NA_character_ } data.frame(file = c("system", "xdg", "global", "local"), path = c(.Call(git2r_config_find_file, "system"), .Call(git2r_config_find_file, "xdg"), .Call(git2r_config_find_file, "global"), path), stringsAsFactors = FALSE) } git2r/R/reflog.R0000644000175000017500000000634113526205731013245 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' List and view reflog information ##' ##' @template repo-param ##' @param refname The name of the reference to list. 'HEAD' by ##' default. ##' @return S3 class \code{git_reflog} with git_reflog_entry objects. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Second commit message") ##' ##' ## Change file again and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", ##' "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Third commit message") ##' ##' ## View reflog ##' reflog(repo) ##' } reflog <- function(repo = ".", refname = "HEAD") { structure(.Call(git2r_reflog_list, lookup_repository(repo), refname), class = "git_reflog") } ##' @export print.git_reflog <- function(x, ...) { lapply(x, print) invisible(x) } ##' Print a reflog entry ##' ##' @param x The reflog entry ##' @param ... Unused ##' @return None (invisible 'NULL'). ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## View repository HEAD reflog ##' reflog(repo) ##' } print.git_reflog_entry <- function(x, ...) { cat(sprintf("[%s] %s@{%i}: %s\n", substring(x$sha, 1, 7), x$refname, x$index, x$message)) invisible(x) } git2r/R/branch.R0000644000175000017500000003547613651245347013245 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2020 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Create a branch ##' ##' @param commit Commit to which the branch should point. The default ##' is to use the \code{last_commit()} function to determine the ##' commit to which the branch should point. ##' @param name Name for the branch ##' @param force Overwrite existing branch. Default = FALSE ##' @return invisible git_branch object ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' lines <- "Hello world!" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' ##' ## Create a branch ##' branch_1 <- branch_create(commit_1, name = "test-branch") ##' ##' ## Add one more commit ##' lines <- c("Hello world!", "HELLO WORLD!") ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_2 <- commit(repo, "Another commit message") ##' ##' ## Create a branch with the same name should fail ##' try(branch_create(commit_2, name = "test-branch"), TRUE) ##' ##' ## Force it ##' branch_2 <- branch_create(commit_2, name = "test-branch", force = TRUE) ##' } branch_create <- function(commit = last_commit(), name = NULL, force = FALSE) { invisible(.Call(git2r_branch_create, name, commit, force)) } ##' Delete a branch ##' ##' @param branch The branch ##' @return invisible NULL ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' ##' ## Create a 'dev' branch ##' dev <- branch_create(commit_1, name = "dev") ##' branches(repo) ##' ##' ## Delete 'dev' branch ##' branch_delete(dev) ##' branches(repo) ##' } branch_delete <- function(branch = NULL) { .Call(git2r_branch_delete, branch) invisible(NULL) } ##' Remote name of a branch ##' ##' The name of remote that the remote tracking branch belongs to ##' @param branch The branch ##' @return character string with remote name ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize two temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo, "origin", "refs/heads/master") ##' ##' ## Get remote name ##' branch_remote_name(branches(repo)[[2]]) ##' } branch_remote_name <- function(branch = NULL) { .Call(git2r_branch_remote_name, branch) } ##' Remote url of a branch ##' ##' @param branch The branch ##' @return character string with remote url ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize two temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo, "origin", "refs/heads/master") ##' ##' ## Get remote url of tracking branch to branch 'master' ##' branch_remote_url(branch_get_upstream(repository_head(repo))) ##' } branch_remote_url <- function(branch = NULL) { .Call(git2r_branch_remote_url, branch) } ##' Rename a branch ##' ##' @param branch Branch to rename ##' @param name The new name for the branch ##' @param force Overwrite existing branch. Default is FALSE ##' @return invisible renamed \code{git_branch} object ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Rename 'master' branch to 'dev' ##' branches(repo) ##' branch_rename(repository_head(repo), "dev") ##' branches(repo) ##' } branch_rename <- function(branch = NULL, name = NULL, force = FALSE) { invisible(.Call(git2r_branch_rename, branch, name, force)) } ##' Get target (sha) pointed to by a branch ##' ##' @param branch The branch ##' @return sha or NA if not a direct reference ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Get target (sha) pointed to by 'master' branch ##' branch_target(repository_head(repo)) ##' } branch_target <- function(branch = NULL) { .Call(git2r_branch_target, branch) } ##' Get remote tracking branch ##' ##' Get remote tracking branch, given a local branch. ##' @param branch The branch ##' @return \code{git_branch} object or NULL if no remote tracking ##' branch. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize two temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo, "origin", "refs/heads/master") ##' ##' ## Get remote tracking branch ##' branch_get_upstream(repository_head(repo)) ##' } branch_get_upstream <- function(branch = NULL) { .Call(git2r_branch_get_upstream, branch) } ##' Set remote tracking branch ##' ##' Set the upstream configuration for a given local branch ##' @param branch The branch to configure ##' @param name remote-tracking or local branch to set as ##' upstream. Pass NULL to unset. ##' @return invisible NULL ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize two temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo, "origin", "refs/heads/master") ##' ##' ## Unset remote remote tracking branch ##' branch_get_upstream(repository_head(repo)) ##' branch_set_upstream(repository_head(repo), NULL) ##' branch_get_upstream(repository_head(repo)) ##' ##' ## Set remote tracking branch ##' branch_set_upstream(repository_head(repo), "origin/master") ##' branch_get_upstream(repository_head(repo)) ##' } branch_set_upstream <- function(branch = NULL, name) { if (missing(name)) stop("Missing argument name") .Call(git2r_branch_set_upstream, branch, name) invisible(NULL) } ##' Branches ##' ##' List branches in repository ##' @template repo-param ##' @param flags Filtering flags for the branch listing. Valid values ##' are 'all', 'local' or 'remote' ##' @return list of branches in repository ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config first user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo, "origin", "refs/heads/master") ##' ##' ## List branches ##' branches(repo) ##' } branches <- function(repo = ".", flags=c("all", "local", "remote")) { flags <- switch(match.arg(flags), local = 1L, remote = 2L, all = 3L) .Call(git2r_branch_list, lookup_repository(repo), flags) } ##' Check if branch is head ##' ##' @param branch The branch \code{object} to check if it's head. ##' @return \code{TRUE} if branch is head, else \code{FALSE}. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## List branches ##' branches(repo) ##' ##' ## Check that 'master' is_head ##' master <- branches(repo)[[1]] ##' is_head(master) ##' ##' ## Create and checkout 'dev' branch ##' checkout(repo, "dev", create = TRUE) ##' ##' ## List branches ##' branches(repo) ##' ##' ## Check that 'master' is no longer head ##' is_head(master) ##' } is_head <- function(branch = NULL) { .Call(git2r_branch_is_head, branch) } ##' Check if branch is local ##' ##' @param branch The branch \code{object} to check if it's local ##' @return \code{TRUE} if branch is local, else \code{FALSE}. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config first user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo, "origin", "refs/heads/master") ##' ##' ## List branches ##' branches(repo) ##' ##' ## Check if first branch is_local ##' is_local(branches(repo)[[1]]) ##' ##' ## Check if second branch is_local ##' is_local(branches(repo)[[2]]) ##' } is_local <- function(branch) { if (!is_branch(branch)) stop("argument 'branch' must be a 'git_branch' object") identical(branch$type, 1L) } ##' @export print.git_branch <- function(x, ...) { sha <- branch_target(x) if (!is.na(sha)) { cat(sprintf("[%s] ", substr(sha, 1, 6))) } if (is_local(x)) { cat("(Local) ") } else { cat(sprintf("(%s @ %s) ", branch_remote_name(x), branch_remote_url(x))) } if (is_head(x)) { cat("(HEAD) ") } if (is_local(x)) { cat(sprintf("%s\n", x$name)) } else { cat(sprintf("%s\n", substr(x$name, start = nchar(branch_remote_name(x)) + 2, stop = nchar(x$name)))) } invisible(x) } ##' Check if object is \code{git_branch} ##' ##' @param object Check if object is of class \code{git_branch} ##' @return TRUE if object is class \code{git_branch}, else FALSE ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' branch <- branches(repo)[[1]] ##' ##' ## Check if branch ##' is_branch(branch) ##' } is_branch <- function(object) { inherits(object, "git_branch") } git2r/R/status.R0000644000175000017500000000705713526215551013320 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Status ##' ##' Display state of the repository working directory and the staging ##' area. ##' @template repo-param ##' @param staged Include staged files. Default TRUE. ##' @param unstaged Include unstaged files. Default TRUE. ##' @param untracked Include untracked files and directories. Default ##' TRUE. ##' @param ignored Include ignored files. Default FALSE. ##' @param all_untracked Shows individual files in untracked ##' directories if \code{untracked} is \code{TRUE}. ##' @return \code{git_status} with repository status ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' ##' ## Check status; untracked file ##' status(repo) ##' ##' ## Add file ##' add(repo, "test.txt") ##' ##' ## Check status; staged file ##' status(repo) ##' ##' ## Commit ##' commit(repo, "First commit message") ##' ##' ## Check status; clean ##' status(repo) ##' ##' ## Change the file ##' writeLines(c("Hello again!", "Here is a second line", "And a third"), ##' file.path(path, "test.txt")) ##' ##' ## Check status; unstaged file ##' status(repo) ##' ##' ## Add file and commit ##' add(repo, "test.txt") ##' commit(repo, "Second commit message") ##' ##' ## Check status; clean ##' status(repo) ##'} status <- function(repo = ".", staged = TRUE, unstaged = TRUE, untracked = TRUE, ignored = FALSE, all_untracked = FALSE) { structure(.Call(git2r_status_list, lookup_repository(repo), staged, unstaged, untracked, all_untracked, ignored), class = "git_status") } ##' @export print.git_status <- function(x, ...) { display_status <- function(title, section) { cat(sprintf("%s:\n", title)) for (i in seq_len(length(section))) { label <- names(section)[i] label <- paste0(toupper(substr(label, 1, 1)), substr(label, 2, nchar(label))) cat(sprintf("\t%-12s%s\n", paste0(label, ":"), section[[i]])) } invisible(NULL) } if (max(sapply(x, length)) == 0L) cat("working directory clean\n") if (length(x$ignored)) { display_status("Ignored files", x$ignored) cat("\n") } if (length(x$untracked)) { display_status("Untracked files", x$untracked) cat("\n") } if (length(x$unstaged)) { display_status("Unstaged changes", x$unstaged) cat("\n") } if (length(x$staged)) { display_status("Staged changes", x$staged) cat("\n") } invisible(x) } git2r/R/commit.R0000644000175000017500000004776613565063346013306 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Ahead Behind ##' ##' Count the number of unique commits between two commit objects. ##' @param local a git_commit object. Can also be a tag or a branch, ##' and in that case the commit will be the target of the tag or ##' branch. ##' @param upstream a git_commit object. Can also be a tag or a ##' branch, and in that case the commit will be the target of the ##' tag or branch. ##' @return An integer vector of length 2 with number of commits that ##' the upstream commit is ahead and behind the local commit ##' @export ##' @examples \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit_1 <- commit(repo, "Commit message 1") ##' tag_1 <- tag(repo, "Tagname1", "Tag message 1") ##' ##' # Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit_2 <- commit(repo, "Commit message 2") ##' tag_2 <- tag(repo, "Tagname2", "Tag message 2") ##' ##' ahead_behind(commit_1, commit_2) ##' ahead_behind(tag_1, tag_2) ##' } ahead_behind <- function(local = NULL, upstream = NULL) { .Call(git2r_graph_ahead_behind, lookup_commit(local), lookup_commit(upstream)) } ##' Add sessionInfo to message ##' ##' @param message The message. ##' @return message with appended sessionInfo ##' @importFrom utils capture.output ##' @importFrom utils sessionInfo ##' @noRd add_session_info <- function(message) { paste0(message, "\n\nsessionInfo:\n", paste0(utils::capture.output(utils::sessionInfo()), collapse = "\n")) } ##' Commit ##' ##' @template repo-param ##' @param message The commit message. ##' @param all Stage modified and deleted files. Files not added to ##' Git are not affected. ##' @param session Add sessionInfo to commit message. Default is ##' FALSE. ##' @param author Signature with author and author time of commit. ##' @param committer Signature with committer and commit time of ##' commit. ##' @return A list of class \code{git_commit} with entries: ##' \describe{ ##' \item{sha}{ ##' The 40 character hexadecimal string of the SHA-1 ##' } ##' \item{author}{ ##' An author signature ##' } ##' \item{committer}{ ##' The committer signature ##' } ##' \item{summary}{ ##' The short "summary" of a git commit message, comprising the first ##' paragraph of the message with whitespace trimmed and squashed. ##' } ##' \item{message}{ ##' The message of a commit ##' } ##' \item{repo}{ ##' The \code{git_repository} object that contains the commit ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' } commit <- function(repo = ".", message = NULL, all = FALSE, session = FALSE, author = NULL, committer = NULL) { repo <- lookup_repository(repo) if (is.null(author)) author <- default_signature(repo) if (is.null(committer)) committer <- default_signature(repo) stopifnot(is.character(message), identical(length(message), 1L)) if (!nchar(message[1])) stop("Aborting commit due to empty commit message.") if (isTRUE(all)) { s <- status(repo, unstaged = TRUE, staged = FALSE, untracked = FALSE, ignored = FALSE) ## Convert list of lists to character vector unstaged <- unlist(s$unstaged) for (i in seq_along(unstaged)) { if (names(unstaged)[i] == "modified") { ## Stage modified files add(repo, unstaged[i]) } else if (names(unstaged)[i] == "deleted") { ## Stage deleted files .Call(git2r_index_remove_bypath, repo, unstaged[i]) } } } if (isTRUE(session)) message <- add_session_info(message) .Call(git2r_commit, repo, message, author, committer) } ##' Check limit in number of commits ##' @noRd get_upper_limit_of_commits <- function(n) { if (is.null(n)) { n <- -1L } else if (is.numeric(n)) { if (!identical(length(n), 1L)) stop("'n' must be integer") if (abs(n - round(n)) >= .Machine$double.eps^0.5) stop("'n' must be integer") n <- as.integer(n) } else { stop("'n' must be integer") } n } shallow_commits <- function(repo, sha, n) { ## List to hold result result <- list() ## Get latest commit x <- lookup(repo, sha) ## Repeat until no more parent commits repeat { if (n == 0) { break } else if (n > 0) { n <- n - 1 } if (is.null(x)) break result[[length(result) + 1]] <- x ## Get parent to commit x <- tryCatch(parents(x)[[1]], error = function(e) NULL) } result } ##' Commits ##' ##' @template repo-param ##' @param topological Sort the commits in topological order (parents ##' before children); can be combined with time sorting. Default ##' is TRUE. ##' @param time Sort the commits by commit time; Can be combined with ##' topological sorting. Default is TRUE. ##' @param reverse Sort the commits in reverse order; can be combined ##' with topological and/or time sorting. Default is FALSE. ##' @param n The upper limit of the number of commits to output. The ##' default is NULL for unlimited number of commits. ##' @param ref The name of a reference to list commits from e.g. a tag ##' or a branch. The default is NULL for the current branch. ##' @param path The path to a file. If not NULL, only commits modifying ##' this file will be returned. Note that modifying commits that ##' occurred before the file was given its present name are not ##' returned; that is, the output of \code{git log} with ##' \code{--no-follow} is reproduced. ##' @return list of commits in repository ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Second commit message") ##' ##' ## Create a tag ##' tag(repo, "Tagname", "Tag message") ##' ##' ## Change file again and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", ##' "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Third commit message") ##' ##' ## Create a new file containing R code, and commit. ##' writeLines(c("x <- seq(1,100)", ##' "print(mean(x))"), ##' file.path(path, "mean.R")) ##' add(repo, "mean.R") ##' commit(repo, "Fourth commit message") ##' ##' ## List the commits in the repository ##' commits(repo) ##' ##' ## List the commits starting from the tag ##' commits(repo, ref = "Tagname") ##' ##' ## List the commits modifying example.txt and mean.R. ##' commits(repo, path = "example.txt") ##' commits(repo, path = "mean.R") ##' ##' ## Create and checkout 'dev' branch in the repo ##' checkout(repo, "dev", create = TRUE) ##' ##' ## Add changes to the 'dev' branch ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Commit message in dev branch") ##' ##' ## Checkout the 'master' branch again and list the commits ##' ## starting from the 'dev' branch. ##' checkout(repo, "master") ##' commits(repo, ref = "dev") ##' } commits <- function(repo = ".", topological = TRUE, time = TRUE, reverse = FALSE, n = NULL, ref = NULL, path = NULL) { ## Check limit in number of commits n <- get_upper_limit_of_commits(n) if (!is.null(path)) { if (!(is.character(path) && length(path) == 1)) { stop("path must be a single file") } } repo <- lookup_repository(repo) if (is_empty(repo)) return(list()) if (is.null(ref)) { sha <- sha(repository_head(repo)) } else { sha <- sha(lookup_commit(.Call(git2r_reference_dwim, repo, ref))) } if (is_shallow(repo)) { ## FIXME: Remove this if-statement when libgit2 supports ## shallow clones, see #219. Note: This workaround does not ## use the 'topological', 'time' and 'reverse' flags. return(shallow_commits(repo, sha, n)) } if (!is.null(path)) { repo_wd <- normalizePath(workdir(repo), winslash = "/") path <- sanitize_path(path, repo_wd) path_revwalk <- .Call(git2r_revwalk_list2, repo, sha, topological, time, reverse, n, path) return(path_revwalk[!vapply(path_revwalk, is.null, logical(1))]) } .Call(git2r_revwalk_list, repo, sha, topological, time, reverse, n) } ##' Last commit ##' ##' Get last commit in the current branch. ##' @template repo-param ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Get last commit ##' last_commit(repo) ##' last_commit(path) ##' ##' ## Coerce the last commit to a data.frame ##' as.data.frame(last_commit(path), "data.frame") ##' ##' ## Summary of last commit in repository ##' summary(last_commit(repo)) ##' } last_commit <- function(repo = ".") { commits(lookup_repository(repo), n = 1)[[1]] } ##' Descendant ##' ##' Determine if a commit is the descendant of another commit ##' @param commit a git_commit object. Can also be a tag or a branch, ##' and in that case the commit will be the target of the tag or ##' branch. ##' @param ancestor a git_commit object to check if ancestor to ##' \code{commit}. Can also be a tag or a branch, and in that case ##' the commit will be the target of the tag or branch. ##' @return TRUE if \code{commit} is descendant of \code{ancestor}, ##' else FALSE ##' @export ##' @examples ##' \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit_1 <- commit(repo, "Commit message 1") ##' tag_1 <- tag(repo, "Tagname1", "Tag message 1") ##' ##' # Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit_2 <- commit(repo, "Commit message 2") ##' tag_2 <- tag(repo, "Tagname2", "Tag message 2") ##' ##' descendant_of(commit_1, commit_2) ##' descendant_of(commit_2, commit_1) ##' descendant_of(tag_1, tag_2) ##' descendant_of(tag_2, tag_1) ##' } descendant_of <- function(commit = NULL, ancestor = NULL) { .Call(git2r_graph_descendant_of, lookup_commit(commit), lookup_commit(ancestor)) } ##' Check if object is a git_commit object ##' ##' @param object Check if object is a git_commit object ##' @return TRUE if object is a git_commit, else FALSE ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' ##' ## Check if commit ##' is_commit(commit_1) ##' } is_commit <- function(object) { inherits(object, "git_commit") } ##' Is merge ##' ##' Determine if a commit is a merge commit, i.e. has more than one ##' parent. ##' @param commit a git_commit object. ##' @return TRUE if commit has more than one parent, else FALSE ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines(c("First line in file 1.", "Second line in file 1."), ##' file.path(path, "example-1.txt")) ##' add(repo, "example-1.txt") ##' commit(repo, "First commit message") ##' ##' ## Create and add one more file ##' writeLines(c("First line in file 2.", "Second line in file 2."), ##' file.path(path, "example-2.txt")) ##' add(repo, "example-2.txt") ##' commit(repo, "Second commit message") ##' ##' ## Create a new branch 'fix' ##' checkout(repo, "fix", create = TRUE) ##' ##' ## Update 'example-1.txt' (swap words in first line) and commit ##' writeLines(c("line First in file 1.", "Second line in file 1."), ##' file.path(path, "example-1.txt")) ##' add(repo, "example-1.txt") ##' commit(repo, "Third commit message") ##' ##' checkout(repo, "master") ##' ##' ## Update 'example-2.txt' (swap words in second line) and commit ##' writeLines(c("First line in file 2.", "line Second in file 2."), ##' file.path(path, "example-2.txt")) ##' add(repo, "example-2.txt") ##' commit(repo, "Fourth commit message") ##' ##' ## Merge 'fix' ##' merge(repo, "fix") ##' ##' ## Display parents of last commit ##' parents(lookup(repo, branch_target(repository_head(repo)))) ##' ##' ## Check that last commit is a merge ##' is_merge(lookup(repo, branch_target(repository_head(repo)))) ##' } is_merge <- function(commit = NULL) { length(parents(commit)) > 1 } ##' Parents ##' ##' Get parents of a commit. ##' @param object a git_commit object. ##' @return list of git_commit objects ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("First line.", ##' file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' ##' ## commit_1 has no parents ##' parents(commit_1) ##' ##' ## Update 'example.txt' and commit ##' writeLines(c("First line.", "Second line."), ##' file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_2 <- commit(repo, "Second commit message") ##' ##' ## commit_2 has commit_1 as parent ##' parents(commit_2) ##' } parents <- function(object = NULL) { .Call(git2r_commit_parent_list, object) } ##' @export format.git_commit <- function(x, ...) { sprintf("[%s] %s: %s", substring(x$sha, 1, 7), substring(as.character(x$author$when), 1, 10), x$summary) } ##' @export print.git_commit <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } ##' @export summary.git_commit <- function(object, ...) { is_merge_commit <- is_merge(object) po <- parents(object) cat(sprintf("Commit: %s\n", object$sha)) if (is_merge_commit) { sha <- vapply(po, "[[", character(1), "sha") cat(sprintf("Merge: %s\n", sha[1])) cat(paste0(" ", sha[-1]), sep = "\n") } cat(sprintf(paste0("Author: %s <%s>\n", "When: %s\n\n"), object$author$name, object$author$email, as.character(object$author$when))) msg <- paste0(" ", readLines(textConnection(object$message))) cat("", sprintf("%s\n", msg)) if (is_merge_commit) { cat("\n") lapply(po, function(parent) { cat("Commit message: ", parent$sha, "\n") msg <- paste0(" ", readLines(textConnection(parent$message))) cat("", sprintf("%s\n", msg), "\n") }) } if (identical(length(po), 1L)) { df <- diff(tree(po[[1]]), tree(object)) if (length(df) > 0) { if (length(df) > 1) { cat(sprintf("%i files changed, ", length(df))) } else { cat("1 file changed, ") } cat(sprintf( "%i insertions, %i deletions\n", sum(vapply(lines_per_file(df), "[[", numeric(1), "add")), sum(vapply(lines_per_file(df), "[[", numeric(1), "del")))) plpf <- print_lines_per_file(df) hpf <- hunks_per_file(df) hunk_txt <- ifelse(hpf > 1, " hunks", ifelse(hpf > 0, " hunk", " hunk (binary file)")) phpf <- paste0(" in ", format(hpf), hunk_txt) cat(paste0(plpf, phpf), sep = "\n") } cat("\n") } invisible(NULL) } ##' @export as.data.frame.git_commit <- function(x, ...) { data.frame(sha = x$sha, summary = x$summary, message = x$message, author = x$author$name, email = x$author$email, when = as.POSIXct(x$author$when), stringsAsFactors = FALSE) } git2r/R/plot.R0000644000175000017500000000501313526222161012734 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Plot commits over time ##' ##' @param x The repository to plot ##' @param breaks Default is \code{month}. Change to year, quarter, ##' week or day as necessary. ##' @param main Default title for the plot is "Commits on repo:" and ##' repository workdir basename. Supply a new title if you desire one. ##' @param ... Additional arguments affecting the plot ##' @importFrom graphics axis ##' @importFrom graphics barplot ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- clone("https://github.com/ropensci/git2r.git", path) ##' ##' ## Plot commits ##' plot(repo) ##' } plot.git_repository <- function(x, breaks = c("month", "year", "quarter", "week", "day"), main = NULL, ...) { breaks <- match.arg(breaks) df <- contributions(x, breaks = breaks, by = "commits") tmp <- data.frame(when = seq(from = min(df$when), to = max(df$when), by = breaks), n = 0) i <- match(df$when, tmp$when) tmp$n[i] <- df$n df <- tmp xlab <- sprintf("Time [%s]", breaks) ylab <- "Number of commits" if (is.null(main)) { if (is_bare(x)) { main <- "Commits" } else { main <- sprintf("Commits on repository: %s", basename(workdir(x))) } } mp <- barplot(df$n, xlab = xlab, ylab = ylab, main = main, ...) axis(1, at = mp, labels = seq(min(df$when), max(df$when), breaks)) } git2r/R/push.R0000644000175000017500000001155513526222654012754 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. get_upstream_name <- function(object) { upstream <- branch_get_upstream(object) if (is.null(upstream)) { stop("The branch '", object$name, "' that you are ", "trying to push does not track an upstream branch.") } branch_remote_name(upstream) } ##' Push ##' ##' @param object path to repository, or a \code{git_repository} or ##' \code{git_branch}. ##' @param name The remote's name. Default is NULL. ##' @param refspec The refspec to be pushed. Default is NULL. ##' @param force Force your local revision to the remote repo. Use it ##' with care. Default is FALSE. ##' @param credentials The credentials for remote repository ##' access. Default is NULL. To use and query an ssh-agent for the ##' ssh key credentials, let this parameter be NULL (the default). ##' @param set_upstream Set the current local branch to track the ##' remote branch. Default is FALSE. ##' @return invisible(NULL) ##' @seealso \code{\link{cred_user_pass}}, \code{\link{cred_ssh_key}} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize two temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' ##' ## Clone the bare repository. This creates remote-tracking ##' ## branches for each branch in the cloned repository. ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' push(repo, "origin", "refs/heads/master") ##' ##' ## Now, unset the remote-tracking branch to NULL to demonstrate ##' ## the 'set_upstream' argument. Then push with 'set_upstream = TRUE' ##' ## to add the upstream tracking branch to branch 'master' again. ##' branch_get_upstream(repository_head(repo)) ##' branch_set_upstream(repository_head(repo), NULL) ##' branch_get_upstream(repository_head(repo)) ##' push(repo, "origin", "refs/heads/master", set_upstream = TRUE) ##' branch_get_upstream(repository_head(repo)) ##' ##' ## Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Second commit message") ##' ##' ## Push commits from repository to bare repository ##' push(repo) ##' ##' ## List commits in repository and bare repository ##' commits(repo) ##' commits(repo_bare) ##' } push <- function(object = ".", name = NULL, refspec = NULL, force = FALSE, credentials = NULL, set_upstream = FALSE) { if (is_branch(object)) { name <- get_upstream_name(object) src <- .Call(git2r_branch_canonical_name, object) dst <- .Call(git2r_branch_upstream_canonical_name, object) refspec <- paste0(src, ":", dst) object <- object$repo } else { object <- lookup_repository(object) } if (all(is.null(name), is.null(refspec))) { b <- repository_head(object) name <- get_upstream_name(b) src <- .Call(git2r_branch_canonical_name, b) dst <- .Call(git2r_branch_upstream_canonical_name, b) refspec <- paste0(src, ":", dst) if (isTRUE(force)) refspec <- paste0("+", refspec) } else { opts <- list(force = force) tmp <- get_refspec(object, name, refspec, opts) name <- tmp$remote refspec <- tmp$refspec } .Call(git2r_push, object, name, refspec, credentials) if (isTRUE(set_upstream)) { b <- repository_head(object) if (is_local(b)) branch_set_upstream(b, paste0(name, "/", b$name)) } invisible(NULL) } git2r/R/revparse.R0000644000175000017500000000401013526223131013577 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Revparse ##' ##' Find object specified by revision. ##' @template repo-param ##' @param revision The revision string, see ##' http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions ##' @return a \code{git_commit} or \code{git_tag} or \code{git_tree} ##' object ##' @export ##' @examples ##' \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "First commit message") ##' ##' # Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "Second commit message") ##' ##' revparse_single(repo, "HEAD^") ##' revparse_single(repo, "HEAD:test.txt") ##' } revparse_single <- function(repo = ".", revision = NULL) { .Call(git2r_revparse_single, lookup_repository(repo), revision) } git2r/R/libgit2.R0000644000175000017500000000343213304422356013317 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Compile time options for libgit2. ##' ##' @return A list with threads, https and ssh set to TRUE/FALSE. ##' @keywords methods ##' @export ##' @examples ##' libgit2_features() libgit2_features <- function() { .Call(git2r_libgit2_features) } ##' Version of the libgit2 library ##' ##' Version of the libgit2 library that the bundled source code is ##' based on ##' @return A list with major, minor and rev ##' @keywords methods ##' @export ##' @examples ##' libgit2_version() libgit2_version <- function() { .Call(git2r_libgit2_version) } ##' Set the SSL certificate-authority locations ##' ##' @note Either parameter may be 'NULL', but not both. ##' @param filename Location of a file containing several certificates ##' concatenated together. Default NULL. ##' @param path Location of a directory holding several certificates, ##' one per file. Default NULL. ##' @return invisible(NULL) ##' @keywords methods ##' @export ssl_cert_locations <- function(filename = NULL, path = NULL) { .Call(git2r_ssl_cert_locations, filename, path) invisible(NULL) } git2r/R/signature.R0000644000175000017500000000367613526222103013770 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Get the signature ##' ##' Get the signature according to the repository's configuration ##' @template repo-param ##' @return A \code{git_signature} object with entries: ## \describe{ ## \item{name}{ ## The full name of the author. ## } ## \item{email}{ ## Email of the author. ## } ## \item{when}{ ## Time when the action happened. ## } ## } ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Get the default signature ##' default_signature(repo) ##' ##' ## Change user ##' config(repo, user.name = "Bob", user.email = "bob@@example.org") ##' ##' ## Get the default signature ##' default_signature(repo) ##' } default_signature <- function(repo = ".") { .Call(git2r_signature_default, lookup_repository(repo)) } ##' @export format.git_signature <- function(x, ...) { sprintf("name: %s\nemail: %s\nwhen: %s", x$name, x$email, as.character(x$when)) } ##' @export print.git_signature <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } git2r/R/reference.R0000644000175000017500000000443613765357051013740 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Get all references that can be found in a repository. ##' @template repo-param ##' @return Character vector with references ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize two temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo <- clone(path_bare, path_repo) ##' ##' ## Config user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Push commits from repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo, "origin", "refs/heads/master") ##' ##' ## Add tag to HEAD ##' tag(repo, "v1.0", "First version") ##' ##' ## Create a note ##' note_create(commits(repo)[[1]], "My note") ##' ##' ## List all references in repository ##' references(repo) ##' } ##' references <- function(repo = ".") { .Call(git2r_reference_list, lookup_repository(repo)) } ##' @export format.git_reference <- function(x, ...) { if (identical(x$type, 1L)) return(sprintf("[%s] %s", substr(x$sha, 1, 6), x$shorthand)) sprintf("%s => %s", x$name, x$target) } ##' @export print.git_reference <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } git2r/R/stash.R0000644000175000017500000002551513526215552013117 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. stash_index <- function(object, index) { if (inherits(object, "git_stash")) { ## Determine the index of the stash in the stash list index <- match(object$sha, vapply(stash_list(object$repo), "[[", character(1), "sha")) } ## The stash list is zero-based if (abs(index - round(index)) >= .Machine$double.eps^0.5) stop("'index' must be an integer") as.integer(index) - 1L } stash_object <- function(object) { if (inherits(object, "git_stash")) return(object$repo) lookup_repository(object) } ##' Apply stash ##' ##' Apply a single stashed state from the stash list. ##' ##' If local changes in the working directory conflict with changes in ##' the stash then an error will be raised. In this case, the index ##' will always remain unmodified and all files in the working ##' directory will remain unmodified. However, if you are restoring ##' untracked files or ignored files and there is a conflict when ##' applying the modified files, then those files will remain in the ##' working directory. ##' @param object path to a repository, or a \code{git_repository} ##' object, or the stash \code{object} to pop. Default is a ##' \code{path = '.'} to a reposiory. ##' @param index The index to the stash to apply. Only used when ##' \code{object} is a path to a repository or a ##' \code{git_repository} object. Default is \code{index = 1}. ##' @return invisible NULL ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' # Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' add(repo, 'test.txt') ##' commit(repo, "Commit message") ##' ##' # Change file ##' writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) ##' ##' # Create stash in repository ##' stash(repo) ##' ##' # Change file ##' writeLines(c("Hello world!", "HeLlO wOrLd!"), file.path(path, "test.txt")) ##' ##' # Create stash in repository ##' stash(repo) ##' ##' # View stashes ##' stash_list(repo) ##' ##' # Read file ##' readLines(file.path(path, "test.txt")) ##' ##' # Apply latest git_stash object in repository ##' stash_apply(stash_list(repo)[[1]]) ##' ##' # Read file ##' readLines(file.path(path, "test.txt")) ##' ##' # View stashes ##' stash_list(repo) ##' } stash_apply <- function(object = ".", index = 1) { .Call(git2r_stash_apply, stash_object(object), stash_index(object, index)) invisible(NULL) } ##' Drop stash ##' ##' @param object path to a repository, or a \code{git_repository} ##' object, or the stash \code{object} to drop. Default is a ##' \code{path = '.'} to a reposiory. ##' @param index The index to the stash to drop. Only used when ##' \code{object} is a path to a repository or a ##' \code{git_repository} object. Default is \code{index = 1}. ##' @return invisible NULL ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' # Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' add(repo, 'test.txt') ##' commit(repo, "Commit message") ##' ##' # Change file ##' writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) ##' ##' # Create stash in repository ##' stash(repo) ##' ##' # Change file ##' writeLines(c("Hello world!", "HeLlO wOrLd!"), file.path(path, "test.txt")) ##' ##' # Create stash in repository ##' stash(repo) ##' ##' # View stashes ##' stash_list(repo) ##' ##' # Drop git_stash object in repository ##' stash_drop(stash_list(repo)[[1]]) ##' ##' ## Drop stash using an index to stash ##' stash_drop(repo, 1) ##' ##' # View stashes ##' stash_list(repo) ##' } stash_drop <- function(object = ".", index = 1) { .Call(git2r_stash_drop, stash_object(object), stash_index(object, index)) invisible(NULL) } ##' Stash ##' ##' @template repo-param ##' @param message Optional description. Defaults to current time. ##' @param index All changes already added to the index are left ##' intact in the working directory. Default is FALSE ##' @param untracked All untracked files are also stashed and then ##' cleaned up from the working directory. Default is FALSE ##' @param ignored All ignored files are also stashed and then cleaned ##' up from the working directory. Default is FALSE ##' @param stasher Signature with stasher and time of stash ##' @return invisible \code{git_stash} object if anything to stash ##' else NULL ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' # Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' add(repo, 'test.txt') ##' commit(repo, "Commit message") ##' ##' # Change file ##' writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) ##' ##' # Check status of repository ##' status(repo) ##' ##' # Create stash in repository ##' stash(repo) ##' ##' # Check status of repository ##' status(repo) ##' ##' # View stash ##' stash_list(repo) ##' } stash <- function(repo = ".", message = as.character(Sys.time()), index = FALSE, untracked = FALSE, ignored = FALSE, stasher = NULL) { repo <- lookup_repository(repo) if (is.null(stasher)) stasher <- default_signature(repo) invisible(.Call(git2r_stash_save, repo, message, index, untracked, ignored, stasher)) } ##' List stashes in repository ##' ##' @template repo-param ##' @return list of stashes in repository ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' # Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test-1.txt")) ##' add(repo, 'test-1.txt') ##' commit(repo, "Commit message") ##' ##' # Make one more commit ##' writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-1.txt")) ##' add(repo, 'test-1.txt') ##' commit(repo, "Next commit message") ##' ##' # Create one more file ##' writeLines("Hello world!", file.path(path, "test-2.txt")) ##' ##' # Check that there are no stashes ##' stash_list(repo) ##' ##' # Stash ##' stash(repo) ##' ##' # Only untracked changes, therefore no stashes ##' stash_list(repo) ##' ##' # Stash and include untracked changes ##' stash(repo, "Stash message", untracked=TRUE) ##' ##' # View stash ##' stash_list(repo) ##' } stash_list <- function(repo = ".") { .Call(git2r_stash_list, lookup_repository(repo)) } ##' Pop stash ##' ##' Apply a single stashed state from the stash list and remove it ##' from the list if successful. ##' @param object path to a repository, or a \code{git_repository} ##' object, or the stash \code{object} to pop. Default is a ##' \code{path = '.'} to a reposiory. ##' @param index The index to the stash to pop. Only used when ##' \code{object} is a path to a repository or a ##' \code{git_repository} object. Default is \code{index = 1}. ##' @return invisible NULL ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' # Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' add(repo, 'test.txt') ##' commit(repo, "Commit message") ##' ##' # Change file ##' writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) ##' ##' # Create stash in repository ##' stash(repo) ##' ##' # Change file ##' writeLines(c("Hello world!", "HeLlO wOrLd!"), file.path(path, "test.txt")) ##' ##' # Create stash in repository ##' stash(repo) ##' ##' # View stashes ##' stash_list(repo) ##' ##' # Read file ##' readLines(file.path(path, "test.txt")) ##' ##' # Pop latest git_stash object in repository ##' stash_pop(stash_list(repo)[[1]]) ##' ##' # Read file ##' readLines(file.path(path, "test.txt")) ##' ##' # View stashes ##' stash_list(repo) ##' } stash_pop <- function(object = ".", index = 1) { .Call(git2r_stash_pop, stash_object(object), stash_index(object, index)) invisible(NULL) } ##' @export format.git_stash <- function(x, ...) { x$message } ##' @export print.git_stash <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } ##' Summary of a stash ##' ##' @param object The stash \code{object} ##' @param ... Additional arguments affecting the summary produced. ##' @return None (invisible 'NULL'). ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' # Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' add(repo, 'test.txt') ##' commit(repo, "Commit message") ##' ##' # Change file ##' writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test.txt")) ##' ##' # Create stash in repository ##' stash(repo, "Stash message") ##' ##' # View summary of stash ##' summary(stash_list(repo)[[1]]) ##' } summary.git_stash <- function(object, ...) { cat(sprintf(paste0("message: %s\n", "stasher: %s <%s>\n", "when: %s\n", "sha: %s\n\n"), object$summary, object$author$name, object$author$email, as.character(object$author$when), object$sha)) } git2r/R/contributions.R0000644000175000017500000001035413526222107014664 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Contributions ##' ##' See contributions to a Git repo ##' @template repo-param ##' @param breaks Default is \code{month}. Change to year, quarter, ##' week or day as necessary. ##' @param by Contributions by "commits" or "author". Default is "commits". ##' @return A \code{data.frame} with contributions. ##' @export ##' @examples ##' \dontrun{ ##' ## Create directories and initialize repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo_1 <- tempfile(pattern="git2r-") ##' path_repo_2 <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo_1) ##' dir.create(path_repo_2) ##' repo_bare <- init(path_bare, bare = TRUE) ##' ##' ## Clone to repo 1 and config user ##' repo_1 <- clone(path_bare, path_repo_1) ##' config(repo_1, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Add changes to repo 1 and push to bare ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo_1, "test.txt")) ##' add(repo_1, "test.txt") ##' commit(repo_1, "First commit message") ##' ##' ## Add more changes to repo 1 ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path_repo_1, "test.txt")) ##' add(repo_1, "test.txt") ##' commit(repo_1, "Second commit message") ##' ##' ## Push to bare ##' push(repo_1, "origin", "refs/heads/master") ##' ##' ## Clone to repo 2 ##' repo_2 <- clone(path_bare, path_repo_2) ##' config(repo_2, user.name = "Bob", user.email = "bob@@example.org") ##' ##' ## Add changes to repo 2 ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", ##' "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") ##' writeLines(lines, file.path(path_repo_2, "test.txt")) ##' add(repo_2, "test.txt") ##' commit(repo_2, "Third commit message") ##' ##' ## Push to bare ##' push(repo_2, "origin", "refs/heads/master") ##' ##' ## Pull changes to repo 1 ##' pull(repo_1) ##' ##' ## View contributions by day ##' contributions(repo_1) ##' ##' ## View contributions by author and day ##' contributions(repo_1, by = "author") ##' } contributions <- function(repo = ".", breaks = c("month", "year", "quarter", "week", "day"), by = c("commits", "author")) { breaks <- match.arg(breaks) by <- match.arg(by) ctbs <- .Call(git2r_revwalk_contributions, lookup_repository(repo), TRUE, TRUE, FALSE) ctbs$when <- as.POSIXct(ctbs$when, origin = "1970-01-01", tz = "GMT") ctbs$when <- as.POSIXct(cut(ctbs$when, breaks = breaks)) if (identical(by, "commits")) { ctbs <- as.data.frame(table(ctbs$when)) names(ctbs) <- c("when", "n") ctbs$when <- as.Date(ctbs$when) } else { ## Create an index and tabulate ctbs$index <- paste0(ctbs$when, ctbs$author, ctbs$email) count <- as.data.frame(table(ctbs$index), stringsAsFactors = FALSE) names(count) <- c("index", "n") ## Match counts and clean result ctbs <- as.data.frame(ctbs) ctbs$n <- count$n[match(ctbs$index, count$index)] ctbs <- unique(ctbs[, c("when", "author", "n")]) ctbs$when <- as.Date(substr(as.character(ctbs$when), 1, 10)) ctbs <- ctbs[order(ctbs$when, ctbs$author), ] row.names(ctbs) <- NULL } ctbs } git2r/R/credential.R0000644000175000017500000001730513765064765014122 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013 - 2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Create a new environmental credential object ##' ##' Environmental variables can be written to the file ##' \code{.Renviron}. This file is read by \emph{R} during startup, ##' see \code{\link[base]{Startup}}. ##' @family git credential functions ##' @param username The name of the environmental variable that holds ##' the username for the authentication. ##' @param password The name of the environmental variable that holds ##' the password for the authentication. ##' @return A list of class \code{cred_env} with entries: ##' \describe{ ##' \item{username}{ ##' The name of the environmental variable that holds ##' the username for the authentication. ##' } ##' \item{password}{ ##' The name of the environmental variable that holds ##' the password for the authentication. ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Create an environmental credential object for the username and ##' ## password. ##' cred <- cred_env("NAME_OF_ENV_VARIABLE_WITH_USERNAME", ##' "NAME_OF_ENV_VARIABLE_WITH_PASSWORD") ##' repo <- repository("git2r") ##' push(repo, credentials = cred) ##' } cred_env <- function(username = NULL, password = NULL) { structure(list(username = username, password = password), class = "cred_env") } ##' Create a new personal access token credential object ##' ##' The personal access token is stored in an envrionmental variable. ##' Environmental variables can be written to the file ##' \code{.Renviron}. This file is read by \emph{R} during startup, ##' see \code{\link[base]{Startup}}. On GitHub, personal access tokens ##' function like ordinary OAuth access tokens. They can be used ##' instead of a password for Git over HTTPS, see ##' \url{https://help.github.com/articles/creating-an-access-token-for-command-line-use} ##' @family git credential functions ##' @param token The name of the environmental variable that holds the ##' personal access token for the authentication. Default is ##' \code{GITHUB_PAT}. ##' @return A list of class \code{cred_token} with entry: ##' \describe{ ##' \item{token}{ ##' The name of the environmental variable that holds ##' the personal access token for the authentication. ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Create a personal access token credential object. ##' ## This example assumes that the token is stored in ##' ## the 'GITHUB_PAT' environmental variable. ##' repo <- repository("git2r") ##' cred <- cred_token() ##' push(repo, credentials = cred) ##' } cred_token <- function(token = "GITHUB_PAT") { structure(list(token = token), class = "cred_token") } ##' Create a new plain-text username and password credential object ##' ##' @family git credential functions ##' @param username The username of the credential ##' @param password The password of the credential. If getPass is installed ##' and the only input is username, \code{getPass::getPass()} will be ##' called to allow for interactive and obfuscated interactive ##' input of the password. ##' @return A list of class \code{cred_user_pass} with entries: ##' \describe{ ##' \item{username}{ ##' The username of the credential ##' } ##' \item{password}{ ##' The password of the credential ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Create a plain-text username and password credential object ##' cred_user_pass("Random Developer", "SecretPassword") ##' } cred_user_pass <- function(username = NULL, password = NULL) { if (!is.null(username)) { if (is.null(password)) { if (requireNamespace("getPass", quietly = TRUE)) { password <- getPass::getPass() } } } structure(list(username = username, password = password), class = "cred_user_pass") } ##' Create a new passphrase-protected ssh key credential object ##' ##' @family git credential functions ##' @param publickey The path to the public key of the ##' credential. Default is \code{ssh_path("id_rsa.pub")} ##' @param privatekey The path to the private key of the ##' credential. Default is \code{ssh_path("id_rsa")} ##' @param passphrase The passphrase of the credential. Default is ##' \code{character(0)}. If getPass is installed and private key ##' is passphrase protected \code{getPass::getPass()} will be ##' called to allow for interactive and obfuscated interactive ##' input of the passphrase. ##' @return A list of class \code{cred_ssh_key} with entries: ##' \describe{ ##' \item{publickey}{ ##' The path to the public key of the credential ##' } ##' \item{privatekey}{ ##' The path to the private key of the credential ##' } ##' \item{passphrase}{ ##' The passphrase of the credential ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Create a ssh key credential object. It can optionally be ##' ## passphrase-protected ##' cred <- cred_ssh_key(ssh_path("id_rsa.pub"), ssh_path("id_rsa")) ##' repo <- repository("git2r") ##' push(repo, credentials = cred) ##' } cred_ssh_key <- function(publickey = ssh_path("id_rsa.pub"), privatekey = ssh_path("id_rsa"), passphrase = character(0)) { publickey <- normalizePath(publickey, mustWork = TRUE) privatekey <- normalizePath(privatekey, mustWork = TRUE) if (length(passphrase) == 0) { if (ssh_key_needs_passphrase(privatekey)) { if (requireNamespace("getPass", quietly = TRUE)) { passphrase <- getPass::getPass() } } } structure(list(publickey = publickey, privatekey = privatekey, passphrase = passphrase), class = "cred_ssh_key") } ##' Check if private key is passphrase protected ##' @param privatekey The path to the private key of the ##' credential. Default is \code{ssh_path("id_rsa")} ##' @noRd ssh_key_needs_passphrase <- function(privatekey = ssh_path("id_rsa")) { private_content <- readLines(privatekey, n = 3) contains_encrypted <- grepl("encrypted", private_content, ignore.case = TRUE) any(contains_encrypted) } ##' Compose usual path to ssh keys ##' ##' This function provides a consistent means across OS-types to access the ##' \code{.ssh} directory. ##' ##' On Windows-based systems, ##' \code{path.expand("~")} returns \code{"C:/Users/username/Documents"}, ##' whereas the usual path to the \code{.ssh} directory is ##' \code{"C:/Users/username"}. ##' ##' On other operating systems, \code{path.expand("~")} returns the usual path ##' to the \code{.ssh} directory. ##' ##' Calling \code{ssh_path()} with no arguments will return the usual path to ##' the \code{.ssh} directory. ##' ##' @param file basename of file for which path is requested ##' @return Full path to the file ##' @export ##' @examples ##' ssh_path() ##' ssh_path("is_rsa.pub") ssh_path <- function(file = "") { file.path(home_dir(), ".ssh", file) } # Return the user's home directory regardless of operating system home_dir <- function() { if (.Platform$OS.type == "windows") { home <- Sys.getenv("USERPROFILE") } else { home <- path.expand("~") } return(home) } git2r/R/refspec.R0000644000175000017500000000465613526224476013434 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Create push refspec from arguments ##' ##' @param repo a \code{git_repository} object. ##' @param name The remote's name. Default is NULL. ##' @param refspec The refspec to be pushed. Default is NULL. ##' @param opts List with push options. Default is NULL. ##' @return List with remote (character vector) and refspec (character ##' vector). ##' @noRd get_refspec <- function(repo = NULL, remote = NULL, spec = NULL, opts = NULL) { stopifnot(inherits(repo, "git_repository")) if (is_detached(repo)) stop("You are not currently on a branch.") ## Options: if (!is.null(opts)) { stopifnot(is.list(opts)) } else { opts <- list() } ## Remote: ## From: http://git-scm.com/docs/git-push ## When the command line does not specify where to push with the ## argument, branch.*.remote configuration for the ## current branch is consulted to determine where to push. If the ## configuration is missing, it defaults to origin. if (!is.null(remote)) { stopifnot(is.character(remote), identical(length(remote), 1L)) remote <- sub("^[[:space:]]*", "", sub("[[:space:]]*$", "", remote)) if (identical(nchar(remote), 0L)) remote <- NULL } if (is.null(remote)) { remote <- .Call(git2r_config_get_string, repo, paste0("branch.", repository_head(repo)$name, ".remote")) if (is.null(remote)) remote <- "origin" } ## Refspec: stopifnot(is.character(spec)) if (isTRUE(opts$force)) spec <- paste0("+", spec) list(remote = remote, refspec = spec) } git2r/R/tag.R0000644000175000017500000001402213526215551012536 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Create tag targeting HEAD commit in repository ##' ##' @param object The repository \code{object}. ##' @param name Name for the tag. ##' @param message The tag message. Specify a tag message to create an ##' annotated tag. A lightweight tag is created if the message ##' parameter is \code{NULL}. ##' @param session Add sessionInfo to tag message. Default is FALSE. ##' @param tagger The tagger (author) of the tag ##' @param force Overwrite existing tag. Default = FALSE ##' @return invisible(\code{git_tag}) object ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' filename <- file.path(path, "example.txt") ##' writeLines("Hello world!", filename) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Create an annotated tag ##' tag(repo, "v1.0", "Tag message") ##' ##' ## List tags ##' tags(repo) ##' ##' ## Make a change to the text file and commit. ##' writeLines(c("Hello world!", "HELLO WORLD!"), filename) ##' add(repo, "example.txt") ##' commit(repo, "Second commit message") ##' ##' ## Create a lightweight tag ##' tag(repo, "v2.0") ##' ##' ## List tags ##' tags(repo) ##' } tag <- function(object = ".", name = NULL, message = NULL, session = FALSE, tagger = NULL, force = FALSE) { object <- lookup_repository(object) if (isTRUE(session)) message <- add_session_info(message) if (is.null(tagger)) tagger <- default_signature(object) invisible(.Call(git2r_tag_create, object, name, message, tagger, force)) } ##' Check if object is a git_tag object ##' ##' @param object Check if object is a git_tag object ##' @return TRUE if object is a git_tag, else FALSE ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Create tag ##' tag(repo, "Tagname", "Tag message") ##' ##' is_tag(tags(repo)[[1]]) ##' is_tag(last_commit(repo)) ##' } is_tag <- function(object) { inherits(object, "git_tag") } ##' Delete an existing tag reference ##' ##' @param object Can be either the path (default is ".") to a ##' repository, or a \code{git_repository} object, or a ##' \code{git_tag} object. or the tag name. ##' @param name If the \code{object} argument is a path to a ##' repository or a \code{git_repository}, the name of the tag to ##' delete. ##' @return \code{invisible(NULL)} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Create two tags ##' tag(repo, "Tag1", "Tag message 1") ##' t2 <- tag(repo, "Tag2", "Tag message 2") ##' ##' ## List the two tags in the repository ##' tags(repo) ##' ##' ## Delete the two tags in the repository ##' tag_delete(repo, "Tag1") ##' tag_delete(t2) ##' ##' ## Show the empty list with tags in the repository ##' tags(repo) ##' } tag_delete <- function(object = ".", name = NULL) { if (is_tag(object)) { name <- object$name object <- object$repo } else { object <- lookup_repository(object) } .Call(git2r_tag_delete, object, name) invisible(NULL) } ##' Tags ##' ##' @template repo-param ##' @return list of tags in repository ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Create tag ##' tag(repo, "Tagname", "Tag message") ##' ##' ## List tags ##' tags(repo) ##' } tags <- function(repo = ".") { .Call(git2r_tag_list, lookup_repository(repo)) } ##' @export format.git_tag <- function(x, ...) { sprintf("[%s] %s", substr(x$target, 1, 6), x$name) } ##' @export print.git_tag <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } ##' @export summary.git_tag <- function(object, ...) { cat(sprintf(paste0("name: %s\n", "target: %s\n", "tagger: %s <%s>\n", "when: %s\n", "message: %s\n"), object$name, object$target, object$tagger$name, object$tagger$email, as.character(object$tagger$when), object$message)) } git2r/R/reset.R0000644000175000017500000000732413526232037013112 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Reset current HEAD to the specified state ##' ##' @param object Either a \code{git_commit}, a \code{git_repository} ##' or a character vector. If \code{object} is a ##' \code{git_commit}, HEAD is moved to the \code{git_commit}. If ##' \code{object} is a \code{git_repository}, resets the index ##' entries in the \code{path} argument to their state at HEAD. If ##' \code{object} is a character vector with paths, resets the ##' index entries in \code{object} to their state at HEAD if the ##' current working directory is in a repository. ##' @param reset_type If object is a 'git_commit', the kind of reset ##' operation to perform. 'soft' means the HEAD will be moved to ##' the commit. 'mixed' reset will trigger a 'soft' reset, plus ##' the index will be replaced with the content of the commit ##' tree. 'hard' reset will trigger a 'mixed' reset and the ##' working directory will be replaced with the content of the ##' index. ##' @param path If object is a 'git_repository', resets the index ##' entries for all paths to their state at HEAD. ##' @return invisible NULL ##' @export ##' @examples \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test-1.txt")) ##' add(repo, "test-1.txt") ##' commit_1 <- commit(repo, "Commit message") ##' ##' ## Change and stage the file ##' writeLines(c("Hello world!", "HELLO WORLD!"), file.path(path, "test-1.txt")) ##' add(repo, "test-1.txt") ##' status(repo) ##' ##' ## Unstage file ##' reset(repo, path = "test-1.txt") ##' status(repo) ##' ##' ## Make one more commit ##' add(repo, "test-1.txt") ##' commit(repo, "Next commit message") ##' ##' ## Create one more file ##' writeLines("Hello world!", file.path(path, "test-2.txt")) ##' ##' ## 'soft' reset to first commit and check status ##' reset(commit_1) ##' status(repo) ##' ##' ## 'mixed' reset to first commit and check status ##' commit(repo, "Next commit message") ##' reset(commit_1, "mixed") ##' status(repo) ##' ##' ## 'hard' reset to first commit and check status ##' add(repo, "test-1.txt") ##' commit(repo, "Next commit message") ##' reset(commit_1, "hard") ##' status(repo) ##' } reset <- function(object, reset_type = c("soft", "mixed", "hard"), path = NULL) { if (is_commit(object)) { reset_type <- switch(match.arg(reset_type), soft = 1L, mixed = 2L, hard = 3L) .Call(git2r_reset, object, reset_type) } else { object <- lookup_repository(object) if (is_empty(object)) { .Call(git2r_index_remove_bypath, object, path) } else { .Call(git2r_reset_default, object, path) } } invisible(NULL) } git2r/R/odb.R0000644000175000017500000000775113526222366012544 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Blobs in the object database ##' ##' List all blobs reachable from the commits in the object ##' database. For each commit, list blob's in the commit tree and ##' sub-trees. ##' @template repo-param ##' @return A data.frame with the following columns: ##' \describe{ ##' \item{sha}{The sha of the blob} ##' \item{path}{The path to the blob from the tree and sub-trees} ##' \item{name}{The name of the blob from the tree that contains the blob} ##' \item{len}{The length of the blob} ##' \item{commit}{The sha of the commit} ##' \item{author}{The author of the commit} ##' \item{when}{The timestamp of the author signature in the commit} ##' } ##' @note A blob sha can have several entries ##' @export ##' @examples \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "Commit message 1") ##' ##' ## Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "Commit message 2") ##' ##' ## Commit same content under different name in a sub-directory ##' dir.create(file.path(path, "sub-directory")) ##' file.copy(file.path(path, "test.txt"), ##' file.path(path, "sub-directory", "copy.txt")) ##' add(repo, "sub-directory/copy.txt") ##' commit(repo, "Commit message 3") ##' ##' ## List blobs ##' odb_blobs(repo) ##' } odb_blobs <- function(repo = ".") { blobs <- .Call(git2r_odb_blobs, lookup_repository(repo)) blobs <- data.frame(blobs, stringsAsFactors = FALSE) blobs <- blobs[order(blobs$when), ] index <- paste0(blobs$sha, ":", blobs$path, "/", blobs$name) blobs <- blobs[!duplicated(index), ] rownames(blobs) <- NULL blobs$when <- as.POSIXct(blobs$when, origin = "1970-01-01", tz = "GMT") blobs } ##' List all objects available in the database ##' ##' @template repo-param ##' @return A data.frame with the following columns: ##' \describe{ ##' \item{sha}{The sha of the object} ##' \item{type}{The type of the object} ##' \item{len}{The length of the object} ##' } ##' @export ##' @examples \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "Commit message 1") ##' ##' ## Create tag ##' tag(repo, "Tagname", "Tag message") ##' ##' ## List objects in repository ##' odb_objects(repo) ##' } odb_objects <- function(repo = ".") { data.frame(.Call(git2r_odb_objects, lookup_repository(repo)), stringsAsFactors = FALSE) } git2r/R/diff.R0000644000175000017500000002356613526215555012714 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Number of files in git_diff object ##' ##' @param x The git_diff \code{object} ##' @return a non-negative integer ##' @export length.git_diff <- function(x) { length(x$files) } ##' @export print.git_diff <- function(x, ...) { cat("Old: ") if (is.character(x$old)) { cat(x$old, "\n") } else if (inherits(x$old, "git_tree")) { print(x$old) } else { cat("\n") print(x$old) } cat("New: ") if (is.character(x$new)) { cat(x$new, "\n") } else if (inherits(x$new, "git_tree")) { print(x$new) } else { cat("\n") print(x$new) } invisible(x) } lines_per_file <- function(diff) { lapply(diff$files, function(x) { del <- add <- 0 for (h in x$hunks) { for (l in h$lines) { if (l$origin == 45) { del <- del + l$num_lines } else if (l$origin == 43) { add <- add + l$num_lines } } } list(file = x$new_file, del = del, add = add) }) } print_lines_per_file <- function(diff) { lpf <- lines_per_file(diff) files <- vapply(lpf, function(x) x$file, character(1)) del <- vapply(lpf, function(x) x$del, numeric(1)) add <- vapply(lpf, function(x) x$add, numeric(1)) paste0(format(files), " | ", "-", format(del), " +", format(add)) } hunks_per_file <- function(diff) { vapply(diff$files, function(x) length(x$hunks), numeric(1)) } ##' @export summary.git_diff <- function(object, ...) { print(object) if (length(object) > 0) { plpf <- print_lines_per_file(object) hpf <- hunks_per_file(object) hunk_txt <- ifelse(hpf > 1, " hunks", ifelse(hpf > 0, " hunk", " hunk (binary file)")) phpf <- paste0(" in ", format(hpf), hunk_txt) cat("Summary:", paste0(plpf, phpf), sep = "\n") } else { cat("No changes.\n") } } ##' Changes between commits, trees, working tree, etc. ##' ##' @rdname diff-methods ##' @export ##' @param x A \code{git_repository} object or the old \code{git_tree} ##' object to compare to. ##' @param index \describe{ ##' \item{\emph{When object equals a git_repository}}{ ##' Whether to compare the index to HEAD. If FALSE (the default), ##' then the working tree is compared to the index. ##' } ##' \item{\emph{When object equals a git_tree}}{ ##' Whether to use the working directory (by default), or the index ##' (if set to TRUE) in the comparison to \code{object}. ##' } ##' } ##' @param as_char logical: should the result be converted to a ##' character string?. Default is FALSE. ##' @param filename If as_char is TRUE, then the diff can be written ##' to a file with name filename (the file is overwritten if it ##' exists). Default is NULL. ##' @param context_lines The number of unchanged lines that define the ##' boundary of a hunk (and to display before and after). Defaults ##' to 3. ##' @param interhunk_lines The maximum number of unchanged lines ##' between hunk boundaries before the hunks will be merged into ##' one. Defaults to 0. ##' @param old_prefix The virtual "directory" prefix for old file ##' names in hunk headers. Default is "a". ##' @param new_prefix The virtual "directory" prefix for new file ##' names in hunk headers. Defaults to "b". ##' @param id_abbrev The abbreviation length to use when formatting ##' object ids. Defaults to the value of 'core.abbrev' from the ##' config, or 7 if NULL. ##' @param path A character vector of paths / fnmatch patterns to ##' constrain diff. Default is NULL which include all paths. ##' @param max_size A size (in bytes) above which a blob will be ##' marked as binary automatically; pass a negative value to ##' disable. Defaults to 512MB when max_size is NULL. ##' @return A \code{git_diff} object if as_char is FALSE. If as_char ##' is TRUE and filename is NULL, a character string, else NULL. ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add, commit ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "Commit message") ##' ##' ## Change the file ##' writeLines(c("Hello again!", "Here is a second line", "And a third"), ##' file.path(path, "test.txt")) ##' ##' ## diff between index and workdir ##' diff_1 <- diff(repo) ##' summary(diff_1) ##' cat(diff(repo, as_char=TRUE)) ##' ##' ## Diff between index and HEAD is empty ##' diff_2 <- diff(repo, index=TRUE) ##' summary(diff_2) ##' cat(diff(repo, index=TRUE, as_char=TRUE)) ##' ##' ## Diff between tree and working dir, same as diff_1 ##' diff_3 <- diff(tree(commits(repo)[[1]])) ##' summary(diff_3) ##' cat(diff(tree(commits(repo)[[1]]), as_char=TRUE)) ##' ##' ## Add changes, diff between index and HEAD is the same as diff_1 ##' add(repo, "test.txt") ##' diff_4 <- diff(repo, index=TRUE) ##' summary(diff_4) ##' cat(diff(repo, index=TRUE, as_char=TRUE)) ##' ##' ## Diff between tree and index ##' diff_5 <- diff(tree(commits(repo)[[1]]), index=TRUE) ##' summary(diff_5) ##' cat(diff(tree(commits(repo)[[1]]), index=TRUE, as_char=TRUE)) ##' ##' ## Diff between two trees ##' commit(repo, "Second commit") ##' tree_1 <- tree(commits(repo)[[2]]) ##' tree_2 <- tree(commits(repo)[[1]]) ##' diff_6 <- diff(tree_1, tree_2) ##' summary(diff_6) ##' cat(diff(tree_1, tree_2, as_char=TRUE)) ##' ##' ## Binary files ##' set.seed(42) ##' writeBin(as.raw((sample(0:255, 1000, replace=TRUE))), ##' con=file.path(path, "test.bin")) ##' add(repo, "test.bin") ##' diff_7 <- diff(repo, index=TRUE) ##' summary(diff_7) ##' cat(diff(repo, index=TRUE, as_char=TRUE)) ##' } diff.git_repository <- function(x, index = FALSE, as_char = FALSE, filename = NULL, context_lines = 3, interhunk_lines = 0, old_prefix = "a", new_prefix = "b", id_abbrev = NULL, path = NULL, max_size = NULL, ...) { if (as_char) { ## Make sure filename is character(0) to write to a ## character vector or a character vector with path in ## order to write to a file. filename <- as.character(filename) if (any(identical(filename, NA_character_), identical(nchar(filename), 0L))) { filename <- character(0) } else if (length(filename)) { filename <- normalizePath(filename, mustWork = FALSE) } } else { ## Make sure filename is NULL filename <- NULL } if (!is.null(id_abbrev)) id_abbrev <- as.integer(id_abbrev) if (!is.null(max_size)) max_size <- as.integer(max_size) .Call(git2r_diff, x, NULL, NULL, index, filename, as.integer(context_lines), as.integer(interhunk_lines), old_prefix, new_prefix, id_abbrev, path, max_size) } ##' @rdname diff-methods ##' @param new_tree The new git_tree object to compare, or NULL. If ##' NULL, then we use the working directory or the index (see the ##' \code{index} argument). ##' @param ... Not used. ##' @export diff.git_tree <- function(x, new_tree = NULL, index = FALSE, as_char = FALSE, filename = NULL, context_lines = 3, interhunk_lines = 0, old_prefix = "a", new_prefix = "b", id_abbrev = NULL, path = NULL, max_size = NULL, ...) { if (as_char) { ## Make sure filename is character(0) to write to a character ## vector or a character vector with path in order to write to ## a file. filename <- as.character(filename) if (any(identical(filename, NA_character_), identical(nchar(filename), 0L))) { filename <- character(0) } else if (length(filename)) { filename <- normalizePath(filename, mustWork = FALSE) } } else { ## Make sure filename is NULL filename <- NULL } if (!is.null(new_tree)) { if (!inherits(new_tree, "git_tree")) { stop("Not a git tree") } if (x$repo$path != new_tree$repo$path) { stop("Cannot compare trees in different repositories") } } if (!is.null(id_abbrev)) id_abbrev <- as.integer(id_abbrev) if (!is.null(max_size)) max_size <- as.integer(max_size) .Call(git2r_diff, NULL, x, new_tree, index, filename, as.integer(context_lines), as.integer(interhunk_lines), old_prefix, new_prefix, id_abbrev, path, max_size) } ##' @export base::diff git2r/R/when.R0000644000175000017500000000502313565057155012734 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' When ##' ##' Help method to extract the time as a character string from a ##' git_commit, git_signature, git_tag and git_time object. ##' @param object the \code{object} to extract the time slot from. ##' @inheritParams base::as.POSIXct ##' @inheritParams base::strptime ##' @return A \code{character} vector of length one. ##' @seealso \code{\link{git_time}} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a first user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Create tag ##' tag(repo, "Tagname", "Tag message") ##' ##' when(commits(repo)[[1]]) ##' when(tags(repo)[[1]]) ##' when(tags(repo)[[1]], tz = Sys.timezone()) ##' } when <- function(object, tz = "GMT", origin = "1970-01-01", usetz = TRUE) { if (inherits(object, "git_commit")) return(as.character(object$author$when, tz = tz, origin = origin, usetz = usetz)) if (inherits(object, "git_signature")) return(as.character(object$when, tz = tz, origin = origin, usetz = usetz)) if (inherits(object, "git_stash")) return(as.character(object$stasher$when, tz = tz, origin = origin, usetz = usetz)) if (inherits(object, "git_tag")) return(as.character(object$tagger$when, tz = tz, origin = origin, usetz = usetz)) if (inherits(object, "git_time")) return(as.character(object, tz = tz, origin = origin, usetz = usetz)) stop("Invalid 'object'") } git2r/R/sha.R0000644000175000017500000000461213526223032012533 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Get the SHA-1 of a git object ##' ##' Get the 40 character hexadecimal string of the SHA-1. ##' @param object a git object to get the SHA-1 from. ##' @return The 40 character hexadecimal string of the SHA-1. ##' @export ##' @examples \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "Commit message 1") ##' ##' ## Get the SHA-1 of the last commit ##' sha(last_commit(repo)) ##' } sha <- function(object) { UseMethod("sha", object) } ##' @rdname sha ##' @export sha.git_blob <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_branch <- function(object) { branch_target(object) } ##' @rdname sha ##' @export sha.git_commit <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_note <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_reference <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_reflog_entry <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_tag <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_tree <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_fetch_head <- function(object) { object$sha } ##' @rdname sha ##' @export sha.git_merge_result <- function(object) { object$sha } git2r/R/remote.R0000644000175000017500000001037713304500130013247 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Get the configured remotes for a repo ##' ##' @template repo-param ##' @return Character vector with remotes ##' @export ##' @template remote-example remotes <- function(repo = ".") { .Call(git2r_remote_list, lookup_repository(repo)) } ##' Add a remote to a repo ##' ##' @template repo-param ##' @param name Short name of the remote repository ##' @param url URL of the remote repository ##' @return NULL, invisibly ##' @export ##' @template remote-example remote_add <- function(repo = ".", name = NULL, url = NULL) { .Call(git2r_remote_add, lookup_repository(repo), name, url) invisible(NULL) } ##' Rename a remote ##' ##' @template repo-param ##' @param oldname Old name of the remote ##' @param newname New name of the remote ##' @return NULL, invisibly ##' @export ##' @template remote-example remote_rename <- function(repo = ".", oldname = NULL, newname = NULL) { .Call(git2r_remote_rename, lookup_repository(repo), oldname, newname) invisible(NULL) } ##' Remove a remote ##' ##' All remote-tracking branches and configuration settings for the ##' remote will be removed. ##' @template repo-param ##' @param name The name of the remote to remove ##' @return NULL, invisibly ##' @export ##' @template remote-example remote_remove <- function(repo = ".", name = NULL) { .Call(git2r_remote_remove, lookup_repository(repo), name) invisible(NULL) } ##' Set the remote's url in the configuration ##' ##' This assumes the common case of a single-url remote and will ##' otherwise raise an error. ##' @template repo-param ##' @param name The name of the remote ##' @param url The \code{url} to set ##' @return NULL, invisibly ##' @export ##' @template remote-example remote_set_url <- function(repo = ".", name = NULL, url = NULL) { .Call(git2r_remote_set_url, lookup_repository(repo), name, url) invisible(NULL) } ##' Get the remote url for remotes in a repo ##' ##' @template repo-param ##' @param remote Character vector with the remotes to get the url ##' from. Default is the remotes of the repository. ##' @return Character vector with remote_url for each of the remote ##' @export ##' @template remote-example remote_url <- function(repo = ".", remote = NULL) { repo <- lookup_repository(repo) if (is.null(remote)) remote <- remotes(repo) .Call(git2r_remote_url, repo, remote) } ##' List references in a remote repository ##' ##' Displays references available in a remote repository along with ##' the associated commit IDs. Akin to the 'git ls-remote' command. ##' @param name Character vector with the "remote" repository URL to ##' query or the name of the remote if a \code{repo} argument is ##' given. ##' @param repo an optional repository object used if remotes are ##' specified by name. ##' @param credentials The credentials for remote repository ##' access. Default is NULL. To use and query an ssh-agent for the ##' ssh key credentials, let this parameter be NULL (the default). ##' @return Character vector for each reference with the associated ##' commit IDs. ##' @export ##' @examples ##' \dontrun{ ##' remote_ls("https://github.com/ropensci/git2r") ##' } remote_ls <- function(name = NULL, repo = NULL, credentials = NULL) { if (is.null(repo)) { ver <- libgit2_version() if (ver$major == 0 && ver$minor < 27) { path <- tempdir() repo <- init(path) on.exit(unlink(file.path(path, ".git"), recursive = TRUE)) } } else { repo <- lookup_repository(repo) } .Call(git2r_remote_ls, name, repo, credentials) } git2r/R/merge.R0000644000175000017500000001057413526215555013076 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Find a merge base between two commits ##' ##' @param one One of the commits ##' @param two The other commit ##' @return git_commit ##' @export ##' @examples \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Master branch", file.path(path, "master_branch.txt")) ##' add(repo, "master_branch.txt") ##' commit_1 <- commit(repo, "Commit message 1") ##' ##' ## Create first branch, checkout, add file and commit ##' branch_1 <- branch_create(commit_1, "branch_1") ##' checkout(branch_1) ##' writeLines("Branch 1", file.path(path, "branch_1.txt")) ##' add(repo, "branch_1.txt") ##' commit_2 <- commit(repo, "Commit message branch_1") ##' ##' ## Create second branch, checkout, add file and commit ##' branch_2 <- branch_create(commit_1, "branch_2") ##' checkout(branch_2) ##' writeLines("Branch 2", file.path(path, "branch_2.txt")) ##' add(repo, "branch_2.txt") ##' commit_3 <- commit(repo, "Commit message branch_2") ##' ##' ## Check that merge base equals commit_1 ##' stopifnot(identical(merge_base(commit_2, commit_3), commit_1)) ##' } merge_base <- function(one = NULL, two = NULL) { .Call(git2r_merge_base, one, two) } ##' Merge a branch into HEAD ##' ##' @rdname merge ##' @param x A path (default '.') to a repository, or a ##' \code{git_repository} object, or a \code{git_branch}. ##' @param y If \code{x} is a \code{git_repository}, the name of the ##' branch to merge into HEAD. Not used if \code{x} is a ##' \code{git_branch}. ##' @param commit_on_success If there are no conflicts written to the ##' index, the merge commit will be committed. Default is TRUE. ##' @param merger Who made the merge. The default (\code{NULL}) is to ##' use \code{default_signature} for the repository. ##' @param fail If a conflict occurs, exit immediately instead of ##' attempting to continue resolving conflicts. Default is ##' \code{FALSE}. ##' @param ... Additional arguments (unused). ##' @template return-git_merge_result ##' @export ##' @template merge-example merge.git_branch <- function(x, y = NULL, commit_on_success = TRUE, merger = NULL, fail = FALSE, ...) { if (is.null(merger)) merger <- default_signature(x$repo) .Call(git2r_merge_branch, x, merger, commit_on_success, fail) } ##' @export ##' @rdname merge merge.git_repository <- function(x, y = NULL, commit_on_success = TRUE, merger = NULL, fail = FALSE, ...) { ## Check branch argument if (is.null(y) || !is.character(y) || !identical(length(y), 1L)) stop("'branch' must be a character vector of length one") b <- branches(x) b <- b[vapply(b, "[[", character(1), "name") == y][[1]] merge.git_branch(b, commit_on_success = commit_on_success, merger = merger, fail = fail) } ##' @export ##' @rdname merge merge.character <- function(x = ".", y = NULL, commit_on_success = TRUE, merger = NULL, fail = FALSE, ...) { x <- lookup_repository(x) merge.git_repository(x, y, commit_on_success, merger, fail) } ##' @export base::merge ##' @export format.git_merge_result <- function(x, ...) { if (isTRUE(x$up_to_date)) return("Already up-to-date") if (isTRUE(x$conflicts)) return("Merge: Conflicts") if (isTRUE(x$fast_forward)) return("Merge: Fast-forward") return("Merge") } ##' @export print.git_merge_result <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } git2r/R/pull.R0000644000175000017500000001040013526222106012725 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Pull ##' ##' @template repo-param ##' @param credentials The credentials for remote repository ##' access. Default is NULL. To use and query an ssh-agent for the ##' ssh key credentials, let this parameter be NULL (the default). ##' @param merger Who made the merge, if the merge is non-fast forward ##' merge that creates a merge commit. The ##' \code{default_signature} for \code{repo} is used if this ##' parameter is \code{NULL}. ##' @template return-git_merge_result ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo_1 <- tempfile(pattern="git2r-") ##' path_repo_2 <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo_1) ##' dir.create(path_repo_2) ##' repo_bare <- init(path_bare, bare = TRUE) ##' repo_1 <- clone(path_bare, path_repo_1) ##' ##' ## Config first user and commit a file ##' config(repo_1, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "First commit message") ##' ##' ## Push commits from first repository to bare repository ##' ## Adds an upstream tracking branch to branch 'master' ##' push(repo_1, "origin", "refs/heads/master") ##' ##' ## Clone to second repository ##' repo_2 <- clone(path_bare, path_repo_2) ##' config(repo_2, user.name = "Bob", user.email = "bob@@example.org") ##' ##' ## Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "Second commit message") ##' ##' ## Push commits from first repository to bare repository ##' push(repo_1) ##' ##' ## Pull changes to repo_2 ##' pull(repo_2) ##' ##' ## Change file again and commit. This time in repository 2 ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", ##' "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") ##' writeLines(lines, file.path(path_repo_2, "example.txt")) ##' add(repo_2, "example.txt") ##' commit(repo_2, "Third commit message") ##' ##' ## Push commits from second repository to bare repository ##' push(repo_2) ##' ##' ## Pull changes to repo_1 ##' pull(repo_1) ##' ##' ## List commits in repositories ##' commits(repo_1) ##' commits(repo_2) ##' commits(repo_bare) ##' } pull <- function(repo = ".", credentials = NULL, merger = NULL) { repo <- lookup_repository(repo) if (is.null(merger)) merger <- default_signature(repo) current_branch <- repository_head(repo) if (is.null(current_branch)) stop("'branch' is NULL") if (!is_local(current_branch)) stop("'branch' is not local") upstream_branch <- branch_get_upstream(current_branch) if (is.null(upstream_branch)) stop("'branch' is not tracking a remote branch") fetch(repo = repo, name = branch_remote_name(upstream_branch), credentials = credentials) ## fetch heads marked for merge fh <- fetch_heads(repo) fh <- fh[vapply(fh, "[[", logical(1), "is_merge")] if (identical(length(fh), 0L)) stop("Remote ref was not fetched") .Call(git2r_merge_fetch_heads, fh, merger) } git2r/R/fetch.R0000644000175000017500000001165213526222106013054 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Fetch new data and update tips ##' ##' @template repo-param ##' @param name the remote's name ##' @param credentials The credentials for remote repository ##' access. Default is NULL. To use and query an ssh-agent for the ##' ssh key credentials, let this parameter be NULL (the default). ##' @param verbose Print information each time a reference is updated ##' locally. Default is \code{TRUE}. ##' @param refspec The refs to fetch and which local refs to update, ##' see examples. Pass NULL to use the ##' \code{remote..fetch} variable. Default is ##' \code{NULL}. ##' @return invisible list of class \code{git_transfer_progress} ##' with statistics from the fetch operation: ##' \describe{ ##' \item{total_objects}{ ##' Number of objects in the packfile being downloaded ##' } ##' \item{indexed_objects}{ ##' Received objects that have been hashed ##' } ##' \item{received_objects}{ ##' Objects which have been downloaded ##' } ##' \item{total_deltas}{ ##' Total number of deltas in the pack ##' } ##' \item{indexed_deltas}{ ##' Deltas which have been indexed ##' } ##' \item{local_objects}{ ##' Locally-available objects that have been injected in order to ##' fix a thin pack ##' } ##' \item{received_bytes}{ ##' Size of the packfile received up to now ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize three temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo_1 <- tempfile(pattern="git2r-") ##' path_repo_2 <- tempfile(pattern="git2r-") ##' ##' dir.create(path_bare) ##' dir.create(path_repo_1) ##' dir.create(path_repo_2) ##' ##' bare_repo <- init(path_bare, bare = TRUE) ##' repo_1 <- clone(path_bare, path_repo_1) ##' repo_2 <- clone(path_bare, path_repo_2) ##' ##' config(repo_1, user.name = "Alice", user.email = "alice@@example.org") ##' config(repo_2, user.name = "Bob", user.email = "bob@@example.org") ##' ##' ## Add changes to repo 1 ##' writeLines("Lorem ipsum dolor sit amet", ##' con = file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "Commit message") ##' ##' ## Push changes from repo 1 to origin (bare_repo) ##' push(repo_1, "origin", "refs/heads/master") ##' ##' ## Fetch changes from origin (bare_repo) to repo 2 ##' fetch(repo_2, "origin") ##' ##' ## List updated heads ##' fetch_heads(repo_2) ##' ##' ## Checking out GitHub pull requests locally ##' path <- tempfile(pattern="ghit-") ##' repo <- clone("https://github.com/leeper/ghit", path) ##' fetch(repo, "origin", refspec = "pull/13/head:refs/heads/BRANCHNAME") ##' checkout(repo, "BRANCHNAME") ##' summary(repo) ##' } fetch <- function(repo = ".", name = NULL, credentials = NULL, verbose = TRUE, refspec = NULL) { invisible(.Call(git2r_remote_fetch, lookup_repository(repo), name, credentials, "fetch", verbose, refspec)) } ##' Get updated heads during the last fetch. ##' ##' @template repo-param ##' @return list with \code{git_fetch_head} entries. NULL if there is ##' no FETCH_HEAD file. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize three temporary repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo_1 <- tempfile(pattern="git2r-") ##' path_repo_2 <- tempfile(pattern="git2r-") ##' ##' dir.create(path_bare) ##' dir.create(path_repo_1) ##' dir.create(path_repo_2) ##' ##' bare_repo <- init(path_bare, bare = TRUE) ##' repo_1 <- clone(path_bare, path_repo_1) ##' repo_2 <- clone(path_bare, path_repo_2) ##' ##' config(repo_1, user.name = "Alice", user.email = "alice@@example.org") ##' config(repo_2, user.name = "Bob", user.email = "bob@@example.org") ##' ##' ## Add changes to repo 1 ##' writeLines("Lorem ipsum dolor sit amet", ##' con = file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "Commit message") ##' ##' ## Push changes from repo 1 to origin (bare_repo) ##' push(repo_1, "origin", "refs/heads/master") ##' ##' ## Fetch changes from origin (bare_repo) to repo 2 ##' fetch(repo_2, "origin") ##' ##' ## List updated heads ##' fetch_heads(repo_2) ##' } fetch_heads <- function(repo = ".") { .Call(git2r_repository_fetch_heads, lookup_repository(repo)) } git2r/R/time.R0000644000175000017500000000521713565056761012740 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Time ##' ##' The class \code{git_time} stores the time a Git object was created. ##' ##' The default is to use \code{tz = "GMT"} and \code{origin = ##' "1970-01-01"}. To use your local timezone, set \code{tz = ##' Sys.timezone()}. ##' ##' @inheritParams base::as.POSIXct ##' @inheritParams base::strptime ##' @seealso \code{\link{when}} ##' @name git_time ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a first user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Create tag ##' tag(repo, "Tagname", "Tag message") ##' ##' as.POSIXct(commits(repo)[[1]]$author$when) ##' as.POSIXct(tags(repo)[[1]]$tagger$when) ##' as.POSIXct(tags(repo)[[1]]$tagger$when, tz = Sys.timezone()) ##' } NULL ##' @rdname git_time ##' @export as.character.git_time <- function(x, tz = "GMT", origin = "1970-01-01", usetz = TRUE, ...) { as.character(format(as.POSIXct(x, tz = tz, origin = origin), usetz = usetz), ...) } ##' @rdname git_time ##' @export format.git_time <- function(x, tz = "GMT", origin = "1970-01-01", usetz = TRUE, ...) { format(as.POSIXct(x, tz = tz, origin = origin), usetz = usetz, ...) } ##' @rdname git_time ##' @export as.POSIXct.git_time <- function(x, tz = "GMT", origin = "1970-01-01", ...) { as.POSIXct(x$time, tz = tz, origin = origin, ...) } ##' @rdname git_time ##' @export print.git_time <- function(x, tz = "GMT", origin = "1970-01-01", usetz = TRUE, ...) { cat(sprintf("%s\n", as.character(x, tz = tz, origin = origin, usetz = usetz, ...))) invisible(x) } git2r/R/punch_card.R0000644000175000017500000000614513526222552014077 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Punch card ##' ##' @template repo-param ##' @param main Default title for the plot is "Punch card on repo:" ##' and repository workdir basename. Supply a new title if you ##' desire one. ##' @param ... Additional arguments affecting the plot ##' @return invisible NULL ##' @importFrom graphics axis ##' @importFrom graphics par ##' @importFrom graphics plot.new ##' @importFrom graphics plot.window ##' @importFrom graphics symbols ##' @importFrom graphics title ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- clone("https://github.com/ropensci/git2r.git", path) ##' ##' ## Plot ##' punch_card(repo) ##' } punch_card <- function(repo = ".", main = NULL, ...) { savepar <- graphics::par(las = 1, mar = c(2.2, 6, 2, 0)) on.exit(par(savepar)) wd <- c("Saturday", "Friday", "Thursday", "Wednesday", "Tuesday", "Monday", "Sunday") ## Extract information from repository repo <- lookup_repository(repo) df <- as.data.frame(repo) df$when <- as.POSIXlt(df$when) df$hour <- df$when$hour df$weekday <- df$when$wday ## Create a key and tabulate df$key <- paste0(df$weekday, "-", df$hour) df <- as.data.frame(table(df$key), stringsAsFactors = FALSE) names(df) <- c("key", "Commits") ## Convert key to Weekday and Hour df$Weekday <- sapply(strsplit(df$key, "-"), "[", 1) df$Weekday <- factor(df$Weekday, levels = c(6, 5, 4, 3, 2, 1, 0), labels = wd) df$Hour <- as.integer(sapply(strsplit(df$key, "-"), "[", 2)) df$key <- paste0(df$Weekday, "-", df$Hour) ## Scale df$Commits <- sqrt((df$Commits / max(df$Commits)) / pi) plot.new() plot.window(xlim = c(0, 23), ylim = c(0.8, 7.2)) symbols(df$ Hour, df$Weekday, circles = df$Commits, xaxt = "n", yaxt = "n", inches = FALSE, fg = "white", bg = "black", add = TRUE, ...) h <- 0:23 h <- paste0(ifelse(h > 9, as.character(h), paste0("0", as.character(h))), ":00") axis(1, at = 0:23, labels = h) axis(2, at = 1:7, labels = wd) if (is.null(main)) { if (is_bare(repo)) { main <- "Punch card" } else { main <- sprintf("Punch card on repository: %s", basename(workdir(repo))) } } title(main) invisible(NULL) } git2r/R/blame.R0000644000175000017500000000611613526222107013043 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Get blame for file ##' ##' @template repo-param ##' @param path Path to the file to consider ##' @return git_blame object with the following entries: ##' \describe{ ##' \item{path}{ ##' The path to the file of the blame ##' } ##' \item{hunks}{ ##' List of blame hunks ##' } ##' \item{repo}{ ##' The git_repository that contains the file ##' } ##' } ##' \describe{ ##' \item{lines_in_hunk}{ ##' The number of lines in this hunk ##' } ##' \item{final_commit_id}{ ##' The sha of the commit where this line was last changed ##' } ##' \item{final_start_line_number}{ ##' The 1-based line number where this hunk begins, in the final ##' version of the file ##' } ##' \item{final_signature}{ ##' Final committer ##' } ##' \item{orig_commit_id}{ ##' The sha of the commit where this hunk was found. This will usually ##' be the same as 'final_commit_id'. ##' } ##' \item{orig_start_line_number}{ ##' The 1-based line number where this hunk begins in the file ##' named by 'orig_path' in the commit specified by 'orig_commit_id'. ##' } ##' \item{orig_signature}{ ##' Origin committer ##' } ##' \item{orig_path}{ ##' The path to the file where this hunk originated, as of the commit ##' specified by 'orig_commit_id' ##' } ##' \item{boundary}{ ##' TRUE iff the hunk has been tracked to a boundary commit. ##' } ##' \item{repo}{ ##' The \code{git_repository} object that contains the blame hunk ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a first user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Create a second user and change the file ##' config(repo, user.name = "Bob", user.email = "bob@@example.org") ##' writeLines(c("Hello world!", "HELLO WORLD!", "HOLA"), ##' file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Second commit message") ##' ##' ## Check blame ##' blame(repo, "example.txt") ##' } blame <- function(repo = ".", path = NULL) { .Call(git2r_blame_file, lookup_repository(repo), path) } git2r/R/blob.R0000644000175000017500000001717013526215657012717 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Create blob from file on disk ##' ##' Read a file from the filesystem and write its content to the ##' Object Database as a loose blob. The method is vectorized and ##' accepts a vector of files to create blobs from. ##' @param repo The repository where the blob(s) will be written. Can ##' be a bare repository. A \code{git_repository} object, or a ##' path to a repository, or \code{NULL}. If the \code{repo} ##' argument is \code{NULL}, the repository is searched for with ##' \code{\link{discover_repository}} in the current working ##' directory. ##' @param path The file(s) from which the blob will be created. ##' @param relative TRUE if the file(s) from which the blob will be ##' created is relative to the repository's working dir. Default ##' is TRUE. ##' @return list of S3 class git_blob \code{objects} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create blobs from files relative to workdir ##' writeLines("Hello, world!", file.path(path, "example-1.txt")) ##' writeLines("test content", file.path(path, "example-2.txt")) ##' blob_list_1 <- blob_create(repo, c("example-1.txt", ##' "example-2.txt")) ##' ##' ## Create blobs from files not relative to workdir ##' temp_file_1 <- tempfile() ##' temp_file_2 <- tempfile() ##' writeLines("Hello, world!", temp_file_1) ##' writeLines("test content", temp_file_2) ##' blob_list_2 <- blob_create(repo, c(temp_file_1, temp_file_2), ##' relative = FALSE) ##' } blob_create <- function(repo = ".", path = NULL, relative = TRUE) { repo <- lookup_repository(repo) if (isTRUE(relative)) return(.Call(git2r_blob_create_fromworkdir, repo, path)) path <- normalizePath(path, mustWork = TRUE) .Call(git2r_blob_create_fromdisk, repo, path) } ##' Content of blob ##' ##' @param blob The blob object. ##' @param split Split blob content to text lines. Default TRUE. ##' @return The content of the blob. NA_character_ if the blob is binary. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Display content of blob. ##' content(tree(commits(repo)[[1]])["example.txt"]) ##' } content <- function(blob = NULL, split = TRUE) { if (is_binary(blob)) return(NA_character_) ret <- .Call(git2r_blob_content, blob) if (isTRUE(split)) ret <- strsplit(ret, "\n")[[1]] ret } ##' Determine the sha from a blob string ##' ##' The blob is not written to the object database. ##' @param data The string vector to hash. ##' @return A string vector with the sha for each string in data. ##' @export ##' @examples ##' \dontrun{ ##' identical(hash(c("Hello, world!\n", ##' "test content\n")), ##' c("af5626b4a114abcb82d63db7c8082c3c4756e51b", ##' "d670460b4b4aece5915caf5c68d12f560a9fe3e4")) ##' } hash <- function(data = NULL) { .Call(git2r_odb_hash, data) } ##' Determine the sha from a blob in a file ##' ##' The blob is not written to the object database. ##' @param path The path vector with files to hash. ##' @return A vector with the sha for each file in path. ##' @export ##' @examples ##' \dontrun{ ##' ## Create a file. NOTE: The line endings from writeLines gives ##' ## LF (line feed) on Unix/Linux and CRLF (carriage return, line feed) ##' ## on Windows. The example use writeChar to have more control. ##' path <- tempfile() ##' f <- file(path, "wb") ##' writeChar("Hello, world!\n", f, eos = NULL) ##' close(f) ##' ##' ## Generate hash ##' hashfile(path) ##' identical(hashfile(path), hash("Hello, world!\n")) ##' } hashfile <- function(path = NULL) { path <- normalizePath(path, mustWork = TRUE) if (any(is.na(path))) stop("Invalid 'path' argument") .Call(git2r_odb_hashfile, path) } ##' Is blob binary ##' ##' @param blob The blob \code{object}. ##' @return TRUE if binary data, FALSE if not. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' ##' ## Check if binary ##' b_text <- tree(commit_1)["example.txt"] ##' is_binary(b_text) ##' ##' ## Commit plot file (binary) ##' x <- 1:100 ##' y <- x^2 ##' png(file.path(path, "plot.png")) ##' plot(y ~ x, type = "l") ##' dev.off() ##' add(repo, "plot.png") ##' commit_2 <- commit(repo, "Second commit message") ##' ##' ## Check if binary ##' b_png <- tree(commit_2)["plot.png"] ##' is_binary(b_png) ##' } is_binary <- function(blob = NULL) { .Call(git2r_blob_is_binary, blob) } ##' Check if object is S3 class git_blob ##' ##' @param object Check if object is S3 class git_blob ##' @return TRUE if object is S3 class git_blob, else FALSE ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' blob_1 <- tree(commit_1)["example.txt"] ##' ##' ## Check if blob ##' is_blob(commit_1) ##' is_blob(blob_1) ##' } is_blob <- function(object) { inherits(object, "git_blob") } ##' Size in bytes of the contents of a blob ##' ##' @param x The blob \code{object} ##' @return a non-negative integer ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' blob_1 <- tree(commit_1)["example.txt"] ##' ##' ## Get length in size of bytes of the content of the blob ##' length(blob_1) ##' } length.git_blob <- function(x) { .Call(git2r_blob_rawsize, x) } ##' @export format.git_blob <- function(x, ...) { sprintf("blob: %s\nsize: %i bytes", x$sha, length(x)) } ##' @export print.git_blob <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } git2r/R/git2r.R0000644000175000017500000000214513643133524013014 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2020 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' git2r: R bindings to the libgit2 library ##' ##' git2r: R bindings to the libgit2 library. ##' @docType package ##' @name git2r ##' @useDynLib git2r, .registration=TRUE NULL ##' Unload hook function ##' ##' @param libpath A character string giving the complete path to the ##' package. ##' @noRd .onUnload <- function(libpath) { library.dynam.unload("git2r", libpath) } git2r/R/index.R0000644000175000017500000002027313526215555013103 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Add file(s) to index ##' ##' @template repo-param ##' @param path Character vector with file names or shell glob ##' patterns that will matched against files in the repository's ##' working directory. Each file that matches will be added to the ##' index (either updating an existing entry or adding a new ##' entry). ##' @param force Add ignored files. Default is FALSE. ##' @return invisible(NULL) ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file ##' writeLines("a", file.path(path, "a.txt")) ##' ##' ## Add file to repository and view status ##' add(repo, "a.txt") ##' status(repo) ##' ##' ## Add file with a leading './' when the repository working ##' ## directory is the current working directory ##' setwd(path) ##' writeLines("b", file.path(path, "b.txt")) ##' add(repo, "./b.txt") ##' status(repo) ##' ##' ## Add a file in a sub-folder with sub-folder as the working ##' ## directory. Create a file in the root of the repository ##' ## working directory that will remain untracked. ##' dir.create(file.path(path, "sub_dir")) ##' setwd("./sub_dir") ##' writeLines("c", file.path(path, "c.txt")) ##' writeLines("c", file.path(path, "sub_dir/c.txt")) ##' add(repo, "c.txt") ##' status(repo) ##' ##' ## Add files with glob expansion when the current working ##' ## directory is outside the repository's working directory. ##' setwd(tempdir()) ##' dir.create(file.path(path, "glob_dir")) ##' writeLines("d", file.path(path, "glob_dir/d.txt")) ##' writeLines("e", file.path(path, "glob_dir/e.txt")) ##' writeLines("f", file.path(path, "glob_dir/f.txt")) ##' writeLines("g", file.path(path, "glob_dir/g.md")) ##' add(repo, "glob_dir/*txt") ##' status(repo) ##' ##' ## Add file with glob expansion with a relative path when ##' ## the current working directory is inside the repository's ##' ## working directory. ##' setwd(path) ##' add(repo, "./glob_dir/*md") ##' status(repo) ##' } add <- function(repo = ".", path = NULL, force = FALSE) { ## Documentation for the pathspec argument in the libgit2 function ## 'git_index_add_all' that git2r use internally: ## ## The pathspec is a list of file names or shell glob patterns ## that will matched against files in the repository's working ## directory. Each file that matches will be added to the index ## (either updating an existing entry or adding a new entry). if (is.null(path) || !is.character(path)) stop("'path' must be a character vector") repo <- lookup_repository(repo) repo_wd <- normalizePath(workdir(repo), winslash = "/") path <- vapply(path, sanitize_path, character(1), repo_wd = repo_wd) .Call(git2r_index_add_all, repo, path, isTRUE(force)) invisible(NULL) } sanitize_path <- function(p, repo_wd) { np <- suppressWarnings(normalizePath(p, winslash = "/")) if (!length(grep("/$", repo_wd))) repo_wd <- paste0(repo_wd, "/") ## Check if the normalized path is a non-file e.g. a glob. if (!file.exists(np)) { ## Check if the normalized path starts with a leading './' if (length(grep("^[.]/", np))) { nd <- suppressWarnings(normalizePath(dirname(p), winslash = "/")) if (!length(grep("/$", nd))) nd <- paste0(nd, "/") np <- paste0(nd, basename(np)) } } ## Check if the file is in the repository's working directory, ## else let libgit2 handle this path unmodified. if (!length(grep(paste0("^", repo_wd), np))) return(p) ## Change the path to be relative to the repository's working ## directory. Substitute common prefix with "" sub(paste0("^", repo_wd), "", np) } ##' Remove files from the working tree and from the index ##' ##' @template repo-param ##' @param path character vector with filenames to remove. Only files ##' known to Git are removed. ##' @return invisible(NULL) ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file ##' writeLines("Hello world!", file.path(path, "file-to-remove.txt")) ##' ##' ## Add file to repository ##' add(repo, "file-to-remove.txt") ##' commit(repo, "First commit message") ##' ##' ## Remove file ##' rm_file(repo, "file-to-remove.txt") ##' ##' ## View status of repository ##' status(repo) ##' } rm_file <- function(repo = ".", path = NULL) { if (is.null(path) || !is.character(path)) stop("'path' must be a character vector") repo <- lookup_repository(repo) if (length(path)) { repo_wd <- workdir(repo) repo_wd <- normalizePath(workdir(repo), winslash = "/") path <- vapply(path, sanitize_path, character(1), repo_wd = repo_wd) ## Check that files exists and are known to Git if (!all(file.exists(file.path(repo_wd, path)))) { stop(sprintf("pathspec '%s' did not match any files. ", path[!file.exists(file.path(repo_wd, path))])) } if (any(file.info(file.path(repo_wd, path))$isdir)) { stop(sprintf("pathspec '%s' did not match any files. ", path[exists(file.path(repo_wd, path))])) } s <- status(repo, staged = TRUE, unstaged = TRUE, untracked = TRUE, ignored = TRUE) if (any(path %in% c(s$ignored, s$untracked))) { stop(sprintf("pathspec '%s' did not match any files. ", path[path %in% c(s$ignored, s$untracked)])) } if (any(path %in% s$staged)) { stop(sprintf("'%s' has changes staged in the index. ", path[path %in% s$staged])) } if (any(path %in% s$unstaged)) { stop(sprintf("'%s' has local modifications. ", path[path %in% s$unstaged])) } ## Remove and stage files lapply(path, function(x) { file.remove(file.path(repo_wd, x)) .Call(git2r_index_remove_bypath, repo, x) }) } invisible(NULL) } ##' Remove an index entry corresponding to a file on disk ##' ##' @template repo-param ##' @param path character vector with filenames to remove. The path ##' must be relative to the repository's working folder. It may ##' exist. If this file currently is the result of a merge ##' conflict, this file will no longer be marked as ##' conflicting. The data about the conflict will be moved to the ##' "resolve undo" (REUC) section. ##' @return invisible(NULL) ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file ##' writeLines("Hello world!", file.path(path, "file-to-remove.txt")) ##' ##' ## Add file to repository ##' add(repo, "file-to-remove.txt") ##' ##' ## View status of repository ##' status(repo) ##' ##' ## Remove file ##' index_remove_bypath(repo, "file-to-remove.txt") ##' ##' ## View status of repository ##' status(repo) ##' } index_remove_bypath <- function(repo = ".", path = NULL) { .Call(git2r_index_remove_bypath, lookup_repository(repo), path) invisible(NULL) } git2r/R/tree.R0000644000175000017500000002425313556622745012743 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Coerce entries in a git_tree to a \code{data.frame} ##' ##' The entries in a tree are coerced to a \code{data.frame} ##' ##' ##' The \code{data.frame} have the following columns: ##' \describe{ ##' ##' \item{filemode}{ ##' The UNIX file attributes of a tree entry ##' } ##' ##' \item{type}{ ##' String representation of the tree entry type ##' } ##' ##' \item{sha}{ ##' The sha of a tree entry ##' } ##' ##' \item{name}{ ##' The filename of a tree entry ##' } ##' ##' } ##' @param x The tree \code{object} ##' @param ... Additional arguments. Not used. ##' @return \code{data.frame} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' dir.create(file.path(path, "subfolder")) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create three files and commit ##' writeLines("First file", file.path(path, "example-1.txt")) ##' writeLines("Second file", file.path(path, "subfolder/example-2.txt")) ##' writeLines("Third file", file.path(path, "example-3.txt")) ##' add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) ##' commit(repo, "Commit message") ##' ##' ## Display tree ##' tree(last_commit(repo)) ##' ##' ## Coerce tree to a data.frame ##' df <- as.data.frame(tree(last_commit(repo))) ##' df ##' } as.data.frame.git_tree <- function(x, ...) { data.frame(mode = sprintf("%06o", x$filemode), type = x$type, sha = x$id, name = x$name, stringsAsFactors = FALSE) } ##' @export base::as.data.frame ##' Coerce entries in a git_tree to a list of entry objects ##' ##' @param x The tree \code{object} ##' @param ... Unused ##' @return list of entry objects ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' dir.create(file.path(path, "subfolder")) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create three files and commit ##' writeLines("First file", file.path(path, "example-1.txt")) ##' writeLines("Second file", file.path(path, "subfolder/example-2.txt")) ##' writeLines("Third file", file.path(path, "example-3.txt")) ##' add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) ##' commit(repo, "Commit message") ##' ##' ## Inspect size of each blob in tree ##' invisible(lapply(as(tree(last_commit(repo)), "list"), ##' function(obj) { ##' if (is_blob(obj)) ##' summary(obj) ##' NULL ##' })) ##' } as.list.git_tree <- function(x, ...) { lapply(x$id, function(sha) lookup(x$repo, sha)) } ##' Tree ##' ##' Get the tree pointed to by a commit or stash. ##' @param object the \code{commit} or \code{stash} object ##' @return A S3 class git_tree object ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a first user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' tree(last_commit(repo)) ##' } tree <- function(object = NULL) { .Call(git2r_commit_tree, object) } ##' @export print.git_tree <- function(x, ...) { cat(sprintf("tree: %s\n\n", x$sha)) print(as.data.frame(x)) invisible(x) } ##' Summary of tree ##' ##' @param object The tree \code{object} ##' @param ... Additional arguments affecting the summary produced. ##' @return None (invisible 'NULL'). ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' summary(tree(last_commit(repo))) ##' } summary.git_tree <- function(object, ...) { print(as.data.frame(object)) } ##' Extract object from tree ##' ##' Lookup a tree entry by its position in the tree ##' @param x The tree \code{object} ##' @param i The index (integer or logical) of the tree object to ##' extract. If negative values, all elements except those indicated ##' are selected. A character vector to match against the names of ##' objects to extract. ##' @return Git object ##' @export ##' @examples ##' \dontrun{ ##' ##' Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' dir.create(file.path(path, "subfolder")) ##' repo <- init(path) ##' ##' ##' Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ##' Create three files and commit ##' writeLines("First file", file.path(path, "example-1.txt")) ##' writeLines("Second file", file.path(path, "subfolder/example-2.txt")) ##' writeLines("Third file", file.path(path, "example-3.txt")) ##' add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) ##' new_commit <- commit(repo, "Commit message") ##' ##' ##' Pick a tree in the repository ##' tree_object <- tree(new_commit) ##' ##' ##' Display tree ##' tree_object ##' ##' ##' Select item by name ##' tree_object["example-1.txt"] ##' ##' ##' Select first item in tree ##' tree_object[1] ##' ##' ##' Select first three items in tree ##' tree_object[1:3] ##' ##' ##' Select all blobs in tree ##' tree_object[vapply(as(tree_object, 'list'), is_blob, logical(1))] ##' } "[.git_tree" <- function(x, i) { if (is.logical(i)) return(x[seq_along(x)[i]]) if (is.character(i)) return(x[which(x$name %in% i)]) if (!is.numeric(i)) stop("Invalid index") i <- seq_len(length(x))[as.integer(i)] ret <- lapply(i, function(j) lookup(x$repo, x$id[j])) if (identical(length(ret), 1L)) ret <- ret[[1]] ret } ##' Number of entries in tree ##' ##' @param x The tree \code{object} ##' @return a non-negative integer or double (which will be rounded ##' down) ##' @export length.git_tree <- function(x) { length(x$id) } ##' Check if object is S3 class git_tree ##' ##' @param object Check if object is S3 class git_tree ##' @return TRUE if object is S3 class git_tree, else FALSE ##' @keywords methods ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Commit a text file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' tree_1 <- tree(commit_1) ##' ##' ## Check if tree ##' is_tree(commit_1) ##' is_tree(tree_1) ##' } is_tree <- function(object) { inherits(object, "git_tree") } ##' List the contents of a tree object ##' ##' Traverse the entries in a tree and its subtrees. Akin to the 'git ##' ls-tree' command. ##' @param tree default (\code{NULL}) is the tree of the last commit ##' in \code{repo}. Can also be a \code{git_tree} object or a ##' character that identifies a tree in the repository (see ##' \sQuote{Examples}). ##' @param repo never used if \code{tree} is a \code{git_tree} ##' object. A \code{git_repository} object, or a path (default = ##' '.') to a repository. ##' @param recursive default is to recurse into sub-trees. ##' @return A data.frame with the following columns: \describe{ ##' \item{mode}{UNIX file attribute of the tree entry} ##' \item{type}{type of object} \item{sha}{sha of the object} ##' \item{path}{path relative to the root tree} ##' \item{name}{filename of the tree entry} \item{len}{object size ##' of blob (file) entries. NA for other objects.} } ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' dir.create(file.path(path, "subfolder")) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create three files and commit ##' writeLines("First file", file.path(path, "example-1.txt")) ##' writeLines("Second file", file.path(path, "subfolder/example-2.txt")) ##' writeLines("Third file", file.path(path, "example-3.txt")) ##' add(repo, c("example-1.txt", "subfolder/example-2.txt", "example-3.txt")) ##' commit(repo, "Commit message") ##' ##' ## Traverse tree entries and its subtrees. ##' ## Various approaches that give identical result. ##' ls_tree(tree = tree(last_commit(path))) ##' ls_tree(tree = tree(last_commit(repo))) ##' ls_tree(repo = path) ##' ls_tree(repo = repo) ##' ##' ## Skip content in subfolder ##' ls_tree(repo = repo, recursive = FALSE) ##' ##' ## Start in subfolder ##' ls_tree(tree = "HEAD:subfolder", repo = repo) ##' } ls_tree <- function(tree = NULL, repo = ".", recursive = TRUE) { if (is.null(tree)) { tree <- tree(last_commit(lookup_repository(repo))) } else if (is.character(tree)) { tree <- revparse_single(repo = lookup_repository(repo), revision = tree) if (!is_tree(tree)) tree <- tree(tree) } data.frame(.Call(git2r_tree_walk, tree, recursive), stringsAsFactors = FALSE) } git2r/R/note.R0000644000175000017500000001656113526215554012745 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Default notes reference ##' ##' Get the default notes reference for a repository ##' @template repo-param ##' @return Character vector of length one with name of default notes ##' reference ##' @export ##' @examples ##' \dontrun{ ##' ## Create and initialize a repository in a temporary directory ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## View default notes reference ##' note_default_ref(repo) ##' } note_default_ref <- function(repo = ".") { .Call(git2r_note_default_ref, lookup_repository(repo)) } ##' Add note for a object ##' ##' @param object The object to annotate (git_blob, git_commit or ##' git_tree). ##' @param message Content of the note to add ##' @param ref Canonical name of the reference to use. Default is ##' \code{note_default_ref}. ##' @param author Signature of the notes note author ##' @param committer Signature of the notes note committer ##' @param force Overwrite existing note. Default is FALSE ##' @return git_note ##' @export ##' @examples ##' \dontrun{ ##' ## Create and initialize a repository in a temporary directory ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "Commit message 1") ##' ##' ## Create another commit ##' writeLines(c("Hello world!", ##' "HELLO WORLD!"), ##' file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_2 <- commit(repo, "Commit message 2") ##' ##' ## Check that notes is an empty list ##' notes(repo) ##' ##' ## Create note in default namespace ##' note_create(commit_1, "Note-1") ##' ##' ## Create note in named (review) namespace ##' note_create(commit_1, "Note-2", ref="refs/notes/review") ##' note_create(commit_2, "Note-3", ref="review") ##' ##' ## Create note on blob and tree ##' note_create(tree(commit_1), "Note-4") ##' note_create(tree(commit_1)["example.txt"], "Note-5") ##' } note_create <- function(object = NULL, message = NULL, ref = NULL, author = NULL, committer = NULL, force = FALSE) { if (is.null(object)) stop("'object' is missing") if (!any(is_blob(object), is_commit(object), is_tree(object))) stop("'object' must be a 'git_blob', 'git_commit' or 'git_tree' object") repo <- object$repo sha <- object$sha if (is.null(ref)) ref <- note_default_ref(repo) stopifnot(is.character(ref), identical(length(ref), 1L)) if (!length(grep("^refs/notes/", ref))) ref <- paste0("refs/notes/", ref) if (is.null(author)) author <- default_signature(repo) if (is.null(committer)) committer <- default_signature(repo) .Call(git2r_note_create, repo, sha, message, ref, author, committer, force) } ##' List notes ##' ##' List all the notes within a specified namespace. ##' @template repo-param ##' @param ref Reference to read from. Default (ref = NULL) is to call ##' \code{note_default_ref}. ##' @return list with git_note objects ##' @export ##' @examples ##' \dontrun{ ##' ## Create and initialize a repository in a temporary directory ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "Commit message 1") ##' ##' ## Create another commit ##' writeLines(c("Hello world!", ##' "HELLO WORLD!"), ##' file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_2 <- commit(repo, "Commit message 2") ##' ##' ## Create note in default namespace ##' note_create(commit_1, "Note-1") ##' note_create(commit_1, "Note-2", force = TRUE) ##' ##' ## Create note in named (review) namespace ##' note_create(commit_1, "Note-3", ref="refs/notes/review") ##' note_create(commit_2, "Note-4", ref="review") ##' ##' ## Create note on blob and tree ##' note_create(tree(commit_1), "Note-5") ##' note_create(tree(commit_1)["example.txt"], "Note-6") ##' ##' ## List notes in default namespace ##' notes(repo) ##' ##' ## List notes in 'review' namespace ##' notes(repo, "review") ##' } notes <- function(repo = ".", ref = NULL) { repo <- lookup_repository(repo) if (is.null(ref)) ref <- note_default_ref(repo) stopifnot(is.character(ref), identical(length(ref), 1L)) if (!length(grep("^refs/notes/", ref))) ref <- paste0("refs/notes/", ref) .Call(git2r_notes, repo, ref) } ##' Remove the note for an object ##' ##' @param note The note to remove ##' @param author Signature of the notes commit author. ##' @param committer Signature of the notes commit committer. ##' @return invisible NULL ##' @export ##' @examples ##' \dontrun{ ##' ## Create and initialize a repository in a temporary directory ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "Commit message 1") ##' ##' ##' ## Create note in default namespace ##' note_1 <- note_create(commit_1, "Note-1") ##' ##' ## Create note in named (review) namespace ##' note_2 <- note_create(commit_1, "Note-2", ref="refs/notes/review") ##' ##' ## List notes in default namespace ##' notes(repo) ##' ##' ## List notes in 'review' namespace ##' notes(repo, "review") ##' ##' ## Remove notes ##' note_remove(note_1) ##' note_remove(note_2) ##' ##' ## List notes in default namespace ##' notes(repo) ##' ##' ## List notes in 'review' namespace ##' notes(repo, "review") ##' } note_remove <- function(note = NULL, author = NULL, committer = NULL) { if (!inherits(note, "git_note")) stop("'note' is not a git_note") if (is.null(author)) author <- default_signature(note$repo) if (is.null(committer)) committer <- default_signature(note$repo) .Call(git2r_note_remove, note, author, committer) invisible(NULL) } ##' @export format.git_note <- function(x, ...) { sprintf("note: %s", x$sha) } ##' @export print.git_note <- function(x, ...) { cat(format(x, ...), "\n", sep = "") invisible(x) } git2r/R/bundle_r_package.R0000644000175000017500000000441213556615027015237 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2018 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Bundle bare repo of package ##' ##' Clone the package git repository as a bare repository to ##' \code{pkg/inst/pkg.git} ##' @template repo-param ##' @return Invisible bundled \code{git_repository} object ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repository ##' path <- tempfile() ##' dir.create(path) ##' path <- file.path(path, "git2r") ##' repo <- clone("https://github.com/ropensci/git2r.git", path) ##' ##' ## Bundle bare repository in package ##' bundle_r_package(repo) ##' ##' ## Build and install bundled package ##' wd <- setwd(dirname(path)) ##' system(sprintf("R CMD build %s", path)) ##' pkg <- list.files(".", pattern = "[.]tar[.]gz$") ##' system(sprintf("R CMD INSTALL %s", pkg)) ##' setwd(wd) ##' ##' ## Reload package ##' detach("package:git2r", unload = TRUE) ##' library(git2r) ##' ##' ## Summarize last five commits of bundled repo ##' repo <- repository(system.file("git2r.git", package = "git2r")) ##' invisible(lapply(commits(repo, n = 5), summary)) ##' ##' ## Plot content of bundled repo ##' plot(repo) ##' } bundle_r_package <- function(repo = ".") { repo <- lookup_repository(repo) ## Check for 'inst' folder inst <- file.path(workdir(repo), "inst") if (!isTRUE(file.info(inst)$isdir)) dir.create(inst) ## Check for 'pkg.git' folder local_path <- paste0(basename(workdir(repo)), ".git", sep = "") local_path <- file.path(inst, local_path) if (file.exists(local_path)) stop("Repo already exists:", local_path) invisible(clone(workdir(repo), local_path, bare = TRUE)) } git2r/R/checkout.R0000644000175000017500000001751713765355224013613 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2019 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Determine previous branch name ##' ##' @param repo The repository. ##' @noRd previous_branch_name <- function(repo) { branch <- revparse_single(repo, "@{-1}")$sha branch <- sapply(references(repo), function(x) { if (is.null(x$sha)) return(NA_character_) if (x$sha == branch) return(x$shorthand) NA_character_ }) branch <- branch[vapply(branch, Negate(is.na), logical(1))] branch <- sapply(branches(repo, "local"), function(x) { ifelse(x$name %in% branch, x$name, NA_character_) }) branch <- branch[vapply(branch, Negate(is.na), logical(1))] if (any(!is.character(branch), !identical(length(branch), 1L))) { stop("'branch' must be a character vector of length one") } branch } checkout_branch <- function(object, force) { ref_name <- paste0("refs/heads/", object$name) .Call(git2r_checkout_tree, object$repo, ref_name, force) .Call(git2r_repository_set_head, object$repo, ref_name) } checkout_commit <- function(object, force) { .Call(git2r_checkout_tree, object$repo, object$sha, force) .Call(git2r_repository_set_head_detached, object) } checkout_tag <- function(object, force) { .Call(git2r_checkout_tree, object$repo, object$target, force) .Call(git2r_repository_set_head_detached, lookup(object$repo, object$target)) } checkout_git_object <- function(object, force) { if (is_branch(object)) { checkout_branch(object, force) return(TRUE) } if (is_commit(object)) { checkout_commit(object, force) return(TRUE) } if (is_tag(object)) { checkout_tag(object, force) return(TRUE) } FALSE } ##' Checkout ##' ##' Update files in the index and working tree to match the content of ##' the tree pointed at by the treeish object (commit, tag or tree). ##' The default checkout strategy (\code{force = FALSE}) will only ##' make modifications that will not lose changes. Use \code{force = ##' TRUE} to force working directory to look like index. ##' @param object A path to a repository, or a \code{git_repository} ##' object, or a \code{git_commit} object, or a \code{git_tag} ##' object, or a \code{git_tree} object. ##' @param branch name of the branch to check out. Only used if object ##' is a path to a repository or a \code{git_repository} object. ##' @param create create branch if it doesn't exist. Only used if ##' object is a path to a repository or a \code{git_repository} ##' object. ##' @param force If \code{TRUE}, then make working directory match ##' target. This will throw away local changes. Default is ##' \code{FALSE}. ##' @param path Limit the checkout operation to only certain ##' paths. This argument is only used if branch is NULL. Default ##' is \code{NULL}. ##' @param ... Additional arguments. Not used. ##' @return invisible NULL ##' @export ##' @examples ##' \dontrun{ ##' ## Create directories and initialize repositories ##' path_bare <- tempfile(pattern="git2r-") ##' path_repo_1 <- tempfile(pattern="git2r-") ##' path_repo_2 <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' dir.create(path_repo_1) ##' dir.create(path_repo_2) ##' repo_bare <- init(path_bare, bare = TRUE) ##' ##' ## Clone to repo 1 and config user ##' repo_1 <- clone(path_bare, path_repo_1) ##' config(repo_1, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Add changes to repo 1 and push to bare ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo_1, "test.txt")) ##' add(repo_1, "test.txt") ##' commit(repo_1, "First commit message") ##' push(repo_1, "origin", "refs/heads/master") ##' ##' ## Create and checkout 'dev' branch in repo 1 ##' checkout(repo_1, "dev", create = TRUE) ##' ##' ## Add changes to 'dev' branch in repo 1 and push to bare ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path_repo_1, "test.txt")) ##' add(repo_1, "test.txt") ##' commit(repo_1, "Second commit message") ##' push(repo_1, "origin", "refs/heads/dev") ##' ##' ## Clone to repo 2 ##' repo_2 <- clone(path_bare, path_repo_2) ##' config(repo_2, user.name = "Bob", user.email = "bob@@example.org") ##' ##' ## Read content of 'test.txt' ##' readLines(file.path(path_repo_2, "test.txt")) ##' ##' ## Checkout dev branch ##' checkout(repo_2, "dev") ##' ##' ## Read content of 'test.txt' ##' readLines(file.path(path_repo_2, "test.txt")) ##' ##' ## Edit "test.txt" in repo_2 ##' writeLines("Hello world!", con = file.path(path_repo_2, "test.txt")) ##' ##' ## Check status ##' status(repo_2) ##' ##' ## Checkout "test.txt" ##' checkout(repo_2, path = "test.txt") ##' ##' ## Check status ##' status(repo_2) ##' } checkout <- function(object = NULL, branch = NULL, create = FALSE, force = FALSE, path = NULL, ...) { if (checkout_git_object(object, force)) return(invisible(NULL)) object <- lookup_repository(object) if (is.null(branch)) { if (is.null(path)) stop("missing 'branch' or 'path' argument") .Call(git2r_checkout_path, object, path) return(invisible(NULL)) } if (!is.character(branch) || !identical(length(branch), 1L)) stop("'branch' must be a character vector of length one") if (is_empty(object)) { if (!isTRUE(create)) stop(sprintf("'%s' did not match any branch", branch)) ref_name <- paste0("refs/heads/", branch) .Call(git2r_repository_set_head, object, ref_name) return(invisible(NULL)) } if (identical(branch, "-")) branch <- previous_branch_name(object) ## Check if branch exists in a local branch lb <- branches(object, "local") lb <- lb[vapply(lb, "[[", character(1), "name") == branch] if (length(lb)) { checkout_branch(lb[[1]], force) return(invisible(NULL)) } ## Check if there exists exactly one remote branch with a matching ## name. rb <- branches(object, "remote") ## Split remote/name to check for a unique name name <- vapply(rb, function(x) { remote <- strsplit(x$name, "/")[[1]][1] sub(paste0("^", remote, "/"), "", x$name) }, character(1)) i <- which(name == branch) if (identical(length(i), 1L)) { ## Create branch and track remote commit <- lookup(object, branch_target(rb[[i]])) branch <- branch_create(commit, branch) branch_set_upstream(branch, rb[[i]]$name) checkout_branch(branch, force) return(invisible(NULL)) } if (isTRUE(create)) { ## Create branch commit <- lookup(object, branch_target(repository_head(object))) checkout_branch(branch_create(commit, branch), force) return(invisible(NULL)) } ## Check if branch object is specified by revision. if (checkout_git_object(revparse_single(object, branch), force)) return(invisible(NULL)) stop(sprintf("'%s' did not match any branch", branch)) } git2r/R/repository.R0000644000175000017500000006404014076203020014174 0ustar nileshnilesh## git2r, R bindings to the libgit2 library. ## Copyright (C) 2013-2021 The git2r contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License, version 2, ## as published by the Free Software Foundation. ## ## git2r is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##' Coerce Git repository to a \code{data.frame} ##' ##' The commits in the repository are coerced to a \code{data.frame} ##' ##' ##' The \code{data.frame} have the following columns: ##' \describe{ ##' ##' \item{sha}{ ##' The 40 character hexadecimal string of the SHA-1 ##' } ##' ##' \item{summary}{ ##' the short "summary" of the git commit message. ##' } ##' ##' \item{message}{ ##' the full message of a commit ##' } ##' ##' \item{author}{ ##' full name of the author ##' } ##' ##' \item{email}{ ##' email of the author ##' } ##' ##' \item{when}{ ##' time when the commit happened ##' } ##' ##' } ##' @param x The repository \code{object} ##' @param ... Additional arguments. Not used. ##' @return \code{data.frame} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create three files and commit ##' writeLines("First file", file.path(path, "example-1.txt")) ##' writeLines("Second file", file.path(path, "example-2.txt")) ##' writeLines("Third file", file.path(path, "example-3.txt")) ##' add(repo, "example-1.txt") ##' commit(repo, "Commit first file") ##' add(repo, "example-2.txt") ##' commit(repo, "Commit second file") ##' add(repo, "example-3.txt") ##' commit(repo, "Commit third file") ##' ##' ## Coerce commits to a data.frame ##' df <- as.data.frame(repo) ##' df ##' } as.data.frame.git_repository <- function(x, ...) { do.call("rbind", lapply(commits(x), as.data.frame)) } ##' Open a repository ##' ##' @param path A path to an existing local git repository. ##' @param discover Discover repository from path. Default is TRUE. ##' @return A \code{git_repository} object with entries: ##' \describe{ ##' \item{path}{ ##' Path to a git repository ##' } ##' } ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' # Configure a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "test-1.txt")) ##' add(repo, 'test-1.txt') ##' commit_1 <- commit(repo, "Commit message") ##' ##' ## Make one more commit ##' writeLines(c("Hello world!", "HELLO WORLD!"), ##' file.path(path, "test-1.txt")) ##' add(repo, 'test-1.txt') ##' commit(repo, "Next commit message") ##' ##' ## Create one more file ##' writeLines("Hello world!", ##' file.path(path, "test-2.txt")) ##' ##' ## Brief summary of repository ##' repo ##' ##' ## Summary of repository ##' summary(repo) ##' ##' ## Workdir of repository ##' workdir(repo) ##' ##' ## Check if repository is bare ##' is_bare(repo) ##' ##' ## Check if repository is empty ##' is_empty(repo) ##' ##' ## Check if repository is a shallow clone ##' is_shallow(repo) ##' ##' ## List all references in repository ##' references(repo) ##' ##' ## List all branches in repository ##' branches(repo) ##' ##' ## Get HEAD of repository ##' repository_head(repo) ##' ##' ## Check if HEAD is head ##' is_head(repository_head(repo)) ##' ##' ## Check if HEAD is local ##' is_local(repository_head(repo)) ##' ##' ## List all tags in repository ##' tags(repo) ##' } repository <- function(path = ".", discover = TRUE) { if (isTRUE(discover)) { path <- discover_repository(path) if (is.null(path)) stop("The 'path' is not in a git repository") } else { path <- normalizePath(path, winslash = "/", mustWork = TRUE) if (!file.info(path)$isdir) stop("'path' is not a directory") } if (!isTRUE(.Call(git2r_repository_can_open, path))) stop("Unable to open repository at 'path'") structure(list(path = path), class = "git_repository") } ##' Init a repository ##' ##' @param path A path to where to init a git repository ##' @param bare If TRUE, a Git repository without a working directory ##' is created at the pointed path. If FALSE, provided path will ##' be considered as the working directory into which the .git ##' directory will be created. ##' @param branch Use the specified name for the initial branch in the ##' newly created repository. If \code{branch=NULL}, fall back to ##' the default name. ##' @return A \code{git_repository} object ##' @export ##' @seealso \link{repository} ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' is_bare(repo) ##' ##' ## Initialize a bare repository ##' path_bare <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' repo_bare <- init(path_bare, bare = TRUE) ##' is_bare(repo_bare) ##' } init <- function(path = ".", bare = FALSE, branch = NULL) { path <- normalizePath(path, winslash = "/", mustWork = TRUE) if (!file.info(path)$isdir) stop("'path' is not a directory") .Call(git2r_repository_init, path, bare, branch) repository(path) } ##' Clone a remote repository ##' ##' @param url The remote repository to clone ##' @param local_path Local directory to clone to. ##' @param bare Create a bare repository. Default is FALSE. ##' @param branch The name of the branch to checkout. Default is NULL ##' which means to use the remote's default branch. ##' @param checkout Checkout HEAD after the clone is complete. Default ##' is TRUE. ##' @param credentials The credentials for remote repository ##' access. Default is NULL. To use and query an ssh-agent for the ##' ssh key credentials, let this parameter be NULL (the default). ##' @param progress Show progress. Default is TRUE. ##' @return A \code{git_repository} object. ##' @seealso \link{repository}, \code{\link{cred_user_pass}}, ##' \code{\link{cred_ssh_key}} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repository ##' path_repo_1 <- tempfile(pattern="git2r-") ##' path_repo_2 <- tempfile(pattern="git2r-") ##' dir.create(path_repo_1) ##' dir.create(path_repo_2) ##' repo_1 <- init(path_repo_1) ##' ##' ## Config user and commit a file ##' config(repo_1, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' writeLines( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "First commit message") ##' ##' ## Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "Second commit message") ##' ##' ## Change file again and commit. ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", ##' "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") ##' writeLines(lines, file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "Third commit message") ##' ##' ## Clone to second repository ##' repo_2 <- clone(path_repo_1, path_repo_2) ##' ##' ## List commits in repositories ##' commits(repo_1) ##' commits(repo_2) ##' } clone <- function(url = NULL, local_path = NULL, bare = FALSE, branch = NULL, checkout = TRUE, credentials = NULL, progress = TRUE) { .Call(git2r_clone, url, local_path, bare, branch, checkout, credentials, progress) repository(local_path) } ##' Get HEAD for a repository ##' ##' @param x The repository \code{x} to check head ##' @param ... Additional arguments. Unused. ##' @return NULL if unborn branch or not found. A git_branch if not a ##' detached head. A git_commit if detached head ##' @importFrom utils head ##' @export ##' @examples ##' \dontrun{ ##' ## Create and initialize a repository in a temporary directory ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Commit message") ##' ##' ## Get HEAD of repository ##' repository_head(repo) ##' } head.git_repository <- function(x, ...) { .Deprecated("repository_head") .Call(git2r_repository_head, x) } ##' @export utils::head ##' Get HEAD for a repository ##' ##' @template repo-param ##' @return NULL if unborn branch or not found. A git_branch if not a ##' detached head. A git_commit if detached head ##' @export ##' @examples ##' \dontrun{ ##' ## Create and initialize a repository in a temporary directory ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Commit message") ##' ##' ## Get HEAD of repository ##' repository_head(repo) ##' } repository_head <- function(repo = ".") { .Call(git2r_repository_head, lookup_repository(repo)) } ##' Check if repository is bare ##' ##' @template repo-param ##' @return \code{TRUE} if bare repository, else \code{FALSE} ##' @seealso \link{init} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' is_bare(repo) ##' ##' ## Initialize a bare repository ##' path_bare <- tempfile(pattern="git2r-") ##' dir.create(path_bare) ##' repo_bare <- init(path_bare, bare = TRUE) ##' is_bare(repo_bare) ##' } is_bare <- function(repo = ".") { .Call(git2r_repository_is_bare, lookup_repository(repo)) } ##' Check if HEAD of repository is detached ##' ##' @template repo-param ##' @return \code{TRUE} if repository HEAD is detached, else ##' \code{FALSE}. ##' @export ##' @examples ##' \dontrun{ ##' ## Create and initialize a repository in a temporary directory ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "Commit message 1") ##' ##' ## Change file, add and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "Commit message 2") ##' ##' ## HEAD of repository is not detached ##' is_detached(repo) ##' ##' ## Checkout first commit ##' checkout(commit_1) ##' ##' ## HEAD of repository is detached ##' is_detached(repo) ##' } is_detached <- function(repo = ".") { .Call(git2r_repository_head_detached, lookup_repository(repo)) } ##' Check if repository is empty ##' ##' @template repo-param ##' @return \code{TRUE} if repository is empty else \code{FALSE}. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Check if it's an empty repository ##' is_empty(repo) ##' ##' ## Commit a file ##' writeLines("Hello world!", file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit(repo, "First commit message") ##' ##' ## Check if it's an empty repository ##' is_empty(repo) ##' } is_empty <- function(repo = ".") { .Call(git2r_repository_is_empty, lookup_repository(repo)) } ##' Determine if a directory is in a git repository ##' ##' The lookup start from path and walk across parent directories if ##' nothing has been found. ##' @param path The path to the directory. ##' @return TRUE if directory is in a git repository else FALSE ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Check if path is in a git repository ##' in_repository(path) ##' ##' ## Check if working directory is in a git repository ##' setwd(path) ##' in_repository() ##' } in_repository <- function(path = ".") { !is.null(discover_repository(path)) } ##' Determine if the repository is a shallow clone ##' ##' @template repo-param ##' @return \code{TRUE} if shallow clone, else \code{FALSE} ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize repository ##' path_repo_1 <- tempfile(pattern="git2r-") ##' path_repo_2 <- tempfile(pattern="git2r-") ##' dir.create(path_repo_1) ##' dir.create(path_repo_2) ##' repo_1 <- init(path_repo_1) ##' ##' ## Config user and commit a file ##' config(repo_1, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Write to a file and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "First commit message") ##' ##' ## Change file and commit ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua.") ##' writeLines(lines, file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "Second commit message") ##' ##' ## Change file again and commit. ##' lines <- c( ##' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", ##' "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", ##' "minim veniam, quis nostrud exercitation ullamco laboris nisi ut") ##' writeLines(lines, file.path(path_repo_1, "example.txt")) ##' add(repo_1, "example.txt") ##' commit(repo_1, "Third commit message") ##' ##' ## Clone to second repository ##' repo_2 <- clone(path_repo_1, path_repo_2) ##' ##' ## Check if it's a shallow clone ##' is_shallow(repo_2) ##' } is_shallow <- function(repo = ".") { .Call(git2r_repository_is_shallow, lookup_repository(repo)) } ##' Lookup ##' ##' Lookup one object in a repository. ##' @template repo-param ##' @param sha The identity of the object to lookup. Must be 4 to 40 ##' characters long. ##' @return a \code{git_blob} or \code{git_commit} or \code{git_tag} ##' or \code{git_tree} object ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example.txt")) ##' add(repo, "example.txt") ##' commit_1 <- commit(repo, "First commit message") ##' ##' ## Create tag ##' tag(repo, "Tagname", "Tag message") ##' ##' ## First, get SHAs to lookup in the repository ##' sha_commit <- sha(commit_1) ##' sha_tree <- sha(tree(commit_1)) ##' sha_blob <- sha(tree(commit_1)["example.txt"]) ##' sha_tag <- sha(tags(repo)[[1]]) ##' ##' ## SHAs ##' sha_commit ##' sha_tree ##' sha_blob ##' sha_tag ##' ##' ## Lookup objects ##' lookup(repo, sha_commit) ##' lookup(repo, sha_tree) ##' lookup(repo, sha_blob) ##' lookup(repo, sha_tag) ##' ##' ## Lookup objects, using only the first seven characters ##' lookup(repo, substr(sha_commit, 1, 7)) ##' lookup(repo, substr(sha_tree, 1, 7)) ##' lookup(repo, substr(sha_blob, 1, 7)) ##' lookup(repo, substr(sha_tag, 1, 7)) ##' } lookup <- function(repo = ".", sha = NULL) { .Call(git2r_object_lookup, lookup_repository(repo), sha) } ##' Lookup the commit related to a git object ##' ##' Lookup the commit related to a git_reference, git_tag or ##' git_branch object. ##' @param object a git object to get the related commit from. ##' @return A git commit object. ##' @export ##' @examples \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file, add and commit ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, con = file.path(path, "test.txt")) ##' add(repo, "test.txt") ##' commit(repo, "Commit message 1") ##' ##' ## Get the commit pointed to by the 'master' branch ##' lookup_commit(repository_head(repo)) ##' ##' ## Create a tag ##' a_tag <- tag(repo, "Tagname", "Tag message") ##' ##' ## Get the commit pointed to by 'a_tag' ##' lookup_commit(a_tag) ##' } lookup_commit <- function(object) { UseMethod("lookup_commit", object) } ##' @rdname lookup_commit ##' @export lookup_commit.git_branch <- function(object) { lookup(object$repo, branch_target(object)) } ##' @rdname lookup_commit ##' @export lookup_commit.git_commit <- function(object) { object } ##' @rdname lookup_commit ##' @export lookup_commit.git_tag <- function(object) { lookup(object$repo, object$target) } ##' @rdname lookup_commit ##' @export lookup_commit.git_reference <- function(object) { lookup_commit(lookup(object$repo, object$sha)) } ##' @export print.git_repository <- function(x, ...) { if (any(is_empty(x), is.null(repository_head(x)))) { cat(sprintf("Local: %s\n", workdir(x))) cat("Head: nothing commited (yet)\n") } else { if (is_detached(x)) { cat(sprintf("Local: (detached) %s\n", workdir(x))) h <- repository_head(x) } else { cat(sprintf("Local: %s %s\n", repository_head(x)$name, workdir(x))) h <- repository_head(x) u <- branch_get_upstream(h) if (!is.null(u)) { rn <- branch_remote_name(u) cat(sprintf("Remote: %s @ %s (%s)\n", substr(u$name, nchar(rn) + 2, nchar(u$name)), rn, branch_remote_url(u))) } h <- lookup(x, branch_target(repository_head(x))) } cat(sprintf("Head: [%s] %s: %s\n", substring(h$sha, 1, 7), substring(as.character(h$author$when), 1, 10), h$summary)) } invisible(x) } ##' Summary of repository ##' ##' @param object The repository \code{object} ##' @param ... Additional arguments affecting the summary produced. ##' @return None (invisible 'NULL'). ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Config user ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' ##' ## Create a file ##' writeLines("Hello world!", file.path(path, "test.txt")) ##' summary(repo) ##' ##' ## Add file ##' add(repo, "test.txt") ##' summary(repo) ##' ##' ## Commit ##' commit(repo, "First commit message") ##' summary(repo) ##' ##' ## Change the file ##' writeLines(c("Hello again!", "Here is a second line", "And a third"), ##' file.path(path, "test.txt")) ##' summary(repo) ##' ##' ## Add file and commit ##' add(repo, "test.txt") ##' commit(repo, "Second commit message") ##' summary(repo) ##'} summary.git_repository <- function(object, ...) { print(object) cat("\n") n_branches <- sum(!is.na(unique(sapply(branches(object), branch_target)))) n_tags <- sum(!is.na(unique(vapply(tags(object), "[[", character(1), "sha")))) work <- commits(object) n_commits <- length(work) n_authors <- length(unique(vapply(lapply(work, "[[", "author"), "[[", character(1), "name"))) s <- .Call(git2r_status_list, object, TRUE, TRUE, TRUE, FALSE, TRUE) n_ignored <- length(s$ignored) n_untracked <- length(s$untracked) n_unstaged <- length(s$unstaged) n_staged <- length(s$staged) n_stashes <- length(stash_list(object)) ## Determine max characters needed to display numbers n <- max(vapply(c(n_branches, n_tags, n_commits, n_authors, n_stashes, n_ignored, n_untracked, n_unstaged, n_staged), nchar, numeric(1))) fmt <- paste0("Branches: %", n, "i\n", "Tags: %", n, "i\n", "Commits: %", n, "i\n", "Contributors: %", n, "i\n", "Stashes: %", n, "i\n", "Ignored files: %", n, "i\n", "Untracked files: %", n, "i\n", "Unstaged files: %", n, "i\n", "Staged files: %", n, "i\n") cat(sprintf(fmt, n_branches, n_tags, n_commits, n_authors, n_stashes, n_ignored, n_untracked, n_unstaged, n_staged)) cat("\nLatest commits:\n") lapply(commits(object, n = 5), print) invisible(NULL) } ## Strip trailing slash or backslash, unless it's the current drive ## root (/) or a Windows drive, for example, 'c:\'. strip_trailing_slash <- function(path) { if (!is.null(path) && grep("^(/|[a-zA-Z]:[/\\\\]?)$", path, invert = TRUE)) path <- sub("/?$", "", path) path } ##' Workdir of repository ##' ##' @template repo-param ##' @return Character vector with the path of the workdir. If the ##' repository is bare, \code{NULL} will be returned. ##' @export ##' @examples ##' \dontrun{ ##' ## Create a directory in tempdir ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' ##' ## Initialize a repository ##' repo <- init(path) ##' ##' ## Get the path of the workdir for repository ##' workdir(repo) ##' } workdir <- function(repo = ".") { path <- .Call(git2r_repository_workdir, lookup_repository(repo)) strip_trailing_slash(path) } ##' Find path to repository for any file ##' ##' @param path A character vector specifying the path to a file or ##' folder ##' @param ceiling The default is to not use the ceiling argument and ##' start the lookup from path and walk across parent ##' directories. When ceiling is 0, the lookup is only in ##' path. When ceiling is 1, the lookup is in both the path and ##' the parent to path. ##' @return Character vector with path (terminated by a file ##' separator) to repository or NULL if this cannot be ##' established. ##' @export ##' @examples ##' \dontrun{ ##' ## Initialize a temporary repository ##' path <- tempfile(pattern="git2r-") ##' dir.create(path) ##' repo <- init(path) ##' ##' ## Create a user and commit a file ##' config(repo, user.name = "Alice", user.email = "alice@@example.org") ##' lines <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do" ##' writeLines(lines, file.path(path, "example-1.txt")) ##' add(repo, "example-1.txt") ##' commit(repo, "First commit message") ##' ##' ## Create a second file. The file is not added for version control ##' ## in the repository. ##' dir.create(file.path(path, "example")) ##' file_2 <- file.path(path, "example/example-2.txt") ##' writeLines("Not under version control", file_2) ##' ##' ## Find the path to the repository using the path to the second file ##' discover_repository(file_2) ##' ##' ## Demonstrate the 'ceiling' argument ##' wd <- workdir(repo) ##' dir.create(file.path(wd, "temp")) ##' ##' ## Lookup repository in 'file.path(wd, "temp")'. Should return NULL ##' discover_repository(file.path(wd, "temp"), ceiling = 0) ##' ##' ## Lookup repository in parent to 'file.path(wd, "temp")'. ##' ## Should not return NULL ##' discover_repository(file.path(wd, "temp"), ceiling = 1) ##' } discover_repository <- function(path = ".", ceiling = NULL) { if (identical(path, ".")) path <- getwd() path <- normalizePath(path) if (!is.null(ceiling)) { ceiling <- as.integer(ceiling) if (identical(ceiling, 0L)) { ceiling <- dirname(path) } else if (identical(ceiling, 1L)) { ceiling <- dirname(dirname(path)) } else { stop("'ceiling' must be either 0 or 1") } } path <- .Call(git2r_repository_discover, path, ceiling) strip_trailing_slash(path) } ##' Internal utility function to lookup repository for methods ##' ##' @param repo repository \code{object} \code{git_repository}, or a ##' path to a repository, or \code{NULL}. If the \code{repo} ##' argument is \code{NULL}, the repository is searched for with ##' \code{\link{discover_repository}} in the current working ##' directory. ##' @return git_repository ##' @noRd lookup_repository <- function(repo = NULL) { if (is.null(repo) || identical(repo, ".")) { ## Try current working directory repo <- discover_repository(getwd()) if (is.null(repo)) stop("The working directory is not in a git repository") } else if (inherits(repo, "git_repository")) { return(repo) } repository(repo) } git2r/inst/0000755000175000017500000000000013671131056012413 5ustar nileshnileshgit2r/inst/CITATION0000644000175000017500000000123713671131056013553 0ustar nileshnileshcitHeader(paste0("See AUTHORS file for a list of all authors.\n", "To cite git2r in publications, please use:")) year <- "2020" vers <- "0.27.1" pkg.url <- "https://CRAN.R-project.org/package=git2r" citEntry(entry = "Manual", author = "Stefan Widgren and others", title = "{git2r}: Provides Access to Git Repositories", year = year, note = paste0("R package version ", vers), url = pkg.url, textVersion = paste(paste0("Widgren, S., et al. (", year, ")"), "git2r: Provides Access to Git Repositories.", paste0("R package version ", vers, "."), paste0("URL ", pkg.url, ".")) ) git2r/inst/COPYING0000644000175000017500000013551113443425137013457 0ustar nileshnileshThe git2r R bindings to the libgit2 library is Copyright (C) the git2r contributors, unless otherwise stated. See the AUTHORS file for details. The git2r package is licensed under GPLv2 ---------------------------------------------------------------------- The git2r R package includes the source code of the libgit2 library (src/libgit2). Note that the libgit2 library has been modified to build as an R package. The license for the libgit2 library is as follows: ---------------------------------------------------------------------- libgit2 is Copyright (C) the libgit2 contributors, unless otherwise stated. See the AUTHORS file for details. Note that the only valid version of the GPL as far as this project is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. ---------------------------------------------------------------------- LINKING EXCEPTION In addition to the permissions in the GNU General Public License, the authors give you unlimited permission to link the compiled version of this library into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combined executable.) ---------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ---------------------------------------------------------------------- The regex library (src/regex/) is licensed under the GNU LGPL GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ---------------------------------------------------------------------- src/http-parser/http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev. Additional changes are licensed under the same terms as NGINX and copyright Joyent, Inc. and other Node contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------- src/libgit2/src/hash/sha1dc/sha1.c src/libgit2/src/hash/sha1dc/sha1.h src/libgit2/src/hash/sha1dc/ubc_check.c src/libgit2/src/hash/sha1dc/ubc_check.h Copyright 2017 Marc Stevens , Dan Shumow Distributed under the MIT Software License. See accompanying file LICENSE.txt or copy at https://opensource.org/licenses/MIT git2r/inst/AUTHORS0000644000175000017500000000627713500524345013475 0ustar nileshnileshgit2r authors ------------- The following people contribute or have contributed to the git2r project (sorted alphabetically): Gabor Csardi Diff support Better remotes support Gregory Jefferis Discover repository Jennifer Bryan Various improvements to code and documentation Jeroen Ooms Building libssh2 for static linking on Windows (https://github.com/rwinlib/libssh2) Improvements to the build scripts Jim Hester List references in a remote repository John Blischak Improvements to the commits functionality Karthik Ram Summarize contributions to a repository Various fixes of code and documentation Peter Carbonetto Improvements to the commits functionality Stefan Widgren Most of the git2r bindings to libgit2 Other contributors to git2r --------------------------- Scott Chamberlain Fixes to run CI with Travis and AppVeyor Thomas Rosendal Better check of package with valgrind libgit2 authors (https://github.com/libgit2/libgit2/blob/master/AUTHORS) ------------------------------------------------------------------------ The following people contribute or have contributed to the libgit2 project (sorted alphabetically): Alex Budovski Alexei Sholik Andreas Ericsson Anton "antong" Gyllenberg Ankur Sethi Arthur Schreiber Ben Noordhuis Ben Straub Benjamin C Meyer Brian Downing Brian Lopez Carlos Martín Nieto Colin Timmermans Daniel Huckstep Dave Borowitz David Boyce David Glesser Dmitry Kakurin Dmitry Kovega Emeric Fermas Emmanuel Rodriguez Eric Myhre Florian Forster Holger Weiss Ingmar Vanhassel J. David Ibáñez Jacques Germishuys Jakob Pfender Jason Penny Jason R. McNeil Jerome Lambourg Johan 't Hart John Wiegley Jonathan "Duke" Leto Julien Miotte Julio Espinoza-Sokal Justin Love Kelly "kelly.leahy" Leahy Kirill A. Shutemov Lambert CLARA Luc Bertrand Marc Pegon Marcel Groothuis Marco Villegas Michael "schu" Schubert Microsoft Corporation Olivier Ramonat Peter Drahoš Pierre Habouzit Pierre-Olivier Latour Przemyslaw Pawelczyk Ramsay Jones Robert G. Jakabosky Romain Geissler Romain Muller Russell Belfer Sakari Jokinen Samuel Charles "Sam" Day Sarath Lakshman Sascha Cunz Sascha Peilicke Scott Chacon Sebastian Schuberth Sergey Nikishin Shawn O. Pearce Shuhei Tanuma Steve Frécinaux Sven Strickroth Tim Branyen Tim Clem Tim Harder Torsten Bögershausen Trent Mick Vicent Marti Other authors in alphabetical order ----------------------------------- Bruno Haible tools/iconv.m4 and tools/lib-link.m4, tools/lib-prefix.m4 Dan Nicholson Macros to locate and utilise pkg-config (tools/pkg.m4) Dan Shumow SHA1 collision detection (src/libgit2/src/hash/sha1dc/*) Davide Libenzi LibXDiff (src/libgit2/xdiff/*) Francois Pinard Common wrapper for a few potentially missing GNU programs (tools/missing) Isamu Hasegawa Extended regular expression matching and search library (src/regex/*) Linus Torvalds src/libgit2/date.c Marc Stevens SHA1 collision detection (src/libgit2/src/hash/sha1dc/*) Per Bothner Attempt to guess a canonical system name (tools/config.guess) Scott James Remnant Macros to locate and utilise pkg-config (tools/pkg.m4) git2r/inst/COPYRIGHTS0000644000175000017500000004076613443426745014057 0ustar nileshnileshFiles: * Comment: git2r, R bindings to the libgit2 library. Copyright: Copyright (C) 2013-2015 the git2r contributors, unless otherwise stated. See the AUTHORS file for details. License: GPL-2 Files: src/libgit2/* Copyright: libgit2 is Copyright (C) the libgit2 contributors, unless otherwise stated. See the AUTHORS file for details. Comment: For full terms see the included COPYING file. License: GPL-2 with a linking exception Note that the only valid version of the GPL as far as this project is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. ---------------------------------------------------------------------- LINKING EXCEPTION In addition to the permissions in the GNU General Public License, the authors give you unlimited permission to link the compiled version of this library into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combined executable.) Files: src/libgit2/util.c: Copyright: Copyright (c) 1990 Regents of the University of California. All rights reserved. License: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. [rescinded 22 July 1999] 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Files: src/libgit2/util.c: Copyright: Copyright (c) 2009 Public Software Group e. V., Berlin, Germany License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: src/libgit2/path.c src/libgit2/fnmatch.h Copyright: Copyright (C) 2008 The Android Open Source Project All rights reserved. License: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Files: src/libgit2/fnmatch.c Comment: This file contains code originally derrived from OpenBSD fnmatch.c Copyright: Copyright (c) 1989, 1993, 1994 The Regents of the University of California. All rights reserved. License: This code is derived from software contributed to Berkeley by Guido van Rossum. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Files: src/libgit2/khash.h Copyright: Copyright (c) 2008, 2009, 2011 by Attractive Chaos License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: src/libgit2/date.c Copyright: Copyright (C) Linus Torvalds, 2005 License: GPL-2 Files: src/libgit2/xdiff/* Copyright: Copyright (C) 2003 Davide Libenzi License: LGPL-2.1+ Files: src/libgit2/xdiff/xhistogram.c Copyright: Copyright (C) 2010, Google Inc. and other copyright owners as documented in JGit's IP log. License: This program and the accompanying materials are made available under the terms of the Eclipse Distribution License v1.0 which accompanies this distribution, is reproduced below, and is available at http://www.eclipse.org/org/documents/edl-v10.php All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Files: src/http-parser/http_parser.c Copyright: Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev Additional changes are licensed under the same terms as NGINX and copyright Joyent, Inc. and other Node contributors. All rights reserved. License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: src/regex/* Copyright: Copyright (C) 2002-2005, 2007, 2009, 2010 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Isamu Hasegawa . License: LGPL-2.1+ Files: tools/install-sh Copyright: Copyright (C) 1994 X Consortium License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: tools/config.sub Copyright: Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. License: GPL-2+ Files: tools/config.guess Copyright: Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. License: GPL-2+ Files: tools/missing Copyright: Copyright (C) 1996-2015 Free Software Foundation, Inc. Originally written by Fran,cois Pinard , 1996. License: GPL-2+ Files: src/libgit2/src/hash/sha1dc/* Copyright: Copyright 2017 Marc Stevens , Dan Shumow License: Distributed under the MIT Software License. See accompanying file LICENSE.txt or copy at https://opensource.org/licenses/MIT Files: tools/iconv.m4 Copyright: Copyright (C) 2000-2002, 2007-2014, 2016 Free Software Foundation, Inc. License: This file is free software; the Free Software Foundation gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. Files: tools/lib-ld.m4 Copyright: Copyright (C) 1996-2003, 2009-2016 Free Software Foundation, Inc. License: This file is free software; the Free Software Foundation gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. Files: tools/lib-link.m4 Copyright: Copyright (C) 2001-2016 Free Software Foundation, Inc. License: This file is free software; the Free Software Foundation gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. Files: tools/lib-prefix.m4 Copyright: Copyright (C) 2001-2005, 2008-2016 Free Software Foundation, Inc. License: This file is free software; the Free Software Foundation gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. Files: tools/pkg.m4 Copyright: Copyright (C) 2004 Scott James Remnant . Copyright (C) 2012-2015 Dan Nicholson License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. As a special exception to the GNU General Public License, if you distribute this file as part of a program that contains a configuration script generated by Autoconf, you may include it under the same distribution terms that you use for the rest of that program. Use RShowDoc("GPL-2") from GNU R to display the GPL license (version 2) Use RShowDoc("LGPL-2.1") from GNU R to display the LGPL license (version 2.1) git2r/cleanup0000755000175000017500000000100214145550451013005 0ustar nileshnilesh#!/bin/sh rm -f config.* rm -f confdefs.h rm -f src/Makevars rm -rf autom4te.cache rm -f src/*.o rm -f src/*.so rm -f src/libmygit.a rm -f src/libgit2/src/*.o rm -f src/libgit2/src/allocators/*.o rm -f src/libgit2/src/hash/sha1/*.o rm -f src/libgit2/src/hash/sha1/sha1dc/*.o rm -f src/libgit2/src/streams/*.o rm -f src/libgit2/src/transports/*.o rm -f src/libgit2/src/unix/*.o rm -f src/libgit2/src/xdiff/*.o rm -f src/libgit2/deps/http-parser/*.o rm -f src/libgit2/deps/regex/*.o rm -rf src/libgit2/deps/libssh2 git2r/NAMESPACE0000644000175000017500000000764414145546030012667 0ustar nileshnilesh# Generated by roxygen2: do not edit by hand S3method("[",git_tree) S3method(as.POSIXct,git_time) S3method(as.character,git_time) S3method(as.data.frame,git_commit) S3method(as.data.frame,git_repository) S3method(as.data.frame,git_tree) S3method(as.list,git_tree) S3method(diff,git_repository) S3method(diff,git_tree) S3method(format,git_blob) S3method(format,git_commit) S3method(format,git_merge_result) S3method(format,git_note) S3method(format,git_reference) S3method(format,git_signature) S3method(format,git_stash) S3method(format,git_tag) S3method(format,git_time) S3method(head,git_repository) S3method(length,git_blob) S3method(length,git_diff) S3method(length,git_tree) S3method(lookup_commit,git_branch) S3method(lookup_commit,git_commit) S3method(lookup_commit,git_reference) S3method(lookup_commit,git_tag) S3method(merge,character) S3method(merge,git_branch) S3method(merge,git_repository) S3method(plot,git_repository) S3method(print,git_blob) S3method(print,git_branch) S3method(print,git_commit) S3method(print,git_config) S3method(print,git_diff) S3method(print,git_merge_result) S3method(print,git_note) S3method(print,git_reference) S3method(print,git_reflog) S3method(print,git_reflog_entry) S3method(print,git_repository) S3method(print,git_signature) S3method(print,git_stash) S3method(print,git_status) S3method(print,git_tag) S3method(print,git_time) S3method(print,git_tree) S3method(sha,git_blob) S3method(sha,git_branch) S3method(sha,git_commit) S3method(sha,git_fetch_head) S3method(sha,git_merge_result) S3method(sha,git_note) S3method(sha,git_reference) S3method(sha,git_reflog_entry) S3method(sha,git_tag) S3method(sha,git_tree) S3method(summary,git_commit) S3method(summary,git_diff) S3method(summary,git_repository) S3method(summary,git_stash) S3method(summary,git_tag) S3method(summary,git_tree) export(add) export(ahead_behind) export(as.data.frame) export(blame) export(blob_create) export(branch_create) export(branch_delete) export(branch_get_upstream) export(branch_remote_name) export(branch_remote_url) export(branch_rename) export(branch_set_upstream) export(branch_target) export(branches) export(bundle_r_package) export(checkout) export(clone) export(commit) export(commits) export(config) export(content) export(contributions) export(cred_env) export(cred_ssh_key) export(cred_token) export(cred_user_pass) export(default_signature) export(descendant_of) export(diff) export(discover_repository) export(fetch) export(fetch_heads) export(git_config_files) export(hash) export(hashfile) export(head) export(in_repository) export(index_remove_bypath) export(init) export(is_bare) export(is_binary) export(is_blob) export(is_branch) export(is_commit) export(is_detached) export(is_empty) export(is_head) export(is_local) export(is_merge) export(is_shallow) export(is_tag) export(is_tree) export(last_commit) export(libgit2_features) export(libgit2_version) export(lookup) export(lookup_commit) export(ls_tree) export(merge) export(merge_base) export(note_create) export(note_default_ref) export(note_remove) export(notes) export(odb_blobs) export(odb_objects) export(parents) export(pull) export(punch_card) export(push) export(references) export(reflog) export(remote_add) export(remote_ls) export(remote_remove) export(remote_rename) export(remote_set_url) export(remote_url) export(remotes) export(repository) export(repository_head) export(reset) export(revparse_single) export(rm_file) export(sha) export(ssh_path) export(ssl_cert_locations) export(stash) export(stash_apply) export(stash_drop) export(stash_list) export(stash_pop) export(status) export(tag) export(tag_delete) export(tags) export(tree) export(when) export(workdir) importFrom(graphics,axis) importFrom(graphics,barplot) importFrom(graphics,par) importFrom(graphics,plot.new) importFrom(graphics,plot.window) importFrom(graphics,symbols) importFrom(graphics,title) importFrom(utils,capture.output) importFrom(utils,head) importFrom(utils,sessionInfo) useDynLib(git2r, .registration=TRUE) git2r/configure.ac0000644000175000017500000004431114145515650013732 0ustar nileshnilesh# git2r, R bindings to the libgit2 library. # Copyright (C) 2013-2020 The git2r contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # git2r is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. AC_PREREQ([2.69]) AC_INIT([git2r], [see.DESCRIPTION.file], [https://github.com/ropensci/git2r/issues]) AC_CONFIG_SRCDIR([src/git2r.c]) AC_CONFIG_AUX_DIR([tools]) AC_CANONICAL_HOST m4_include([tools/iconv.m4]) m4_include([tools/lib-ld.m4]) m4_include([tools/lib-link.m4]) m4_include([tools/lib-prefix.m4]) m4_include([tools/pkg.m4]) # System libgit2 AC_ARG_WITH([libgit2], AS_HELP_STRING([--without-libgit2], [Ignore presence of a system libgit2 library and instead use the internal git2r libgit2 library])) # Checks for programs. AC_PROG_CC ## Check for brew on macOS AC_DEFUN([AC_PROG_BREW], [AC_CHECK_PROG(BREW,brew,yes)]) # Check for pkg-config PKG_PROG_PKG_CONFIG # Check for R : ${R_HOME=`R RHOME`} if test -z "${R_HOME}"; then AC_MSG_FAILURE([Could not determine R_HOME]) fi RBIN="${R_HOME}/bin/R" # Library settings m4_pattern_allow([PKG_CONFIG_NAME]) m4_pattern_allow([PKG_BREW_NAME]) PKG_CONFIG_NAME="libgit2" PKG_BREW_NAME="libgit2" PKG_LIBS="-lgit2" PKG_CFLAGS="" # The minimum version of libgit2 that is compatible with git2r. The # version 0.26 is in all Fedora releases and at least the latest # Ubuntu. LIBGIT2_MIN_VERSION=0.26.0 # Check if building against bundled libgit2 or system libgit2 USE_BUNDLED_LIBGIT2=yes if test "x$with_libgit2" = "xno"; then AC_MSG_NOTICE([ignore presence of a system libgit2 library]) elif test "x${INCLUDE_DIR}${LIB_DIR}" = x; then if test [ -n "$PKG_CONFIG" ] ; then # Check if libgit2 is installed and have a version that is # compatible with git2r. if $PKG_CONFIG ${PKG_CONFIG_NAME} --atleast-version=${LIBGIT2_MIN_VERSION}; then PKGCONFIG_CFLAGS=`"${PKG_CONFIG}" --cflags "${PKG_CONFIG_NAME}"` PKGCONFIG_LIBS=`"${PKG_CONFIG}" --libs "${PKG_CONFIG_NAME}"` fi fi if test "x${PKGCONFIG_CFLAGS}${PKGCONFIG_LIBS}" = x; then case "${host_os}" in darwin*) AC_PROG_BREW if test "x${BREW}" = xyes; then BREWDIR=`brew --prefix` USE_BUNDLED_LIBGIT2=no else curl -sfL "https://autobrew.github.io/scripts/libgit2" > autobrew . autobrew USE_BUNDLED_LIBGIT2=no fi ;; esac else echo "Found pkg-config cflags and libs!" PKG_CFLAGS="${PKGCONFIG_CFLAGS}" PKG_LIBS="${PKGCONFIG_LIBS}" USE_BUNDLED_LIBGIT2=no fi else echo "Found INCLUDE_DIR and/or LIB_DIR!" PKG_CFLAGS="-I${INCLUDE_DIR} ${PKG_CFLAGS}" PKG_LIBS="-L${LIB_DIR} ${PKG_LIBS}" USE_BUNDLED_LIBGIT2=no fi # Find the compiler and compiler flags to use CC=`"${RBIN}" CMD config CC` CFLAGS=`"${RBIN}" CMD config CFLAGS` CPPFLAGS=`"${RBIN}" CMD config CPPFLAGS` # If a system installation of libgit2 is available, check that the version # works with git2r. if test "x${USE_BUNDLED_LIBGIT2}" = xno; then AC_MSG_CHECKING([whether the libgit2 version will work in git2r]) libgit2_ver_ok=no ${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E tools/version.c >/dev/null 2>&1 && libgit2_ver_ok=yes AC_MSG_RESULT([${libgit2_ver_ok}]) if test "x${libgit2_ver_ok}" = xno; then USE_BUNDLED_LIBGIT2=yes fi fi ################# Begin configuration to use system libgit2 ################## if test "x${USE_BUNDLED_LIBGIT2}" = xno; then # The function 'git_buf_free' is deprecated in libgit2 # v0.28.0. Use 'git_buf_dispose', if available, instead. AC_MSG_CHECKING([whether the libgit2 function git_buf_dispose is available]) have_buf_dispose=no AC_LANG_CONFTEST([AC_LANG_PROGRAM( [[#include ]], [[git_buf_dispose(NULL);]])]) PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD && have_buf_dispose=yes AC_MSG_RESULT([${have_buf_dispose}]) if test "x${have_buf_dispose}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_HAVE_BUF_DISPOSE" fi # The constants GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, # GIT_OBJ_TAG_GIT_OBJ_TREE and GIT_REF_OID are deprecated in # libgit2 v0.28.0. Use GIT_OBJECT_ANY, GIT_OBJECT_BLOB, # GIT_OBJECT_COMMIT, GIT_OBJECT_TAG_GIT_OBJECT_TREE and # GIT_REFERENCE_DIRECT, if available, instead. AC_MSG_CHECKING([whether the libgit2 constant GIT_OBJECT_ANY is available]) have_git_object_any=no AC_LANG_CONFTEST([AC_LANG_PROGRAM( [[#include ]], [[git_object_typeisloose(GIT_OBJECT_ANY);]])]) PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD && have_git_object_any=yes AC_MSG_RESULT([${have_git_object_any}]) if test "x${have_git_object_any}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_HAVE_OBJECT_ANY" fi # Several libgit2 error functions and enumaration values have been # deprecated, use newer versions. AC_MSG_CHECKING([whether the libgit2 function git_error_last is available]) have_git_error_last=no AC_LANG_CONFTEST([AC_LANG_PROGRAM( [[#include ]], [[git_error_last();]])]) PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD && have_git_error_last=yes AC_MSG_RESULT([${have_git_error_last}]) if test "x${have_git_error_last}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_HAVE_GIT_ERROR" fi # libgit v0.99.0: Several structures, enums and values have been # renamed in libgit version 0.99.0. The former names are # deprecated. See # https://github.com/libgit2/libgit2/releases/tag/v0.99.0 AC_MSG_CHECKING([whether the libgit2 function git_oid_is_zero is available]) have_git_oid_is_zero=no AC_LANG_CONFTEST([AC_LANG_PROGRAM( [[#include ]], [[git_oid_is_zero(NULL);]])]) PKG_CFLAGS="${PKG_CFLAGS} -Werror" "$RBIN" CMD SHLIB conftest.c \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD && have_git_oid_is_zero=yes AC_MSG_RESULT([${have_git_oid_is_zero}]) if test "x${have_git_oid_is_zero}" = xyes; then PKG_CFLAGS="${PKG_CFLAGS} -DGIT2R_LIBGIT2_V0_99_0_RENAMES" fi # For debugging echo "----- Results of the git2r package configure -----" echo "" echo " PKG_CFLAGS: ${PKG_CFLAGS}" echo " PKG_LIBS: ${PKG_LIBS}" echo "" echo "--------------------------------------------------" AC_SUBST([PKG_CFLAGS], ["${PKG_CFLAGS}"]) AC_SUBST([PKG_LIBS], ["${PKG_LIBS}"]) AC_CONFIG_FILES([src/Makevars]) AC_OUTPUT fi ################# End configuration to use system libgit2 #################### ################# Begin configuration to build bundled libgit2 ############### if test "x${USE_BUNDLED_LIBGIT2}" = xyes; then if test "x$with_libgit2" = "xyes"; then AC_MSG_FAILURE([system libgit2 requested but not found]) elif test "x$with_libgit2" = "xno"; then AC_MSG_NOTICE([attempting configuration of bundled libgit2]) else AC_MSG_NOTICE([package dependency requirement 'libgit2 >= ${LIBGIT2_MIN_VERSION}' could not be satisfied.]) echo " ----------------------------------------------------------------------- Unable to find the libgit2 library on this system. Building 'git2r' using the bundled source of the libgit2 library. To build git2r with a system installation of libgit2, please install: libgit2-dev (package on e.g. Debian and Ubuntu) libgit2-devel (package on e.g. Fedora, CentOS and RHEL) libgit2 (Homebrew package on OS X) and try again. If the libgit2 library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to libgit2 with: given you downloaded a tar-gz archive: R CMD INSTALL git2r-.tar.gz --configure-vars='INCLUDE_DIR=/path/to/include LIB_DIR=/path/to/lib' or cloned the GitHub git2r repository into a directory: R CMD INSTALL git2r/ --configure-vars='INCLUDE_DIR=/path/to/include LIB_DIR=/path/to/lib' or download and install git2r in R using install.packages('git2r', type='source', configure.vars='LIB_DIR=-L/path/to/libs INCLUDE_DIR=-I/path/to/headers') On macOS, another possibility is to let the configuration automatically download the libgit2 library from the Homebrew package manager with: R CMD INSTALL git2r-.tar.gz --configure-vars='autobrew=yes' or R CMD INSTALL git2r/ --configure-vars='autobrew=yes' or install.packages('git2r', type='source', configure.vars='autobrew=yes') ----------------------------------------------------------------------- " AC_MSG_NOTICE([attempting configuration of bundled libgit2]) fi # Use R to determine architecture of the machine AC_MSG_CHECKING([size of void*]) sizeof_voidp=`"${RBIN}" --slave --vanilla -e "cat(.Machine\\$sizeof.pointer)"` AC_MSG_RESULT([$sizeof_voidp]) if test "x$sizeof_voidp" = "x8"; then CPPFLAGS="${CPPFLAGS} -DGIT_ARCH_64" elif test "x$sizeof_voidp" = "x4"; then CPPFLAGS="${CPPFLAGS} -DGIT_ARCH_32" else AC_MSG_FAILURE([Unsupported architecture]) fi # Check for zlib have_zlib=no if test [ -n "$PKG_CONFIG" ] ; then PKG_CHECK_MODULES([zlib], [zlib], [CPPFLAGS="${zlib_CFLAGS} ${CPPFLAGS}" LIBS="${zlib_LIBS} ${LIBS}" have_zlib=yes], [ ]) fi if test "x${have_zlib}" = xno; then AC_SEARCH_LIBS([inflate], [z], [have_zlib=yes]) fi if test "x${have_zlib}" = xno; then AC_MSG_FAILURE([ --------------------------------------------- The zlib library that is required to build git2r was not found. Please install: zlib1g-dev (package on e.g. Debian and Ubuntu) zlib-devel (package on e.g. Fedora, CentOS and RHEL) and try again. If the zlib library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to zlib with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' ---------------------------------------------]) fi # Check for SSL for https transport have_ssl=no case "${host_os}" in darwin*) # On macOS, use the Security and CoreFoundation framework have_ssl=yes CPPFLAGS="${CPPFLAGS} -DGIT_SECURE_TRANSPORT=1 -DGIT_HTTPS=1" LIBS="${LIBS} -framework Security -framework CoreFoundation" ;; *) if test "x${OPENSSL_INCLUDES}" = x; then :; else CPPFLAGS="${CPPFLAGS} -I${OPENSSL_INCLUDES}" fi if test [ -n "$PKG_CONFIG" ] ; then PKG_CHECK_MODULES([openssl], [openssl], [CPPFLAGS="${openssl_CFLAGS} ${CPPFLAGS}" LIBS="${openssl_LIBS} ${LIBS}" have_ssl=yes], [ ]) fi if test "x${have_ssl}" = xno; then AC_SEARCH_LIBS([EVP_EncryptInit], [crypto], [AC_SEARCH_LIBS([SSL_library_init], [ssl], [have_ssl=yes]) AC_SEARCH_LIBS([OPENSSL_init_ssl], [ssl], [have_ssl=yes])]) fi if test "x${have_ssl}" = xyes; then CPPFLAGS="${CPPFLAGS} -DGIT_OPENSSL=1 -DGIT_HTTPS=1" else AC_MSG_WARN([ --------------------------------------------- Unable to find the OpenSSL library on this system. Building a version without support for https transport. To build with https support, please install: libssl-dev (package on e.g. Debian and Ubuntu) openssl-devel (package on e.g. Fedora, CentOS and RHEL) and try again. If the OpenSSL library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to OpenSSL with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' ---------------------------------------------]) fi ;; esac # Check for LibSSH2 have_ssh2=no if test [ -n "$PKG_CONFIG" ] ; then PKG_CHECK_MODULES([libssh2], [libssh2 >= 1.8], [CPPFLAGS="${libssh2_CFLAGS} ${CPPFLAGS}" LIBS="${libssh2_LIBS} ${LIBS}" have_ssh2=yes], [ ]) fi if test "x${have_ssh2}" = xno; then AC_MSG_WARN([ --------------------------------------------- Unable to find the LibSSH2 (ver >= v1.8) library on this system. Building git2r without support for SSH transport. To build with SSH support, please install: libssh2-1-dev (package on e.g. Debian and Ubuntu) libssh2-devel (package on e.g. Fedora, CentOS and RHEL) libssh2 (Homebrew package on OS X) and try again. If the LibSSH2 library is installed on your system but the git2r configuration is unable to find it, you can specify the include and lib path to LibSSH2 with: R CMD INSTALL git2r --configure-vars='LIBS=-L/path/to/libs CPPFLAGS=-I/path/to/headers' ---------------------------------------------]) else CPPFLAGS="${CPPFLAGS} -DGIT_SSH" fi # Check for iconv case "${host_os}" in darwin*) AM_ICONV if test "x${am_cv_func_iconv}" = xyes; then CPPFLAGS="${CPPFLAGS} -DGIT_USE_ICONV" fi ;; esac # Configuration specific for solaris case "${host_os}" in solaris*) AC_SEARCH_LIBS(connect, [socket]) AC_SEARCH_LIBS(gethostbyname, [nsl socket]) # Include and use regex on solaris CPPFLAGS="-Ilibgit2/deps/regex ${CPPFLAGS}" GIT2R_SRC_REGEX=libgit2/deps/regex/regex.o ;; esac # Add include paths for git2r CPPFLAGS="-I. -Ilibgit2/src -Ilibgit2/include -Ilibgit2/deps/http-parser ${CPPFLAGS}" # Add definitions CPPFLAGS="${CPPFLAGS} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DLIBGIT2_NO_FEATURES_H -DR_NO_REMAP -DSTRICT_R_HEADERS" # Specify sha1 implementation case "${host_os}" in darwin*) CPPFLAGS="${CPPFLAGS} -DGIT_SHA1_COLLISIONDETECT=1 -DSHA1DC_NO_STANDARD_INCLUDES=1" CPPFLAGS="${CPPFLAGS} -DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\\\"common.h\\\" -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\\\"common.h\\\"" GIT2R_SRC_SHA1="libgit2/src/hash/sha1/collisiondetect.o libgit2/src/hash/sha1/sha1dc/sha1.o libgit2/src/hash/sha1/sha1dc/ubc_check.o" ;; *) if test "x${have_ssl}" = xyes; then CPPFLAGS="${CPPFLAGS} -DGIT_SHA1_OPENSSL=1" GIT2R_SRC_SHA1="libgit2/src/hash/sha1/openssl.o" else CPPFLAGS="${CPPFLAGS} -DGIT_SHA1_COLLISIONDETECT=1 -DSHA1DC_NO_STANDARD_INCLUDES=1" CPPFLAGS="${CPPFLAGS} -DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\\\"common.h\\\" -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\\\"common.h\\\"" GIT2R_SRC_SHA1="libgit2/src/hash/sha1/sha1dc/sha1.o libgit2/src/hash/sha1/sha1dc/ubc_check.o" fi ;; esac # Add definitions specific for solaris case "${host_os}" in solaris*) CPPFLAGS="${CPPFLAGS} -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS" ;; esac # Checks for structures AC_CHECK_MEMBER([struct stat.st_mtim], [CPPFLAGS="${CPPFLAGS} -DGIT_USE_STAT_MTIM"], [], [AC_INCLUDES_DEFAULT]) AC_CHECK_MEMBER([struct stat.st_mtimespec], [CPPFLAGS="${CPPFLAGS} -DGIT_USE_STAT_MTIMESPEC"], [], [AC_INCLUDES_DEFAULT]) AC_CHECK_MEMBER([struct stat.st_mtime_nsec], [CPPFLAGS="${CPPFLAGS} -DGIT_USE_STAT_MTIME_NSEC"], [], [AC_INCLUDES_DEFAULT]) if test "x$ac_cv_member_struct_stat_st_mtim" = "xyes"; then AC_CHECK_MEMBER([struct stat.st_mtim.tv_nsec], [CPPFLAGS="${CPPFLAGS} -DGIT_USE_NSEC"], [], [AC_INCLUDES_DEFAULT]) elif test "x$ac_cv_member_struct_stat_st_mtimespec" = "xyes"; then AC_CHECK_MEMBER([struct stat.st_mtimespec.tv_nsec], [CPPFLAGS="${CPPFLAGS} -DGIT_USE_NSEC"], [], [AC_INCLUDES_DEFAULT]) else CPPFLAGS="${CPPFLAGS} -DGIT_USE_NSEC" fi # Checks for library functions. AC_CHECK_FUNCS([futimens qsort_r qsort_s]) if test $ac_cv_func_futimens = yes; then CPPFLAGS="${CPPFLAGS} -DHAVE_FUTIMENS" fi if test $ac_cv_func_qsort_r = yes; then CPPFLAGS="${CPPFLAGS} -DHAVE_QSORT_R" fi if test $ac_cv_func_qsort_s = yes; then CPPFLAGS="${CPPFLAGS} -DHAVE_QSORT_S" fi # The function 'git_buf_free' is deprecated in libgit2. Use # 'git_buf_dispose' instead. # # CPPFLAGS="${CPPFLAGS} -DGIT_DEPRECATE_HARD -DGIT2R_HAVE_BUF_DISPOSE" CPPFLAGS="${CPPFLAGS} -DGIT2R_HAVE_BUF_DISPOSE" # The constants GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT, # GIT_OBJ_TAG_GIT_OBJ_TREE and GIT_REF_OID are deprecated in # libgit2. Use GIT_OBJECT_ANY, GIT_OBJECT_BLOB, GIT_OBJECT_COMMIT, # GIT_OBJECT_TAG_GIT_OBJECT_TREE and GIT_REFERENCE_DIRECT instead. CPPFLAGS="${CPPFLAGS} -DGIT2R_HAVE_OBJECT_ANY" # Several libgit2 error functions and enumaration values have been # deprecated, use newer versions. CPPFLAGS="${CPPFLAGS} -DGIT2R_HAVE_GIT_ERROR" # libgit v0.99.0: Several structures, enums and values have been # renamed in libgit version 0.99.0. The former names are # deprecated. See # https://github.com/libgit2/libgit2/releases/tag/v0.99.0 CPPFLAGS="${CPPFLAGS} -DGIT2R_LIBGIT2_V0_99_0_RENAMES" CPPFLAGS="${CPPFLAGS} -DGIT_REGEX_REGCOMP -DTHREADSAFE=OFF" AC_SUBST(GIT2R_SRC_REGEX) AC_SUBST(GIT2R_SRC_SHA1) AC_SUBST([PKG_CFLAGS], ["${PKG_CFLAGS}"]) AC_SUBST([PKG_CPPFLAGS], ["${CPPFLAGS}"]) AC_SUBST([PKG_LIBS], ["${LIBS} ${LIBICONV}"]) AC_CONFIG_FILES([src/Makevars_libgit2]) echo " ----- Results of the git2r package configure ----- HTTPS transport......................: ${have_ssl} LibSSH2 to enable the SSH transport..: ${have_ssh2} -------------------------------------------------- " AC_OUTPUT cd src; mv Makevars_libgit2 Makevars fi ################# End configuration to build bundled libgit2 #################