diff --git a/.gitattributes b/.gitattributes index be48d6222..9d132e955 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,5 +3,3 @@ # Custom for Visual Studio *.cs diff=csharp -*.sln merge=union -*.csproj merge=union diff --git a/.gitignore b/.gitignore index 7effe32b9..6a6337edb 100644 --- a/.gitignore +++ b/.gitignore @@ -37,8 +37,8 @@ _ReSharper*/ *.DotSettings #Ignore custom generated files LibGit2Sharp/Core/UniqueIdentifier.cs +LibGit2Sharp/Core/NativeDllName.cs -!Lib/NativeBinaries/*/*.pdb !nuget.package/build/ _NCrunch_LibGit2Sharp/ packages/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 30eb68a28..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "libgit2"] - path = libgit2 - url = https://github.com/libgit2/libgit2.git diff --git a/.nuget/packages.config b/.nuget/packages.config index 05ac50048..0335ab080 100644 --- a/.nuget/packages.config +++ b/.nuget/packages.config @@ -1,4 +1,5 @@  - + + diff --git a/.travis.yml b/.travis.yml index 5ab00c105..700fbd296 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,10 @@ os: - osx - linux +env: + global: + - MONO_OPTIONS=--debug + before_install: - date -u - uname -a diff --git a/CHANGES.md b/CHANGES.md index fe14217c1..22a41d5b2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,53 @@ - @libgit2sharp: - CI servers: - Windows (x86/amd64): - - Linux/MacOsX: + - Linux/Mac OS X: + +## v0.22 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.21.1...v0.22)) + +### Additions + + - Add CustomHeaders in the push options (#1217) + - Expose the minimal diff algorithm (#1229) + - Expose Reset() with checkout options (#1219) + - Add a prettify option to history rewrite options (#1185) + - Add option to describe to only follow the first parent (#1190) + - Allow setting the config search path (#1123) + - Provide access to the remote's host HTTPS certificate (#1134) + - Add support for rebase (#964) + - ListReferences() now accepts a credentials provider (#1099) + - Introduce FileStatus.Conflicted and introduce staging of conflicts (#1062) + - Support streaming filters written in C# (#1030) + - Add support for the pre-push callback (#1061) + - Add support for listing remote references without a Repository instance (#1065) + - Add StashCollection.Apply() and .Pop() (#1068) + - Support retrieving a configuration for a repository without instantiating it (#1042) + - Implement 'log --follow'-like functionality (#963) + - Introduce in-memory merging via Repository.MergeCommits() (#990) + - Allow setting whether to prune during a fetch (#1258) + +### Changes + + - Deprecate MergeConflictException in a backwards-compatible way (#1243) + - Improve type safety in the generic type for Diff.Compare() (#1180) + - Obsolete Repository.Commit(), NoteCollection.Add() and + NoteCollection.Remove() overloads which do not require a signature (#1173) + - BuildSignature() no longer tries to build a signature from the + environment if there is none configured (#1171) + - Rename the commit walker's Since to IncludeReachableFrom and Until to ExcludeReachableFrom (#1069) + - Rename MergeConflictException to CheckoutConflictException to more + accurately reflect what it means (#1059) + - Specify the diff algorithm instead of setting a boolean to use patience (#1043) + - Remove optional parameters (#1031) + - Move Repository.Reset(paths) into Index (#959) + - Move FindMergeBase() overloads to ObjectDatabase (#957) + +### Fixes + + - ListReferences() is now able to handle symbolic references (#1132) + - Repository.IsValid() returns false on empty paths (#1156) + - The included version of libgit2 includes racy-git support + - Fix a racy NRE in the filters (#1113) ## v0.21.1 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.21...v0.21.1)) diff --git a/CI/build.msbuild b/CI/build.msbuild index f7bb2ee6a..97c503354 100644 --- a/CI/build.msbuild +++ b/CI/build.msbuild @@ -6,7 +6,7 @@ $(RootDir)\Build - @@ -37,7 +37,7 @@ - + diff --git a/CI/travis.linux.install.deps.sh b/CI/travis.linux.install.deps.sh index 1a820d07b..365fc51a6 100755 --- a/CI/travis.linux.install.deps.sh +++ b/CI/travis.linux.install.deps.sh @@ -1,9 +1,10 @@ #!/bin/bash set -ev -sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/tpokorra:/mono/xUbuntu_12.04/ /' >> /etc/apt/sources.list.d/mono-opt.list" +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF -curl http://download.opensuse.org/repositories/home:/tpokorra:/mono/xUbuntu_12.04/Release.key | sudo apt-key add - +echo "deb http://download.mono-project.com/repo/debian wheezy/snapshots/3.12.0 main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list +echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list sudo apt-get update -sudo apt-get install mono-opt cmake +sudo apt-get install mono-devel cmake diff --git a/Lib/CustomBuildTasks/CustomBuildTasks.csproj b/Lib/CustomBuildTasks/CustomBuildTasks.csproj index 2da370371..351e4873f 100644 --- a/Lib/CustomBuildTasks/CustomBuildTasks.csproj +++ b/Lib/CustomBuildTasks/CustomBuildTasks.csproj @@ -9,7 +9,7 @@ Properties CustomBuildTasks CustomBuildTasks - v3.5 + v4.0 512 @@ -33,10 +33,11 @@ - + + diff --git a/Lib/CustomBuildTasks/CustomBuildTasks.dll b/Lib/CustomBuildTasks/CustomBuildTasks.dll index 3cd023b83..cd763a2e1 100644 Binary files a/Lib/CustomBuildTasks/CustomBuildTasks.dll and b/Lib/CustomBuildTasks/CustomBuildTasks.dll differ diff --git a/Lib/CustomBuildTasks/GenerateNativeDllNameTask.cs b/Lib/CustomBuildTasks/GenerateNativeDllNameTask.cs new file mode 100644 index 000000000..9b31fba34 --- /dev/null +++ b/Lib/CustomBuildTasks/GenerateNativeDllNameTask.cs @@ -0,0 +1,41 @@ +using System; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace CustomBuildTasks +{ + public class GenerateNativeDllNameTask : Task + { + public ITaskItem InputHashFile { get; set; } + + public string OutputFile { get; set; } + + public override bool Execute() + { + var fileName = InputHashFile.GetMetadata("FullPath"); + string libgit2FileName; + + using (var sr = new StreamReader(fileName)) + { + libgit2FileName = sr.ReadLine(); + } + + var nativeDllName = @"namespace LibGit2Sharp.Core +{{ + internal static class NativeDllName + {{ + public const string Name = ""{0}""; + }} +}} +"; + + using (var sw = new StreamWriter(OutputFile)) + { + sw.Write(nativeDllName, libgit2FileName); + } + + return true; + } + } +} diff --git a/Lib/NativeBinaries/amd64/git2-e0902fb.dll b/Lib/NativeBinaries/amd64/git2-e0902fb.dll deleted file mode 100644 index f4931dbdd..000000000 Binary files a/Lib/NativeBinaries/amd64/git2-e0902fb.dll and /dev/null differ diff --git a/Lib/NativeBinaries/amd64/git2-e0902fb.pdb b/Lib/NativeBinaries/amd64/git2-e0902fb.pdb deleted file mode 100644 index bdef63b09..000000000 Binary files a/Lib/NativeBinaries/amd64/git2-e0902fb.pdb and /dev/null differ diff --git a/Lib/NativeBinaries/libgit2.license.txt b/Lib/NativeBinaries/libgit2.license.txt deleted file mode 100644 index d1ca4d401..000000000 --- a/Lib/NativeBinaries/libgit2.license.txt +++ /dev/null @@ -1,930 +0,0 @@ - libgit2 is Copyright (C) the libgit2 contributors, - unless otherwise stated. See the AUTHORS file for details. - - Note that the only valid version of the GPL as far as this project - is concerned is _this_ particular version of the license (ie v2, not - v2.2 or v3.x or whatever), unless explicitly otherwise stated. - ----------------------------------------------------------------------- - - LINKING EXCEPTION - - In addition to the permissions in the GNU General Public License, - the authors give you unlimited permission to link the compiled - version of this library into combinations with other programs, - and to distribute those combinations without any restriction - coming from the use of this file. (The General Public License - restrictions do apply in other respects; for example, they cover - modification of the file, and distribution when not linked into - a combined executable.) - ----------------------------------------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. - ----------------------------------------------------------------------- - -The bundled ZLib code is licensed under the ZLib license: - -Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - ----------------------------------------------------------------------- - -The priority queue implementation is based on code licensed under the -Apache 2.0 license: - - Copyright 2010 Volkan Yazıcı - Copyright 2006-2010 The Apache Software Foundation - -The full text of the Apache 2.0 license is available at: - - http://www.apache.org/licenses/LICENSE-2.0 - ----------------------------------------------------------------------- - -The Clay framework is licensed under the MIT license: - -Copyright (C) 2011 by Vicent Marti - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------- - -The regex library (deps/regex/) is licensed under the GNU LGPL - - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/Lib/NativeBinaries/x86/git2-e0902fb.dll b/Lib/NativeBinaries/x86/git2-e0902fb.dll deleted file mode 100644 index b0c55e6cf..000000000 Binary files a/Lib/NativeBinaries/x86/git2-e0902fb.dll and /dev/null differ diff --git a/Lib/NativeBinaries/x86/git2-e0902fb.pdb b/Lib/NativeBinaries/x86/git2-e0902fb.pdb deleted file mode 100644 index d466f239a..000000000 Binary files a/Lib/NativeBinaries/x86/git2-e0902fb.pdb and /dev/null differ diff --git a/Lib/NuGet/NuGet.exe b/Lib/NuGet/NuGet.exe index ed2b0a221..9ca66594f 100644 Binary files a/Lib/NuGet/NuGet.exe and b/Lib/NuGet/NuGet.exe differ diff --git a/LibGit2Sharp.Tests/ArchiveFixture.cs b/LibGit2Sharp.Tests/ArchiveFixture.cs index 7253e2fe8..19860ca0b 100644 --- a/LibGit2Sharp.Tests/ArchiveFixture.cs +++ b/LibGit2Sharp.Tests/ArchiveFixture.cs @@ -18,6 +18,8 @@ public void CanArchiveATree() var archiver = new MockArchiver(); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + repo.ObjectDatabase.Archive(tree, archiver); var expected = new ArrayList @@ -30,7 +32,7 @@ public void CanArchiveATree() }; Assert.Equal(expected, archiver.Files); Assert.Null(archiver.ReceivedCommitSha); - Assert.InRange(archiver.ModificationTime, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMilliseconds(100)), DateTimeOffset.UtcNow); + Assert.InRange(archiver.ModificationTime, before, DateTimeOffset.UtcNow); } } @@ -66,8 +68,10 @@ public void ArchivingANullTreeOrCommitThrows() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Throws(() => repo.ObjectDatabase.Archive((Commit)null, null)); - Assert.Throws(() => repo.ObjectDatabase.Archive((Tree)null, null)); + Assert.Throws(() => repo.ObjectDatabase.Archive(default(Commit), default(ArchiverBase))); + Assert.Throws(() => repo.ObjectDatabase.Archive(default(Commit), default(string))); + Assert.Throws(() => repo.ObjectDatabase.Archive(default(Tree), default(ArchiverBase))); + Assert.Throws(() => repo.ObjectDatabase.Archive(default(Tree), default(string))); } } diff --git a/LibGit2Sharp.Tests/ArchiveTarFixture.cs b/LibGit2Sharp.Tests/ArchiveTarFixture.cs index c1ae15783..a21847ea0 100644 --- a/LibGit2Sharp.Tests/ArchiveTarFixture.cs +++ b/LibGit2Sharp.Tests/ArchiveTarFixture.cs @@ -1,4 +1,3 @@ -using System; using System.IO; using System.Text; using LibGit2Sharp.Tests.TestHelpers; diff --git a/LibGit2Sharp.Tests/AttributesFixture.cs b/LibGit2Sharp.Tests/AttributesFixture.cs index c9c4eb712..c607ce651 100644 --- a/LibGit2Sharp.Tests/AttributesFixture.cs +++ b/LibGit2Sharp.Tests/AttributesFixture.cs @@ -9,8 +9,7 @@ public class AttributesFixture : BaseFixture [Fact] public void StagingHonorsTheAttributesFiles() { - string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) + using (var repo = InitIsolatedRepository()) { CreateAttributesFile(repo); diff --git a/LibGit2Sharp.Tests/BlobFixture.cs b/LibGit2Sharp.Tests/BlobFixture.cs index 223a4d673..4c984bd34 100644 --- a/LibGit2Sharp.Tests/BlobFixture.cs +++ b/LibGit2Sharp.Tests/BlobFixture.cs @@ -222,7 +222,7 @@ public void CanTellIfTheBlobContentLooksLikeBinary() private static void SkipIfNotSupported(string autocrlf) { - InconclusiveIf(() => autocrlf == "true" && IsRunningOnLinux(), "Non-Windows does not support core.autocrlf = true"); + InconclusiveIf(() => autocrlf == "true" && Constants.IsRunningOnUnix, "Non-Windows does not support core.autocrlf = true"); } } } diff --git a/LibGit2Sharp.Tests/BranchFixture.cs b/LibGit2Sharp.Tests/BranchFixture.cs index ded3fbc76..715defd87 100644 --- a/LibGit2Sharp.Tests/BranchFixture.cs +++ b/LibGit2Sharp.Tests/BranchFixture.cs @@ -18,15 +18,17 @@ public class BranchFixture : BaseFixture public void CanCreateBranch(string name) { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); const string committish = "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.CreateBranch(name, committish); Assert.NotNull(newBranch); - Assert.Equal(name, newBranch.Name); + Assert.Equal(name, newBranch.FriendlyName); Assert.Equal("refs/heads/" + name, newBranch.CanonicalName); Assert.NotNull(newBranch.Tip); Assert.Equal(committish, newBranch.Tip.Sha); @@ -36,13 +38,15 @@ public void CanCreateBranch(string name) // when they're read back: // - from InlineData: C5-00-6E-00-67-00-73-00-74-00-72-00-F6-00-6D-00 // - from filesystem: 41-00-0A-03-6E-00-67-00-73-00-74-00-72-00-6F-00-08-03-6D-00 - Assert.NotNull(repo.Branches.SingleOrDefault(p => p.Name.Normalize() == name)); + Assert.NotNull(repo.Branches.SingleOrDefault(p => p.FriendlyName.Normalize() == name)); AssertRefLogEntry(repo, newBranch.CanonicalName, + "branch: Created from " + committish, + null, newBranch.Tip.Id, - "branch: Created from " + committish); + Constants.Identity, before); - repo.Branches.Remove(newBranch.Name); + repo.Branches.Remove(newBranch.FriendlyName); Assert.Null(repo.Branches[name]); } } @@ -86,20 +90,24 @@ public void CanCreateAnUnbornBranch() public void CanCreateBranchUsingAbbreviatedSha() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions{ Identity = Constants.Identity })) { EnableRefLog(repo); const string name = "unit_test"; const string committish = "be3563a"; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.CreateBranch(name, committish); Assert.Equal("refs/heads/" + name, newBranch.CanonicalName); Assert.Equal("be3563ae3f795b2b4353bcce3a527ad0a4f7f644", newBranch.Tip.Sha); AssertRefLogEntry(repo, newBranch.CanonicalName, + "branch: Created from " + committish, + null, newBranch.Tip.Id, - "branch: Created from " + committish); + Constants.Identity, before); } } @@ -109,25 +117,30 @@ public void CanCreateBranchUsingAbbreviatedSha() public void CanCreateBranchFromImplicitHead(string headCommitOrBranchSpec) { string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); repo.Checkout(headCommitOrBranchSpec); const string name = "unit_test"; + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.CreateBranch(name); Assert.NotNull(newBranch); - Assert.Equal(name, newBranch.Name); + Assert.Equal(name, newBranch.FriendlyName); Assert.Equal("refs/heads/" + name, newBranch.CanonicalName); Assert.False(newBranch.IsCurrentRepositoryHead); Assert.NotNull(newBranch.Tip); Assert.Equal("32eab9cb1f450b5fe7ab663462b77d7f4b703344", newBranch.Tip.Sha); - Assert.NotNull(repo.Branches.SingleOrDefault(p => p.Name == name)); + Assert.NotNull(repo.Branches.SingleOrDefault(p => p.FriendlyName == name)); AssertRefLogEntry(repo, newBranch.CanonicalName, + "branch: Created from " + headCommitOrBranchSpec, + null, newBranch.Tip.Id, - "branch: Created from " + headCommitOrBranchSpec); + Constants.Identity, before); } } @@ -137,20 +150,25 @@ public void CanCreateBranchFromImplicitHead(string headCommitOrBranchSpec) public void CanCreateBranchFromExplicitHead(string headCommitOrBranchSpec) { string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); repo.Checkout(headCommitOrBranchSpec); const string name = "unit_test"; + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.CreateBranch(name, "HEAD"); Assert.NotNull(newBranch); Assert.Equal("32eab9cb1f450b5fe7ab663462b77d7f4b703344", newBranch.Tip.Sha); AssertRefLogEntry(repo, newBranch.CanonicalName, + "branch: Created from HEAD", + null, newBranch.Tip.Id, - "branch: Created from " + headCommitOrBranchSpec); + Constants.Identity, before); } } @@ -158,19 +176,24 @@ public void CanCreateBranchFromExplicitHead(string headCommitOrBranchSpec) public void CanCreateBranchFromCommit() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); const string name = "unit_test"; var commit = repo.Lookup("HEAD"); + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.CreateBranch(name, commit); Assert.NotNull(newBranch); Assert.Equal("4c062a6361ae6959e06292c1fa5e2822d9c96345", newBranch.Tip.Sha); AssertRefLogEntry(repo, newBranch.CanonicalName, + "branch: Created from " + newBranch.Tip.Sha, + null, newBranch.Tip.Id, - "branch: Created from " + newBranch.Tip.Sha); + Constants.Identity, before); } } @@ -178,20 +201,24 @@ public void CanCreateBranchFromCommit() public void CanCreateBranchFromRevparseSpec() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); const string name = "revparse_branch"; const string committish = "master~2"; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.CreateBranch(name, committish); Assert.NotNull(newBranch); Assert.Equal("9fd738e8f7967c078dceed8190330fc8648ee56a", newBranch.Tip.Sha); AssertRefLogEntry(repo, newBranch.CanonicalName, + "branch: Created from " + committish, + null, newBranch.Tip.Id, - "branch: Created from " + committish); + Constants.Identity, before); } } @@ -201,19 +228,23 @@ public void CanCreateBranchFromRevparseSpec() public void CreatingABranchFromATagPeelsToTheCommit(string committish) { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); const string name = "i-peel-tag"; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.CreateBranch(name, committish); Assert.NotNull(newBranch); Assert.Equal("e90810b8df3e80c413d903f631643c716887138d", newBranch.Tip.Sha); AssertRefLogEntry(repo, newBranch.CanonicalName, + "branch: Created from " + committish, + null, newBranch.Tip.Id, - "branch: Created from " + committish); + Constants.Identity, before); } } @@ -289,7 +320,7 @@ public void CanListAllBranches() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(expectedBranches, SortedBranches(repo.Branches, b => b.Name)); + Assert.Equal(expectedBranches, SortedBranches(repo.Branches, b => b.FriendlyName)); Assert.Equal(5, repo.Branches.Count()); } @@ -310,7 +341,7 @@ public void CanListBranchesWithRemoteAndLocalBranchWithSameShortName() }; Assert.Equal(expectedWdBranches, - SortedBranches(repo.Branches.Where(b => !b.IsRemote), b => b.Name)); + SortedBranches(repo.Branches.Where(b => !b.IsRemote), b => b.FriendlyName)); } } @@ -327,7 +358,7 @@ public void CanListAllBranchesWhenGivenWorkingDir() "origin/test" }; - Assert.Equal(expectedWdBranches, SortedBranches(repo.Branches, b => b.Name)); + Assert.Equal(expectedWdBranches, SortedBranches(repo.Branches, b => b.FriendlyName)); } } @@ -352,7 +383,7 @@ public void CanListAllBranchesIncludingRemoteRefs() new { Name = "origin/test", Sha = "e90810b8df3e80c413d903f631643c716887138d", IsRemote = true }, }; Assert.Equal(expectedBranchesIncludingRemoteRefs, - SortedBranches(repo.Branches, b => new { b.Name, b.Tip.Sha, b.IsRemote })); + SortedBranches(repo.Branches, b => new { Name = b.FriendlyName, b.Tip.Sha, b.IsRemote })); } } @@ -443,8 +474,8 @@ public void QueryAmbigousRemoteForRemoteBranch() { var path = SandboxStandardTestRepo(); - var fetchRefSpec = "+refs/heads/*:refs/remotes/origin/*"; - var url = "http://github.com/libgit2/TestGitRepository"; + const string fetchRefSpec = "+refs/heads/*:refs/remotes/origin/*"; + const string url = "http://github.com/libgit2/TestGitRepository"; using (var repo = InitIsolatedRepository(path)) { @@ -469,11 +500,11 @@ public void CanLookupABranchByItsCanonicalName() { Branch branch = repo.Branches["refs/heads/br2"]; Assert.NotNull(branch); - Assert.Equal("br2", branch.Name); + Assert.Equal("br2", branch.FriendlyName); Branch branch2 = repo.Branches["refs/heads/br2"]; Assert.NotNull(branch2); - Assert.Equal("br2", branch2.Name); + Assert.Equal("br2", branch2.FriendlyName); Assert.Equal(branch, branch2); Assert.True((branch2 == branch)); @@ -489,7 +520,7 @@ public void CanLookupLocalBranch() Branch master = repo.Branches["master"]; Assert.NotNull(master); Assert.False(master.IsRemote); - Assert.Equal("master", master.Name); + Assert.Equal("master", master.FriendlyName); Assert.Equal("refs/heads/master", master.CanonicalName); Assert.True(master.IsCurrentRepositoryHead); Assert.Equal("4c062a6361ae6959e06292c1fa5e2822d9c96345", master.Tip.Sha); @@ -537,7 +568,7 @@ public void CanGetInformationFromUnbornBranch() Assert.Equal(0, head.Commits.Count()); Assert.True(head.IsCurrentRepositoryHead); Assert.False(head.IsRemote); - Assert.Equal("master", head.Name); + Assert.Equal("master", head.FriendlyName); Assert.Null(head.Tip); Assert.Null(head["huh?"]); @@ -559,7 +590,7 @@ public void CanGetTrackingInformationFromBranchSharingNoHistoryWithItsTrackedBra { Branch master = repo.Branches["master"]; const string logMessage = "update target message"; - repo.Refs.UpdateTarget("refs/remotes/origin/master", "origin/test", Constants.Signature, logMessage); + repo.Refs.UpdateTarget("refs/remotes/origin/master", "origin/test", logMessage); Assert.True(master.IsTracking); Assert.NotNull(master.TrackedBranch); @@ -888,6 +919,24 @@ public void CanRemoveAnExistingBranch(string branchName) } } + [Fact] + public void CanCreateBranchInDeletedNestedBranchNamespace() + { + const string namespaceName = "level_one"; + string branchWithNamespaceName = string.Join("/", namespaceName, "level_two"); + + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + Commit commit = repo.Head.Tip; + + Branch branchWithNamespace = repo.Branches.Add(branchWithNamespaceName, commit); + repo.Branches.Remove(branchWithNamespace); + + repo.Branches.Add(namespaceName, commit); + } + } + [Theory] [InlineData("I-donot-exist", false)] [InlineData("me/neither", true)] @@ -902,7 +951,7 @@ public void RemovingABranchWhichIsTheCurrentHeadThrows() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Throws(() => repo.Branches.Remove(repo.Head.Name)); + Assert.Throws(() => repo.Branches.Remove(repo.Head.FriendlyName)); } } @@ -913,7 +962,8 @@ public void RemovingABranchWithBadParamsThrows() using (var repo = new Repository(path)) { Assert.Throws(() => repo.Branches.Remove(string.Empty)); - Assert.Throws(() => repo.Branches.Remove(null)); + Assert.Throws(() => repo.Branches.Remove(default(string))); + Assert.Throws(() => repo.Branches.Remove(default(Branch))); } } @@ -964,7 +1014,7 @@ public void TwoBranchesPointingAtTheSameCommitAreNotBothCurrent() public void CanRenameABranch() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); @@ -972,16 +1022,20 @@ public void CanRenameABranch() var br2 = repo.Branches["br2"]; Assert.NotNull(br2); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.Branches.Rename("br2", "br3"); - Assert.Equal("br3", newBranch.Name); + Assert.Equal("br3", newBranch.FriendlyName); Assert.Null(repo.Branches["br2"]); Assert.NotNull(repo.Branches["br3"]); AssertRefLogEntry(repo, newBranch.CanonicalName, + string.Format("branch: renamed {0} to {1}", br2.CanonicalName, newBranch.CanonicalName), + br2.Tip.Id, newBranch.Tip.Id, - string.Format("branch: renamed {0} to {1}", br2.CanonicalName, newBranch.CanonicalName)); + Constants.Identity, before); } } @@ -999,7 +1053,7 @@ public void BlindlyRenamingABranchOverAnExistingOneThrows() public void CanRenameABranchWhileOverwritingAnExistingOne() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); @@ -1009,8 +1063,10 @@ public void CanRenameABranchWhileOverwritingAnExistingOne() Branch br2 = repo.Branches["br2"]; Assert.NotNull(br2); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Branch newBranch = repo.Branches.Rename("br2", "test", true); - Assert.Equal("test", newBranch.Name); + Assert.Equal("test", newBranch.FriendlyName); Assert.Null(repo.Branches["br2"]); @@ -1021,9 +1077,10 @@ public void CanRenameABranchWhileOverwritingAnExistingOne() Assert.Equal(br2.Tip, newTest.Tip); AssertRefLogEntry(repo, newBranch.CanonicalName, - newBranch.Tip.Id, string.Format("branch: renamed {0} to {1}", br2.CanonicalName, newBranch.CanonicalName), - test.Tip.Id); + br2.Tip.Id, + newTest.Tip.Id, + Constants.Identity, before); } } @@ -1068,7 +1125,7 @@ public void TrackedBranchExistsFromDefaultConfigInEmptyClone() using (var repo = new Repository(clonedRepoPath)) { Assert.Empty(Directory.GetFiles(scd2.RootedDirectoryPath)); - Assert.Equal(repo.Head.Name, "master"); + Assert.Equal(repo.Head.FriendlyName, "master"); Assert.Null(repo.Head.Tip); Assert.NotNull(repo.Head.TrackedBranch); @@ -1129,15 +1186,27 @@ private static T[] SortedBranches(IEnumerable branches, Func(() => repo.Checkout(master.CanonicalName)); + Assert.Throws(() => repo.Checkout(master.CanonicalName)); // Checkout with force option should succeed. repo.Checkout(master.CanonicalName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force}); @@ -304,11 +304,11 @@ public void CheckingOutWithMergeConflictsThrows() // Assert that checking out master throws // when there are unstaged commits - Assert.Throws(() => repo.Checkout("master")); + Assert.Throws(() => repo.Checkout("master")); // And when there are staged commits repo.Stage(originalFilePath); - Assert.Throws(() => repo.Checkout("master")); + Assert.Throws(() => repo.Checkout("master")); } } @@ -319,7 +319,7 @@ public void CanCancelCheckoutThroughNotifyCallback() using (var repo = new Repository(repoPath)) { - string relativePath = "a.txt"; + const string relativePath = "a.txt"; Touch(repo.Info.WorkingDirectory, relativePath, "Hello\n"); repo.Stage(relativePath); @@ -451,13 +451,13 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri { PopulateBasicRepository(repo); - string relativePathUpdated = "updated.txt"; + const string relativePathUpdated = "updated.txt"; Touch(repo.Info.WorkingDirectory, relativePathUpdated, "updated file text A"); repo.Stage(relativePathUpdated); repo.Commit("Commit initial update file", Constants.Signature, Constants.Signature); // Create conflicting change - string relativePathConflict = "conflict.txt"; + const string relativePathConflict = "conflict.txt"; Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text A"); repo.Stage(relativePathConflict); repo.Commit("Initial commit of conflict.txt and update.txt", Constants.Signature, Constants.Signature); @@ -492,7 +492,7 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri Touch(repo.Info.WorkingDirectory, relativePathIgnore, "ignored file"); // Create untracked change - string relativePathUntracked = "untracked.txt"; + const string relativePathUntracked = "untracked.txt"; Touch(repo.Info.WorkingDirectory, relativePathUntracked, "untracked file"); bool wasCalled = false; @@ -505,7 +505,7 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri CheckoutNotifyFlags = notifyFlags, }; - Assert.Throws(() => repo.Checkout("master", options)); + Assert.Throws(() => repo.Checkout("master", options)); Assert.True(wasCalled); Assert.Equal(expectedNotificationPath, actualNotificationPath); @@ -527,13 +527,13 @@ public void CheckoutRetainsUntrackedChanges() // Verify that there is an untracked entry. Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); repo.Checkout(otherBranchName); // Verify untracked entry still exists. Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); } } @@ -551,13 +551,13 @@ public void ForceCheckoutRetainsUntrackedChanges() // Verify that there is an untracked entry. Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); repo.Checkout(otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force }); // Verify untracked entry still exists. Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); } } @@ -575,13 +575,13 @@ public void CheckoutRetainsUnstagedChanges() // Verify that there is a modified entry. Assert.Equal(1, repo.RetrieveStatus().Modified.Count()); - Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(fullPathFileA)); + Assert.Equal(FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(fullPathFileA)); repo.Checkout(otherBranchName); // Verify modified entry still exists. Assert.Equal(1, repo.RetrieveStatus().Modified.Count()); - Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(fullPathFileA)); + Assert.Equal(FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(fullPathFileA)); } } @@ -600,13 +600,13 @@ public void CheckoutRetainsStagedChanges() // Verify that there is a staged entry. Assert.Equal(1, repo.RetrieveStatus().Staged.Count()); - Assert.Equal(FileStatus.Staged, repo.RetrieveStatus(fullPathFileA)); + Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus(fullPathFileA)); repo.Checkout(otherBranchName); // Verify staged entry still exists. Assert.Equal(1, repo.RetrieveStatus().Staged.Count()); - Assert.Equal(FileStatus.Staged, repo.RetrieveStatus(fullPathFileA)); + Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus(fullPathFileA)); } } @@ -769,8 +769,8 @@ public void CheckoutFromDetachedHead(string commitPointer) var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); Assert.Equal(initialHead.Tip.Id, reflogEntry.From); Assert.Equal(commitSha, reflogEntry.To.Sha); - Assert.NotNull(reflogEntry.Commiter.Email); - Assert.NotNull(reflogEntry.Commiter.Name); + Assert.NotNull(reflogEntry.Committer.Email); + Assert.NotNull(reflogEntry.Committer.Name); Assert.Equal(string.Format("checkout: moving from {0} to {1}", initialHead.Tip.Sha, commitPointer), reflogEntry.Message); } } @@ -779,7 +779,7 @@ public void CheckoutFromDetachedHead(string commitPointer) public void CheckoutBranchFromDetachedHead() { string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions{ Identity = Constants.Identity })) { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); @@ -789,12 +789,14 @@ public void CheckoutBranchFromDetachedHead() Assert.True(repo.Info.IsHeadDetached); - Branch newHead = repo.Checkout(repo.Branches["master"], Constants.Signature); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + + Branch newHead = repo.Checkout(repo.Branches["master"]); // Assert reflog entry is created - AssertRefLogEntry(repo, "HEAD", newHead.Tip.Id, - string.Format("checkout: moving from {0} to {1}", initialHead.Tip.Sha, newHead.Name), - initialHead.Tip.Id, Constants.Signature); + AssertRefLogEntry(repo, "HEAD", + string.Format("checkout: moving from {0} to {1}", initialHead.Tip.Sha, newHead.FriendlyName), + initialHead.Tip.Id, newHead.Tip.Id, Constants.Identity, before); } } @@ -847,7 +849,7 @@ public void CheckoutPreviousCheckedOutBranch() public void CheckoutCurrentReference() { string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { Branch master = repo.Branches["master"]; Assert.True(master.IsCurrentRepositoryHead); @@ -862,12 +864,15 @@ public void CheckoutCurrentReference() Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count()); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + // Checkout in detached mode repo.Checkout(master.Tip.Sha); Assert.True(repo.Info.IsHeadDetached); - AssertRefLogEntry(repo, "HEAD", master.Tip.Id, - string.Format("checkout: moving from master to {0}", master.Tip.Sha), master.Tip.Id); + AssertRefLogEntry(repo, "HEAD", + string.Format("checkout: moving from master to {0}", master.Tip.Sha), + master.Tip.Id, master.Tip.Id, Constants.Identity, before); // Checkout detached "HEAD" => nothing should happen reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count(); @@ -935,10 +940,10 @@ public void CanCheckoutDetachedHead() } [Theory] - [InlineData("master", "6dcf9bf", "readme.txt", FileStatus.Added)] - [InlineData("master", "refs/tags/lw", "readme.txt", FileStatus.Added)] - [InlineData("master", "i-do-numbers", "super-file.txt", FileStatus.Added)] - [InlineData("i-do-numbers", "diff-test-cases", "numbers.txt", FileStatus.Staged)] + [InlineData("master", "6dcf9bf", "readme.txt", FileStatus.NewInIndex)] + [InlineData("master", "refs/tags/lw", "readme.txt", FileStatus.NewInIndex)] + [InlineData("master", "i-do-numbers", "super-file.txt", FileStatus.NewInIndex)] + [InlineData("i-do-numbers", "diff-test-cases", "numbers.txt", FileStatus.ModifiedInIndex)] public void CanCheckoutPath(string originalBranch, string checkoutFrom, string path, FileStatus expectedStatus) { string repoPath = SandboxStandardTestRepo(); @@ -973,7 +978,7 @@ public void CanCheckoutPaths() foreach (string checkoutPath in checkoutPaths) { - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(checkoutPath)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(checkoutPath)); } } } @@ -1024,6 +1029,23 @@ public void CanCheckoutPathFromCurrentBranch(string fileName) } } + [Fact] + public void CanCatchDeprecatedException() + { + bool caught = false; + + try + { + throw new CheckoutConflictException(); + } + catch (MergeConflictException) + { + caught = true; + } + + Assert.True(caught); + } + /// /// Helper method to populate a simple repository with /// a single file and two branches. diff --git a/LibGit2Sharp.Tests/CherryPickFixture.cs b/LibGit2Sharp.Tests/CherryPickFixture.cs index c4b61455f..d9828e266 100644 --- a/LibGit2Sharp.Tests/CherryPickFixture.cs +++ b/LibGit2Sharp.Tests/CherryPickFixture.cs @@ -1,9 +1,9 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; using Xunit.Extensions; -using System; namespace LibGit2Sharp.Tests { diff --git a/LibGit2Sharp.Tests/CloneFixture.cs b/LibGit2Sharp.Tests/CloneFixture.cs index d291ddcdc..ae98788eb 100644 --- a/LibGit2Sharp.Tests/CloneFixture.cs +++ b/LibGit2Sharp.Tests/CloneFixture.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using LibGit2Sharp.Handlers; using LibGit2Sharp.Tests.TestHelpers; using Xunit; using Xunit.Extensions; @@ -31,7 +33,7 @@ public void CanClone(string url) Assert.False(repo.Info.IsBare); Assert.True(File.Exists(Path.Combine(scd.RootedDirectoryPath, "master.txt"))); - Assert.Equal(repo.Head.Name, "master"); + Assert.Equal(repo.Head.FriendlyName, "master"); Assert.Equal(repo.Head.Tip.Id.ToString(), "49322bb17d3acc9146f98c97d078513228bbf3c0"); } } @@ -50,7 +52,7 @@ public void CanCloneWithCheckoutBranchName(string branchName, string headTipId) { var head = repo.Head; - Assert.Equal(branchName, head.Name); + Assert.Equal(branchName, head.FriendlyName); Assert.True(head.IsTracking); Assert.Equal(headTipId, head.Tip.Sha); } @@ -193,19 +195,34 @@ public void CanCloneWithCredentials() } } + static Credentials CreateUsernamePasswordCredentials (string user, string pass, bool secure) + { + if (secure) + { + return new SecureUsernamePasswordCredentials + { + Username = user, + Password = Constants.StringToSecureString(pass), + }; + } + + return new UsernamePasswordCredentials + { + Username = user, + Password = pass, + }; + } + [Theory] - [InlineData("https://libgit2@bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3")] - public void CanCloneFromBBWithCredentials(string url, string user, string pass) + [InlineData("https://libgit2@bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3", true)] + [InlineData("https://libgit2@bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3", false)] + public void CanCloneFromBBWithCredentials(string url, string user, string pass, bool secure) { var scd = BuildSelfCleaningDirectory(); string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath, new CloneOptions() { - CredentialsProvider = (_url, _user, _cred) => new UsernamePasswordCredentials - { - Username = user, - Password = pass, - } + CredentialsProvider = (_url, _user, _cred) => CreateUsernamePasswordCredentials (user, pass, secure) }); using (var repo = new Repository(clonedRepoPath)) @@ -220,6 +237,66 @@ public void CanCloneFromBBWithCredentials(string url, string user, string pass) } } + [SkippableTheory] + [InlineData("https://github.com/libgit2/TestGitRepository.git", "github.com", typeof(CertificateX509))] + [InlineData("git@github.com:libgit2/TestGitRepository.git", "github.com", typeof(CertificateSsh))] + public void CanInspectCertificateOnClone(string url, string hostname, Type certType) + { + var scd = BuildSelfCleaningDirectory(); + + InconclusiveIf( + () => + certType == typeof (CertificateSsh) && !GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh), + "SSH not supported"); + + bool wasCalled = false; + bool checksHappy = false; + + var options = new CloneOptions { + CertificateCheck = (cert, valid, host) => { + wasCalled = true; + + Assert.Equal(hostname, host); + Assert.Equal(certType, cert.GetType()); + + if (certType == typeof(CertificateX509)) { + Assert.True(valid); + var x509 = ((CertificateX509)cert).Certificate; + // we get a string with the different fields instead of a structure, so... + Assert.True(x509.Subject.Contains("CN=github.com,")); + checksHappy = true; + return false; + } + + if (certType == typeof(CertificateSsh)) { + var hostkey = (CertificateSsh)cert; + Assert.True(hostkey.HasMD5); + /* + * Once you've connected and thus your ssh has stored the hostkey, + * you can get the hostkey for a host with + * + * ssh-keygen -F github.com -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':' + * + * though GitHub's hostkey won't change anytime soon. + */ + Assert.Equal("1627aca576282d36631b564debdfa648", + BitConverter.ToString(hostkey.HashMD5).ToLower().Replace("-", "")); + checksHappy = true; + return false; + } + + return false; + }, + }; + + Assert.Throws(() => + Repository.Clone(url, scd.DirectoryPath, options) + ); + + Assert.True(wasCalled); + Assert.True(checksHappy); + } + [Fact] public void CloningAnUrlWithoutPathThrows() { @@ -242,5 +319,243 @@ public void CloningWithoutUrlThrows() Assert.Throws(() => Repository.Clone(null, scd.DirectoryPath)); } + + /// + /// Private helper to record the callbacks that were called as part of a clone. + /// + private class CloneCallbackInfo + { + /// + /// Was checkout progress called. + /// + public bool CheckoutProgressCalled { get; set; } + + /// + /// The reported remote URL. + /// + public string RemoteUrl { get; set; } + + /// + /// Was remote ref update called. + /// + public bool RemoteRefUpdateCalled { get; set; } + + /// + /// Was the transition callback called when starting + /// work on this repository. + /// + public bool StartingWorkInRepositoryCalled { get; set; } + + /// + /// Was the transition callback called when finishing + /// work on this repository. + /// + public bool FinishedWorkInRepositoryCalled { get; set; } + + /// + /// The reported recursion depth. + /// + public int RecursionDepth { get; set; } + } + + [Fact] + public void CanRecursivelyCloneSubmodules() + { + var uri = new Uri(Path.GetFullPath(SandboxSubmoduleSmallTestRepo())); + var scd = BuildSelfCleaningDirectory(); + string relativeSubmodulePath = "submodule_target_wd"; + + // Construct the expected URL the submodule will clone from. + string expectedSubmoduleUrl = Path.Combine(Path.GetDirectoryName(uri.AbsolutePath), relativeSubmodulePath); + expectedSubmoduleUrl = expectedSubmoduleUrl.Replace('\\', '/'); + + Dictionary callbacks = new Dictionary(); + + CloneCallbackInfo currentEntry = null; + bool unexpectedOrderOfCallbacks = false; + + CheckoutProgressHandler checkoutProgressHandler = (x, y, z) => + { + if (currentEntry != null) + { + currentEntry.CheckoutProgressCalled = true; + } + else + { + // Should not be called if there is not a current + // callbackInfo entry. + unexpectedOrderOfCallbacks = true; + } + }; + + UpdateTipsHandler remoteRefUpdated = (x, y, z) => + { + if (currentEntry != null) + { + currentEntry.RemoteRefUpdateCalled = true; + } + else + { + // Should not be called if there is not a current + // callbackInfo entry. + unexpectedOrderOfCallbacks = true; + } + + return true; + }; + + RepositoryOperationStarting repositoryOperationStarting = (x) => + { + if (currentEntry != null) + { + // Should not be called if there is a current + // callbackInfo entry. + unexpectedOrderOfCallbacks = true; + } + + currentEntry = new CloneCallbackInfo(); + currentEntry.StartingWorkInRepositoryCalled = true; + currentEntry.RecursionDepth = x.RecursionDepth; + currentEntry.RemoteUrl = x.RemoteUrl; + callbacks.Add(x.RepositoryPath, currentEntry); + + return true; + }; + + RepositoryOperationCompleted repositoryOperationCompleted = (x) => + { + if (currentEntry != null) + { + currentEntry.FinishedWorkInRepositoryCalled = true; + currentEntry = null; + } + else + { + // Should not be called if there is not a current + // callbackInfo entry. + unexpectedOrderOfCallbacks = true; + } + }; + + CloneOptions options = new CloneOptions() + { + RecurseSubmodules = true, + OnCheckoutProgress = checkoutProgressHandler, + OnUpdateTips = remoteRefUpdated, + RepositoryOperationStarting = repositoryOperationStarting, + RepositoryOperationCompleted = repositoryOperationCompleted, + }; + + string clonedRepoPath = Repository.Clone(uri.AbsolutePath, scd.DirectoryPath, options); + string workDirPath; + + using(Repository repo = new Repository(clonedRepoPath)) + { + workDirPath = repo.Info.WorkingDirectory.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); + } + + // Verification: + // Verify that no callbacks were called in an unexpected order. + Assert.False(unexpectedOrderOfCallbacks); + + Dictionary expectedCallbackInfo = new Dictionary(); + expectedCallbackInfo.Add(workDirPath, new CloneCallbackInfo() + { + RecursionDepth = 0, + RemoteUrl = uri.AbsolutePath, + StartingWorkInRepositoryCalled = true, + FinishedWorkInRepositoryCalled = true, + CheckoutProgressCalled = true, + RemoteRefUpdateCalled = true, + }); + + expectedCallbackInfo.Add(Path.Combine(workDirPath, relativeSubmodulePath), new CloneCallbackInfo() + { + RecursionDepth = 1, + RemoteUrl = expectedSubmoduleUrl, + StartingWorkInRepositoryCalled = true, + FinishedWorkInRepositoryCalled = true, + CheckoutProgressCalled = true, + RemoteRefUpdateCalled = true, + }); + + // Callbacks for each expected repository that is cloned + foreach (KeyValuePair kvp in expectedCallbackInfo) + { + CloneCallbackInfo entry = null; + Assert.True(callbacks.TryGetValue(kvp.Key, out entry), string.Format("{0} was not found in callbacks.", kvp.Key)); + + Assert.Equal(kvp.Value.RemoteUrl, entry.RemoteUrl); + Assert.Equal(kvp.Value.RecursionDepth, entry.RecursionDepth); + Assert.Equal(kvp.Value.StartingWorkInRepositoryCalled, entry.StartingWorkInRepositoryCalled); + Assert.Equal(kvp.Value.FinishedWorkInRepositoryCalled, entry.FinishedWorkInRepositoryCalled); + Assert.Equal(kvp.Value.CheckoutProgressCalled, entry.CheckoutProgressCalled); + Assert.Equal(kvp.Value.RemoteRefUpdateCalled, entry.RemoteRefUpdateCalled); + } + + // Verify the state of the submodule + using(Repository repo = new Repository(clonedRepoPath)) + { + var sm = repo.Submodules[relativeSubmodulePath]; + Assert.True(sm.RetrieveStatus().HasFlag(SubmoduleStatus.InWorkDir | + SubmoduleStatus.InConfig | + SubmoduleStatus.InIndex | + SubmoduleStatus.InHead)); + + Assert.NotNull(sm.HeadCommitId); + Assert.Equal("480095882d281ed676fe5b863569520e54a7d5c0", sm.HeadCommitId.Sha); + + Assert.False(repo.RetrieveStatus().IsDirty); + } + } + + [Fact] + public void CanCancelRecursiveClone() + { + var uri = new Uri(Path.GetFullPath(SandboxSubmoduleSmallTestRepo())); + var scd = BuildSelfCleaningDirectory(); + string relativeSubmodulePath = "submodule_target_wd"; + + int cancelDepth = 0; + + RepositoryOperationStarting repositoryOperationStarting = (x) => + { + return !(x.RecursionDepth >= cancelDepth); + }; + + CloneOptions options = new CloneOptions() + { + RecurseSubmodules = true, + RepositoryOperationStarting = repositoryOperationStarting, + }; + + Assert.Throws(() => + Repository.Clone(uri.AbsolutePath, scd.DirectoryPath, options)); + + // Cancel after super repository is cloned, but before submodule is cloned. + cancelDepth = 1; + + string clonedRepoPath = null; + + try + { + Repository.Clone(uri.AbsolutePath, scd.DirectoryPath, options); + } + catch(RecurseSubmodulesException ex) + { + Assert.NotNull(ex.InnerException); + Assert.Equal(typeof(UserCancelledException), ex.InnerException.GetType()); + clonedRepoPath = ex.InitialRepositoryPath; + } + + // Verify that the submodule was not initialized. + using(Repository repo = new Repository(clonedRepoPath)) + { + var submoduleStatus = repo.Submodules[relativeSubmodulePath].RetrieveStatus(); + Assert.Equal(SubmoduleStatus.InConfig | SubmoduleStatus.InHead | SubmoduleStatus.InIndex | SubmoduleStatus.WorkDirUninitialized, + submoduleStatus); + + } + } } } diff --git a/LibGit2Sharp.Tests/CommitAncestorFixture.cs b/LibGit2Sharp.Tests/CommitAncestorFixture.cs index 6ea6176b6..c752f7415 100644 --- a/LibGit2Sharp.Tests/CommitAncestorFixture.cs +++ b/LibGit2Sharp.Tests/CommitAncestorFixture.cs @@ -41,7 +41,7 @@ public void FindCommonAncestorForTwoCommits(string result, string sha1, string s var first = sha1 == "-" ? CreateOrphanedCommit(repo) : repo.Lookup(sha1); var second = sha2 == "-" ? CreateOrphanedCommit(repo) : repo.Lookup(sha2); - Commit ancestor = repo.Commits.FindMergeBase(first, second); + Commit ancestor = repo.ObjectDatabase.FindMergeBase(first, second); if (result == null) { @@ -71,7 +71,7 @@ public void FindCommonAncestorForCommitsAsEnumerable(string result, string[] sha { var commits = shas.Select(sha => sha == "-" ? CreateOrphanedCommit(repo) : repo.Lookup(sha)).ToArray(); - Commit ancestor = repo.Commits.FindMergeBase(commits, strategy); + Commit ancestor = repo.ObjectDatabase.FindMergeBase(commits, strategy); if (result == null) { @@ -96,7 +96,7 @@ public void FindCommonAncestorForTwoCommitsThrows(string sha1, string sha2) var first = repo.Lookup(sha1); var second = repo.Lookup(sha2); - Assert.Throws(() => repo.Commits.FindMergeBase(first, second)); + Assert.Throws(() => repo.ObjectDatabase.FindMergeBase(first, second)); } } @@ -112,7 +112,7 @@ public void FindCommonAncestorForCommitsAsEnumerableThrows(string[] shas, MergeB { var commits = shas.Select(sha => sha == "-" ? CreateOrphanedCommit(repo) : repo.Lookup(sha)).ToArray(); - Assert.Throws(() => repo.Commits.FindMergeBase(commits, strategy)); + Assert.Throws(() => repo.ObjectDatabase.FindMergeBase(commits, strategy)); } } diff --git a/LibGit2Sharp.Tests/CommitFixture.cs b/LibGit2Sharp.Tests/CommitFixture.cs index fa76f585f..82148246d 100644 --- a/LibGit2Sharp.Tests/CommitFixture.cs +++ b/LibGit2Sharp.Tests/CommitFixture.cs @@ -92,7 +92,7 @@ public void CanEnumerateCommitsFromSha() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter { Since = "a4a7dce85cf63874e984719f4fdd239f5145052f" })) + foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f" })) { Assert.NotNull(commit); count++; @@ -107,9 +107,9 @@ public void QueryingTheCommitHistoryWithUnknownShaOrInvalidEntryPointThrows() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { Since = Constants.UnknownSha }).Count()); - Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { Since = "refs/heads/deadbeef" }).Count()); - Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { Since = null }).Count()); + Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = Constants.UnknownSha }).Count()); + Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = "refs/heads/deadbeef" }).Count()); + Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = null }).Count()); } } @@ -121,8 +121,8 @@ public void QueryingTheCommitHistoryFromACorruptedReferenceThrows() { CreateCorruptedDeadBeefHead(repo.Info.Path); - Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { Since = repo.Branches["deadbeef"] }).Count()); - Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs["refs/heads/deadbeef"] }).Count()); + Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Branches["deadbeef"] }).Count()); + Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs["refs/heads/deadbeef"] }).Count()); } } @@ -132,8 +132,8 @@ public void QueryingTheCommitHistoryWithBadParamsThrows() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { Since = string.Empty })); - Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { Since = null })); + Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = string.Empty })); + Assert.Throws(() => repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = null })); Assert.Throws(() => repo.Commits.QueryBy(default(CommitFilter))); } } @@ -150,7 +150,7 @@ public void CanEnumerateCommitsWithReverseTimeSorting() { foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter { - Since = "a4a7dce85cf63874e984719f4fdd239f5145052f", + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", SortBy = CommitSortStrategies.Time | CommitSortStrategies.Reverse })) { @@ -170,7 +170,7 @@ public void CanEnumerateCommitsWithReverseTopoSorting() { List commits = repo.Commits.QueryBy(new CommitFilter { - Since = "a4a7dce85cf63874e984719f4fdd239f5145052f", + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", SortBy = CommitSortStrategies.Time | CommitSortStrategies.Reverse }).ToList(); foreach (Commit commit in commits) @@ -189,7 +189,7 @@ public void CanEnumerateCommitsWithReverseTopoSorting() public void CanSimplifyByFirstParent() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = repo.Head, FirstParentOnly = true }, + repo => new CommitFilter { IncludeReachableFrom = repo.Head, FirstParentOnly = true }, new[] { "4c062a6", "be3563a", "9fd738e", @@ -216,7 +216,7 @@ public void CanEnumerateCommitsWithTimeSorting() { foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter { - Since = "a4a7dce85cf63874e984719f4fdd239f5145052f", + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", SortBy = CommitSortStrategies.Time })) { @@ -236,7 +236,7 @@ public void CanEnumerateCommitsWithTopoSorting() { List commits = repo.Commits.QueryBy(new CommitFilter { - Since = "a4a7dce85cf63874e984719f4fdd239f5145052f", + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", SortBy = CommitSortStrategies.Topological }).ToList(); foreach (Commit commit in commits) @@ -255,7 +255,7 @@ public void CanEnumerateCommitsWithTopoSorting() public void CanEnumerateFromHead() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = repo.Head }, + repo => new CommitFilter { IncludeReachableFrom = repo.Head }, new[] { "4c062a6", "be3563a", "c47800c", "9fd738e", @@ -277,7 +277,7 @@ public void CanEnumerateFromDetachedHead() repoClone.Checkout(headSha); AssertEnumerationOfCommitsInRepo(repoClone, - repo => new CommitFilter { Since = repo.Head }, + repo => new CommitFilter { IncludeReachableFrom = repo.Head }, new[] { "32eab9c", "592d3c8", "4c062a6", @@ -291,7 +291,7 @@ public void CanEnumerateFromDetachedHead() public void CanEnumerateUsingTwoHeadsAsBoundaries() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = "HEAD", Until = "refs/heads/br2" }, + repo => new CommitFilter { IncludeReachableFrom = "HEAD", ExcludeReachableFrom = "refs/heads/br2" }, new[] { "4c062a6", "be3563a" } ); } @@ -300,7 +300,7 @@ public void CanEnumerateUsingTwoHeadsAsBoundaries() public void CanEnumerateUsingImplicitHeadAsSinceBoundary() { AssertEnumerationOfCommits( - repo => new CommitFilter { Until = "refs/heads/br2" }, + repo => new CommitFilter { ExcludeReachableFrom = "refs/heads/br2" }, new[] { "4c062a6", "be3563a" } ); } @@ -309,7 +309,7 @@ public void CanEnumerateUsingImplicitHeadAsSinceBoundary() public void CanEnumerateUsingTwoAbbreviatedShasAsBoundaries() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = "a4a7dce", Until = "4a202b3" }, + repo => new CommitFilter { IncludeReachableFrom = "a4a7dce", ExcludeReachableFrom = "4a202b3" }, new[] { "a4a7dce", "c47800c", "9fd738e" } ); } @@ -318,7 +318,7 @@ public void CanEnumerateUsingTwoAbbreviatedShasAsBoundaries() public void CanEnumerateCommitsFromTwoHeads() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = new[] { "refs/heads/br2", "refs/heads/master" } }, + repo => new CommitFilter { IncludeReachableFrom = new[] { "refs/heads/br2", "refs/heads/master" } }, new[] { "4c062a6", "a4a7dce", "be3563a", "c47800c", @@ -330,7 +330,7 @@ public void CanEnumerateCommitsFromTwoHeads() public void CanEnumerateCommitsFromMixedStartingPoints() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = new object[] { repo.Branches["br2"], + repo => new CommitFilter { IncludeReachableFrom = new object[] { repo.Branches["br2"], "refs/heads/master", new ObjectId("e90810b8df3e80c413d903f631643c716887138d") } }, new[] @@ -345,7 +345,7 @@ public void CanEnumerateCommitsFromMixedStartingPoints() public void CanEnumerateCommitsUsingGlob() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = repo.Refs.FromGlob("refs/heads/*") }, + repo => new CommitFilter { IncludeReachableFrom = repo.Refs.FromGlob("refs/heads/*") }, new[] { "4c062a6", "e90810b", "6dcf9bf", "a4a7dce", "be3563a", "c47800c", "9fd738e", "4a202b3", "41bc8c6", "5001298", "5b5b025", "8496071" @@ -356,7 +356,7 @@ public void CanEnumerateCommitsUsingGlob() public void CanHideCommitsUsingGlob() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = "refs/heads/packed-test", Until = repo.Refs.FromGlob("*/packed") }, + repo => new CommitFilter { IncludeReachableFrom = "refs/heads/packed-test", ExcludeReachableFrom = repo.Refs.FromGlob("*/packed") }, new[] { "4a202b3", "5b5b025", "8496071" @@ -378,7 +378,7 @@ public void CanEnumerateCommitsFromATagAnnotation() private void CanEnumerateCommitsFromATag(Func transformer) { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = transformer(repo.Tags["test"]) }, + repo => new CommitFilter { IncludeReachableFrom = transformer(repo.Tags["test"]) }, new[] { "e90810b", "6dcf9bf", } ); } @@ -389,7 +389,7 @@ public void CanEnumerateAllCommits() AssertEnumerationOfCommits( repo => new CommitFilter { - Since = repo.Refs.OrderBy(r => r.CanonicalName, StringComparer.Ordinal), + IncludeReachableFrom = repo.Refs.OrderBy(r => r.CanonicalName, StringComparer.Ordinal), }, new[] { @@ -404,7 +404,7 @@ public void CanEnumerateAllCommits() public void CanEnumerateCommitsFromATagWhichPointsToABlob() { AssertEnumerationOfCommits( - repo => new CommitFilter { Since = repo.Tags["point_to_blob"] }, + repo => new CommitFilter { IncludeReachableFrom = repo.Tags["point_to_blob"] }, new string[] { }); } @@ -419,7 +419,7 @@ public void CanEnumerateCommitsFromATagWhichPointsToATree() Tag tag = repo.ApplyTag("point_to_tree", headTreeSha); AssertEnumerationOfCommitsInRepo(repo, - r => new CommitFilter { Since = tag }, + r => new CommitFilter { IncludeReachableFrom = tag }, new string[] { }); } } @@ -538,32 +538,11 @@ public void DirectlyAccessingAnUnknownTreeEntryOfTheCommitReturnsNull() } } - [Theory] - [InlineData(null, "x@example.com")] - [InlineData("", "x@example.com")] - [InlineData("X", null)] - [InlineData("X", "")] - public void CommitWithInvalidSignatureConfigThrows(string name, string email) - { - string repoPath = InitNewRepository(); - string configPath = CreateConfigurationWithDummyUser(name, email); - var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; - - using (var repo = new Repository(repoPath, options)) - { - Assert.Equal(name, repo.Config.GetValueOrDefault("user.name")); - Assert.Equal(email, repo.Config.GetValueOrDefault("user.email")); - - Assert.Throws( - () => repo.Commit("Initial egotistic commit", new CommitOptions { AllowEmptyCommit = true })); - } - } - [Fact] public void CanCommitWithSignatureFromConfig() { string repoPath = InitNewRepository(); - string configPath = CreateConfigurationWithDummyUser(Constants.Signature); + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; using (var repo = new Repository(repoPath, options)) @@ -581,12 +560,14 @@ public void CanCommitWithSignatureFromConfig() Assert.Null(repo.Head[relativeFilepath]); - Commit commit = repo.Commit("Initial egotistic commit"); + Signature signature = repo.Config.BuildSignature(DateTimeOffset.Now); + + Commit commit = repo.Commit("Initial egotistic commit", signature, signature); AssertBlobContent(repo.Head[relativeFilepath], "nulltoken\n"); AssertBlobContent(commit[relativeFilepath], "nulltoken\n"); - AssertCommitSignaturesAre(commit, Constants.Signature); + AssertCommitIdentitiesAre(commit, Constants.Identity); } } @@ -615,8 +596,8 @@ public void CommitParentsAreMergeHeads() // Assert reflog entry is created var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); Assert.Equal(repo.Head.Tip.Id, reflogEntry.To); - Assert.NotNull(reflogEntry.Commiter.Email); - Assert.NotNull(reflogEntry.Commiter.Name); + Assert.NotNull(reflogEntry.Committer.Email); + Assert.NotNull(reflogEntry.Committer.Name); Assert.Equal(string.Format("commit (merge): {0}", newMergedCommit.MessageShort), reflogEntry.Message); } } @@ -661,7 +642,9 @@ public void CanCommitALittleBit() { string repoPath = InitNewRepository(); - using (var repo = new Repository(repoPath)) + var identity = Constants.Identity; + + using (var repo = new Repository(repoPath, new RepositoryOptions { Identity = identity })) { string dir = repo.Info.Path; Assert.True(Path.IsPathRooted(dir)); @@ -680,6 +663,8 @@ public void CanCommitALittleBit() const string shortMessage = "Initial egotistic commit"; const string commitMessage = shortMessage + "\n\nOnly the coolest commits from us"; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Commit commit = repo.Commit(commitMessage, author, author); AssertBlobContent(repo.Head[relativeFilepath], "nulltoken\n"); @@ -691,7 +676,13 @@ public void CanCommitALittleBit() // Assert a reflog entry is created on HEAD Assert.Equal(1, repo.Refs.Log("HEAD").Count()); var reflogEntry = repo.Refs.Log("HEAD").First(); - Assert.Equal(author, reflogEntry.Commiter); + + Assert.Equal(identity.Name, reflogEntry.Committer.Name); + Assert.Equal(identity.Email, reflogEntry.Committer.Email); + + var now = DateTimeOffset.Now; + Assert.InRange(reflogEntry.Committer.When, before, now); + Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(ObjectId.Zero, reflogEntry.From); Assert.Equal(string.Format("commit (initial): {0}", shortMessage), reflogEntry.Message); @@ -805,7 +796,7 @@ public void CanAmendARootCommit() public void CanAmendACommitWithMoreThanOneParent() { string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { var mergedCommit = repo.Lookup("be3563a"); Assert.NotNull(mergedCommit); @@ -816,16 +807,18 @@ public void CanAmendACommitWithMoreThanOneParent() CreateAndStageANewFile(repo); const string commitMessage = "I'm rewriting the history!"; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Commit amendedCommit = repo.Commit(commitMessage, Constants.Signature, Constants.Signature, new CommitOptions { AmendPreviousCommit = true }); AssertCommitHasBeenAmended(repo, amendedCommit, mergedCommit); AssertRefLogEntry(repo, "HEAD", - amendedCommit.Id, string.Format("commit (amend): {0}", commitMessage), mergedCommit.Id, - amendedCommit.Committer); + amendedCommit.Id, + Constants.Identity, before); } } @@ -868,10 +861,10 @@ public void CanRetrieveChildrenOfASpecificCommit() var filter = new CommitFilter { /* Revwalk from all the refs (git log --all) ... */ - Since = repo.Refs, + IncludeReachableFrom = repo.Refs, /* ... and stop when the parent is reached */ - Until = parentSha + ExcludeReachableFrom = parentSha }; var commits = repo.Commits.QueryBy(filter); diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs index edce94932..30c8b207f 100644 --- a/LibGit2Sharp.Tests/ConfigurationFixture.cs +++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; +using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -14,35 +16,6 @@ private static void AssertValueInLocalConfigFile(string repoPath, string regex) AssertValueInConfigFile(configFilePath, regex); } - private static string RetrieveGlobalConfigLocation() - { - string[] variables = { "HOME", "USERPROFILE", }; - - foreach (string variable in variables) - { - string potentialLocation = Environment.GetEnvironmentVariable(variable); - if (string.IsNullOrEmpty(potentialLocation)) - { - continue; - } - - string potentialPath = Path.Combine(potentialLocation, ".gitconfig"); - - if (File.Exists(potentialPath)) - { - return potentialPath; - } - } - - throw new InvalidOperationException("Unable to determine the location of '.gitconfig' file."); - } - - private static void AssertValueInGlobalConfigFile(string regex) - { - string configFilePath = RetrieveGlobalConfigLocation(); - AssertValueInConfigFile(configFilePath, regex); - } - [Fact] public void CanUnsetAnEntryFromTheLocalConfiguration() { @@ -170,7 +143,7 @@ public void CanReadStringValue() [Fact] public void CanEnumerateGlobalConfig() { - string configPath = CreateConfigurationWithDummyUser(Constants.Signature); + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; var path = SandboxStandardTestRepoGitDir(); @@ -217,14 +190,17 @@ public void CanFindInLocalConfig() Assert.NotNull(matches); Assert.Equal(new[] { "unittests.intsetting", "unittests.longsetting" }, - matches.Select(m => m.Key).ToArray()); + matches + .Select(m => m.Key) + .OrderBy(s => s) + .ToArray()); } } [Fact] public void CanFindInGlobalConfig() { - string configPath = CreateConfigurationWithDummyUser(Constants.Signature); + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; var path = SandboxStandardTestRepoGitDir(); @@ -253,7 +229,7 @@ public void CanSetBooleanValue() [Fact] public void SettingLocalConfigurationOutsideAReposThrows() { - using (var config = new Configuration()) + using (var config = Configuration.BuildFrom(null, null, null, null)) { Assert.Throws(() => config.Set("unittests.intsetting", 3)); } @@ -392,5 +368,190 @@ public void CanTellIfASpecificStoreContainsAKey() Assert.Null(repo.Config.Get("MCHammer.You-cant-touch-this", ConfigurationLevel.System)); } } + + public static IEnumerable ConfigAccessors + { + get + { + return new List + { + new[] { new Func(p => Path.Combine(p, ".git", "config")) }, + new[] { new Func(p => Path.Combine(p, ".git")) }, + new[] { new Func(p => p) }, + }; + } + } + + [Theory, PropertyData("ConfigAccessors")] + public void CanAccessConfigurationWithoutARepository(Func localConfigurationPathProvider) + { + var path = SandboxStandardTestRepoGitDir(); + + string globalConfigPath = CreateConfigurationWithDummyUser(Constants.Identity); + var options = new RepositoryOptions { GlobalConfigurationLocation = globalConfigPath }; + + using (var repo = new Repository(path, options)) + { + repo.Config.Set("my.key", "local"); + repo.Config.Set("my.key", "mouse", ConfigurationLevel.Global); + } + + using (var config = Configuration.BuildFrom(localConfigurationPathProvider(path), globalConfigPath)) + { + Assert.Equal("local", config.Get("my.key").Value); + Assert.Equal("mouse", config.Get("my.key", ConfigurationLevel.Global).Value); + } + } + + [Fact] + public void PassingANonExistingLocalConfigurationFileToBuildFromthrowss() + { + Assert.Throws(() => Configuration.BuildFrom( + Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()))); + } + + [Theory] + [InlineData(null, "x@example.com")] + [InlineData("", "x@example.com")] + [InlineData("X", null)] + [InlineData("X", "")] + public void CannotBuildAProperSignatureFromConfigWhenFullIdentityCannotBeFoundInTheConfig(string name, string email) + { + string repoPath = InitNewRepository(); + string configPath = CreateConfigurationWithDummyUser(name, email); + var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + + using (var repo = new Repository(repoPath, options)) + { + Assert.Equal(name, repo.Config.GetValueOrDefault("user.name")); + Assert.Equal(email, repo.Config.GetValueOrDefault("user.email")); + + Signature signature = repo.Config.BuildSignature(DateTimeOffset.Now); + + Assert.Null(signature); + } + } + + [Fact] + public void CanSetAndGetSearchPath() + { + string globalPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); + string systemPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); + string xdgPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); + + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, globalPath); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.System, systemPath); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Xdg, xdgPath); + + Assert.Equal(globalPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global).Single()); + Assert.Equal(systemPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.System).Single()); + Assert.Equal(xdgPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Xdg).Single()); + + // reset the search paths to their defaults + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.System, null); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Xdg, null); + } + + [Fact] + public void CanSetAndGetMultipleSearchPaths() + { + string[] paths = + { + Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()), + Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()), + Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()), + }; + + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, paths); + + Assert.Equal(paths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global)); + + // set back to the defaults + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); + } + + [Fact] + public void CanResetSearchPaths() + { + // all of these calls should reset the config path to the default + Action[] resetActions = + { + () => GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global), + () => GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null), + () => GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, string.Empty), + () => GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, new string[] { }), + }; + + // record the default search path + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); + var oldPaths = GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global); + Assert.NotNull(oldPaths); + + // generate a non-default path to set + var newPaths = new string[] { Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()) }; + + foreach (var tryToReset in resetActions) + { + // change to the non-default path + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, newPaths); + Assert.Equal(newPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global)); + + // set it back to the default + tryToReset(); + Assert.Equal(oldPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global)); + } + + // make sure the config paths are reset after the test ends + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); + } + + [Fact] + public void CanAppendToSearchPaths() + { + string appendMe = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); + var prevPaths = GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global); + + // append using the special name $PATH + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, "$PATH", appendMe); + + var currentPaths = GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global); + Assert.Equal(currentPaths, prevPaths.Concat(new[] { appendMe })); + + // set it back to the default + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); + } + + [Fact] + public void CanRedirectConfigAccess() + { + var scd1 = BuildSelfCleaningDirectory(); + var scd2 = BuildSelfCleaningDirectory(); + + Touch(scd1.RootedDirectoryPath, ".gitconfig"); + Touch(scd2.RootedDirectoryPath, ".gitconfig"); + + // redirect global access to the first path + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, scd1.RootedDirectoryPath); + + // set a value in the first config + using (var config = Configuration.BuildFrom(null)) + { + config.Set("luggage.code", 9876, ConfigurationLevel.Global); + Assert.Equal(9876, config.Get("luggage.code", ConfigurationLevel.Global).Value); + } + + // redirect global config access to path2 + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, scd2.RootedDirectoryPath); + + // if the redirect succeeds, the value set in the prior config should not be visible + using (var config = Configuration.BuildFrom(null)) + { + Assert.Equal(-1, config.GetValueOrDefault("luggage.code", ConfigurationLevel.Global, -1)); + } + + // reset the search path to the default + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); + } } } diff --git a/LibGit2Sharp.Tests/ConflictFixture.cs b/LibGit2Sharp.Tests/ConflictFixture.cs index af1663676..65a98659e 100644 --- a/LibGit2Sharp.Tests/ConflictFixture.cs +++ b/LibGit2Sharp.Tests/ConflictFixture.cs @@ -47,22 +47,22 @@ private static List RenameConflictData } [Theory] - [InlineData(true, "ancestor-and-ours.txt", true, false, FileStatus.Removed, 2)] - [InlineData(false, "ancestor-and-ours.txt", true, true, FileStatus.Removed |FileStatus.Untracked, 2)] + [InlineData(true, "ancestor-and-ours.txt", true, false, FileStatus.DeletedFromIndex, 2)] + [InlineData(false, "ancestor-and-ours.txt", true, true, FileStatus.DeletedFromIndex |FileStatus.NewInWorkdir, 2)] [InlineData(true, "ancestor-and-theirs.txt", true, false, FileStatus.Nonexistent, 2)] - [InlineData(false, "ancestor-and-theirs.txt", true, true, FileStatus.Untracked, 2)] + [InlineData(false, "ancestor-and-theirs.txt", true, true, FileStatus.NewInWorkdir, 2)] [InlineData(true, "ancestor-only.txt", false, false, FileStatus.Nonexistent, 1)] [InlineData(false, "ancestor-only.txt", false, false, FileStatus.Nonexistent, 1)] - [InlineData(true, "conflicts-one.txt", true, false, FileStatus.Removed, 3)] - [InlineData(false, "conflicts-one.txt", true, true, FileStatus.Removed | FileStatus.Untracked, 3)] - [InlineData(true, "conflicts-two.txt", true, false, FileStatus.Removed, 3)] - [InlineData(false, "conflicts-two.txt", true, true, FileStatus.Removed | FileStatus.Untracked, 3)] - [InlineData(true, "ours-and-theirs.txt", true, false, FileStatus.Removed, 2)] - [InlineData(false, "ours-and-theirs.txt", true, true, FileStatus.Removed | FileStatus.Untracked, 2)] - [InlineData(true, "ours-only.txt", true, false, FileStatus.Removed, 1)] - [InlineData(false, "ours-only.txt", true, true, FileStatus.Removed | FileStatus.Untracked, 1)] + [InlineData(true, "conflicts-one.txt", true, false, FileStatus.DeletedFromIndex, 3)] + [InlineData(false, "conflicts-one.txt", true, true, FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, 3)] + [InlineData(true, "conflicts-two.txt", true, false, FileStatus.DeletedFromIndex, 3)] + [InlineData(false, "conflicts-two.txt", true, true, FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, 3)] + [InlineData(true, "ours-and-theirs.txt", true, false, FileStatus.DeletedFromIndex, 2)] + [InlineData(false, "ours-and-theirs.txt", true, true, FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, 2)] + [InlineData(true, "ours-only.txt", true, false, FileStatus.DeletedFromIndex, 1)] + [InlineData(false, "ours-only.txt", true, true, FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, 1)] [InlineData(true, "theirs-only.txt", true, false, FileStatus.Nonexistent, 1)] - [InlineData(false, "theirs-only.txt", true, true, FileStatus.Untracked, 1)] + [InlineData(false, "theirs-only.txt", true, true, FileStatus.NewInWorkdir, 1)] public void CanResolveConflictsByRemovingFromTheIndex( bool removeFromWorkdir, string filename, bool existsBeforeRemove, bool existsAfterRemove, FileStatus lastStatus, int removedIndexEntries) { diff --git a/LibGit2Sharp.Tests/DescribeFixture.cs b/LibGit2Sharp.Tests/DescribeFixture.cs index 52e0ac8fc..cf4f033da 100644 --- a/LibGit2Sharp.Tests/DescribeFixture.cs +++ b/LibGit2Sharp.Tests/DescribeFixture.cs @@ -1,6 +1,7 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; +using System; namespace LibGit2Sharp.Tests { @@ -49,5 +50,35 @@ public void CanDescribeACommit() new DescribeOptions{ AlwaysRenderLongFormat = true })); } } + + [Fact] + public void CanFollowFirstParent() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var branch = repo.CreateBranch("branch"); + + // Make an earlier tag on master + repo.Commit("A", Constants.Signature, Constants.Signature, new CommitOptions { AllowEmptyCommit = true }); + repo.ApplyTag("firstParentTag"); + + // Make a later tag on branch + repo.Checkout(branch); + repo.Commit("B", Constants.Signature, Constants.Signature, new CommitOptions { AllowEmptyCommit = true }); + repo.ApplyTag("mostRecentTag"); + + repo.Checkout("master"); + repo.Commit("C", Constants.Signature, Constants.Signature, new CommitOptions { AllowEmptyCommit = true }); + repo.Merge(branch, Constants.Signature, new MergeOptions() { FastForwardStrategy = FastForwardStrategy.NoFastForward }); + + // With OnlyFollowFirstParent = false, the most recent tag reachable should be returned + Assert.Equal("mostRecentTag-3-gf17be71", repo.Describe(repo.Head.Tip, new DescribeOptions { OnlyFollowFirstParent = false, Strategy = DescribeStrategy.Tags })); + + // With OnlyFollowFirstParent = true, the most recent tag on the current branch should be returned + Assert.Equal("firstParentTag-2-gf17be71", repo.Describe(repo.Head.Tip, new DescribeOptions { OnlyFollowFirstParent = true, Strategy = DescribeStrategy.Tags })); + + } + } } } diff --git a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs index b5de35e57..da48dbc68 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs @@ -163,7 +163,7 @@ public void ShowcaseTheDifferenceBetweenTheTwoKindOfComparison() File.Move(fullpath + ".bak", fullpath); FileStatus state = repo.RetrieveStatus("file.txt"); - Assert.Equal(FileStatus.Removed | FileStatus.Untracked, state); + Assert.Equal(FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, state); var wrkDirToIdxToTree = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.Index | DiffTargets.WorkingDirectory); diff --git a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs index 99603b69e..bebbdded0 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs @@ -39,7 +39,7 @@ public void RetrievingANonExistentFileChangeReturnsNull() var changes = repo.Diff.Compare(tree, tree); - Assert.Null(changes["batman"]); + Assert.Equal(0, changes.Count(c => c.Path == "batman")); } } @@ -62,7 +62,7 @@ public void CanCompareACommitTreeAgainstItsParent() Assert.Equal(1, changes.Count()); Assert.Equal(1, changes.Added.Count()); - TreeEntryChanges treeEntryChanges = changes["1.txt"]; + TreeEntryChanges treeEntryChanges = changes.Single(c => c.Path == "1.txt"); var patch = repo.Diff.Compare(parentCommitTree, commitTree); Assert.False(patch["1.txt"].IsBinaryComparison); @@ -272,8 +272,7 @@ public void DetectsTheRenamingOfAModifiedFileByDefault() var changes = repo.Diff.Compare(rootCommitTree, commitTreeWithRenamedFile); Assert.Equal(1, changes.Count()); - Assert.Equal("super-file.txt", changes["super-file.txt"].Path); - Assert.Equal("my-name-does-not-feel-right.txt", changes["super-file.txt"].OldPath); + Assert.Equal("my-name-does-not-feel-right.txt", changes.Single(c => c.Path == "super-file.txt").OldPath); Assert.Equal(1, changes.Renamed.Count()); } } @@ -888,7 +887,7 @@ public void CanCompareTwoVersionsOfAFileWithADiffOfTwoHunks(int contextLines, in Assert.Equal(1, changes.Deleted.Count()); Assert.Equal(1, changes.Added.Count()); - Assert.Equal(Mode.Nonexistent, changes["my-name-does-not-feel-right.txt"].Mode); + Assert.Equal(Mode.Nonexistent, changes.Single(c => c.Path =="my-name-does-not-feel-right.txt").Mode); var patch = repo.Diff.Compare(rootCommitTree, mergedCommitTree, compareOptions: compareOptions); @@ -904,17 +903,16 @@ public void CanCompareTwoVersionsOfAFileWithADiffOfTwoHunks(int contextLines, in } } - [Fact] - public void CanHandleTwoTreeEntryChangesWithTheSamePath() + private void CanHandleTwoTreeEntryChangesWithTheSamePath(SimilarityOptions similarity, Action verifier) { string repoPath = InitNewRepository(); using (var repo = new Repository(repoPath)) { - Blob mainContent = OdbHelper.CreateBlob(repo, "awesome content\n"); + Blob mainContent = OdbHelper.CreateBlob(repo, "awesome content\n" + new string('b', 4096)); Blob linkContent = OdbHelper.CreateBlob(repo, "../../objc/Nu.h"); - string path = string.Format("include{0}Nu{0}Nu.h", Path.DirectorySeparatorChar); + string path = Path.Combine("include", "Nu", "Nu.h"); var tdOld = new TreeDefinition() .Add(path, linkContent, Mode.SymbolicLink) @@ -930,45 +928,61 @@ public void CanHandleTwoTreeEntryChangesWithTheSamePath() var changes = repo.Diff.Compare(treeOld, treeNew, compareOptions: new CompareOptions { - Similarity = SimilarityOptions.None, + Similarity = similarity, }); - /* - * $ git diff-tree -p 5c87b67 d5278d0 - * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h - * deleted file mode 120000 - * index 19bf568..0000000 - * --- a/include/Nu/Nu.h - * +++ /dev/null - * @@ -1 +0,0 @@ - * -../../objc/Nu.h - * \ No newline at end of file - * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h - * new file mode 100644 - * index 0000000..f9e6561 - * --- /dev/null - * +++ b/include/Nu/Nu.h - * @@ -0,0 +1 @@ - * +awesome content - * diff --git a/objc/Nu.h b/objc/Nu.h - * deleted file mode 100644 - * index f9e6561..0000000 - * --- a/objc/Nu.h - * +++ /dev/null - * @@ -1 +0,0 @@ - * -awesome content - */ + verifier(path, changes); + } + } - Assert.Equal(1, changes.Deleted.Count()); - Assert.Equal(0, changes.Modified.Count()); - Assert.Equal(1, changes.TypeChanged.Count()); + [Fact] + public void CanHandleTwoTreeEntryChangesWithTheSamePathUsingSimilarityNone() + { + // $ git diff-tree --name-status --no-renames -r 2ccccf8 e829333 + // T include/Nu/Nu.h + // D objc/Nu.h - TreeEntryChanges change = changes[path]; - Assert.Equal(Mode.SymbolicLink, change.OldMode); - Assert.Equal(Mode.NonExecutableFile, change.Mode); - Assert.Equal(ChangeKind.TypeChanged, change.Status); - Assert.Equal(path, change.Path); - } + CanHandleTwoTreeEntryChangesWithTheSamePath(SimilarityOptions.None, + (path, changes) => + { + Assert.Equal(2, changes.Count()); + Assert.Equal(1, changes.Deleted.Count()); + Assert.Equal(1, changes.TypeChanged.Count()); + + TreeEntryChanges change = changes.Single(c => c.Path== path); + Assert.Equal(Mode.SymbolicLink, change.OldMode); + Assert.Equal(Mode.NonExecutableFile, change.Mode); + Assert.Equal(ChangeKind.TypeChanged, change.Status); + Assert.Equal(path, change.Path); + }); + } + + [Fact] + public void CanHandleTwoTreeEntryChangesWithTheSamePathUsingSimilarityDefault() + { + // $ git diff-tree --name-status --find-renames -r 2ccccf8 e829333 + // T include/Nu/Nu.h + // D objc/Nu.h + + CanHandleTwoTreeEntryChangesWithTheSamePath(SimilarityOptions.Default, + (path, changes) => + { + Assert.Equal(2, changes.Count()); + Assert.Equal(1, changes.Deleted.Count()); + Assert.Equal(1, changes.Renamed.Count()); + + TreeEntryChanges renamed = changes.Renamed.Single(); + Assert.Equal(Mode.NonExecutableFile, renamed.OldMode); + Assert.Equal(Mode.NonExecutableFile, renamed.Mode); + Assert.Equal(ChangeKind.Renamed, renamed.Status); + Assert.Equal(path, renamed.Path); + + TreeEntryChanges deleted = changes.Deleted.Single(); + Assert.Equal(Mode.SymbolicLink, deleted.OldMode); + Assert.Equal(Mode.Nonexistent, deleted.Mode); + Assert.Equal(ChangeKind.Deleted, deleted.Status); + Assert.Equal(path, deleted.Path); + }); } [Fact] @@ -1104,19 +1118,78 @@ public void RetrievingDiffChangesMustAlwaysBeCaseSensitive() { var changes = repo.Diff.Compare(repo.Lookup(treeOldOid), repo.Lookup(treeNewOid)); - Assert.Equal(ChangeKind.Modified, changes["a.txt"].Status); - Assert.Equal(ChangeKind.Modified, changes["A.TXT"].Status); + Assert.Equal(ChangeKind.Modified, changes.Single(c => c.Path == "a.txt").Status); + Assert.Equal(ChangeKind.Modified, changes.Single(c => c.Path == "A.TXT").Status); } } [Fact] - public void CallingCompareWithAnUnsupportedGenericParamThrows() + public void UsingPatienceAlgorithmCompareOptionProducesPatienceDiff() { - var path = SandboxStandardTestRepoGitDir(); - using (var repo = new Repository(path)) + string repoPath = InitNewRepository(); + using (var repo = new Repository(repoPath)) { - Assert.Throws(() => repo.Diff.Compare(default(Tree), default(Tree))); - Assert.Throws(() => repo.Diff.Compare()); + Func fromString = + s => + repo.ObjectDatabase.CreateTree(new TreeDefinition().Add("file.txt", + OdbHelper.CreateBlob(repo, s), Mode.NonExecutableFile)); + + Tree treeOld = fromString(new StringBuilder() + .Append("aaaaaa\n") + .Append("aaaaaa\n") + .Append("bbbbbb\n") + .Append("bbbbbb\n") + .Append("cccccc\n") + .Append("cccccc\n") + .Append("abc\n").ToString()); + + Tree treeNew = fromString(new StringBuilder() + .Append("abc\n") + .Append("aaaaaa\n") + .Append("aaaaaa\n") + .Append("bbbbbb\n") + .Append("bbbbbb\n") + .Append("cccccc\n") + .Append("cccccc\n").ToString()); + + string diffDefault = new StringBuilder() + .Append("diff --git a/file.txt b/file.txt\n") + .Append("index 3299d68..accc3bd 100644\n") + .Append("--- a/file.txt\n") + .Append("+++ b/file.txt\n") + .Append("@@ -1,7 +1,7 @@\n") + .Append("+abc\n") + .Append(" aaaaaa\n") + .Append(" aaaaaa\n") + .Append(" bbbbbb\n") + .Append(" bbbbbb\n") + .Append(" cccccc\n") + .Append(" cccccc\n") + .Append("-abc\n").ToString(); + + string diffPatience = new StringBuilder() + .Append("diff --git a/file.txt b/file.txt\n") + .Append("index 3299d68..accc3bd 100644\n") + .Append("--- a/file.txt\n") + .Append("+++ b/file.txt\n") + .Append("@@ -1,7 +1,7 @@\n") + .Append("-aaaaaa\n") + .Append("-aaaaaa\n") + .Append("-bbbbbb\n") + .Append("-bbbbbb\n") + .Append("-cccccc\n") + .Append("-cccccc\n") + .Append(" abc\n") + .Append("+aaaaaa\n") + .Append("+aaaaaa\n") + .Append("+bbbbbb\n") + .Append("+bbbbbb\n") + .Append("+cccccc\n") + .Append("+cccccc\n").ToString(); + + Assert.Equal(diffDefault, repo.Diff.Compare(treeOld, treeNew)); + Assert.Equal(diffPatience, repo.Diff.Compare(treeOld, treeNew, + compareOptions: new CompareOptions { Algorithm = DiffAlgorithm.Patience })); } } } diff --git a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs index c16fd9882..65f075826 100644 --- a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs +++ b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs @@ -42,7 +42,7 @@ public void CanCompareTheWorkDirAgainstTheIndex() } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir)] [InlineData("really-i-cant-exist.txt", FileStatus.Nonexistent)] public void CanCompareTheWorkDirAgainstTheIndexWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus currentStatus) { @@ -60,7 +60,7 @@ public void CanCompareTheWorkDirAgainstTheIndexWithLaxUnmatchedExplicitPathsVali } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir)] [InlineData("really-i-cant-exist.txt", FileStatus.Nonexistent)] public void ComparingTheWorkDirAgainstTheIndexWithStrictUnmatchedExplicitPathsValidationAndANonExistentPathspecThrows(string relativePath, FileStatus currentStatus) { @@ -74,7 +74,7 @@ public void ComparingTheWorkDirAgainstTheIndexWithStrictUnmatchedExplicitPathsVa } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir)] [InlineData("where-am-I.txt", FileStatus.Nonexistent)] public void CallbackForUnmatchedExplicitPathsIsCalledWhenSet(string relativePath, FileStatus currentStatus) { diff --git a/LibGit2Sharp.Tests/FetchFixture.cs b/LibGit2Sharp.Tests/FetchFixture.cs index af69ace61..89a7e8a62 100644 --- a/LibGit2Sharp.Tests/FetchFixture.cs +++ b/LibGit2Sharp.Tests/FetchFixture.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -132,6 +134,18 @@ public void CanFetchCustomRefSpecsIntoAnEmptyRepository(string url, string local var expectedFetchState = new ExpectedFetchState(remoteName); expectedFetchState.AddExpectedBranch(localBranchName, ObjectId.Zero, remoteInfo.BranchTips[remoteBranchName]); + // Let's account for opportunistic updates during the Fetch() call + if (!string.Equals("master", localBranchName, StringComparison.OrdinalIgnoreCase)) + { + expectedFetchState.AddExpectedBranch("master", ObjectId.Zero, remoteInfo.BranchTips["master"]); + } + + if (string.Equals("master", localBranchName, StringComparison.OrdinalIgnoreCase) + && !string.Equals("master", remoteBranchName, StringComparison.OrdinalIgnoreCase)) + { + expectedFetchState.AddExpectedBranch(remoteBranchName, ObjectId.Zero, remoteInfo.BranchTips[remoteBranchName]); + } + // Perform the actual fetch repo.Network.Fetch(remote, new string[] { refSpec }, new FetchOptions { TagFetchMode = TagFetchMode.None, @@ -188,5 +202,44 @@ public void CanFetchAllTagsAfterAnInitialClone() repo.Fetch("origin", new FetchOptions { TagFetchMode = TagFetchMode.All }); } } + + [Fact] + public void FetchHonorsTheFetchPruneConfigurationEntry() + { + var source = SandboxBareTestRepo(); + var url = new Uri(Path.GetFullPath(source)).AbsoluteUri; + + var scd = BuildSelfCleaningDirectory(); + + string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + var options = BuildFakeConfigs(BuildSelfCleaningDirectory()); + + using (var clonedRepo = new Repository(clonedRepoPath, options)) + { + Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote)); + + // Drop one of the branches in the remote repository + using (var sourceRepo = new Repository(source)) + { + sourceRepo.Branches.Remove("packed-test"); + } + + // No pruning when the configuration entry isn't defined + Assert.Null(clonedRepo.Config.Get("fetch.prune")); + clonedRepo.Fetch("origin"); + Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote)); + + // No pruning when the configuration entry is set to false + clonedRepo.Config.Set("fetch.prune", false); + clonedRepo.Fetch("origin"); + Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote)); + + // Auto pruning when the configuration entry is set to true + clonedRepo.Config.Set("fetch.prune", true); + clonedRepo.Fetch("origin"); + Assert.Equal(4, clonedRepo.Branches.Count(b => b.IsRemote)); + } + } } } diff --git a/LibGit2Sharp.Tests/FileHistoryFixture.cs b/LibGit2Sharp.Tests/FileHistoryFixture.cs new file mode 100644 index 000000000..3d09858b3 --- /dev/null +++ b/LibGit2Sharp.Tests/FileHistoryFixture.cs @@ -0,0 +1,396 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class FileHistoryFixture : BaseFixture + { + [Theory] + [InlineData("https://github.com/nulltoken/follow-test.git")] + public void CanDealWithFollowTest(string url) + { + var scd = BuildSelfCleaningDirectory(); + var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + using (var repo = new Repository(clonedRepoPath)) + { + // $ git log --follow --format=oneline so-renamed.txt + // 88f91835062161febb46fb270ef4188f54c09767 Update not-yet-renamed.txt AND rename into so-renamed.txt + // ef7cb6a63e32595fffb092cb1ae9a32310e58850 Add not-yet-renamed.txt + var fileHistoryEntries = repo.Commits.QueryBy("so-renamed.txt").ToList(); + Assert.Equal(2, fileHistoryEntries.Count()); + Assert.Equal("88f91835062161febb46fb270ef4188f54c09767", fileHistoryEntries[0].Commit.Sha); + Assert.Equal("ef7cb6a63e32595fffb092cb1ae9a32310e58850", fileHistoryEntries[1].Commit.Sha); + + // $ git log --follow --format=oneline untouched.txt + // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit + fileHistoryEntries = repo.Commits.QueryBy("untouched.txt").ToList(); + Assert.Equal(1, fileHistoryEntries.Count()); + Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[0].Commit.Sha); + + // $ git log --follow --format=oneline under-test.txt + // 0b5b18f2feb917dee98df1210315b2b2b23c5bec Rename file renamed.txt into under-test.txt + // 49921d463420a892c9547a326632ef6a9ba3b225 Update file renamed.txt + // 70f636e8c64bbc2dfef3735a562bb7e195d8019f Rename file under-test.txt into renamed.txt + // d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d Updated file under test + // 9da10ef7e139c49604a12caa866aae141f38b861 Updated file under test + // 599a5d821fb2c0a25855b4233e26d475c2fbeb34 Updated file under test + // 678b086b44753000567aa64344aa0d8034fa0083 Updated file under test + // 8f7d9520f306771340a7c79faea019ad18e4fa1f Updated file under test + // bd5f8ee279924d33be8ccbde82e7f10b9d9ff237 Updated file under test + // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit + fileHistoryEntries = repo.Commits.QueryBy("under-test.txt").ToList(); + Assert.Equal(10, fileHistoryEntries.Count()); + Assert.Equal("0b5b18f2feb917dee98df1210315b2b2b23c5bec", fileHistoryEntries[0].Commit.Sha); + Assert.Equal("49921d463420a892c9547a326632ef6a9ba3b225", fileHistoryEntries[1].Commit.Sha); + Assert.Equal("70f636e8c64bbc2dfef3735a562bb7e195d8019f", fileHistoryEntries[2].Commit.Sha); + Assert.Equal("d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d", fileHistoryEntries[3].Commit.Sha); + Assert.Equal("9da10ef7e139c49604a12caa866aae141f38b861", fileHistoryEntries[4].Commit.Sha); + Assert.Equal("599a5d821fb2c0a25855b4233e26d475c2fbeb34", fileHistoryEntries[5].Commit.Sha); + Assert.Equal("678b086b44753000567aa64344aa0d8034fa0083", fileHistoryEntries[6].Commit.Sha); + Assert.Equal("8f7d9520f306771340a7c79faea019ad18e4fa1f", fileHistoryEntries[7].Commit.Sha); + Assert.Equal("bd5f8ee279924d33be8ccbde82e7f10b9d9ff237", fileHistoryEntries[8].Commit.Sha); + Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[9].Commit.Sha); + } + } + + [Theory] + [InlineData(null)] + public void CanFollowBranches(string specificRepoPath) + { + var repoPath = specificRepoPath ?? CreateEmptyRepository(); + var path = "Test.txt"; + + var dummy = new string('a', 1024); + + using (var repo = new Repository(repoPath)) + { + var master0 = AddCommitToOdb(repo, "0. Initial commit for this test", path, "Before merge", dummy); + var fix1 = AddCommitToOdb(repo, "1. Changed on fix", path, "Change on fix branch", dummy, master0); + var master2 = AddCommitToOdb(repo, "2. Changed on master", path, "Independent change on master branch", + dummy, master0); + + path = "New" + path; + + var fix3 = AddCommitToOdb(repo, "3. Changed and renamed on fix", path, "Another change on fix branch", + dummy, fix1); + var master4 = AddCommitToOdb(repo, "4. Changed and renamed on master", path, + "Another independent change on master branch", dummy, master2); + var master5 = AddCommitToOdb(repo, "5. Merged fix into master", path, + "Manual resolution of merge conflict", dummy, master4, fix3); + var master6 = AddCommitToOdb(repo, "6. Changed on master", path, "Change after merge", dummy, master5); + var nextfix7 = AddCommitToOdb(repo, "7. Changed on next-fix", path, "Change on next-fix branch", dummy, + master6); + var master8 = AddCommitToOdb(repo, "8. Changed on master", path, + "Some arbitrary change on master branch", dummy, master6); + var master9 = AddCommitToOdb(repo, "9. Merged next-fix into master", path, + "Another manual resolution of merge conflict", dummy, master8, nextfix7); + var master10 = AddCommitToOdb(repo, "10. Changed on master", path, "A change on master after merging", + dummy, master9); + + repo.CreateBranch("master", master10); + repo.Checkout("master", new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force }); + + // Test --date-order. + var timeHistory = repo.Commits.QueryBy(path, + new FollowFilter { SortBy = CommitSortStrategies.Time }); + var timeCommits = new List + { + master10, // master + + master8, // master + nextfix7, // next-fix + master6, // master + + master4, // master + fix3, // fix + master2, // master + fix1, // fix + master0 // master (initial commit) + }; + Assert.Equal(timeCommits, timeHistory.Select(e => e.Commit)); + + // Test --topo-order. + var topoHistory = repo.Commits.QueryBy(path, + new FollowFilter { SortBy = CommitSortStrategies.Topological }); + var topoCommits = new List + { + master10, // master + + nextfix7, // next-fix + master8, // master + master6, // master + + fix3, // fix + fix1, // fix + master4, // master + master2, // master + master0 // master (initial commit) + }; + Assert.Equal(topoCommits, topoHistory.Select(e => e.Commit)); + } + } + + [Fact] + public void CanTellComplexCommitHistory() + { + var repoPath = CreateEmptyRepository(); + const string path1 = "Test1.txt"; + const string path2 = "Test2.txt"; + + using (var repo = new Repository(repoPath)) + { + // Make initial changes. + var commit1 = MakeAndCommitChange(repo, repoPath, path1, "Hello World"); + MakeAndCommitChange(repo, repoPath, path2, "Second file's contents"); + var commit2 = MakeAndCommitChange(repo, repoPath, path1, "Hello World again"); + + // Move the first file to a new directory. + var newPath1 = Path.Combine(SubFolderPath1, path1); + repo.Move(path1, newPath1); + var commit3 = repo.Commit("Moved " + path1 + " to " + newPath1, + Constants.Signature, Constants.Signature); + + // Make further changes. + MakeAndCommitChange(repo, repoPath, path2, "Changed second file's contents"); + var commit4 = MakeAndCommitChange(repo, repoPath, newPath1, "I have done it again!"); + + // Perform tests. + var fileHistoryEntries = repo.Commits.QueryBy(newPath1).ToList(); + var changedBlobs = fileHistoryEntries.Blobs().Distinct().ToList(); + + Assert.Equal(4, fileHistoryEntries.Count()); + Assert.Equal(3, changedBlobs.Count()); + + Assert.Equal(2, fileHistoryEntries.Count(e => e.Path == newPath1)); + Assert.Equal(2, fileHistoryEntries.Count(e => e.Path == path1)); + + Assert.Equal(commit4, fileHistoryEntries[0].Commit); + Assert.Equal(commit3, fileHistoryEntries[1].Commit); + Assert.Equal(commit2, fileHistoryEntries[2].Commit); + Assert.Equal(commit1, fileHistoryEntries[3].Commit); + + Assert.Equal(commit4.Tree[newPath1].Target, changedBlobs[0]); + Assert.Equal(commit2.Tree[path1].Target, changedBlobs[1]); + Assert.Equal(commit1.Tree[path1].Target, changedBlobs[2]); + } + } + + [Fact] + public void CanTellSimpleCommitHistory() + { + var repoPath = CreateEmptyRepository(); + const string path1 = "Test1.txt"; + const string path2 = "Test2.txt"; + + using (var repo = new Repository(repoPath)) + { + // Set up repository. + var commit1 = MakeAndCommitChange(repo, repoPath, path1, "Hello World"); + MakeAndCommitChange(repo, repoPath, path2, "Second file's contents"); + var commit3 = MakeAndCommitChange(repo, repoPath, path1, "Hello World again"); + + // Perform tests. + IEnumerable history = repo.Commits.QueryBy(path1).ToList(); + var changedBlobs = history.Blobs().Distinct(); + + Assert.Equal(2, history.Count()); + Assert.Equal(2, changedBlobs.Count()); + + Assert.Equal(commit3, history.ElementAt(0).Commit); + Assert.Equal(commit1, history.ElementAt(1).Commit); + } + } + + [Fact] + public void CanTellSingleCommitHistory() + { + var repoPath = CreateEmptyRepository(); + + using (var repo = new Repository(repoPath)) + { + // Set up repository. + const string path = "Test.txt"; + var commit = MakeAndCommitChange(repo, repoPath, path, "Hello World"); + + // Perform tests. + IEnumerable history = repo.Commits.QueryBy(path).ToList(); + var changedBlobs = history.Blobs().Distinct(); + + Assert.Equal(1, history.Count()); + Assert.Equal(1, changedBlobs.Count()); + + Assert.Equal(path, history.First().Path); + Assert.Equal(commit, history.First().Commit); + } + } + + [Fact] + public void EmptyRepositoryHasNoHistory() + { + var repoPath = CreateEmptyRepository(); + + using (var repo = new Repository(repoPath)) + { + IEnumerable history = repo.Commits.QueryBy("Test.txt").ToList(); + Assert.Equal(0, history.Count()); + Assert.Equal(0, history.Blobs().Count()); + } + } + + [Fact] + public void UnsupportedSortStrategyThrows() + { + var repoPath = CreateEmptyRepository(); + + using (var repo = new Repository(repoPath)) + { + // Set up repository. + const string path = "Test.txt"; + MakeAndCommitChange(repo, repoPath, path, "Hello World"); + + Assert.Throws(() => + repo.Commits.QueryBy(path, new FollowFilter + { + SortBy = CommitSortStrategies.None + })); + + Assert.Throws(() => + repo.Commits.QueryBy(path, new FollowFilter + { + SortBy = CommitSortStrategies.Reverse + })); + + Assert.Throws(() => + repo.Commits.QueryBy(path, new FollowFilter + { + SortBy = CommitSortStrategies.Reverse | + CommitSortStrategies.Topological + })); + + Assert.Throws(() => + repo.Commits.QueryBy(path, new FollowFilter + { + SortBy = CommitSortStrategies.Reverse | + CommitSortStrategies.Time + })); + + Assert.Throws(() => + repo.Commits.QueryBy(path, new FollowFilter + { + SortBy = CommitSortStrategies.Reverse | + CommitSortStrategies.Topological | + CommitSortStrategies.Time + })); + } + } + + #region Helpers + + private Signature _signature = Constants.Signature; + private const string SubFolderPath1 = "SubFolder1"; + + private Signature GetNextSignature() + { + _signature = _signature.TimeShift(TimeSpan.FromMinutes(1)); + return _signature; + } + + private string CreateEmptyRepository() + { + // Create a new empty directory with subfolders. + var scd = BuildSelfCleaningDirectory(); + Directory.CreateDirectory(Path.Combine(scd.DirectoryPath, SubFolderPath1)); + + // Initialize a GIT repository in that directory. + Repository.Init(scd.DirectoryPath); + using (var repo = new Repository(scd.DirectoryPath)) + { + repo.Config.Set("user.name", _signature.Name); + repo.Config.Set("user.email", _signature.Email); + } + + // Done. + return scd.DirectoryPath; + } + + /// + /// Adds a commit to the object database. The tree will have a single text file with the given specific content. + /// + /// The repository. + /// The commit message. + /// The file's path. + /// The file's content. + /// The commit's parents. + /// The commit added to the object database. + private Commit AddCommitToOdb(Repository repo, string message, string path, string specificContent, + params Commit[] parents) + { + return AddCommitToOdb(repo, message, path, specificContent, null, parents); + } + + /// + /// Adds a commit to the object database. The tree will have a single text file with the given specific content + /// at the beginning of the file and the given common content at the end of the file. + /// + /// The repository. + /// The commit message. + /// The file's path. + /// The content specific to that file. + /// The content shared with other files. + /// The commit's parents. + /// The commit added to the object database. + private Commit AddCommitToOdb(Repository repo, string message, string path, string specificContent, + string commonContent, params Commit[] parents) + { + var content = string.IsNullOrEmpty(commonContent) + ? specificContent + : specificContent + Environment.NewLine + commonContent + Environment.NewLine; + + var td = new TreeDefinition(); + td.Add(path, OdbHelper.CreateBlob(repo, content), Mode.NonExecutableFile); + var t = repo.ObjectDatabase.CreateTree(td); + + var commitSignature = GetNextSignature(); + + return repo.ObjectDatabase.CreateCommit(commitSignature, commitSignature, message, t, parents, true); + } + + private Commit MakeAndCommitChange(Repository repo, string repoPath, string path, string text, + string message = null) + { + Touch(repoPath, path, text); + repo.Stage(path); + + var commitSignature = GetNextSignature(); + return repo.Commit(message ?? "Changed " + path, commitSignature, commitSignature); + } + + #endregion + } + + /// + /// Defines extensions used by . + /// + internal static class FileHistoryFixtureExtensions + { + /// + /// Gets the instances contained in each . + /// + /// + /// Use the extension method + /// to retrieve the changed blobs. + /// + /// The file history. + /// The collection of instances included in the file history. + public static IEnumerable Blobs(this IEnumerable fileHistory) + { + return fileHistory.Select(entry => entry.Commit.Tree[entry.Path].Target).OfType(); + } + } +} diff --git a/LibGit2Sharp.Tests/FilterBranchFixture.cs b/LibGit2Sharp.Tests/FilterBranchFixture.cs index dfe14329e..d71cb22d8 100644 --- a/LibGit2Sharp.Tests/FilterBranchFixture.cs +++ b/LibGit2Sharp.Tests/FilterBranchFixture.cs @@ -29,7 +29,7 @@ public override void Dispose() public void CanRewriteHistoryWithoutChangingCommitMetadata() { var originalRefs = repo.Refs.ToList().OrderBy(r => r.CanonicalName); - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray(); // Noop header rewriter repo.Refs.RewriteHistory(new RewriteHistoryOptions @@ -42,14 +42,14 @@ public void CanRewriteHistoryWithoutChangingCommitMetadata() AssertSucceedingButNotError(); Assert.Equal(originalRefs, repo.Refs.ToList().OrderBy(r => r.CanonicalName)); - Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray()); + Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray()); } [Fact] public void CanRewriteHistoryWithoutChangingTrees() { var originalRefs = repo.Refs.ToList().OrderBy(r => r.CanonicalName); - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray(); // Noop tree rewriter repo.Refs.RewriteHistory(new RewriteHistoryOptions @@ -62,14 +62,14 @@ public void CanRewriteHistoryWithoutChangingTrees() AssertSucceedingButNotError(); Assert.Equal(originalRefs, repo.Refs.ToList().OrderBy(r => r.CanonicalName)); - Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray()); + Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray()); } [Fact] public void CanRollbackRewriteByThrowingInOnCompleting() { var originalRefs = repo.Refs.ToList().OrderBy(r => r.CanonicalName); - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray(); Assert.Throws( () => @@ -90,14 +90,14 @@ public void CanRollbackRewriteByThrowingInOnCompleting() AssertSucceedingButNotError(); Assert.Equal(originalRefs, repo.Refs.ToList().OrderBy(r => r.CanonicalName)); - Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray()); + Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray()); } [Fact] public void ErrorThrownInOnErrorTakesPrecedenceOverErrorDuringCommitHeaderRewriter() { var originalRefs = repo.Refs.ToList().OrderBy(r => r.CanonicalName); - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray(); var thrown = Assert.Throws( () => @@ -117,14 +117,14 @@ public void ErrorThrownInOnErrorTakesPrecedenceOverErrorDuringCommitHeaderRewrit Assert.Equal("From CommitHeaderRewriter", thrown.InnerException.Message); Assert.Equal(originalRefs, repo.Refs.ToList().OrderBy(r => r.CanonicalName)); - Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray()); + Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray()); } [Fact] public void ErrorThrownInOnErrorTakesPrecedenceOverErrorDuringCommitTreeRewriter() { var originalRefs = repo.Refs.ToList().OrderBy(r => r.CanonicalName); - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray(); var thrown = Assert.Throws( () => @@ -144,13 +144,13 @@ public void ErrorThrownInOnErrorTakesPrecedenceOverErrorDuringCommitTreeRewriter Assert.Equal("From CommitTreeRewriter", thrown.InnerException.Message); Assert.Equal(originalRefs, repo.Refs.ToList().OrderBy(r => r.CanonicalName)); - Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray()); + Assert.Equal(commits, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray()); } [Fact] public void CanRewriteAuthorOfCommits() { - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray(); repo.Refs.RewriteHistory(new RewriteHistoryOptions { OnError = OnError, @@ -162,8 +162,9 @@ public void CanRewriteAuthorOfCommits() AssertSucceedingButNotError(); - var nonBackedUpRefs = repo.Refs.Where(x => !x.CanonicalName.StartsWith("refs/original")); - Assert.Empty(repo.Commits.QueryBy(new CommitFilter { Since = nonBackedUpRefs }) + var nonBackedUpRefs = repo.Refs.Where( + x => !x.CanonicalName.StartsWith("refs/original/") && !x.CanonicalName.StartsWith("refs/notes/")); + Assert.Empty(repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = nonBackedUpRefs }) .Where(c => c.Author.Name != "Ben Straub")); } @@ -190,10 +191,10 @@ public void CanRewriteAuthorOfCommitsOnlyBeingPointedAtByTags() AssertSucceedingButNotError(); var lightweightTag = repo.Tags["so-lonely"]; - Assert.Equal("Bam!\n", ((Commit)lightweightTag.Target).Message); + Assert.Equal("Bam!", ((Commit)lightweightTag.Target).Message); var annotatedTag = repo.Tags["so-lonely-but-annotated"]; - Assert.Equal("Bam!\n", ((Commit)annotatedTag.Target).Message); + Assert.Equal("Bam!", ((Commit)annotatedTag.Target).Message); } [Fact] @@ -216,7 +217,7 @@ public void CanRewriteTrees() [Fact] public void CanRewriteTreesByInjectingTreeEntry() { - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Branches }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Branches }).ToArray(); var currentReadme = repo.Head["README"]; @@ -235,7 +236,7 @@ public void CanRewriteTreesByInjectingTreeEntry() Assert.Equal(new Commit[0], repo.Commits - .QueryBy(new CommitFilter {Since = repo.Branches}) + .QueryBy(new CommitFilter {IncludeReachableFrom = repo.Branches}) .Where(c => c["README"] != null && c["README"].Target.Id != currentReadme.Target.Id) .ToArray()); @@ -494,7 +495,7 @@ public void DoesNotRewriteRefsThatDontChange() var parents = repo.Branches["br2"].Tip.Parents.ToList(); Assert.Equal(2, parents.Count()); Assert.NotEmpty(parents.Where(c => c.Sha.StartsWith("9fd738e"))); - Assert.Equal("abc\n", parents.Single(c => !c.Sha.StartsWith("9fd738e")).Message); + Assert.Equal("abc", parents.Single(c => !c.Sha.StartsWith("9fd738e")).Message); } [Fact] @@ -529,7 +530,7 @@ public void CanNotOverWriteBackedUpReferences() AssertErrorFired(ex); AssertSucceedingNotFired(); - Assert.Equal("abc\n", repo.Head.Tip.Message); + Assert.Equal("abc", repo.Head.Tip.Message); var newOriginalRefs = repo.Refs.FromGlob("refs/original/*").OrderBy(r => r.CanonicalName).ToArray(); Assert.Equal(originalRefs, newOriginalRefs); @@ -540,7 +541,7 @@ public void CanNotOverWriteBackedUpReferences() [Fact] public void CanNotOverWriteAnExistingReference() { - var commits = repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }).ToArray(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs }).ToArray(); var ex = Assert.Throws( () => @@ -699,7 +700,7 @@ public void CanProvideNewNamesForTags() CommitHeaderRewriter = c => CommitRewriteInfo.From(c, message: ""), TagNameRewriter = TagNameRewriter, - }, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs["refs/heads/test"] })); + }, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs["refs/heads/test"] })); AssertSucceedingButNotError(); @@ -790,7 +791,7 @@ public void HandlesNameRewritingOfChainedTags() var newCommit = newAnnotationC.Target as Commit; Assert.NotNull(newCommit); Assert.NotEqual(newCommit, theCommit); - Assert.Equal("Rewrote\n", newCommit.Message); + Assert.Equal("Rewrote", newCommit.Message); // Ensure the original tag doesn't exist anymore Assert.Null(repo.Tags["lightweightA"]); @@ -801,6 +802,26 @@ public void HandlesNameRewritingOfChainedTags() Assert.Equal(annotationB, backedUpTag.ResolveToDirectReference().Target); } + [Fact] + public void RewritingNotesHasNoEffect() + { + var notesRefsRetriever = new Func>(() => repo.Refs.Where(r => r.CanonicalName.StartsWith("refs/notes/"))); + var originalNotesRefs = notesRefsRetriever().ToList(); + var commits = repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = originalNotesRefs }).ToArray(); + + repo.Refs.RewriteHistory(new RewriteHistoryOptions + { + OnError = OnError, + OnSucceeding = OnSucceeding, + CommitHeaderRewriter = + c => CommitRewriteInfo.From(c, author: Constants.Signature), + }, commits); + + AssertSucceedingButNotError(); + + Assert.Equal(originalNotesRefs.OrderBy(r => r.CanonicalName), notesRefsRetriever().OrderBy(r => r.CanonicalName)); + } + private static string TagNameRewriter(string name, bool isAnnotated, string target) { const string tagPrefix = "refs/tags/"; diff --git a/LibGit2Sharp.Tests/FilterFixture.cs b/LibGit2Sharp.Tests/FilterFixture.cs new file mode 100644 index 000000000..0afc9f702 --- /dev/null +++ b/LibGit2Sharp.Tests/FilterFixture.cs @@ -0,0 +1,492 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using System.Threading.Tasks; + +namespace LibGit2Sharp.Tests +{ + public class FilterFixture : BaseFixture + { + readonly Action successCallback = (reader, writer) => + { + reader.CopyTo(writer); + }; + + private const string FilterName = "the-filter"; + readonly List attributes = new List { new FilterAttributeEntry("test") }; + + [Fact] + public void CanRegisterFilterWithSingleAttribute() + { + var filter = new EmptyFilter(FilterName, attributes); + Assert.Equal(attributes, filter.Attributes); + } + + [Fact] + public void CanRegisterAndUnregisterTheSameFilter() + { + var filter = new EmptyFilter(FilterName, attributes); + + var registration = GlobalSettings.RegisterFilter(filter); + GlobalSettings.DeregisterFilter(registration); + + var secondRegistration = GlobalSettings.RegisterFilter(filter); + GlobalSettings.DeregisterFilter(secondRegistration); + } + + [Fact] + public void CanRegisterAndDeregisterAfterGarbageCollection() + { + var filterRegistration = GlobalSettings.RegisterFilter(new EmptyFilter(FilterName, attributes)); + + GC.Collect(); + + GlobalSettings.DeregisterFilter(filterRegistration); + } + + [Fact] + public void SameFilterIsEqual() + { + var filter = new EmptyFilter(FilterName, attributes); + Assert.Equal(filter, filter); + } + + [Fact] + public void InitCallbackNotMadeWhenFilterNeverUsed() + { + bool called = false; + Action initializeCallback = () => + { + called = true; + }; + + var filter = new FakeFilter(FilterName, + attributes, + successCallback, + successCallback, + initializeCallback); + var registration = GlobalSettings.RegisterFilter(filter); + + try + { + Assert.False(called); + } + finally + { + GlobalSettings.DeregisterFilter(registration); + } + } + + [Fact] + public void InitCallbackMadeWhenUsingTheFilter() + { + bool called = false; + Action initializeCallback = () => + { + called = true; + }; + + var filter = new FakeFilter(FilterName, + attributes, + successCallback, + successCallback, + initializeCallback); + var registration = GlobalSettings.RegisterFilter(filter); + + try + { + Assert.False(called); + + string repoPath = InitNewRepository(); + using (var repo = CreateTestRepository(repoPath)) + { + StageNewFile(repo); + Assert.True(called); + } + } + finally + { + GlobalSettings.DeregisterFilter(registration); + } + } + + [Fact] + public void WhenStagingFileApplyIsCalledWithCleanForCorrectPath() + { + string repoPath = InitNewRepository(); + bool called = false; + + Action clean = (reader, writer) => + { + called = true; + reader.CopyTo(writer); + }; + + var filter = new FakeFilter(FilterName, attributes, clean); + var registration = GlobalSettings.RegisterFilter(filter); + + try + { + using (var repo = CreateTestRepository(repoPath)) + { + StageNewFile(repo); + Assert.True(called); + } + } + finally + { + GlobalSettings.DeregisterFilter(registration); + } + } + + [Fact] + public void CleanFilterWritesOutputToObjectTree() + { + const string decodedInput = "This is a substitution cipher"; + const string encodedInput = "Guvf vf n fhofgvghgvba pvcure"; + + string repoPath = InitNewRepository(); + + Action cleanCallback = SubstitutionCipherFilter.RotateByThirteenPlaces; + + var filter = new FakeFilter(FilterName, attributes, cleanCallback); + var registration = GlobalSettings.RegisterFilter(filter); + + try + { + using (var repo = CreateTestRepository(repoPath)) + { + FileInfo expectedFile = StageNewFile(repo, decodedInput); + var commit = repo.Commit("Clean that file", Constants.Signature, Constants.Signature); + var blob = (Blob)commit.Tree[expectedFile.Name].Target; + + var textDetected = blob.GetContentText(); + Assert.Equal(encodedInput, textDetected); + } + } + finally + { + GlobalSettings.DeregisterFilter(registration); + } + } + + [Fact] + public void CanHandleMultipleSmudgesConcurrently() + { + const string decodedInput = "This is a substitution cipher"; + const string encodedInput = "Guvf vf n fhofgvghgvba pvcure"; + + const string branchName = "branch"; + + Action smudgeCallback = SubstitutionCipherFilter.RotateByThirteenPlaces; + + var filter = new FakeFilter(FilterName, attributes, null, smudgeCallback); + var registration = GlobalSettings.RegisterFilter(filter); + + try + { + int count = 30; + var tasks = new Task[count]; + + for (int i = 0; i < count; i++) + { + tasks[i] = Task.Factory.StartNew(() => + { + string repoPath = InitNewRepository(); + return CheckoutFileForSmudge(repoPath, branchName, encodedInput); + }); + } + + Task.WaitAll(tasks); + + foreach(var task in tasks) + { + FileInfo expectedFile = task.Result; + + string readAllText = File.ReadAllText(expectedFile.FullName); + Assert.Equal(decodedInput, readAllText); + } + } + finally + { + GlobalSettings.DeregisterFilter(registration); + } + } + + [Fact] + public void WhenCheckingOutAFileFileSmudgeWritesCorrectFileToWorkingDirectory() + { + const string decodedInput = "This is a substitution cipher"; + const string encodedInput = "Guvf vf n fhofgvghgvba pvcure"; + + const string branchName = "branch"; + string repoPath = InitNewRepository(); + + Action smudgeCallback = SubstitutionCipherFilter.RotateByThirteenPlaces; + + var filter = new FakeFilter(FilterName, attributes, null, smudgeCallback); + var registration = GlobalSettings.RegisterFilter(filter); + + try + { + FileInfo expectedFile = CheckoutFileForSmudge(repoPath, branchName, encodedInput); + + string combine = Path.Combine(repoPath, "..", expectedFile.Name); + string readAllText = File.ReadAllText(combine); + Assert.Equal(decodedInput, readAllText); + } + finally + { + GlobalSettings.DeregisterFilter(registration); + } + } + + [Fact] + public void CanFilterLargeFiles() + { + const int ContentLength = 128 * 1024 * 1024 - 13; + const char ContentValue = 'x'; + + char[] content = (new string(ContentValue, 1024)).ToCharArray(); + + string repoPath = InitNewRepository(); + + var filter = new FileExportFilter(FilterName, attributes); + var registration = GlobalSettings.RegisterFilter(filter); + + try + { + string filePath = Path.Combine(Directory.GetParent(repoPath).Parent.FullName, Guid.NewGuid().ToString() + ".blob"); + FileInfo contentFile = new FileInfo(filePath); + using (var writer = new StreamWriter(contentFile.OpenWrite()) { AutoFlush = true }) + { + int remain = ContentLength; + + while (remain > 0) + { + int chunkSize = remain > content.Length ? content.Length : remain; + writer.Write(content, 0, chunkSize); + remain -= chunkSize; + } + } + + string attributesPath = Path.Combine(Directory.GetParent(repoPath).Parent.FullName, ".gitattributes"); + FileInfo attributesFile = new FileInfo(attributesPath); + + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); + var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + + using (Repository repo = new Repository(repoPath, repositoryOptions)) + { + File.WriteAllText(attributesPath, "*.blob filter=test"); + repo.Stage(attributesFile.Name); + repo.Stage(contentFile.Name); + repo.Commit("test", Constants.Signature, Constants.Signature); + contentFile.Delete(); + repo.Checkout("HEAD", new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force }); + } + + contentFile = new FileInfo(filePath); + Assert.True(contentFile.Exists, "Contents not restored correctly by forced checkout."); + using (StreamReader reader = contentFile.OpenText()) + { + int totalRead = 0; + char[] block = new char[1024]; + int read; + while ((read = reader.Read(block, 0, block.Length)) > 0) + { + Assert.True(CharArrayAreEqual(block, content, read)); + totalRead += read; + } + + Assert.Equal(ContentLength, totalRead); + } + + contentFile.Delete(); + } + finally + { + GlobalSettings.DeregisterFilter(registration); + } + } + + [Fact] + public void DoubleRegistrationFailsButDoubleDeregistrationDoesNot() + { + Assert.Equal(0, GlobalSettings.GetRegisteredFilters().Count()); + + var filter = new EmptyFilter(FilterName, attributes); + var registration = GlobalSettings.RegisterFilter(filter); + + Assert.Throws(() => { GlobalSettings.RegisterFilter(filter); }); + Assert.Equal(1, GlobalSettings.GetRegisteredFilters().Count()); + + Assert.True(registration.IsValid, "FilterRegistration.IsValid should be true."); + + GlobalSettings.DeregisterFilter(registration); + Assert.Equal(0, GlobalSettings.GetRegisteredFilters().Count()); + + Assert.False(registration.IsValid, "FilterRegistration.IsValid should be false."); + + GlobalSettings.DeregisterFilter(registration); + Assert.Equal(0, GlobalSettings.GetRegisteredFilters().Count()); + + Assert.False(registration.IsValid, "FilterRegistration.IsValid should be false."); + } + + private unsafe bool CharArrayAreEqual(char[] array1, char[] array2, int count) + { + if (ReferenceEquals(array1, array2)) + { + return true; + } + if (ReferenceEquals(array1, null) || ReferenceEquals(null, array2)) + { + return false; + } + if (array1.Length < count || array2.Length < count) + { + return false; + } + + int len = count * sizeof(char); + int cnt = len / sizeof(long); + + fixed (char* c1 = array1, c2 = array2) + { + long* p1 = (long*)c1, + p2 = (long*)c2; + + for (int i = 0; i < cnt; i++) + { + if (p1[i] != p2[i]) + { + return false; + } + } + + byte* b1 = (byte*)c1, + b2 = (byte*)c2; + + for (int i = len * sizeof(long); i < len; i++) + { + if (b1[i] != b2[i]) + { + return false; + } + } + } + + return true; + } + + private FileInfo CheckoutFileForSmudge(string repoPath, string branchName, string content) + { + FileInfo expectedPath; + using (var repo = CreateTestRepository(repoPath)) + { + StageNewFile(repo, content); + + repo.Commit("Initial commit", Constants.Signature, Constants.Signature); + + expectedPath = CommitFileOnBranch(repo, branchName, content); + + repo.Checkout("master"); + + repo.Checkout(branchName); + } + return expectedPath; + } + + private static FileInfo CommitFileOnBranch(Repository repo, string branchName, String content) + { + var branch = repo.CreateBranch(branchName); + repo.Checkout(branch.FriendlyName); + + FileInfo expectedPath = StageNewFile(repo, content); + repo.Commit("Commit", Constants.Signature, Constants.Signature); + return expectedPath; + } + + private static FileInfo StageNewFile(IRepository repo, string contents = "null") + { + string newFilePath = Touch(repo.Info.WorkingDirectory, Guid.NewGuid() + ".txt", contents); + var stageNewFile = new FileInfo(newFilePath); + repo.Stage(newFilePath); + return stageNewFile; + } + + private Repository CreateTestRepository(string path) + { + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); + var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + var repository = new Repository(path, repositoryOptions); + CreateAttributesFile(repository, "* filter=test"); + return repository; + } + + class EmptyFilter : Filter + { + public EmptyFilter(string name, IEnumerable attributes) + : base(name, attributes) + { } + } + + class FakeFilter : Filter + { + private readonly Action cleanCallback; + private readonly Action smudgeCallback; + private readonly Action initCallback; + + public FakeFilter(string name, IEnumerable attributes, + Action cleanCallback = null, + Action smudgeCallback = null, + Action initCallback = null) + : base(name, attributes) + { + this.cleanCallback = cleanCallback; + this.smudgeCallback = smudgeCallback; + this.initCallback = initCallback; + } + + protected override void Clean(string path, string root, Stream input, Stream output) + { + if (cleanCallback == null) + { + base.Clean(path, root, input, output); + } + else + { + cleanCallback(input, output); + } + } + + protected override void Smudge(string path, string root, Stream input, Stream output) + { + if (smudgeCallback == null) + { + base.Smudge(path, root, input, output); + } + else + { + smudgeCallback(input, output); + } + } + + protected override void Initialize() + { + if (initCallback == null) + { + base.Initialize(); + } + else + { + initCallback(); + } + } + } + } +} diff --git a/LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs b/LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs new file mode 100644 index 000000000..a57197626 --- /dev/null +++ b/LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.IO; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class FilterSubstitutionCipherFixture : BaseFixture + { + [Fact] + public void SmugdeIsNotCalledForFileWhichDoesNotMatchAnAttributeEntry() + { + const string decodedInput = "This is a substitution cipher"; + const string encodedInput = "Guvf vf n fhofgvghgvba pvcure"; + + var attributes = new List { new FilterAttributeEntry("rot13") }; + var filter = new SubstitutionCipherFilter("cipher-filter", attributes); + var filterRegistration = GlobalSettings.RegisterFilter(filter); + + string repoPath = InitNewRepository(); + string fileName = Guid.NewGuid() + ".rot13"; + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); + var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + using (var repo = new Repository(repoPath, repositoryOptions)) + { + CreateAttributesFile(repo, "*.rot13 filter=rot13"); + + var blob = CommitOnBranchAndReturnDatabaseBlob(repo, fileName, decodedInput); + var textDetected = blob.GetContentText(); + + Assert.Equal(encodedInput, textDetected); + Assert.Equal(1, filter.CleanCalledCount); + Assert.Equal(0, filter.SmudgeCalledCount); + + var branch = repo.CreateBranch("delete-files"); + repo.Checkout(branch.FriendlyName); + + DeleteFile(repo, fileName); + + repo.Checkout("master"); + + var fileContents = ReadTextFromFile(repo, fileName); + Assert.Equal(1, filter.SmudgeCalledCount); + Assert.Equal(decodedInput, fileContents); + } + + GlobalSettings.DeregisterFilter(filterRegistration); + } + + [Fact] + public void CorrectlyEncodesAndDecodesInput() + { + const string decodedInput = "This is a substitution cipher"; + const string encodedInput = "Guvf vf n fhofgvghgvba pvcure"; + + var attributes = new List { new FilterAttributeEntry("rot13") }; + var filter = new SubstitutionCipherFilter("cipher-filter", attributes); + var filterRegistration = GlobalSettings.RegisterFilter(filter); + + string repoPath = InitNewRepository(); + string fileName = Guid.NewGuid() + ".rot13"; + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); + var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + using (var repo = new Repository(repoPath, repositoryOptions)) + { + CreateAttributesFile(repo, "*.rot13 filter=rot13"); + + var blob = CommitOnBranchAndReturnDatabaseBlob(repo, fileName, decodedInput); + var textDetected = blob.GetContentText(); + + Assert.Equal(encodedInput, textDetected); + Assert.Equal(1, filter.CleanCalledCount); + Assert.Equal(0, filter.SmudgeCalledCount); + + var branch = repo.CreateBranch("delete-files"); + repo.Checkout(branch.FriendlyName); + + DeleteFile(repo, fileName); + + repo.Checkout("master"); + + var fileContents = ReadTextFromFile(repo, fileName); + Assert.Equal(1, filter.SmudgeCalledCount); + Assert.Equal(decodedInput, fileContents); + } + + GlobalSettings.DeregisterFilter(filterRegistration); + } + + [Theory] + [InlineData("*.txt", ".bat", 0, 0)] + [InlineData("*.txt", ".txt", 1, 0)] + public void WhenStagedFileDoesNotMatchPathSpecFileIsNotFiltered(string pathSpec, string fileExtension, int cleanCount, int smudgeCount) + { + const string filterName = "rot13"; + const string decodedInput = "This is a substitution cipher"; + string attributeFileEntry = string.Format("{0} filter={1}", pathSpec, filterName); + + var filterForAttributes = new List { new FilterAttributeEntry(filterName) }; + var filter = new SubstitutionCipherFilter("cipher-filter", filterForAttributes); + + var filterRegistration = GlobalSettings.RegisterFilter(filter); + + string repoPath = InitNewRepository(); + string fileName = Guid.NewGuid() + fileExtension; + + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); + var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + using (var repo = new Repository(repoPath, repositoryOptions)) + { + CreateAttributesFile(repo, attributeFileEntry); + + CommitOnBranchAndReturnDatabaseBlob(repo, fileName, decodedInput); + + Assert.Equal(cleanCount, filter.CleanCalledCount); + Assert.Equal(smudgeCount, filter.SmudgeCalledCount); + } + + GlobalSettings.DeregisterFilter(filterRegistration); + } + + [Theory] + [InlineData("rot13", "*.txt filter=rot13", 1)] + [InlineData("rot13", "*.txt filter=fake", 0)] + [InlineData("rot13", "*.bat filter=rot13", 0)] + [InlineData("rot13", "*.txt filter=fake", 0)] + [InlineData("fake", "*.txt filter=fake", 1)] + [InlineData("fake", "*.bat filter=fake", 0)] + [InlineData("rot13", "*.txt filter=rot13 -crlf", 1)] + public void CleanIsCalledIfAttributeEntryMatches(string filterAttribute, string attributeEntry, int cleanCount) + { + const string decodedInput = "This is a substitution cipher"; + + var filterForAttributes = new List { new FilterAttributeEntry(filterAttribute) }; + var filter = new SubstitutionCipherFilter("cipher-filter", filterForAttributes); + + var filterRegistration = GlobalSettings.RegisterFilter(filter); + + string repoPath = InitNewRepository(); + string fileName = Guid.NewGuid() + ".txt"; + + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); + var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + using (var repo = new Repository(repoPath, repositoryOptions)) + { + CreateAttributesFile(repo, attributeEntry); + + CommitOnBranchAndReturnDatabaseBlob(repo, fileName, decodedInput); + + Assert.Equal(cleanCount, filter.CleanCalledCount); + } + + GlobalSettings.DeregisterFilter(filterRegistration); + } + + [Theory] + + [InlineData("rot13", "*.txt filter=rot13", 1)] + [InlineData("rot13", "*.txt filter=fake", 0)] + [InlineData("rot13", "*.txt filter=rot13 -crlf", 1)] + public void SmudgeIsCalledIfAttributeEntryMatches(string filterAttribute, string attributeEntry, int smudgeCount) + { + const string decodedInput = "This is a substitution cipher"; + + var filterForAttributes = new List { new FilterAttributeEntry(filterAttribute) }; + var filter = new SubstitutionCipherFilter("cipher-filter", filterForAttributes); + + var filterRegistration = GlobalSettings.RegisterFilter(filter); + + string repoPath = InitNewRepository(); + string fileName = Guid.NewGuid() + ".txt"; + + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); + var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + using (var repo = new Repository(repoPath, repositoryOptions)) + { + CreateAttributesFile(repo, attributeEntry); + + CommitOnBranchAndReturnDatabaseBlob(repo, fileName, decodedInput); + + var branch = repo.CreateBranch("delete-files"); + repo.Checkout(branch.FriendlyName); + + DeleteFile(repo, fileName); + + repo.Checkout("master"); + + Assert.Equal(smudgeCount, filter.SmudgeCalledCount); + } + + GlobalSettings.DeregisterFilter(filterRegistration); + + } + + private static string ReadTextFromFile(Repository repo, string fileName) + { + return File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, fileName)); + } + + private static void DeleteFile(Repository repo, string fileName) + { + File.Delete(Path.Combine(repo.Info.WorkingDirectory, fileName)); + repo.Stage(fileName); + repo.Commit("remove file", Constants.Signature, Constants.Signature); + } + + private static Blob CommitOnBranchAndReturnDatabaseBlob(Repository repo, string fileName, string input) + { + Touch(repo.Info.WorkingDirectory, fileName, input); + repo.Stage(fileName); + + var commit = repo.Commit("new file", Constants.Signature, Constants.Signature); + + var blob = (Blob)commit.Tree[fileName].Target; + return blob; + } + } +} diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index 28d0b12c5..698595042 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -1,5 +1,4 @@ -using System; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -20,33 +19,35 @@ public void CanGetMinimumCompiledInFeatures() public void CanRetrieveValidVersionString() { // Version string format is: - // Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features) + // Major.Minor.Patch[-preDateTime]-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features) // Example output: - // "0.17.0-unknown-06d772d (x86 - Threads, Https)" + // "0.17.0[-pre20170914123547]-deadcafe-06d772d (x86 - Threads, Https)" string versionInfo = GlobalSettings.Version.ToString(); // The GlobalSettings.Version returned string should contain : - // version: '0.17.0[.198[-pre]]' LibGit2Sharp version number. + // version: '0.17.0[-pre20170914123547]' LibGit2Sharp version number. // git2SharpHash:'unknown' ( when compiled from source ) else LibGit2Sharp library hash. // git2hash: '06d772d' LibGit2 library hash. // arch: 'x86' or 'amd64' LibGit2 target. // git2Features: 'Threads, Ssh' LibGit2 features compiled with. - string regex = @"^(?\d{1,}\.\d{1,2}\.\d{1,3}(\.\d{1,5}(-pre)?)?)-(?\w+)-(?\w+) \((?\w+) - (?(?:\w*(?:, )*\w+)*)\)$"; + string regex = @"^(?\d{1,}\.\d{1,2}\.\d{1,3}(-(pre|dev)\d{14})?)-(?\w+)-(?\w+) \((?\w+) - (?(?:\w*(?:, )*\w+)*)\)$"; Assert.NotNull(versionInfo); Match regexResult = Regex.Match(versionInfo, regex); Assert.True(regexResult.Success, "The following version string format is enforced:" + - "Major.Minor.Patch[.Build['-pre']]-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features)"); + "Major.Minor.Patch[-preDateTime]-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features)"); GroupCollection matchGroups = regexResult.Groups; + Assert.Equal(8, matchGroups.Count); + // Check that all groups are valid for (int i = 0; i < matchGroups.Count; i++) { - if (i == 1 || i == 2) // Build number and '-pre' are optional + if (i == 1 || i == 2) // '-pre' segment is optional { continue; } @@ -54,5 +55,14 @@ public void CanRetrieveValidVersionString() Assert.True(matchGroups[i].Success); } } + + [Fact] + public void TryingToResetNativeLibraryPathAfterLoadedThrows() + { + // Do something that loads the native library + Assert.NotNull(GlobalSettings.Version.Features); + + Assert.Throws(() => { GlobalSettings.NativeLibraryPath = "C:/Foo"; }); + } } } diff --git a/LibGit2Sharp.Tests/IgnoreFixture.cs b/LibGit2Sharp.Tests/IgnoreFixture.cs index 790336512..b9af91404 100644 --- a/LibGit2Sharp.Tests/IgnoreFixture.cs +++ b/LibGit2Sharp.Tests/IgnoreFixture.cs @@ -83,5 +83,39 @@ public void CanCheckIfAPathIsIgnoredUsingThePreferedPlatformDirectorySeparatorCh Assert.True(repo.Ignore.IsPathIgnored(string.Format(@"NewFolder{0}NewFolder{0}File.txt", Path.DirectorySeparatorChar))); } } + + [Fact] + public void HonorDeeplyNestedGitIgnoreFile() + { + string path = InitNewRepository(); + using (var repo = new Repository(path)) + { + char pd = Path.DirectorySeparatorChar; + + var gitIgnoreFile = string.Format("deeply{0}nested{0}.gitignore", pd); + Touch(repo.Info.WorkingDirectory, gitIgnoreFile, "SmtCounters.h"); + + repo.Stage(gitIgnoreFile); + repo.Commit("Add .gitignore", Constants.Signature, Constants.Signature); + + Assert.False(repo.RetrieveStatus().IsDirty); + + var ignoredFile = string.Format("deeply{0}nested{0}SmtCounters.h", pd); + Touch(repo.Info.WorkingDirectory, ignoredFile, "Content"); + Assert.False(repo.RetrieveStatus().IsDirty); + + var file = string.Format("deeply{0}nested{0}file.txt", pd); + Touch(repo.Info.WorkingDirectory, file, "Yeah!"); + + var repositoryStatus = repo.RetrieveStatus(); + Assert.True(repositoryStatus.IsDirty); + + Assert.Equal(FileStatus.Ignored, repositoryStatus[ignoredFile].State); + Assert.Equal(FileStatus.NewInWorkdir, repositoryStatus[file].State); + + Assert.True(repo.Ignore.IsPathIgnored(ignoredFile)); + Assert.False(repo.Ignore.IsPathIgnored(file)); + } + } } } diff --git a/LibGit2Sharp.Tests/IndexFixture.cs b/LibGit2Sharp.Tests/IndexFixture.cs index 8dcdc4037..0b0f42553 100644 --- a/LibGit2Sharp.Tests/IndexFixture.cs +++ b/LibGit2Sharp.Tests/IndexFixture.cs @@ -101,10 +101,10 @@ public void CanRenameAFile() Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus(oldName)); Touch(repo.Info.WorkingDirectory, oldName, "hello test file\n"); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(oldName)); repo.Stage(oldName); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(oldName)); // Generated through // $ echo "hello test file" | git hash-object --stdin @@ -121,8 +121,8 @@ public void CanRenameAFile() const string newName = "being.frakking.polite.txt"; repo.Move(oldName, newName); - Assert.Equal(FileStatus.Removed, repo.RetrieveStatus(oldName)); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(newName)); + Assert.Equal(FileStatus.DeletedFromIndex, repo.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(newName)); Assert.Equal(1, repo.Index.Count); Assert.Equal(expectedHash, repo.Index[newName].Id.Sha); @@ -138,10 +138,10 @@ public void CanRenameAFile() } [Theory] - [InlineData("README", FileStatus.Unaltered, "deleted_unstaged_file.txt", FileStatus.Missing, FileStatus.Removed, FileStatus.Staged)] - [InlineData("new_tracked_file.txt", FileStatus.Added, "deleted_unstaged_file.txt", FileStatus.Missing, FileStatus.Nonexistent, FileStatus.Staged)] - [InlineData("modified_staged_file.txt", FileStatus.Staged, "deleted_unstaged_file.txt", FileStatus.Missing, FileStatus.Removed, FileStatus.Staged)] - [InlineData("modified_unstaged_file.txt", FileStatus.Modified, "deleted_unstaged_file.txt", FileStatus.Missing, FileStatus.Removed, FileStatus.Staged)] + [InlineData("README", FileStatus.Unaltered, "deleted_unstaged_file.txt", FileStatus.DeletedFromWorkdir, FileStatus.DeletedFromIndex, FileStatus.ModifiedInIndex)] + [InlineData("new_tracked_file.txt", FileStatus.NewInIndex, "deleted_unstaged_file.txt", FileStatus.DeletedFromWorkdir, FileStatus.Nonexistent, FileStatus.ModifiedInIndex)] + [InlineData("modified_staged_file.txt", FileStatus.ModifiedInIndex, "deleted_unstaged_file.txt", FileStatus.DeletedFromWorkdir, FileStatus.DeletedFromIndex, FileStatus.ModifiedInIndex)] + [InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInWorkdir, "deleted_unstaged_file.txt", FileStatus.DeletedFromWorkdir, FileStatus.DeletedFromIndex, FileStatus.ModifiedInIndex)] public void CanMoveAnExistingFileOverANonExistingFile(string sourcePath, FileStatus sourceStatus, string destPath, FileStatus destStatus, FileStatus sourcePostStatus, FileStatus destPostStatus) { string path = SandboxStandardTestRepo(); @@ -159,24 +159,24 @@ public void CanMoveAnExistingFileOverANonExistingFile(string sourcePath, FileSta [Theory] [InlineData("README", FileStatus.Unaltered, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt" })] - [InlineData("new_tracked_file.txt", FileStatus.Added, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt" })] - [InlineData("modified_staged_file.txt", FileStatus.Staged, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt" })] - [InlineData("modified_unstaged_file.txt", FileStatus.Modified, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt" })] + [InlineData("new_tracked_file.txt", FileStatus.NewInIndex, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt" })] + [InlineData("modified_staged_file.txt", FileStatus.ModifiedInIndex, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt" })] + [InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInWorkdir, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt" })] public void MovingOverAnExistingFileThrows(string sourcePath, FileStatus sourceStatus, IEnumerable destPaths) { InvalidMoveUseCases(sourcePath, sourceStatus, destPaths); } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt", "deleted_unstaged_file.txt", "deleted_staged_file.txt", "i_dont_exist.txt" })] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt", "deleted_unstaged_file.txt", "deleted_staged_file.txt", "i_dont_exist.txt" })] public void MovingAFileWichIsNotUnderSourceControlThrows(string sourcePath, FileStatus sourceStatus, IEnumerable destPaths) { InvalidMoveUseCases(sourcePath, sourceStatus, destPaths); } [Theory] - [InlineData("deleted_unstaged_file.txt", FileStatus.Missing, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt", "deleted_unstaged_file.txt", "deleted_staged_file.txt", "i_dont_exist.txt" })] - [InlineData("deleted_staged_file.txt", FileStatus.Removed, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt", "deleted_unstaged_file.txt", "deleted_staged_file.txt", "i_dont_exist.txt" })] + [InlineData("deleted_unstaged_file.txt", FileStatus.DeletedFromWorkdir, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt", "deleted_unstaged_file.txt", "deleted_staged_file.txt", "i_dont_exist.txt" })] + [InlineData("deleted_staged_file.txt", FileStatus.DeletedFromIndex, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt", "deleted_unstaged_file.txt", "deleted_staged_file.txt", "i_dont_exist.txt" })] [InlineData("i_dont_exist.txt", FileStatus.Nonexistent, new[] { "README", "new_tracked_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new_untracked_file.txt", "deleted_unstaged_file.txt", "deleted_staged_file.txt", "i_dont_exist.txt" })] public void MovingAFileNotInTheWorkingDirectoryThrows(string sourcePath, FileStatus sourceStatus, IEnumerable destPaths) { @@ -300,20 +300,20 @@ public void CanResetFullyMergedIndexFromTree() const string headIndexTreeSha = "e5d221fc5da11a3169bf503d76497c81be3207b6"; Assert.True(repo.Index.IsFullyMerged); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(testFile)); var headIndexTree = repo.Lookup(headIndexTreeSha); Assert.NotNull(headIndexTree); repo.Index.Replace(headIndexTree); Assert.True(repo.Index.IsFullyMerged); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(testFile)); } // Check that the index was persisted to disk. using (var repo = new Repository(path)) { - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(testFile)); } } @@ -331,20 +331,20 @@ public void CanResetIndexWithUnmergedEntriesFromTree() const string headIndexTreeSha = "1cb365141a52dfbb24933515820eb3045fbca12b"; Assert.False(repo.Index.IsFullyMerged); - Assert.Equal(FileStatus.Staged, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus(testFile)); var headIndexTree = repo.Lookup(headIndexTreeSha); Assert.NotNull(headIndexTree); repo.Index.Replace(headIndexTree); Assert.True(repo.Index.IsFullyMerged); - Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(testFile)); } // Check that the index was persisted to disk. using (var repo = new Repository(path)) { - Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(testFile)); } } @@ -364,19 +364,19 @@ public void CanClearTheIndex() repo.Index.Clear(); Assert.Equal(0, repo.Index.Count); - Assert.Equal(FileStatus.Removed | FileStatus.Untracked, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, repo.RetrieveStatus(testFile)); } // Check that the index was persisted to disk. using (var repo = new Repository(path)) { - Assert.Equal(FileStatus.Removed | FileStatus.Untracked, repo.RetrieveStatus(testFile)); + Assert.Equal(FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, repo.RetrieveStatus(testFile)); } } [Theory] - [InlineData("new_tracked_file.txt", FileStatus.Added, FileStatus.Untracked)] - [InlineData("modified_staged_file.txt", FileStatus.Staged, FileStatus.Removed | FileStatus.Untracked)] + [InlineData("new_tracked_file.txt", FileStatus.NewInIndex, FileStatus.NewInWorkdir)] + [InlineData("modified_staged_file.txt", FileStatus.ModifiedInIndex, FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir)] [InlineData("i_dont_exist.txt", FileStatus.Nonexistent, FileStatus.Nonexistent)] public void CanRemoveAnEntryFromTheIndex(string pathInTheIndex, FileStatus expectedBeforeStatus, FileStatus expectedAfterStatus) { @@ -394,8 +394,8 @@ public void CanRemoveAnEntryFromTheIndex(string pathInTheIndex, FileStatus expec } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked, FileStatus.Added)] - [InlineData("modified_unstaged_file.txt", FileStatus.Modified, FileStatus.Staged)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir, FileStatus.NewInIndex)] + [InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInWorkdir, FileStatus.ModifiedInIndex)] public void CanAddAnEntryToTheIndexFromAFileInTheWorkdir(string pathInTheWorkdir, FileStatus expectedBeforeStatus, FileStatus expectedAfterStatus) { var path = SandboxStandardTestRepoGitDir(); @@ -426,7 +426,7 @@ public void CanAddAnEntryToTheIndexFromABlob() repo.Index.Add(blob, targetIndexEntryPath, Mode.NonExecutableFile); var after = repo.RetrieveStatus(targetIndexEntryPath); - Assert.Equal(FileStatus.Staged | FileStatus.Modified, after); + Assert.Equal(FileStatus.ModifiedInIndex | FileStatus.ModifiedInWorkdir, after); } } @@ -451,18 +451,18 @@ public void CanMimicGitAddAll() using (var repo = new Repository(path)) { var before = repo.RetrieveStatus(); - Assert.True(before.Any(se => se.State == FileStatus.Untracked)); - Assert.True(before.Any(se => se.State == FileStatus.Modified)); - Assert.True(before.Any(se => se.State == FileStatus.Missing)); + Assert.True(before.Any(se => se.State == FileStatus.NewInWorkdir)); + Assert.True(before.Any(se => se.State == FileStatus.ModifiedInWorkdir)); + Assert.True(before.Any(se => se.State == FileStatus.DeletedFromWorkdir)); AddSomeCornerCases(repo); repo.Stage("*"); var after = repo.RetrieveStatus(); - Assert.False(after.Any(se => se.State == FileStatus.Untracked)); - Assert.False(after.Any(se => se.State == FileStatus.Modified)); - Assert.False(after.Any(se => se.State == FileStatus.Missing)); + Assert.False(after.Any(se => se.State == FileStatus.NewInWorkdir)); + Assert.False(after.Any(se => se.State == FileStatus.ModifiedInWorkdir)); + Assert.False(after.Any(se => se.State == FileStatus.DeletedFromWorkdir)); } } @@ -471,12 +471,12 @@ public void RetrievingAssumedUnchangedMarkedIndexEntries() { var path = SandboxAssumeUnchangedTestRepo(); using (var repo = new Repository(path)) - { + { var regularFile = repo.Index["hello.txt"]; Assert.False(regularFile.AssumeUnchanged); var assumeUnchangedFile = repo.Index["world.txt"]; - Assert.True(assumeUnchangedFile.AssumeUnchanged); + Assert.True(assumeUnchangedFile.AssumeUnchanged); } } diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 37e7ec73d..0be3440c6 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -1,6 +1,6 @@  - + Debug AnyCPU @@ -23,6 +23,7 @@ TRACE;DEBUG;NET40 prompt 4 + true pdbonly @@ -31,11 +32,11 @@ TRACE prompt 4 + true - - False - ..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll + + ..\packages\Moq.4.2.1507.0118\lib\net40\Moq.dll @@ -49,16 +50,27 @@ + + TestHelpers\Epoch.cs + + + TestHelpers\Platform.cs + + + + + + @@ -95,6 +107,7 @@ + @@ -107,15 +120,19 @@ + + + + @@ -131,10 +148,6 @@ - - $(MSBuildProjectDirectory)\..\Lib\NativeBinaries - - @@ -145,7 +158,7 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - + \ No newline at end of file diff --git a/LibGit2Sharp.Tests/MergeFixture.cs b/LibGit2Sharp.Tests/MergeFixture.cs index af386bd69..eadbffec5 100644 --- a/LibGit2Sharp.Tests/MergeFixture.cs +++ b/LibGit2Sharp.Tests/MergeFixture.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; @@ -296,6 +295,24 @@ public void ConflictingMergeReposBinary() } } + [Fact] + public void CanFailOnFirstMergeConflict() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var mergeResult = repo.Merge("conflicts", Constants.Signature, new MergeOptions() { FailOnConflict = true, }); + Assert.Equal(MergeStatus.Conflicts, mergeResult.Status); + + var master = repo.Branches["master"]; + var branch = repo.Branches["conflicts"]; + var mergeTreeResult = repo.ObjectDatabase.MergeCommits(master.Tip, branch.Tip, new MergeTreeOptions() { FailOnConflict = true }); + Assert.Equal(MergeTreeStatus.Conflicts, mergeTreeResult.Status); + Assert.Empty(mergeTreeResult.Conflicts); + } + + } + [Theory] [InlineData(true, FastForwardStrategy.Default, fastForwardBranchInitialId, MergeStatus.FastForward)] [InlineData(true, FastForwardStrategy.FastForwardOnly, fastForwardBranchInitialId, MergeStatus.FastForward)] @@ -324,9 +341,9 @@ public void CanFastForwardCommit(bool fromDetachedHead, FastForwardStrategy fast [Theory] [InlineData(true, FastForwardStrategy.Default, MergeStatus.NonFastForward)] - [InlineData(true, FastForwardStrategy.NoFastFoward, MergeStatus.NonFastForward)] + [InlineData(true, FastForwardStrategy.NoFastForward, MergeStatus.NonFastForward)] [InlineData(false, FastForwardStrategy.Default, MergeStatus.NonFastForward)] - [InlineData(false, FastForwardStrategy.NoFastFoward, MergeStatus.NonFastForward)] + [InlineData(false, FastForwardStrategy.NoFastForward, MergeStatus.NonFastForward)] public void CanNonFastForwardMergeCommit(bool fromDetachedHead, FastForwardStrategy fastForwardStrategy, MergeStatus expectedMergeStatus) { string path = SandboxMergeTestRepo(); @@ -362,7 +379,7 @@ public void MergeReportsCheckoutProgress() OnCheckoutProgress = (path, completed, total) => wasCalled = true, }; - MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options); + repo.Merge(commitToMerge, Constants.Signature, options); Assert.True(wasCalled); } @@ -385,7 +402,7 @@ public void MergeReportsCheckoutNotifications() CheckoutNotifyFlags = CheckoutNotifyFlags.Updated, }; - MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options); + repo.Merge(commitToMerge, Constants.Signature, options); Assert.True(wasCalled); Assert.Equal(CheckoutNotifyFlags.Updated, actualNotifyFlags); @@ -407,7 +424,7 @@ public void FastForwardMergeReportsCheckoutProgress() OnCheckoutProgress = (path, completed, total) => wasCalled = true, }; - MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options); + repo.Merge(commitToMerge, Constants.Signature, options); Assert.True(wasCalled); } @@ -430,7 +447,7 @@ public void FastForwardMergeReportsCheckoutNotifications() CheckoutNotifyFlags = CheckoutNotifyFlags.Updated, }; - MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options); + repo.Merge(commitToMerge, Constants.Signature, options); Assert.True(wasCalled); Assert.Equal(CheckoutNotifyFlags.Updated, actualNotifyFlags); @@ -504,7 +521,7 @@ public void CanMergeAndNotCommit() // Verify that there is a staged entry. Assert.Equal(1, repoStatus.Count()); - Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("b.txt")); + Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus("b.txt")); } } @@ -516,7 +533,7 @@ public void CanForceNonFastForwardMerge() { Commit commitToMerge = repo.Branches["fast_forward"].Tip; - MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = FastForwardStrategy.NoFastFoward }); + MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = FastForwardStrategy.NoFastForward }); Assert.Equal(MergeStatus.NonFastForward, result.Status); Assert.Equal("f58f780d5a0ae392efd4a924450b1bbdc0577d32", result.Commit.Id.Sha); @@ -550,7 +567,7 @@ public void VerifyUpToDateMerge() { Commit commitToMerge = repo.Branches["master"].Tip; - MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = FastForwardStrategy.NoFastFoward }); + MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = FastForwardStrategy.NoFastForward }); Assert.Equal(MergeStatus.UpToDate, result.Status); Assert.Equal(null, result.Commit); @@ -578,14 +595,14 @@ public void CanMergeCommittish(string committish, FastForwardStrategy strategy, [Theory] [InlineData(true, FastForwardStrategy.FastForwardOnly)] [InlineData(false, FastForwardStrategy.FastForwardOnly)] - [InlineData(true, FastForwardStrategy.NoFastFoward)] - [InlineData(false, FastForwardStrategy.NoFastFoward)] + [InlineData(true, FastForwardStrategy.NoFastForward)] + [InlineData(false, FastForwardStrategy.NoFastForward)] public void MergeWithWorkDirConflictsThrows(bool shouldStage, FastForwardStrategy strategy) { // Merging the fast_forward branch results in a change to file // b.txt. In this test we modify the file in the working directory // and then attempt to perform a merge. We expect the merge to fail - // due to merge conflicts. + // due to checkout conflicts. string committishToMerge = "fast_forward"; using (var repo = new Repository(SandboxMergeTestRepo())) @@ -597,7 +614,7 @@ public void MergeWithWorkDirConflictsThrows(bool shouldStage, FastForwardStrateg repo.Stage("b.txt"); } - Assert.Throws(() => repo.Merge(committishToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = strategy })); + Assert.Throws(() => repo.Merge(committishToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = strategy })); } } @@ -666,7 +683,6 @@ public void MergeCanSpecifyMergeFileFavorOption(MergeFileFavor fileFavorFlag) Branch branch = repo.Branches[conflictBranchName]; Assert.NotNull(branch); - var status = repo.RetrieveStatus(); MergeOptions mergeOptions = new MergeOptions() { MergeFileFavor = fileFavorFlag, @@ -749,6 +765,106 @@ public void CanMergeIntoOrphanedBranch() } } + + [Fact] + public void CanMergeTreeIntoSameTree() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Branches["master"].Tip; + + var result = repo.ObjectDatabase.MergeCommits(master, master, null); + Assert.Equal(MergeTreeStatus.Succeeded, result.Status); + Assert.Equal(0, result.Conflicts.Count()); + } + } + + [Fact] + public void CanMergeTreeIntoTreeFromUnbornBranch() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + repo.Refs.UpdateTarget("HEAD", "refs/heads/unborn"); + + Touch(repo.Info.WorkingDirectory, "README", "Yeah!\n"); + repo.Index.Clear(); + repo.Stage("README"); + + repo.Commit("A new world, free of the burden of the history", Constants.Signature, Constants.Signature); + + var master = repo.Branches["master"].Tip; + var branch = repo.Branches["unborn"].Tip; + + var result = repo.ObjectDatabase.MergeCommits(master, branch, null); + Assert.Equal(MergeTreeStatus.Succeeded, result.Status); + Assert.NotNull(result.Tree); + Assert.Equal(0, result.Conflicts.Count()); + } + } + + [Fact] + public void CanMergeCommitsAndDetectConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + repo.Refs.UpdateTarget("HEAD", "refs/heads/unborn"); + + repo.Index.Replace(repo.Lookup("conflicts")); + + repo.Commit("A conflicting world, free of the burden of the history", Constants.Signature, Constants.Signature); + + var master = repo.Branches["master"].Tip; + var branch = repo.Branches["unborn"].Tip; + + var result = repo.ObjectDatabase.MergeCommits(master, branch, null); + Assert.Equal(MergeTreeStatus.Conflicts, result.Status); + Assert.Null(result.Tree); + Assert.NotEqual(0, result.Conflicts.Count()); + } + } + + [Fact] + public void CanMergeFastForwardTreeWithoutConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Lookup("master"); + var branch = repo.Lookup("fast_forward"); + + var result = repo.ObjectDatabase.MergeCommits(master, branch, null); + Assert.Equal(MergeTreeStatus.Succeeded, result.Status); + Assert.NotNull(result.Tree); + Assert.Equal(0, result.Conflicts.Count()); + } + } + + [Fact] + public void CanIdentifyConflictsInMergeCommits() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Lookup("master"); + var branch = repo.Lookup("conflicts"); + + var result = repo.ObjectDatabase.MergeCommits(master, branch, null); + + Assert.Equal(MergeTreeStatus.Conflicts, result.Status); + + Assert.Null(result.Tree); + Assert.Equal(1, result.Conflicts.Count()); + + var conflict = result.Conflicts.First(); + Assert.Equal(new ObjectId("8e9daea300fbfef6c0da9744c6214f546d55b279"), conflict.Ancestor.Id); + Assert.Equal(new ObjectId("610b16886ca829cebd2767d9196f3c4378fe60b5"), conflict.Ours.Id); + Assert.Equal(new ObjectId("3dd9738af654bbf1c363f6c3bbc323bacdefa179"), conflict.Theirs.Id); + } + } + private Commit AddFileCommitToRepo(IRepository repository, string filename, string content = null) { Touch(repository.Info.WorkingDirectory, filename, content); diff --git a/LibGit2Sharp.Tests/MetaFixture.cs b/LibGit2Sharp.Tests/MetaFixture.cs index 9a53cdd14..057c9a1d4 100644 --- a/LibGit2Sharp.Tests/MetaFixture.cs +++ b/LibGit2Sharp.Tests/MetaFixture.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; using LibGit2Sharp.Tests.TestHelpers; using Xunit; using Xunit.Extensions; +using Moq; namespace LibGit2Sharp.Tests { @@ -14,7 +17,7 @@ public class MetaFixture { private static readonly HashSet explicitOnlyInterfaces = new HashSet { - typeof(IBelongToARepository), + typeof(IBelongToARepository), typeof(IDiffResult), }; [Fact] @@ -105,6 +108,28 @@ public void TypesInLibGit2SharpMustBeExtensibleInATestingContext() { nonTestableTypes.Add(type, new List()); } + + if (type.IsAbstract) + { + continue; + } + + try + { + if (type.ContainsGenericParameters) + { + var constructType = type.MakeGenericType(Enumerable.Repeat(typeof(object), type.GetGenericArguments().Length).ToArray()); + Activator.CreateInstance(constructType, true); + } + else + { + Activator.CreateInstance(type, true); + } + } + catch + { + nonTestableTypes.Add(type, new List()); + } } if (nonTestableTypes.Any()) @@ -244,7 +269,7 @@ public void GetEnumeratorMethodsInLibGit2SharpMustBeVirtualForTestability() var nonVirtualGetEnumeratorMethods = Assembly.GetAssembly(typeof(IRepository)) .GetExportedTypes() .Where(t => - t.Namespace == typeof (IRepository).Namespace && + t.Namespace == typeof(IRepository).Namespace && !t.IsSealed && !t.IsAbstract && t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(IEnumerable<>)))) @@ -254,12 +279,150 @@ public void GetEnumeratorMethodsInLibGit2SharpMustBeVirtualForTestability() (!m.IsVirtual || m.IsFinal)) .ToList(); - foreach (var method in nonVirtualGetEnumeratorMethods) + if (nonVirtualGetEnumeratorMethods.Any()) + { + var sb = new StringBuilder(); + + foreach (var method in nonVirtualGetEnumeratorMethods) + { + sb.AppendFormat("GetEnumerator in type '{0}' isn't virtual.{1}", + method.DeclaringType, Environment.NewLine); + } + + Assert.True(false, Environment.NewLine + sb.ToString()); + } + } + + [Fact] + public void NoPublicTypesUnderLibGit2SharpCoreNamespace() + { + const string coreNamespace = "LibGit2Sharp.Core"; + + var types = Assembly.GetAssembly(typeof(IRepository)) + .GetExportedTypes() + .Where(t => t.FullName.StartsWith(coreNamespace + ".")) + + // Ugly hack to circumvent a Mono bug + // cf. https://bugzilla.xamarin.com/show_bug.cgi?id=27010 + .Where(t => !t.FullName.Contains("+")) + +#if LEAKS_IDENTIFYING + .Where(t => t != typeof(LibGit2Sharp.Core.LeaksContainer)) +#endif + .ToList(); + + if (types.Any()) + { + var sb = new StringBuilder(); + + foreach (var type in types) + { + sb.AppendFormat("Public type '{0}' under the '{1}' namespace.{2}", + type.FullName, coreNamespace, Environment.NewLine); + } + + Assert.True(false, Environment.NewLine + sb.ToString()); + } + } + + [Fact] + public void NoOptionalParametersinMethods() + { + IEnumerable mis = + from t in Assembly.GetAssembly(typeof(IRepository)) + .GetExportedTypes() + from m in t.GetMethods() + where !m.IsObsolete() + from p in m.GetParameters() + where p.IsOptional + select m.DeclaringType + "." + m.Name; + + var sb = new StringBuilder(); + + foreach (var method in mis.Distinct()) { - Debug.WriteLine(String.Format("GetEnumerator in type '{0}' isn't virtual.", method.DeclaringType)); + sb.AppendFormat("At least one overload of method '{0}' accepts an optional parameter.{1}", + method, Environment.NewLine); } - Assert.Empty(nonVirtualGetEnumeratorMethods); + Assert.Equal("", sb.ToString()); + } + + [Fact] + public void NoOptionalParametersinConstructors() + { + IEnumerable mis = + from t in Assembly.GetAssembly(typeof(IRepository)) + .GetExportedTypes() + from c in t.GetConstructors() + from p in c.GetParameters() + where p.IsOptional + select c.DeclaringType.Name; + + var sb = new StringBuilder(); + + foreach (var method in mis.Distinct()) + { + sb.AppendFormat("At least one constructor of type '{0}' accepts an optional parameter.{1}", + method, Environment.NewLine); + } + + Assert.Equal("", sb.ToString()); + } + + [Fact] + public void PublicExtensionMethodsShouldonlyTargetInterfacesOrEnums() + { + IEnumerable mis = + from m in GetInvalidPublicExtensionMethods() + select m.DeclaringType + "." + m.Name; + + var sb = new StringBuilder(); + + foreach (var method in mis.Distinct()) + { + sb.AppendFormat("'{0}' is a public extension method that doesn't target an interface or an enum.{1}", + method, Environment.NewLine); + } + + Assert.Equal("", sb.ToString()); + } + + // Inspired from http://stackoverflow.com/a/299526 + + static IEnumerable GetInvalidPublicExtensionMethods() + { + var query = from type in (Assembly.GetAssembly(typeof(IRepository))).GetTypes() + where type.IsSealed && !type.IsGenericType && !type.IsNested && type.IsPublic + from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public) + where method.IsDefined(typeof(ExtensionAttribute), false) + let parameterType = method.GetParameters()[0].ParameterType + where parameterType != null && !parameterType.IsInterface && !parameterType.IsEnum + select method; + return query; + } + + [Fact] + public void AllIDiffResultsAreInChangesBuilder() + { + var diff = typeof(Diff).GetField("ChangesBuilders", BindingFlags.NonPublic | BindingFlags.Static); + var changesBuilders = (System.Collections.IDictionary)diff.GetValue(null); + + IEnumerable diffResults = typeof(Diff).Assembly.GetExportedTypes() + .Where(type => type.GetInterface("IDiffResult") != null); + + var nonBuilderTypes = diffResults.Where(diffResult => !changesBuilders.Contains(diffResult)); + Assert.False(nonBuilderTypes.Any(), "Classes which implement IDiffResult but are not registered under ChangesBuilders in Diff:" + Environment.NewLine + + string.Join(Environment.NewLine, nonBuilderTypes.Select(type => type.FullName))); + } + } + + internal static class TypeExtensions + { + internal static bool IsObsolete(this MethodInfo methodInfo) + { + var attributes = methodInfo.GetCustomAttributes(false); + return attributes.Any(a => a is ObsoleteAttribute); } } } diff --git a/LibGit2Sharp.Tests/MockingFixture.cs b/LibGit2Sharp.Tests/MockingFixture.cs index 6b99a6bb8..6db7dc645 100644 --- a/LibGit2Sharp.Tests/MockingFixture.cs +++ b/LibGit2Sharp.Tests/MockingFixture.cs @@ -73,8 +73,8 @@ public int NumberOfCommits [Fact] public void CanFakeConfigurationBuildSignature() { - var name = "name"; - var email = "email"; + const string name = "name"; + const string email = "email"; var now = DateTimeOffset.UtcNow; var fakeConfig = new Mock(); diff --git a/LibGit2Sharp.Tests/NetworkFixture.cs b/LibGit2Sharp.Tests/NetworkFixture.cs index 31959286c..dd5350b56 100644 --- a/LibGit2Sharp.Tests/NetworkFixture.cs +++ b/LibGit2Sharp.Tests/NetworkFixture.cs @@ -22,23 +22,26 @@ public void CanListRemoteReferences(string url) using (var repo = new Repository(repoPath)) { Remote remote = repo.Network.Remotes.Add(remoteName, url); - IList references = repo.Network.ListReferences(remote).ToList(); + IList references = repo.Network.ListReferences(remote).ToList(); - foreach (var directReference in references) + + foreach (var reference in references) { // None of those references point to an existing // object in this brand new repository - Assert.Null(directReference.Target); + Assert.Null(reference.ResolveToDirectReference().Target); } List> actualRefs = references. - Select(directRef => new Tuple(directRef.CanonicalName, directRef.TargetIdentifier)).ToList(); + Select(directRef => new Tuple(directRef.CanonicalName, directRef.ResolveToDirectReference() + .TargetIdentifier)).ToList(); - Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count); - for (int i = 0; i < ExpectedRemoteRefs.Count; i++) + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count); + Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference); + for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++) { - Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); - Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); } } } @@ -53,23 +56,25 @@ public void CanListRemoteReferencesFromUrl(string url) using (var repo = new Repository(repoPath)) { - IList references = repo.Network.ListReferences(url).ToList(); + IList references = repo.Network.ListReferences(url).ToList(); - foreach (var directReference in references) + foreach (var reference in references) { // None of those references point to an existing // object in this brand new repository - Assert.Null(directReference.Target); + Assert.Null(reference.ResolveToDirectReference().Target); } List> actualRefs = references. - Select(directRef => new Tuple(directRef.CanonicalName, directRef.TargetIdentifier)).ToList(); + Select(directRef => new Tuple(directRef.CanonicalName, directRef.ResolveToDirectReference() + .TargetIdentifier)).ToList(); - Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count); - for (int i = 0; i < ExpectedRemoteRefs.Count; i++) + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count); + Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference); + for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++) { - Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); - Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); } } } @@ -87,22 +92,26 @@ public void CanListRemoteReferenceObjects() using (var repo = new Repository(clonedRepoPath)) { Remote remote = repo.Network.Remotes[remoteName]; - IEnumerable references = repo.Network.ListReferences(remote); + IEnumerable references = repo.Network.ListReferences(remote).ToList(); var actualRefs = new List>(); - foreach(DirectReference reference in references) + foreach(Reference reference in references) { Assert.NotNull(reference.CanonicalName); - Assert.NotNull(reference.Target); - actualRefs.Add(new Tuple(reference.CanonicalName, reference.Target.Id.Sha)); + + var directReference = reference.ResolveToDirectReference(); + + Assert.NotNull(directReference.Target); + actualRefs.Add(new Tuple(reference.CanonicalName, directReference.Target.Id.Sha)); } - Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count); - for (int i = 0; i < ExpectedRemoteRefs.Count; i++) + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count); + Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference); + for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++) { - Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); - Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); } } } @@ -123,16 +132,16 @@ public void CanListRemoteReferencesWithCredentials() var references = repo.Network.ListReferences(remote, Constants.PrivateRepoCredentials); - foreach (var directReference in references) + foreach (var reference in references) { - Assert.NotNull(directReference); + Assert.NotNull(reference); } } } [Theory] [InlineData(FastForwardStrategy.Default)] - [InlineData(FastForwardStrategy.NoFastFoward)] + [InlineData(FastForwardStrategy.NoFastForward)] public void CanPull(FastForwardStrategy fastForwardStrategy) { string url = "https://github.com/libgit2/TestGitRepository"; @@ -180,7 +189,7 @@ public void CanPullIntoEmptyRepo() using (var repo = new Repository(repoPath)) { // Set up remote - Remote remote = repo.Network.Remotes.Add(remoteName, url); + repo.Network.Remotes.Add(remoteName, url); // Set up tracking information repo.Branches.Update(repo.Head, @@ -228,32 +237,66 @@ public void PullWithoutMergeBranchThrows() } } - /* - * git ls-remote http://github.com/libgit2/TestGitRepository - * 49322bb17d3acc9146f98c97d078513228bbf3c0 HEAD - * 0966a434eb1a025db6b71485ab63a3bfbea520b6 refs/heads/first-merge - * 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master - * 42e4e7c5e507e113ebbb7801b16b52cf867b7ce1 refs/heads/no-parent - * d96c4e80345534eccee5ac7b07fc7603b56124cb refs/tags/annotated_tag - * c070ad8c08840c8116da865b2d65593a6bb9cd2a refs/tags/annotated_tag^{} - * 55a1a760df4b86a02094a904dfa511deb5655905 refs/tags/blob - * 8f50ba15d49353813cc6e20298002c0d17b0a9ee refs/tags/commit_tree - * 6e0c7bdb9b4ed93212491ee778ca1c65047cab4e refs/tags/nearly-dangling - */ - /// - /// Expected references on http://github.com/libgit2/TestGitRepository - /// - private static List> ExpectedRemoteRefs = new List>() + [Fact] + public void CanMergeFetchedRefs() + { + string url = "https://github.com/libgit2/TestGitRepository"; + + var scd = BuildSelfCleaningDirectory(); + string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + using (var repo = new Repository(clonedRepoPath)) + { + repo.Reset(ResetMode.Hard, "HEAD~1"); + + Assert.False(repo.RetrieveStatus().Any()); + Assert.Equal(repo.Lookup("refs/remotes/origin/master~1"), repo.Head.Tip); + + repo.Network.Fetch(repo.Head.Remote); + + MergeOptions mergeOptions = new MergeOptions() + { + FastForwardStrategy = FastForwardStrategy.NoFastForward + }; + + MergeResult mergeResult = repo.MergeFetchedRefs(Constants.Signature, mergeOptions); + Assert.Equal(mergeResult.Status, MergeStatus.NonFastForward); + } + } + + [Fact] + public void CanPruneRefs() { - new Tuple("HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0"), - new Tuple("refs/heads/first-merge", "0966a434eb1a025db6b71485ab63a3bfbea520b6"), - new Tuple("refs/heads/master", "49322bb17d3acc9146f98c97d078513228bbf3c0"), - new Tuple("refs/heads/no-parent", "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1"), - new Tuple("refs/tags/annotated_tag", "d96c4e80345534eccee5ac7b07fc7603b56124cb"), - new Tuple("refs/tags/annotated_tag^{}", "c070ad8c08840c8116da865b2d65593a6bb9cd2a"), - new Tuple("refs/tags/blob", "55a1a760df4b86a02094a904dfa511deb5655905"), - new Tuple("refs/tags/commit_tree", "8f50ba15d49353813cc6e20298002c0d17b0a9ee"), - new Tuple("refs/tags/nearly-dangling", "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e"), - }; + string url = "https://github.com/libgit2/TestGitRepository"; + + var scd = BuildSelfCleaningDirectory(); + string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + var scd2 = BuildSelfCleaningDirectory(); + string clonedRepoPath2 = Repository.Clone(url, scd2.DirectoryPath); + + + using (var repo = new Repository(clonedRepoPath)) + { + repo.Network.Remotes.Add("pruner", clonedRepoPath2); + var remote = repo.Network.Remotes["pruner"]; + repo.Network.Fetch(remote); + Assert.NotNull(repo.Refs["refs/remotes/pruner/master"]); + + // Remove the branch from the source repository + using (var repo2 = new Repository(clonedRepoPath2)) + { + repo2.Refs.Remove("refs/heads/master"); + } + + // and by default we don't prune it + repo.Network.Fetch(remote); + Assert.NotNull(repo.Refs["refs/remotes/pruner/master"]); + + // but we do when asked by the user + repo.Network.Fetch(remote, new FetchOptions { Prune = true} ); + Assert.Null(repo.Refs["refs/remotes/pruner/master"]); + } + } } } diff --git a/LibGit2Sharp.Tests/NoteFixture.cs b/LibGit2Sharp.Tests/NoteFixture.cs index 7da99db07..0c879e3e1 100644 --- a/LibGit2Sharp.Tests/NoteFixture.cs +++ b/LibGit2Sharp.Tests/NoteFixture.cs @@ -168,14 +168,17 @@ public void CreatingANoteWhichAlreadyExistsOverwritesThePreviousNote() [Fact] public void CanAddANoteWithSignatureFromConfig() { - string configPath = CreateConfigurationWithDummyUser(Constants.Signature); + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; string path = SandboxBareTestRepo(); using (var repo = new Repository(path, options)) { var commit = repo.Lookup("9fd738e8f7967c078dceed8190330fc8648ee56a"); - var note = repo.Notes.Add(commit.Id, "I'm batman!\n", "batmobile"); + + Signature signature = repo.Config.BuildSignature(DateTimeOffset.Now); + + var note = repo.Notes.Add(commit.Id, "I'm batman!\n", signature, signature, "batmobile"); var newNote = commit.Notes.Single(); Assert.Equal(note, newNote); @@ -183,7 +186,7 @@ public void CanAddANoteWithSignatureFromConfig() Assert.Equal("I'm batman!\n", newNote.Message); Assert.Equal("batmobile", newNote.Namespace); - AssertCommitSignaturesAre(repo.Lookup("refs/notes/batmobile"), Constants.Signature); + AssertCommitIdentitiesAre(repo.Lookup("refs/notes/batmobile"), Constants.Identity); } } @@ -265,7 +268,7 @@ public void RemovingANonExistingNoteDoesntThrow() [Fact] public void CanRemoveANoteWithSignatureFromConfig() { - string configPath = CreateConfigurationWithDummyUser(Constants.Signature); + string configPath = CreateConfigurationWithDummyUser(Constants.Identity); RepositoryOptions options = new RepositoryOptions() { GlobalConfigurationLocation = configPath }; string path = SandboxBareTestRepo(); @@ -276,11 +279,13 @@ public void CanRemoveANoteWithSignatureFromConfig() Assert.NotEmpty(notes); - repo.Notes.Remove(commit.Id, repo.Notes.DefaultNamespace); + Signature signature = repo.Config.BuildSignature(DateTimeOffset.Now); + + repo.Notes.Remove(commit.Id, signature, signature, repo.Notes.DefaultNamespace); Assert.Empty(notes); - AssertCommitSignaturesAre(repo.Lookup("refs/notes/" + repo.Notes.DefaultNamespace), Constants.Signature); + AssertCommitIdentitiesAre(repo.Lookup("refs/notes/" + repo.Notes.DefaultNamespace), Constants.Identity); } } diff --git a/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs b/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs index 1ec121e93..8e2a1d843 100644 --- a/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs +++ b/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs @@ -41,7 +41,7 @@ public void CanCreateABlobFromAFileInTheWorkingDirectory() Assert.Equal("dc53d4c6b8684c21b0b57db29da4a2afea011565", blob.Sha); /* The file is unknown from the Index nor the Head ... */ - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus("hello.txt")); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus("hello.txt")); /* ...however, it's indeed stored in the repository. */ var fetchedBlob = repo.Lookup(blob.Id); @@ -103,15 +103,13 @@ public void CanCreateABlobIntoTheDatabaseOfABareRepository() [InlineData("e9671e138a780833cb689753570fd10a55be84fb", "dummy.guess")] public void CanCreateABlobFromAStream(string expectedSha, string hintPath) { - string path = InitNewRepository(); - var sb = new StringBuilder(); for (int i = 0; i < 6; i++) { sb.Append("libgit2\n\r\n"); } - using (var repo = new Repository(path)) + using (var repo = InitIsolatedRepository()) { CreateAttributesFiles(Path.Combine(repo.Info.Path, "info"), "attributes"); @@ -654,6 +652,90 @@ public void CanShortenObjectIdentifier() } } + [Fact] + public void TestMergeIntoSelfHasNoConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Lookup("master"); + + var result = repo.ObjectDatabase.CanMergeWithoutConflict(master, master); + + Assert.True(result); + } + } + + [Fact] + public void TestMergeIntoOtherUnbornBranchHasNoConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + repo.Refs.UpdateTarget("HEAD", "refs/heads/unborn"); + + Touch(repo.Info.WorkingDirectory, "README", "Yeah!\n"); + repo.Index.Clear(); + repo.Stage("README"); + + repo.Commit("A new world, free of the burden of the history", Constants.Signature, Constants.Signature); + + var master = repo.Branches["master"].Tip; + var branch = repo.Branches["unborn"].Tip; + + Assert.True(repo.ObjectDatabase.CanMergeWithoutConflict(master, branch)); + } + } + + [Fact] + public void TestMergeIntoOtherUnbornBranchHasConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + repo.Refs.UpdateTarget("HEAD", "refs/heads/unborn"); + + repo.Index.Replace(repo.Lookup("conflicts")); + + repo.Commit("A conflicting world, free of the burden of the history", Constants.Signature, Constants.Signature); + + var master = repo.Branches["master"].Tip; + var branch = repo.Branches["unborn"].Tip; + + Assert.False(repo.ObjectDatabase.CanMergeWithoutConflict(master, branch)); + } + } + + [Fact] + public void TestMergeIntoOtherBranchHasNoConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Lookup("master"); + var branch = repo.Lookup("fast_forward"); + + var result = repo.ObjectDatabase.CanMergeWithoutConflict(master, branch); + + Assert.True(result); + } + } + + [Fact] + public void TestMergeIntoWrongBranchHasConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Lookup("master"); + var branch = repo.Lookup("conflicts"); + + var result = repo.ObjectDatabase.CanMergeWithoutConflict(master, branch); + + Assert.False(result); + } + } + private static Blob CreateBlob(Repository repo, string content) { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content))) diff --git a/LibGit2Sharp.Tests/PackBuilderFixture.cs b/LibGit2Sharp.Tests/PackBuilderFixture.cs new file mode 100644 index 000000000..3d0071df0 --- /dev/null +++ b/LibGit2Sharp.Tests/PackBuilderFixture.cs @@ -0,0 +1,192 @@ +using System; +using System.IO; +using System.Linq; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class PackBuilderFixture : BaseFixture + { + [Fact] + public void TestDefaultPackDelegate() + { + TestIfSameRepoAfterPacking(null); + } + + [Fact] + public void TestCommitsPerBranchPackDelegate() + { + TestIfSameRepoAfterPacking(AddingObjectIdsTestDelegate); + } + + [Fact] + public void TestCommitsPerBranchIdsPackDelegate() + { + TestIfSameRepoAfterPacking(AddingObjectsTestDelegate); + } + + internal void TestIfSameRepoAfterPacking(Action packDelegate) + { + // read a repo + // pack with the provided action + // write the pack file in a mirror repo + // read new repo + // compare + + string orgRepoPath = SandboxPackBuilderTestRepo(); + string mrrRepoPath = SandboxPackBuilderTestRepo(); + string mrrRepoPackDirPath = Path.Combine(mrrRepoPath + "/.git/objects"); + + DirectoryHelper.DeleteDirectory(mrrRepoPackDirPath); + Directory.CreateDirectory(mrrRepoPackDirPath + "/pack"); + + PackBuilderOptions packBuilderOptions = new PackBuilderOptions(mrrRepoPackDirPath + "/pack"); + + using (Repository orgRepo = new Repository(orgRepoPath)) + { + PackBuilderResults results; + if (packDelegate != null) + results = orgRepo.ObjectDatabase.Pack(packBuilderOptions, b => packDelegate(orgRepo, b)); + else + results = orgRepo.ObjectDatabase.Pack(packBuilderOptions); + + // written objects count is the same as in objects database + Assert.Equal(orgRepo.ObjectDatabase.Count(), results.WrittenObjectsCount); + + // loading a repo from the written pack file. + using (Repository mrrRepo = new Repository(mrrRepoPath)) + { + // make sure the objects of the original repo are the same as the ones in the mirror repo + // doing that by making sure the count is the same, and the set difference is empty + Assert.True(mrrRepo.ObjectDatabase.Count() == orgRepo.ObjectDatabase.Count() && !mrrRepo.ObjectDatabase.Except(orgRepo.ObjectDatabase).Any()); + + Assert.Equal(orgRepo.Commits.Count(), mrrRepo.Commits.Count()); + Assert.Equal(orgRepo.Branches.Count(), mrrRepo.Branches.Count()); + Assert.Equal(orgRepo.Refs.Count(), mrrRepo.Refs.Count()); + } + } + } + + internal void AddingObjectIdsTestDelegate(IRepository repo, PackBuilder builder) + { + foreach (Branch branch in repo.Branches) + { + foreach (Commit commit in branch.Commits) + { + builder.AddRecursively(commit.Id); + } + } + + foreach (Tag tag in repo.Tags) + { + builder.Add(tag.Target.Id); + } + } + + internal void AddingObjectsTestDelegate(IRepository repo, PackBuilder builder) + { + foreach (Branch branch in repo.Branches) + { + foreach (Commit commit in branch.Commits) + { + builder.AddRecursively(commit); + } + } + + foreach (Tag tag in repo.Tags) + { + builder.Add(tag.Target); + } + } + + [Fact] + public void ExceptionIfPathDoesNotExist() + { + Assert.Throws(() => new PackBuilderOptions("aaa")); + } + + [Fact] + public void ExceptionIfPathEqualsNull() + { + Assert.Throws(() => new PackBuilderOptions(null)); + } + + [Fact] + public void ExceptionIfOptionsEqualsNull() + { + string orgRepoPath = SandboxPackBuilderTestRepo(); + + using (Repository orgRepo = new Repository(orgRepoPath)) + { + Assert.Throws(() => + { + orgRepo.ObjectDatabase.Pack(null); + }); + } + } + + [Fact] + public void ExceptionIfBuildDelegateEqualsNull() + { + string orgRepoPath = SandboxPackBuilderTestRepo(); + PackBuilderOptions packBuilderOptions = new PackBuilderOptions(orgRepoPath); + + using (Repository orgRepo = new Repository(orgRepoPath)) + { + Assert.Throws(() => + { + orgRepo.ObjectDatabase.Pack(packBuilderOptions, null); + }); + } + } + + [Fact] + public void ExceptionIfNegativeNumberOfThreads() + { + string orgRepoPath = SandboxPackBuilderTestRepo(); + PackBuilderOptions packBuilderOptions = new PackBuilderOptions(orgRepoPath); + + Assert.Throws(() => + { + packBuilderOptions.MaximumNumberOfThreads = -1; + }); + } + + [Fact] + public void ExceptionIfAddNullObjectID() + { + string orgRepoPath = SandboxPackBuilderTestRepo(); + PackBuilderOptions packBuilderOptions = new PackBuilderOptions(orgRepoPath); + + using (Repository orgRepo = new Repository(orgRepoPath)) + { + Assert.Throws(() => + { + orgRepo.ObjectDatabase.Pack(packBuilderOptions, builder => + { + builder.Add(null); + }); + }); + } + } + + [Fact] + public void ExceptionIfAddRecursivelyNullObjectID() + { + string orgRepoPath = SandboxPackBuilderTestRepo(); + PackBuilderOptions packBuilderOptions = new PackBuilderOptions(orgRepoPath); + + using (Repository orgRepo = new Repository(orgRepoPath)) + { + Assert.Throws(() => + { + orgRepo.ObjectDatabase.Pack(packBuilderOptions, builder => + { + builder.AddRecursively(null); + }); + }); + } + } + } +} diff --git a/LibGit2Sharp.Tests/PushFixture.cs b/LibGit2Sharp.Tests/PushFixture.cs index f0e950465..7dde5efd6 100644 --- a/LibGit2Sharp.Tests/PushFixture.cs +++ b/LibGit2Sharp.Tests/PushFixture.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using LibGit2Sharp.Handlers; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -62,7 +64,7 @@ private void AssertPush(Action push) public void CanPushABranchTrackingAnUpstreamBranch() { bool packBuilderCalled = false; - Handlers.PackBuilderProgressHandler packBuilderCb = (x, y, z) => { packBuilderCalled = true; return true; }; + PackBuilderProgressHandler packBuilderCb = (x, y, z) => { packBuilderCalled = true; return true; }; AssertPush(repo => repo.Network.Push(repo.Head)); AssertPush(repo => repo.Network.Push(repo.Branches["master"])); @@ -77,6 +79,63 @@ public void CanPushABranchTrackingAnUpstreamBranch() Assert.True(packBuilderCalled); } + [Fact] + public void CanInvokePrePushCallbackAndSucceed() + { + bool packBuilderCalled = false; + bool prePushHandlerCalled = false; + PackBuilderProgressHandler packBuilderCb = (x, y, z) => { packBuilderCalled = true; return true; }; + PrePushHandler prePushHook = (IEnumerable updates) => + { + Assert.True(updates.Count() == 1, "Expected 1 update, received " + updates.Count()); + prePushHandlerCalled = true; + return true; + }; + + AssertPush(repo => repo.Network.Push(repo.Head)); + AssertPush(repo => repo.Network.Push(repo.Branches["master"])); + + PushOptions options = new PushOptions() + { + OnPushStatusError = OnPushStatusError, + OnPackBuilderProgress = packBuilderCb, + OnNegotiationCompletedBeforePush = prePushHook, + }; + + AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)); + Assert.True(packBuilderCalled); + Assert.True(prePushHandlerCalled); + } + + [Fact] + public void CanInvokePrePushCallbackAndFail() + { + bool packBuilderCalled = false; + bool prePushHandlerCalled = false; + PackBuilderProgressHandler packBuilderCb = (x, y, z) => { packBuilderCalled = true; return true; }; + PrePushHandler prePushHook = (IEnumerable updates) => + { + Assert.True(updates.Count() == 1, "Expected 1 update, received " + updates.Count()); + prePushHandlerCalled = true; + return false; + }; + + AssertPush(repo => repo.Network.Push(repo.Head)); + AssertPush(repo => repo.Network.Push(repo.Branches["master"])); + + PushOptions options = new PushOptions() + { + OnPushStatusError = OnPushStatusError, + OnPackBuilderProgress = packBuilderCb, + OnNegotiationCompletedBeforePush = prePushHook + }; + + Assert.Throws(() => { AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)); }); + + Assert.False(packBuilderCalled); + Assert.True(prePushHandlerCalled); + } + [Fact] public void PushingABranchThatDoesNotTrackAnUpstreamBranchThrows() { @@ -97,7 +156,7 @@ public void CanForcePush() // Create a new repository string localRepoPath = InitNewRepository(); - using (var localRepo = new Repository(localRepoPath)) + using (var localRepo = new Repository(localRepoPath, new RepositoryOptions { Identity = Constants.Identity })) { // Add a commit Commit first = AddCommitToRepo(localRepo); @@ -123,22 +182,26 @@ public void CanForcePush() // Force push the new commit string pushRefSpec = string.Format("+{0}:{0}", localRepo.Head.CanonicalName); + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + localRepo.Network.Push(localRepo.Network.Remotes.Single(), pushRefSpec); AssertRemoteHeadTipEquals(localRepo, second.Sha); AssertRefLogEntry(localRepo, "refs/remotes/origin/master", - localRepo.Head.Tip.Id, "update by push", - oldId); + "update by push", + oldId, localRepo.Head.Tip.Id, + Constants.Identity, before); } } private static void AssertRemoteHeadTipEquals(IRepository localRepo, string sha) { var remoteReferences = localRepo.Network.ListReferences(localRepo.Network.Remotes.Single()); - DirectReference remoteHead = remoteReferences.Single(r => r.CanonicalName == "HEAD"); + Reference remoteHead = remoteReferences.Single(r => r.CanonicalName == "HEAD"); - Assert.Equal(sha, remoteHead.TargetIdentifier); + Assert.Equal(sha, remoteHead.ResolveToDirectReference().TargetIdentifier); } private void UpdateTheRemoteRepositoryWithANewCommit(string remoteRepoPath) diff --git a/LibGit2Sharp.Tests/RebaseFixture.cs b/LibGit2Sharp.Tests/RebaseFixture.cs new file mode 100644 index 000000000..d70851bdc --- /dev/null +++ b/LibGit2Sharp.Tests/RebaseFixture.cs @@ -0,0 +1,784 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class RebaseFixture : BaseFixture + { + const string masterBranch1Name = "M1"; + const string masterBranch2Name = "M2"; + const string topicBranch1Name = "T1"; + const string topicBranch2Name = "T2"; + const string conflictBranch1Name = "C1"; + const string topicBranch1PrimeName = "T1Prime"; + + string filePathA = "a.txt"; + string filePathB = "b.txt"; + string filePathC = "c.txt"; + string filePathD = "d.txt"; + + [Theory] + [InlineData(topicBranch2Name, topicBranch2Name, topicBranch1Name, masterBranch1Name, 3)] + [InlineData(topicBranch2Name, topicBranch2Name, topicBranch1Name, topicBranch1Name, 3)] + [InlineData(topicBranch2Name, topicBranch1Name, masterBranch2Name, masterBranch2Name, 3)] + [InlineData(topicBranch2Name, topicBranch1Name, masterBranch2Name, null, 3)] + [InlineData(topicBranch1Name, null, masterBranch2Name, null, 3)] + public void CanRebase(string initialBranchName, + string branchName, + string upstreamName, + string ontoName, + int stepCount) + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(initialBranchName); + Assert.False(repo.RetrieveStatus().IsDirty); + + Branch branch = (branchName == null) ? null : repo.Branches[branchName]; + Branch upstream = repo.Branches[upstreamName]; + Branch onto = (ontoName == null) ? null : repo.Branches[ontoName]; + Commit expectedSinceCommit = (branch == null) ? repo.Head.Tip : branch.Tip; + Commit expectedUntilCommit = upstream.Tip; + Commit expectedOntoCommit = (onto == null) ? upstream.Tip : onto.Tip; + + int beforeStepCallCount = 0; + int afterStepCallCount = 0; + bool beforeRebaseStepCountCorrect = true; + bool afterRebaseStepCountCorrect = true; + bool totalStepCountCorrect = true; + + List PreRebaseCommits = new List(); + List PostRebaseResults = new List(); + ObjectId expectedParentId = upstream.Tip.Id; + + RebaseOptions options = new RebaseOptions() + { + RebaseStepStarting = x => + { + beforeRebaseStepCountCorrect &= beforeStepCallCount == x.StepIndex; + totalStepCountCorrect &= (x.TotalStepCount == stepCount); + beforeStepCallCount++; + PreRebaseCommits.Add(x.StepInfo.Commit); + }, + RebaseStepCompleted = x => + { + afterRebaseStepCountCorrect &= (afterStepCallCount == x.CompletedStepIndex); + totalStepCountCorrect &= (x.TotalStepCount == stepCount); + afterStepCallCount++; + PostRebaseResults.Add(new CompletedRebaseStepInfo(x.Commit, x.WasPatchAlreadyApplied)); + }, + }; + + RebaseResult rebaseResult = repo.Rebase.Start(branch, upstream, onto, Constants.Identity, options); + + // Validation: + Assert.True(afterRebaseStepCountCorrect, "Unexpected CompletedStepIndex value in RebaseStepCompleted"); + Assert.True(beforeRebaseStepCountCorrect, "Unexpected StepIndex value in RebaseStepStarting"); + Assert.True(totalStepCountCorrect, "Unexpected TotalStepcount value in Rebase step callback"); + Assert.Equal(RebaseStatus.Complete, rebaseResult.Status); + Assert.Equal(stepCount, rebaseResult.TotalStepCount); + Assert.Null(rebaseResult.CurrentStepInfo); + + Assert.Equal(stepCount, rebaseResult.CompletedStepCount); + Assert.False(repo.RetrieveStatus().IsDirty); + + Assert.Equal(stepCount, beforeStepCallCount); + Assert.Equal(stepCount, afterStepCallCount); + + // Verify the chain of source commits that were rebased. + CommitFilter sourceCommitFilter = new CommitFilter() + { + IncludeReachableFrom = expectedSinceCommit, + ExcludeReachableFrom = expectedUntilCommit, + SortBy = CommitSortStrategies.Reverse | CommitSortStrategies.Topological, + }; + Assert.Equal(repo.Commits.QueryBy(sourceCommitFilter), PreRebaseCommits); + + // Verify the chain of commits that resulted from the rebase. + Commit expectedParent = expectedOntoCommit; + foreach (CompletedRebaseStepInfo stepInfo in PostRebaseResults) + { + Commit rebasedCommit = stepInfo.Commit; + Assert.Equal(expectedParent.Id, rebasedCommit.Parents.First().Id); + Assert.False(stepInfo.WasPatchAlreadyApplied); + expectedParent = rebasedCommit; + } + + Assert.Equal(repo.Head.Tip, PostRebaseResults.Last().Commit); + } + } + + [Fact] + public void CanRebaseBranchOntoItself() + { + // Maybe we should have an "up-to-date" return type for scenarios such as these, + // but for now this test is to make sure we do something reasonable + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + repo.Checkout(topicBranch2Name); + Branch b = repo.Branches[topicBranch2Name]; + + RebaseResult result = repo.Rebase.Start(b, b, null, Constants.Identity, new RebaseOptions()); + Assert.Equal(0, result.TotalStepCount); + Assert.Equal(RebaseStatus.Complete, result.Status); + Assert.Equal(0, result.CompletedStepCount); + } + } + + private class CompletedRebaseStepInfo + { + public CompletedRebaseStepInfo(Commit commit, bool wasPatchAlreadyApplied) + { + Commit = commit; + WasPatchAlreadyApplied = wasPatchAlreadyApplied; + } + + public Commit Commit { get; set; } + + public bool WasPatchAlreadyApplied { get; set; } + + public override string ToString() + { + return string.Format("CompletedRebaseStepInfo: {0}", Commit); + } + } + + private class CompletedRebaseStepInfoEqualityComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(CompletedRebaseStepInfo x, CompletedRebaseStepInfo y) + { + if (x == null && y == null) + { + return true; + } + + if ((x == null && y != null) || + (x != null && y == null)) + { + return false; + } + + return x.WasPatchAlreadyApplied == y.WasPatchAlreadyApplied && + ObjectId.Equals(x.Commit, y.Commit); + } + + int IEqualityComparer.GetHashCode(CompletedRebaseStepInfo obj) + { + int hashCode = obj.WasPatchAlreadyApplied.GetHashCode(); + + if (obj.Commit != null) + { + hashCode += obj.Commit.GetHashCode(); + } + + return hashCode; + } + } + + /// + /// Verify a single rebase, but in more detail. + /// + [Theory] + [InlineData("* text=auto", "\r\n", new[] { "2cad6e96a0028f1764dcbde6292a9a1471acb114", "18fd3deebe6124b5dacc8426d589d617a968e8d1", "048977d8cb90d530e83cc615a17a49f3068f68c1" })] + [InlineData("* text=auto", "\n", new[] { "2cad6e96a0028f1764dcbde6292a9a1471acb114", "18fd3deebe6124b5dacc8426d589d617a968e8d1", "048977d8cb90d530e83cc615a17a49f3068f68c1" })] + [InlineData("* text=auto\n*.txt eol=lf", "\n", new[] { "577d176b00a55e88e9b34da87e4357dfc9a486fd", "ea0ad4d8b500394a61874ebfda5904376e2b1098", "521b8383ca3fde9e369587492e7a3945677f1b2c" })] + [InlineData("* text=auto\r\n*.txt eol=crlf", "\r\n", new[] { "67d29fdf654ac4773c9405ab4b54aa7ff092f339", "7b70c02e175d378b44ea28aeeece775cd972047a", "81f203dbfe00a5c1ecd9c0e6b03705e6cffda5c0" })] + [InlineData("* binary", "\r\n", new[] { "f5a5ded935597108709224170accddc5aeb5c287", "518adb8bb1ea1058d1825d3fe08d27f80c0e829b", "d2db503ab553c970d34e1b5e3ff68768adef05bc" })] + [InlineData("* binary", "\n", new[] { "93a0e9680246d1f1e43fbd5308f7936424d9e81a", "5fd40bffbdd884632c330a254a2bd1dfaaaad3c1", "4df5c91b2d8318781b07d04f6bfa77304c372f1e" })] + public void VerifyRebaseDetailed(string attributes, string lineEnding, string[] expectedIds) + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo, attributes, lineEnding); + + Branch initialBranch = repo.Branches[topicBranch1Name]; + Branch upstreamBranch = repo.Branches[masterBranch2Name]; + + repo.Checkout(initialBranch); + Assert.False(repo.RetrieveStatus().IsDirty); + + bool wasCheckoutProgressCalled = false; + bool wasCheckoutProgressCalledForResetingHead = false; + bool wasCheckoutNotifyCalled = false; + bool wasCheckoutNotifyCalledForResetingHead = false; + + bool startedApplyingSteps = false; + + RebaseOptions options = new RebaseOptions() + { + OnCheckoutProgress = (x, y, z) => + { + if (startedApplyingSteps) + { + wasCheckoutProgressCalled = true; + } + else + { + wasCheckoutProgressCalledForResetingHead = true; + } + }, + OnCheckoutNotify = (x, y) => + { + if (startedApplyingSteps) + { + wasCheckoutNotifyCalled = true; + } + else + { + wasCheckoutNotifyCalledForResetingHead = true; + } + + return true; + }, + CheckoutNotifyFlags = CheckoutNotifyFlags.Updated, + + RebaseStepStarting = x => startedApplyingSteps = true, + + }; + + repo.Rebase.Start(null, upstreamBranch, null, Constants.Identity2, options); + + Assert.Equal(true, wasCheckoutNotifyCalledForResetingHead); + Assert.Equal(true, wasCheckoutProgressCalledForResetingHead); + Assert.Equal(true, wasCheckoutNotifyCalled); + Assert.Equal(true, wasCheckoutProgressCalled); + + // Verify the chain of resultant rebased commits. + CommitFilter commitFilter = new CommitFilter() + { + IncludeReachableFrom = repo.Head.Tip, + ExcludeReachableFrom = upstreamBranch.Tip, + SortBy = CommitSortStrategies.Reverse | CommitSortStrategies.Topological, + }; + + List expectedTreeIds = new List() + { + new ObjectId(expectedIds[0]), + new ObjectId(expectedIds[1]), + new ObjectId(expectedIds[2]), + }; + + List rebasedCommits = repo.Commits.QueryBy(commitFilter).ToList(); + + Assert.Equal(3, rebasedCommits.Count); + for(int i = 0; i < 3; i++) + { + Assert.Equal(expectedTreeIds[i], rebasedCommits[i].Tree.Id); + Assert.Equal(Constants.Signature.Name, rebasedCommits[i].Author.Name); + Assert.Equal(Constants.Signature.Email, rebasedCommits[i].Author.Email); + Assert.Equal(Constants.Signature2.Name, rebasedCommits[i].Committer.Name); + Assert.Equal(Constants.Signature2.Email, rebasedCommits[i].Committer.Email); + } + } + } + + [Fact] + public void CanContinueRebase() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(topicBranch1Name); + Assert.False(repo.RetrieveStatus().IsDirty); + + Branch branch = repo.Branches[topicBranch1Name]; + Branch upstream = repo.Branches[conflictBranch1Name]; + Branch onto = repo.Branches[conflictBranch1Name]; + + int beforeStepCallCount = 0; + int afterStepCallCount = 0; + bool wasCheckoutProgressCalled = false; + bool wasCheckoutNotifyCalled = false; + + RebaseOptions options = new RebaseOptions() + { + RebaseStepStarting = x => beforeStepCallCount++, + RebaseStepCompleted = x => afterStepCallCount++, + OnCheckoutProgress = (x, y, z) => wasCheckoutProgressCalled = true, + OnCheckoutNotify = (x, y) => { wasCheckoutNotifyCalled = true; return true; }, + CheckoutNotifyFlags = CheckoutNotifyFlags.Updated, + }; + + RebaseResult rebaseResult = repo.Rebase.Start(branch, upstream, onto, Constants.Identity, options); + + // Verify that we have a conflict. + Assert.Equal(CurrentOperation.RebaseMerge, repo.Info.CurrentOperation); + Assert.Equal(RebaseStatus.Conflicts, rebaseResult.Status); + Assert.True(repo.RetrieveStatus().IsDirty); + Assert.False(repo.Index.IsFullyMerged); + Assert.Equal(0, rebaseResult.CompletedStepCount); + Assert.Equal(3, rebaseResult.TotalStepCount); + + // Verify that expected callbacks were called + Assert.Equal(1, beforeStepCallCount); + Assert.Equal(0, afterStepCallCount); + Assert.True(wasCheckoutProgressCalled, "CheckoutProgress callback was not called."); + + // Resolve the conflict + foreach (Conflict conflict in repo.Index.Conflicts) + { + Touch(repo.Info.WorkingDirectory, + conflict.Theirs.Path, + repo.Lookup(conflict.Theirs.Id).GetContentText(new FilteringOptions(conflict.Theirs.Path))); + repo.Stage(conflict.Theirs.Path); + } + + Assert.True(repo.Index.IsFullyMerged); + + // Clear the flags: + wasCheckoutProgressCalled = false; wasCheckoutNotifyCalled = false; + RebaseResult continuedRebaseResult = repo.Rebase.Continue(Constants.Identity, options); + + Assert.NotNull(continuedRebaseResult); + Assert.Equal(RebaseStatus.Complete, continuedRebaseResult.Status); + Assert.False(repo.RetrieveStatus().IsDirty); + Assert.True(repo.Index.IsFullyMerged); + Assert.Equal(0, rebaseResult.CompletedStepCount); + Assert.Equal(3, rebaseResult.TotalStepCount); + + Assert.Equal(3, beforeStepCallCount); + Assert.Equal(3, afterStepCallCount); + Assert.True(wasCheckoutProgressCalled, "CheckoutProgress callback was not called."); + Assert.True(wasCheckoutNotifyCalled, "CheckoutNotify callback was not called."); + } + } + + [Fact] + public void ContinuingRebaseWithUnstagedChangesThrows() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(topicBranch1Name); + Assert.False(repo.RetrieveStatus().IsDirty); + + Branch branch = repo.Branches[topicBranch1Name]; + Branch upstream = repo.Branches[conflictBranch1Name]; + Branch onto = repo.Branches[conflictBranch1Name]; + + RebaseResult rebaseResult = repo.Rebase.Start(branch, upstream, onto, Constants.Identity, null); + + // Verify that we have a conflict. + Assert.Equal(CurrentOperation.RebaseMerge, repo.Info.CurrentOperation); + Assert.Equal(RebaseStatus.Conflicts, rebaseResult.Status); + Assert.True(repo.RetrieveStatus().IsDirty); + Assert.False(repo.Index.IsFullyMerged); + Assert.Equal(0, rebaseResult.CompletedStepCount); + Assert.Equal(3, rebaseResult.TotalStepCount); + + Assert.Throws(() => + repo.Rebase.Continue(Constants.Identity, null)); + + // Resolve the conflict + foreach (Conflict conflict in repo.Index.Conflicts) + { + Touch(repo.Info.WorkingDirectory, + conflict.Theirs.Path, + repo.Lookup(conflict.Theirs.Id).GetContentText(new FilteringOptions(conflict.Theirs.Path))); + repo.Stage(conflict.Theirs.Path); + } + + Touch(repo.Info.WorkingDirectory, + filePathA, + "Unstaged content"); + + Assert.Throws(() => + repo.Rebase.Continue(Constants.Identity, null)); + + Assert.True(repo.Index.IsFullyMerged); + } + } + + [Fact] + public void CanSpecifyFileConflictStrategy() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(topicBranch1Name); + Assert.False(repo.RetrieveStatus().IsDirty); + + Branch branch = repo.Branches[topicBranch1Name]; + Branch upstream = repo.Branches[conflictBranch1Name]; + Branch onto = repo.Branches[conflictBranch1Name]; + + RebaseOptions options = new RebaseOptions() + { + FileConflictStrategy = CheckoutFileConflictStrategy.Ours, + }; + + RebaseResult rebaseResult = repo.Rebase.Start(branch, upstream, onto, Constants.Identity, options); + + // Verify that we have a conflict. + Assert.Equal(CurrentOperation.RebaseMerge, repo.Info.CurrentOperation); + Assert.Equal(RebaseStatus.Conflicts, rebaseResult.Status); + Assert.True(repo.RetrieveStatus().IsDirty); + Assert.False(repo.Index.IsFullyMerged); + Assert.Equal(0, rebaseResult.CompletedStepCount); + Assert.Equal(3, rebaseResult.TotalStepCount); + + string conflictFile = filePathB; + // Get the information on the conflict. + Conflict conflict = repo.Index.Conflicts[conflictFile]; + + Assert.NotNull(conflict); + Assert.NotNull(conflict.Theirs); + Assert.NotNull(conflict.Ours); + + Blob expectedBlob = repo.Lookup(conflict.Ours.Id); + + // Check the content of the file on disk matches what is expected. + string expectedContent = expectedBlob.GetContentText(new FilteringOptions(conflictFile)); + Assert.Equal(expectedContent, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, conflictFile))); + } + } + + [Fact] + public void CanQueryRebaseOperation() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(topicBranch1Name); + Assert.False(repo.RetrieveStatus().IsDirty); + + Branch branch = repo.Branches[topicBranch1Name]; + Branch upstream = repo.Branches[conflictBranch1Name]; + Branch onto = repo.Branches[conflictBranch1Name]; + + RebaseResult rebaseResult = repo.Rebase.Start(branch, upstream, onto, Constants.Identity, null); + + // Verify that we have a conflict. + Assert.Equal(RebaseStatus.Conflicts, rebaseResult.Status); + Assert.True(repo.RetrieveStatus().IsDirty); + Assert.False(repo.Index.IsFullyMerged); + Assert.Equal(0, rebaseResult.CompletedStepCount); + Assert.Equal(3, rebaseResult.TotalStepCount); + + RebaseStepInfo info = repo.Rebase.GetCurrentStepInfo(); + + Assert.Equal(0, repo.Rebase.GetCurrentStepIndex()); + Assert.Equal(3, repo.Rebase.GetTotalStepCount()); + Assert.Equal(RebaseStepOperation.Pick, info.Type); + } + } + + [Fact] + public void CanAbortRebase() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(topicBranch1Name); + Assert.False(repo.RetrieveStatus().IsDirty); + + Branch branch = repo.Branches[topicBranch1Name]; + Branch upstream = repo.Branches[conflictBranch1Name]; + Branch onto = repo.Branches[conflictBranch1Name]; + + RebaseResult rebaseResult = repo.Rebase.Start(branch, upstream, onto, Constants.Identity, null); + + // Verify that we have a conflict. + Assert.Equal(RebaseStatus.Conflicts, rebaseResult.Status); + Assert.True(repo.RetrieveStatus().IsDirty); + Assert.False(repo.Index.IsFullyMerged); + Assert.Equal(0, rebaseResult.CompletedStepCount); + Assert.Equal(3, rebaseResult.TotalStepCount); + + // Set up the callbacks to verify that checkout progress / notify + // callbacks are called. + bool wasCheckoutProgressCalled = false; + bool wasCheckoutNotifyCalled = false; + RebaseOptions options = new RebaseOptions() + { + OnCheckoutProgress = (x, y, z) => wasCheckoutProgressCalled = true, + OnCheckoutNotify = (x, y) => { wasCheckoutNotifyCalled = true; return true; }, + CheckoutNotifyFlags = CheckoutNotifyFlags.Updated, + }; + + repo.Rebase.Abort(options); + Assert.False(repo.RetrieveStatus().IsDirty, "Repository workdir is dirty after Rebase.Abort."); + Assert.True(repo.Index.IsFullyMerged, "Repository index is not fully merged after Rebase.Abort."); + Assert.Equal(CurrentOperation.None, repo.Info.CurrentOperation); + + Assert.True(wasCheckoutProgressCalled, "Checkout progress callback was not called during Rebase.Abort."); + Assert.True(wasCheckoutNotifyCalled, "Checkout notify callback was not called during Rebase.Abort."); + } + } + + [Fact] + public void RebaseWhileAlreadyRebasingThrows() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(topicBranch1Name); + Assert.False(repo.RetrieveStatus().IsDirty); + + Branch branch = repo.Branches[topicBranch1Name]; + Branch upstream = repo.Branches[conflictBranch1Name]; + Branch onto = repo.Branches[conflictBranch1Name]; + + RebaseResult rebaseResult = repo.Rebase.Start(branch, upstream, onto, Constants.Identity, null); + + // Verify that we have a conflict. + Assert.Equal(RebaseStatus.Conflicts, rebaseResult.Status); + Assert.True(repo.RetrieveStatus().IsDirty); + Assert.Equal(CurrentOperation.RebaseMerge, repo.Info.CurrentOperation); + + Assert.Throws(() => + repo.Rebase.Start(branch, upstream, onto, Constants.Identity, null)); + } + } + + [Fact] + public void RebaseOperationsWithoutRebasingThrow() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + + repo.Checkout(topicBranch1Name); + + Assert.Throws(() => + repo.Rebase.Continue(Constants.Identity, new RebaseOptions())); + + Assert.Throws(() => + repo.Rebase.Abort()); + } + } + + [Fact] + public void CurrentStepInfoIsNullWhenNotRebasing() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo); + repo.Checkout(topicBranch1Name); + + Assert.Null(repo.Rebase.GetCurrentStepInfo()); + } + } + + [Theory] + [InlineData("* text=auto", "\r\n", "379e80ed7824be7672e1e30ddd8f44aa081d57d4")] + [InlineData("* text=auto", "\n", "379e80ed7824be7672e1e30ddd8f44aa081d57d4")] + [InlineData("* text=auto\n*.txt eol=lf", "\n", "94121eeebf7cfe0acf22425eab36fcdc737132b6")] + [InlineData("* text=auto\r\n*.txt eol=crlf", "\r\n", "dad06142cc632aea81cbc8486583011c4d622580")] + [InlineData("* binary", "\r\n", "44492d98b725189cfc0203d4192dfbb1fd34bf02")] + [InlineData("* binary", "\n", "f4b5b95de77f4cd97b4728617bae2dd8ba9af914")] + public void CanRebaseHandlePatchAlreadyApplied(string attributes, string lineEnding, string expectedShaText) + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + var path = Repository.Init(scd.DirectoryPath); + using (Repository repo = new Repository(path)) + { + ConstructRebaseTestRepository(repo, attributes, lineEnding); + + repo.Checkout(topicBranch1Name); + + Branch topicBranch1Prime = repo.CreateBranch(topicBranch1PrimeName, masterBranch1Name); + + string newFileRelativePath = "new_file.txt"; + Touch(repo.Info.WorkingDirectory, newFileRelativePath, "New Content"); + repo.Stage(newFileRelativePath); + Commit commit = repo.Commit("new commit 1", Constants.Signature, Constants.Signature, new CommitOptions()); + + repo.Checkout(topicBranch1Prime); + var cherryPickResult = repo.CherryPick(commit, Constants.Signature2); + Assert.Equal(CherryPickStatus.CherryPicked, cherryPickResult.Status); + + string newFileRelativePath2 = "new_file_2.txt"; + Touch(repo.Info.WorkingDirectory, newFileRelativePath2, "New Content for path 2"); + repo.Stage(newFileRelativePath2); + repo.Commit("new commit 2", Constants.Signature, Constants.Signature, new CommitOptions()); + + Branch upstreamBranch = repo.Branches[topicBranch1Name]; + + List rebaseResults = new List(); + + RebaseOptions options = new RebaseOptions() + { + RebaseStepCompleted = x => + { + rebaseResults.Add(new CompletedRebaseStepInfo(x.Commit, x.WasPatchAlreadyApplied)); + } + }; + + repo.Rebase.Start(null, upstreamBranch, null, Constants.Identity2, options); + ObjectId secondCommitExpectedTreeId = new ObjectId(expectedShaText); + Signature secondCommitAuthorSignature = Constants.Signature; + Identity secondCommitCommiterIdentity = Constants.Identity2; + + Assert.Equal(2, rebaseResults.Count); + Assert.True(rebaseResults[0].WasPatchAlreadyApplied); + + Assert.False(rebaseResults[1].WasPatchAlreadyApplied); + Assert.NotNull(rebaseResults[1].Commit); + + // This is the expected tree ID of the new commit. + Assert.Equal(secondCommitExpectedTreeId, rebaseResults[1].Commit.Tree.Id); + Assert.True(Signature.Equals(secondCommitAuthorSignature, rebaseResults[1].Commit.Author)); + Assert.Equal(secondCommitCommiterIdentity.Name, rebaseResults[1].Commit.Committer.Name, StringComparer.Ordinal); + Assert.Equal(secondCommitCommiterIdentity.Email, rebaseResults[1].Commit.Committer.Email, StringComparer.Ordinal); + } + } + + [Fact] + public void RebasingInBareRepositoryThrows() + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + Branch rebaseUpstreamBranch = repo.Branches["refs/heads/test"]; + + Assert.NotNull(rebaseUpstreamBranch); + Assert.Throws(() => repo.Rebase.Start(null, rebaseUpstreamBranch, null, Constants.Identity, new RebaseOptions())); + Assert.Throws(() => repo.Rebase.Continue(Constants.Identity, new RebaseOptions())); + Assert.Throws(() => repo.Rebase.Abort()); + } + } + + private void ConstructRebaseTestRepository(Repository repo, string attributes = "* text=auto", string lineEnding = "\r\n") + { + // Constructs a graph that looks like: + // * -- * -- * (modifications to c.txt) + // / | + // / T2 + // / + // * -- * -- * (modifications to b.txt) + // / | + // / T1 + // / + // *--*--*--*--*--*---- + // | | \ + // M1 M2 \ + // ---* + // | + // C1 + const string fileContentA1 = "A1"; + + const string fileContentB1 = "B1"; + const string fileContentB2 = "B2"; + const string fileContentB3 = "B3"; + const string fileContentB4 = "B4"; + + const string fileContentC1 = "C1"; + const string fileContentC2 = "C2"; + const string fileContentC3 = "C3"; + const string fileContentC4 = "C4"; + + const string fileContentD1 = "D1"; + const string fileContentD2 = "D2"; + const string fileContentD3 = "D3"; + + string workdir = repo.Info.WorkingDirectory; + Commit commit = null; + + CreateAttributesFile(repo, attributes); + + repo.Stage(".gitattributes"); + commit = repo.Commit("setup", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathA, fileContentA1); + repo.Stage(filePathA); + commit = repo.Commit("commit 1", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathB, fileContentB1); + repo.Stage(filePathB); + commit = repo.Commit("commit 2", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathC, fileContentC1); + repo.Stage(filePathC); + commit = repo.Commit("commit 3", Constants.Signature, Constants.Signature, new CommitOptions()); + + Branch masterBranch1 = repo.CreateBranch(masterBranch1Name, commit); + + Touch(workdir, filePathB, string.Join(lineEnding, fileContentB1, fileContentB2)); + repo.Stage(filePathB); + commit = repo.Commit("commit 4", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathB, string.Join(lineEnding, fileContentB1, fileContentB2, fileContentB3)); + repo.Stage(filePathB); + commit = repo.Commit("commit 5", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathB, string.Join(lineEnding, fileContentB1, fileContentB2, fileContentB3, fileContentB4)); + repo.Stage(filePathB); + commit = repo.Commit("commit 6", Constants.Signature, Constants.Signature, new CommitOptions()); + + repo.CreateBranch(topicBranch1Name, commit); + + Touch(workdir, filePathC, string.Join(lineEnding, fileContentC1, fileContentC2)); + repo.Stage(filePathC); + commit = repo.Commit("commit 7", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathC, string.Join(lineEnding, fileContentC1, fileContentC2, fileContentC3)); + repo.Stage(filePathC); + commit = repo.Commit("commit 8", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathC, string.Join(lineEnding, fileContentC1, fileContentC2, fileContentC3, fileContentC4)); + repo.Stage(filePathC); + commit = repo.Commit("commit 9", Constants.Signature, Constants.Signature, new CommitOptions()); + + repo.CreateBranch(topicBranch2Name, commit); + + repo.Checkout(masterBranch1.Tip); + Touch(workdir, filePathD, fileContentD1); + repo.Stage(filePathD); + commit = repo.Commit("commit 10", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathD, string.Join(lineEnding, fileContentD1, fileContentD2)); + repo.Stage(filePathD); + commit = repo.Commit("commit 11", Constants.Signature, Constants.Signature, new CommitOptions()); + + Touch(workdir, filePathD, string.Join(lineEnding, fileContentD1, fileContentD2, fileContentD3)); + repo.Stage(filePathD); + commit = repo.Commit("commit 12", Constants.Signature, Constants.Signature, new CommitOptions()); + + repo.CreateBranch(masterBranch2Name, commit); + + // Create commit / branch that conflicts with T1 and T2 + Touch(workdir, filePathB, string.Join(lineEnding, fileContentB1, fileContentB2 + fileContentB3 + fileContentB4)); + repo.Stage(filePathB); + commit = repo.Commit("commit 13", Constants.Signature, Constants.Signature, new CommitOptions()); + repo.CreateBranch(conflictBranch1Name, commit); + } + } +} diff --git a/LibGit2Sharp.Tests/RefSpecFixture.cs b/LibGit2Sharp.Tests/RefSpecFixture.cs index 1473ac704..dc82a5fc3 100644 --- a/LibGit2Sharp.Tests/RefSpecFixture.cs +++ b/LibGit2Sharp.Tests/RefSpecFixture.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; using Xunit.Extensions; @@ -184,7 +183,7 @@ public void SettingInvalidRefSpecsThrows(string refSpec) var remote = repo.Network.Remotes["origin"]; var oldRefSpecs = remote.RefSpecs.Select(r => r.Specification).ToList(); - Assert.Throws(() => + Assert.Throws(() => repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs.Add(refSpec))); var newRemote = repo.Network.Remotes["origin"]; diff --git a/LibGit2Sharp.Tests/ReferenceFixture.cs b/LibGit2Sharp.Tests/ReferenceFixture.cs index 4674ecefb..ac2af3c24 100644 --- a/LibGit2Sharp.Tests/ReferenceFixture.cs +++ b/LibGit2Sharp.Tests/ReferenceFixture.cs @@ -22,10 +22,12 @@ public void CanAddADirectReference() const string name = "refs/heads/unit_test"; string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + var newRef = (DirectReference)repo.Refs.Add(name, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); Assert.NotNull(newRef); Assert.Equal(name, newRef.CanonicalName); @@ -35,8 +37,9 @@ public void CanAddADirectReference() Assert.NotNull(repo.Refs[name]); AssertRefLogEntry(repo, name, - newRef.ResolveToDirectReference().Target.Id, - "branch: Created from be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + "branch: Created from be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + null, newRef.ResolveToDirectReference().Target.Id, Constants.Identity, before + ); } } @@ -47,11 +50,13 @@ public void CanAddADirectReferenceFromRevParseSpec() const string logMessage = "Create new ref"; string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); - var newRef = (DirectReference)repo.Refs.Add(name, "master^1^2", Constants.Signature, logMessage); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + + var newRef = (DirectReference)repo.Refs.Add(name, "master^1^2", logMessage); Assert.NotNull(newRef); Assert.Equal(name, newRef.CanonicalName); Assert.NotNull(newRef.Target); @@ -59,9 +64,9 @@ public void CanAddADirectReferenceFromRevParseSpec() Assert.Equal(newRef.Target.Sha, newRef.TargetIdentifier); Assert.NotNull(repo.Refs[name]); - AssertRefLogEntry(repo, name, - newRef.ResolveToDirectReference().Target.Id, - logMessage, committer: Constants.Signature); + AssertRefLogEntry(repo, name, logMessage, + null, newRef.ResolveToDirectReference().Target.Id, + Constants.Identity, before); } } @@ -106,7 +111,7 @@ public void CanAddASymbolicReferenceFromTheTargetReference() var targetRef = repo.Refs[target]; - var newRef = repo.Refs.Add(name, targetRef, Constants.Signature, logMessage); + var newRef = repo.Refs.Add(name, targetRef, logMessage); AssertSymbolicRef(newRef, repo, target, name); Assert.Empty(repo.Refs.Log(newRef)); @@ -151,12 +156,15 @@ public void CanAddAndOverwriteADirectReference() const string logMessage = "Create new ref"; string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); var oldRef = repo.Refs[name]; - var newRef = (DirectReference)repo.Refs.Add(name, target, Constants.Signature, logMessage, true); + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + + var newRef = (DirectReference)repo.Refs.Add(name, target, logMessage, true); Assert.NotNull(newRef); Assert.Equal(name, newRef.CanonicalName); Assert.NotNull(newRef.Target); @@ -164,9 +172,9 @@ public void CanAddAndOverwriteADirectReference() Assert.Equal(target, ((DirectReference)repo.Refs[name]).Target.Sha); AssertRefLogEntry(repo, name, - newRef.ResolveToDirectReference().Target.Id, logMessage, ((DirectReference)oldRef).Target.Id, - Constants.Signature); + newRef.ResolveToDirectReference().Target.Id, + Constants.Identity, before); } } @@ -178,22 +186,25 @@ public void CanAddAndOverwriteASymbolicReference() const string logMessage = "Create new ref"; string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); var oldtarget = repo.Refs[name].ResolveToDirectReference().Target.Id; - var newRef = (SymbolicReference)repo.Refs.Add(name, target, Constants.Signature, logMessage, true); + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + + var newRef = (SymbolicReference)repo.Refs.Add(name, target, logMessage, true); Assert.NotNull(newRef); Assert.Equal(name, newRef.CanonicalName); Assert.NotNull(newRef.Target); Assert.Equal("a4a7dce85cf63874e984719f4fdd239f5145052f", newRef.ResolveToDirectReference().Target.Sha); Assert.Equal(target, ((SymbolicReference)repo.Refs.Head).Target.CanonicalName); - AssertRefLogEntry(repo, name, + AssertRefLogEntry(repo, name, logMessage, + oldtarget, newRef.ResolveToDirectReference().Target.Id, - logMessage, oldtarget, - Constants.Signature); + Constants.Identity, before); } } @@ -498,36 +509,40 @@ public void CanUpdateHeadWithARevparseSpec() public void CanUpdateHeadWithEitherAnObjectIdOrAReference() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); Reference head = repo.Refs.Head; Reference test = repo.Refs["refs/heads/test"]; - Reference direct = repo.Refs.UpdateTarget(head, new ObjectId(test.TargetIdentifier), Constants.Signature, null); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + + Reference direct = repo.Refs.UpdateTarget(head, new ObjectId(test.TargetIdentifier), null); Assert.True((direct is DirectReference)); Assert.Equal(test.TargetIdentifier, direct.TargetIdentifier); Assert.Equal(repo.Refs.Head, direct); var testTargetId = test.ResolveToDirectReference().Target.Id; - AssertRefLogEntry(repo, "HEAD", - testTargetId, - null, + AssertRefLogEntry(repo, "HEAD", null, head.ResolveToDirectReference().Target.Id, - Constants.Signature); + testTargetId, + Constants.Identity, before); const string secondLogMessage = "second update target message"; - Reference symref = repo.Refs.UpdateTarget(head, test, Constants.Signature, secondLogMessage); + + before = DateTimeOffset.Now.TruncateMilliseconds(); + + Reference symref = repo.Refs.UpdateTarget(head, test, secondLogMessage); Assert.True((symref is SymbolicReference)); Assert.Equal(test.CanonicalName, symref.TargetIdentifier); Assert.Equal(repo.Refs.Head, symref); AssertRefLogEntry(repo, "HEAD", - testTargetId, secondLogMessage, testTargetId, - Constants.Signature); + testTargetId, + Constants.Identity, before); } } @@ -535,7 +550,7 @@ public void CanUpdateHeadWithEitherAnObjectIdOrAReference() public void CanUpdateTargetOfADirectReferenceWithARevparseSpec() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); @@ -545,7 +560,10 @@ public void CanUpdateTargetOfADirectReferenceWithARevparseSpec() var @from = master.Target.Id; const string logMessage = "update target message"; - var newRef = (DirectReference)repo.Refs.UpdateTarget(master, "master^1^2", Constants.Signature, logMessage); + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + + var newRef = (DirectReference)repo.Refs.UpdateTarget(master, "master^1^2", logMessage); Assert.NotNull(newRef); Assert.Equal(name, newRef.CanonicalName); Assert.NotNull(newRef.Target); @@ -554,10 +572,10 @@ public void CanUpdateTargetOfADirectReferenceWithARevparseSpec() Assert.NotNull(repo.Refs[name]); AssertRefLogEntry(repo, name, - newRef.Target.Id, logMessage, @from, - Constants.Signature); + newRef.Target.Id, + Constants.Identity, before); } } @@ -648,7 +666,7 @@ public void CanRenameAReferenceToAUpperReferenceHierarchy() public void CanRenameAReferenceToADifferentReferenceHierarchy() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path)) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { const string oldName = "refs/tags/test"; const string newName = "refs/atic/tagtest"; @@ -657,13 +675,18 @@ public void CanRenameAReferenceToADifferentReferenceHierarchy() var oldId = repo.Refs[oldName].ResolveToDirectReference().Target.Id; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Reference renamed = repo.Refs.Rename(oldName, newName); Assert.NotNull(renamed); Assert.Equal(newName, renamed.CanonicalName); Assert.Equal(oldId, renamed.ResolveToDirectReference().Target.Id); - AssertRefLogEntry(repo, newName, renamed.ResolveToDirectReference().Target.Id, - string.Format("reference: renamed {0} to {1}", oldName, newName)); + AssertRefLogEntry(repo, newName, + string.Format("reference: renamed {0} to {1}", oldName, newName), + oldId, + renamed.ResolveToDirectReference().Target.Id, + Constants.Identity, before); } } @@ -806,15 +829,15 @@ public void CanIdentifyReferenceKind() string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { - Assert.True(repo.Refs["refs/heads/master"].IsLocalBranch()); - Assert.True(repo.Refs["refs/remotes/origin/master"].IsRemoteTrackingBranch()); - Assert.True(repo.Refs["refs/tags/lw"].IsTag()); + Assert.True(repo.Refs["refs/heads/master"].IsLocalBranch); + Assert.True(repo.Refs["refs/remotes/origin/master"].IsRemoteTrackingBranch); + Assert.True(repo.Refs["refs/tags/lw"].IsTag); } path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.True(repo.Refs["refs/notes/commits"].IsNote()); + Assert.True(repo.Refs["refs/notes/commits"].IsNote); } } @@ -848,7 +871,7 @@ public void CanQueryReachabilityAmongASubsetOfreferences() using (var repo = new Repository(path)) { var result = repo.Refs.ReachableFrom( - repo.Refs.Where(r => r.IsTag()), + repo.Refs.Where(r => r.IsTag), new[] { repo.Lookup("f8d44d7"), repo.Lookup("6dcf9bf") }); var expected = new[] diff --git a/LibGit2Sharp.Tests/ReflogFixture.cs b/LibGit2Sharp.Tests/ReflogFixture.cs index 9f1339893..72f9faa0f 100644 --- a/LibGit2Sharp.Tests/ReflogFixture.cs +++ b/LibGit2Sharp.Tests/ReflogFixture.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -21,7 +22,7 @@ public void CanReadReflog() Assert.Equal(expectedReflogEntriesCount, reflog.Count()); // Initial commit assertions - Assert.Equal("timothy.clem@gmail.com", reflog.Last().Commiter.Email); + Assert.Equal("timothy.clem@gmail.com", reflog.Last().Committer.Email); Assert.True(reflog.Last().Message.StartsWith("clone: from")); Assert.Equal(ObjectId.Zero, reflog.Last().From); @@ -56,7 +57,9 @@ public void CommitShouldCreateReflogEntryOnHeadAndOnTargetedDirectReference() { string repoPath = InitNewRepository(); - using (var repo = new Repository(repoPath)) + var identity = Constants.Identity; + + using (var repo = new Repository(repoPath, new RepositoryOptions{ Identity = identity })) { // setup refs as HEAD => unit_test => master var newRef = repo.Refs.Add("refs/heads/unit_test", "refs/heads/master"); @@ -69,19 +72,33 @@ public void CommitShouldCreateReflogEntryOnHeadAndOnTargetedDirectReference() var author = Constants.Signature; const string commitMessage = "Hope reflog behaves as it should"; + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + Commit commit = repo.Commit(commitMessage, author, author); // Assert a reflog entry is created on HEAD Assert.Equal(1, repo.Refs.Log("HEAD").Count()); var reflogEntry = repo.Refs.Log("HEAD").First(); - Assert.Equal(author, reflogEntry.Commiter); + + Assert.Equal(identity.Name, reflogEntry.Committer.Name); + Assert.Equal(identity.Email, reflogEntry.Committer.Email); + + var now = DateTimeOffset.Now; + Assert.InRange(reflogEntry.Committer.When, before, now); + Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(ObjectId.Zero, reflogEntry.From); // Assert the same reflog entry is created on refs/heads/master Assert.Equal(1, repo.Refs.Log("refs/heads/master").Count()); reflogEntry = repo.Refs.Log("HEAD").First(); - Assert.Equal(author, reflogEntry.Commiter); + + Assert.Equal(identity.Name, reflogEntry.Committer.Name); + Assert.Equal(identity.Email, reflogEntry.Committer.Email); + + Assert.InRange(reflogEntry.Committer.When, before, now); + Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(ObjectId.Zero, reflogEntry.From); @@ -116,7 +133,9 @@ public void CommitOnDetachedHeadShouldInsertReflogEntry() { string repoPath = SandboxStandardTestRepo(); - using (var repo = new Repository(repoPath)) + var identity = Constants.Identity; + + using (var repo = new Repository(repoPath, new RepositoryOptions { Identity = identity })) { Assert.False(repo.Info.IsHeadDetached); @@ -130,11 +149,20 @@ public void CommitOnDetachedHeadShouldInsertReflogEntry() var author = Constants.Signature; const string commitMessage = "Commit on detached head"; + + var before = DateTimeOffset.Now.TruncateMilliseconds(); + var commit = repo.Commit(commitMessage, author, author); // Assert a reflog entry is created on HEAD var reflogEntry = repo.Refs.Log("HEAD").First(); - Assert.Equal(author, reflogEntry.Commiter); + + Assert.Equal(identity.Name, reflogEntry.Committer.Name); + Assert.Equal(identity.Email, reflogEntry.Committer.Email); + + var now = DateTimeOffset.Now; + Assert.InRange(reflogEntry.Committer.When, before, now); + Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(string.Format("commit: {0}", commitMessage), repo.Refs.Log("HEAD").First().Message); } @@ -174,7 +202,7 @@ public void AppendingToReflogDependsOnCoreLogAllRefUpdatesSetting(bool isBare, b public void UnsignedMethodsWriteCorrectlyToTheReflog() { var repoPath = InitNewRepository(true); - using (var repo = new Repository(repoPath)) + using (var repo = new Repository(repoPath, new RepositoryOptions{ Identity = Constants.Identity })) { EnableRefLog(repo); @@ -183,8 +211,11 @@ public void UnsignedMethodsWriteCorrectlyToTheReflog() var commit = repo.ObjectDatabase.CreateCommit(Constants.Signature, Constants.Signature, "yoink", tree, Enumerable.Empty(), false); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + var direct = repo.Refs.Add("refs/heads/direct", commit.Id); - AssertRefLogEntry(repo, direct.CanonicalName, direct.ResolveToDirectReference().Target.Id, null); + AssertRefLogEntry(repo, direct.CanonicalName, null, null, + direct.ResolveToDirectReference().Target.Id, Constants.Identity, before); var symbolic = repo.Refs.Add("refs/heads/symbolic", direct); Assert.Empty(repo.Refs.Log(symbolic)); // creation of symbolic refs doesn't update the reflog diff --git a/LibGit2Sharp.Tests/RemoteFixture.cs b/LibGit2Sharp.Tests/RemoteFixture.cs index 47d51390f..8b8c81133 100644 --- a/LibGit2Sharp.Tests/RemoteFixture.cs +++ b/LibGit2Sharp.Tests/RemoteFixture.cs @@ -91,6 +91,35 @@ public void CanSetRemoteUrl() r => r.Url = newUrl); Assert.Equal(newUrl, updatedremote.Url); + // with no push url set, PushUrl defaults to the fetch url + Assert.Equal(newUrl, updatedremote.PushUrl); + } + } + + [Fact] + public void CanSetRemotePushUrl() + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + const string name = "upstream"; + const string url = "https://github.com/libgit2/libgit2sharp.git"; + const string pushurl = "https://github.com/libgit2/libgit2.git"; + + repo.Network.Remotes.Add(name, url); + Remote remote = repo.Network.Remotes[name]; + Assert.NotNull(remote); + + // before setting push, both push and fetch urls should match + Assert.Equal(url, remote.Url); + Assert.Equal(url, remote.PushUrl); + + Remote updatedremote = repo.Network.Remotes.Update(remote, + r => r.PushUrl = pushurl); + + // url should not change, push url should be set to new value + Assert.Equal(url, updatedremote.Url); + Assert.Equal(pushurl, updatedremote.PushUrl); } } @@ -334,5 +363,43 @@ public void CanNotRenameWhenRemoteWithSameNameExists() Assert.Throws(() => repo.Network.Remotes.Rename("origin", "upstream")); } } + + [Theory] + [InlineData(null, null, false)] + [InlineData(null, false, false)] + [InlineData(null, true, true)] + [InlineData(false, null, false)] + [InlineData(false, false, false)] + [InlineData(false, true, true)] + [InlineData(true, null, true)] + [InlineData(true, false, false)] + [InlineData(true, true, true)] + public void ShoudlPruneOnFetchReflectsTheConfiguredSetting(bool? fetchPrune, bool? remotePrune, bool expectedFetchPrune) + { + var path = SandboxStandardTestRepo(); + var scd = BuildSelfCleaningDirectory(); + + using (var repo = new Repository(path, BuildFakeConfigs(scd))) + { + Assert.Null(repo.Config.Get("fetch.prune")); + Assert.Null(repo.Config.Get("remote.origin.prune")); + + SetIfNotNull(repo, "fetch.prune", fetchPrune); + SetIfNotNull(repo, "remote.origin.prune", remotePrune); + + var remote = repo.Network.Remotes["origin"]; + Assert.Equal(expectedFetchPrune, remote.AutomaticallyPruneOnFetch); + } + } + + private void SetIfNotNull(IRepository repo, string configName, bool? value) + { + if (!value.HasValue) + { + return; + } + + repo.Config.Set(configName, value.Value); + } } } diff --git a/LibGit2Sharp.Tests/RemoveFixture.cs b/LibGit2Sharp.Tests/RemoveFixture.cs index 52a3aba0f..94a376daf 100644 --- a/LibGit2Sharp.Tests/RemoveFixture.cs +++ b/LibGit2Sharp.Tests/RemoveFixture.cs @@ -14,36 +14,36 @@ public class RemoveFixture : BaseFixture * 'git rm --cached ' works (file removed only from index). * 'git rm ' works (file removed from both index and workdir). */ - [InlineData(false, "1/branch_file.txt", false, FileStatus.Unaltered, true, true, FileStatus.Untracked | FileStatus.Removed)] - [InlineData(true, "1/branch_file.txt", false, FileStatus.Unaltered, true, false, FileStatus.Removed)] + [InlineData(false, "1/branch_file.txt", false, FileStatus.Unaltered, true, true, FileStatus.NewInWorkdir | FileStatus.DeletedFromIndex)] + [InlineData(true, "1/branch_file.txt", false, FileStatus.Unaltered, true, false, FileStatus.DeletedFromIndex)] /*** * Test case: file exists in the index, and has been removed from the wd. * 'git rm and 'git rm --cached ' both work (file removed from the index) */ - [InlineData(true, "deleted_unstaged_file.txt", false, FileStatus.Missing, false, false, FileStatus.Removed)] - [InlineData(false, "deleted_unstaged_file.txt", false, FileStatus.Missing, false, false, FileStatus.Removed)] + [InlineData(true, "deleted_unstaged_file.txt", false, FileStatus.DeletedFromWorkdir, false, false, FileStatus.DeletedFromIndex)] + [InlineData(false, "deleted_unstaged_file.txt", false, FileStatus.DeletedFromWorkdir, false, false, FileStatus.DeletedFromIndex)] /*** * Test case: modified file in wd, the modifications have not been promoted to the index yet. * 'git rm --cached ' works (removes the file from the index) * 'git rm ' fails ("error: '' has local modifications"). */ - [InlineData(false, "modified_unstaged_file.txt", false, FileStatus.Modified, true, true, FileStatus.Untracked | FileStatus.Removed)] - [InlineData(true, "modified_unstaged_file.txt", true, FileStatus.Modified, true, true, 0)] + [InlineData(false, "modified_unstaged_file.txt", false, FileStatus.ModifiedInWorkdir, true, true, FileStatus.NewInWorkdir | FileStatus.DeletedFromIndex)] + [InlineData(true, "modified_unstaged_file.txt", true, FileStatus.ModifiedInWorkdir, true, true, 0)] /*** * Test case: modified file in wd, the modifications have already been promoted to the index. * 'git rm --cached ' works (removes the file from the index) * 'git rm ' fails ("error: '' has changes staged in the index") */ - [InlineData(false, "modified_staged_file.txt", false, FileStatus.Staged, true, true, FileStatus.Untracked | FileStatus.Removed)] - [InlineData(true, "modified_staged_file.txt", true, FileStatus.Staged, true, true, 0)] + [InlineData(false, "modified_staged_file.txt", false, FileStatus.ModifiedInIndex, true, true, FileStatus.NewInWorkdir | FileStatus.DeletedFromIndex)] + [InlineData(true, "modified_staged_file.txt", true, FileStatus.ModifiedInIndex, true, true, 0)] /*** * Test case: modified file in wd, the modifications have already been promoted to the index, and * the file does not exist in the HEAD. * 'git rm --cached ' works (removes the file from the index) * 'git rm ' throws ("error: '' has changes staged in the index") */ - [InlineData(false, "new_tracked_file.txt", false, FileStatus.Added, true, true, FileStatus.Untracked)] - [InlineData(true, "new_tracked_file.txt", true, FileStatus.Added, true, true, 0)] + [InlineData(false, "new_tracked_file.txt", false, FileStatus.NewInIndex, true, true, FileStatus.NewInWorkdir)] + [InlineData(true, "new_tracked_file.txt", true, FileStatus.NewInIndex, true, true, 0)] public void CanRemoveAnUnalteredFileFromTheIndexWithoutRemovingItFromTheWorkingDirectory( bool removeFromWorkdir, string filename, bool throws, FileStatus initialStatus, bool existsBeforeRemove, bool existsAfterRemove, FileStatus lastStatus) { @@ -91,7 +91,7 @@ public void RemovingAModifiedFileWhoseChangesHaveBeenPromotedToTheIndexAndWithAd Assert.Equal(true, File.Exists(fullpath)); File.AppendAllText(fullpath, "additional content"); - Assert.Equal(FileStatus.Staged | FileStatus.Modified, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.ModifiedInIndex | FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(filename)); Assert.Throws(() => repo.Remove(filename)); Assert.Throws(() => repo.Remove(filename, false)); @@ -136,7 +136,7 @@ public void CanRemoveAFolderThroughUsageOfPathspecsForFilesAlreadyInTheIndexAndI } [Theory] - [InlineData("deleted_staged_file.txt", FileStatus.Removed)] + [InlineData("deleted_staged_file.txt", FileStatus.DeletedFromIndex)] [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] public void RemovingAnUnknownFileWithLaxExplicitPathsValidationDoesntThrow(string relativePath, FileStatus status) { @@ -156,7 +156,7 @@ public void RemovingAnUnknownFileWithLaxExplicitPathsValidationDoesntThrow(strin } [Theory] - [InlineData("deleted_staged_file.txt", FileStatus.Removed)] + [InlineData("deleted_staged_file.txt", FileStatus.DeletedFromIndex)] [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] public void RemovingAnUnknownFileThrowsIfExplicitPath(string relativePath, FileStatus status) { diff --git a/LibGit2Sharp.Tests/RepositoryFixture.cs b/LibGit2Sharp.Tests/RepositoryFixture.cs index 381973348..509ff7bac 100644 --- a/LibGit2Sharp.Tests/RepositoryFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryFixture.cs @@ -4,6 +4,7 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; +using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -56,6 +57,26 @@ public void CanCheckIfADirectoryLeadsToAValidRepository() Assert.False(Repository.IsValid(scd.DirectoryPath)); } + + [Fact] + public void IsValidWithNullPathThrows() + { + Assert.Throws(() => Repository.IsValid(null)); + } + + [Fact] + public void IsNotValidWithEmptyPath() + { + Assert.False(Repository.IsValid(string.Empty)); + } + + [Fact] + public void IsValidWithValidPath() + { + string repoPath = InitNewRepository(); + Assert.True(Repository.IsValid(repoPath)); + } + [Fact] public void CanCreateStandardRepo() { @@ -243,10 +264,10 @@ private static void AssertInitializedRepository(IRepository repo, string expecte Assert.Equal(0, repo.Commits.Count()); Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter()).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs.Head }).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { Since = repo.Head }).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { Since = "HEAD" }).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { Since = expectedHeadTargetIdentifier }).Count()); + Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs.Head }).Count()); + Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Head }).Count()); + Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = "HEAD" }).Count()); + Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = expectedHeadTargetIdentifier }).Count()); Assert.Null(repo.Head["subdir/I-do-not-exist"]); @@ -651,5 +672,92 @@ public void CanDetectShallowness() Assert.False(repo.Info.IsShallow); } } + + [SkippableFact] + public void CanListRemoteReferencesWithCredentials() + { + InconclusiveIf(() => string.IsNullOrEmpty(Constants.PrivateRepoUrl), + "Populate Constants.PrivateRepo* to run this test"); + + IEnumerable references = Repository.ListRemoteReferences(Constants.PrivateRepoUrl, + Constants.PrivateRepoCredentials); + + foreach (var reference in references) + { + Assert.NotNull(reference); + } + } + + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] + [InlineData("git://github.com/libgit2/TestGitRepository.git")] + public void CanListRemoteReferences(string url) + { + IEnumerable references = Repository.ListRemoteReferences(url).ToList(); + + List> actualRefs = references. + Select(reference => new Tuple(reference.CanonicalName, reference.ResolveToDirectReference().TargetIdentifier)).ToList(); + + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count); + Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference); + for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++) + { + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + } + } + + [Fact] + public void CanListRemoteReferencesWithDetachedRemoteHead() + { + string originalRepoPath = SandboxStandardTestRepo(); + + string detachedHeadSha; + + using (var originalRepo = new Repository(originalRepoPath)) + { + detachedHeadSha = originalRepo.Head.Tip.Sha; + originalRepo.Checkout(detachedHeadSha); + + Assert.True(originalRepo.Info.IsHeadDetached); + } + + IEnumerable references = Repository.ListRemoteReferences(originalRepoPath); + + Reference head = references.SingleOrDefault(reference => reference.CanonicalName == "HEAD"); + + Assert.NotNull(head); + Assert.True(head is DirectReference); + Assert.Equal(detachedHeadSha, head.TargetIdentifier); + } + + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + public void ReadingReferenceRepositoryThroughListRemoteReferencesThrows(string url) + { + IEnumerable references = Repository.ListRemoteReferences(url); + + foreach (var reference in references) + { + IBelongToARepository repositoryReference = reference; + Assert.Throws(() => repositoryReference.Repository); + } + } + + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + public void ReadingReferenceTargetFromListRemoteReferencesThrows(string url) + { + IEnumerable references = Repository.ListRemoteReferences(url); + + foreach (var reference in references) + { + Assert.Throws(() => + { + var target = reference.ResolveToDirectReference().Target; + }); + } + } } } diff --git a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs index 1d2072863..496cf3029 100644 --- a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs @@ -35,7 +35,7 @@ public void CanOpenABareRepoAsIfItWasAStandardOne() using (var repo = new Repository(path, options)) { var st = repo.RetrieveStatus("1/branch_file.txt"); - Assert.Equal(FileStatus.Missing, st); + Assert.Equal(FileStatus.DeletedFromWorkdir, st); } } @@ -48,7 +48,7 @@ public void CanOpenABareRepoAsIfItWasAStandardOneWithANonExisitingIndexFile() using (var repo = new Repository(path, options)) { var st = repo.RetrieveStatus("1/branch_file.txt"); - Assert.Equal(FileStatus.Removed, st); + Assert.Equal(FileStatus.DeletedFromIndex, st); } } @@ -78,7 +78,7 @@ public void CanProvideADifferentWorkDirToAStandardRepo() var path2 = SandboxStandardTestRepo(); using (var repo = new Repository(path2, options)) { - Assert.Equal(FileStatus.Missing, repo.RetrieveStatus("1/branch_file.txt")); + Assert.Equal(FileStatus.DeletedFromWorkdir, repo.RetrieveStatus("1/branch_file.txt")); } } @@ -88,11 +88,11 @@ public void CanProvideADifferentIndexToAStandardRepo() var path1 = SandboxStandardTestRepo(); using (var repo = new Repository(path1)) { - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus("new_untracked_file.txt")); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus("new_untracked_file.txt")); repo.Stage("new_untracked_file.txt"); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus("new_untracked_file.txt")); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus("new_untracked_file.txt")); File.Copy(Path.Combine(repo.Info.Path, "index"), newIndex); } @@ -102,7 +102,7 @@ public void CanProvideADifferentIndexToAStandardRepo() var path2 = SandboxStandardTestRepo(); using (var repo = new Repository(path2, options)) { - Assert.Equal(FileStatus.Added, repo.RetrieveStatus("new_untracked_file.txt")); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus("new_untracked_file.txt")); } } @@ -131,7 +131,7 @@ public void CanSneakAdditionalCommitsIntoAStandardRepoWithoutAlteringTheWorkdirO Assert.NotEqual(head.Tip.Sha, newHead.Tip.Sha); Assert.Equal(commitSha, newHead.Tip.Sha); - Assert.Equal(FileStatus.Removed, repo.RetrieveStatus("zomg.txt")); + Assert.Equal(FileStatus.DeletedFromIndex, repo.RetrieveStatus("zomg.txt")); } } diff --git a/LibGit2Sharp.Tests/ResetHeadFixture.cs b/LibGit2Sharp.Tests/ResetHeadFixture.cs index b1cfda8df..2a2927328 100644 --- a/LibGit2Sharp.Tests/ResetHeadFixture.cs +++ b/LibGit2Sharp.Tests/ResetHeadFixture.cs @@ -80,7 +80,7 @@ public void ResettingWithBadParamsThrows() public void SoftResetSetsTheHeadToTheSpecifiedCommit() { /* Make the Head point to a branch through its name */ - AssertSoftReset(b => b.Name, false, b => b.Name); + AssertSoftReset(b => b.FriendlyName, false, b => b.FriendlyName); } [Fact] @@ -94,7 +94,7 @@ private void AssertSoftReset(Func branchIdentifierRetriever, boo { string repoPath = InitNewRepository(); - using (var repo = new Repository(repoPath)) + using (var repo = new Repository(repoPath, new RepositoryOptions{ Identity = Constants.Identity })) { FeedTheRepository(repo); @@ -107,49 +107,55 @@ private void AssertSoftReset(Func branchIdentifierRetriever, boo Assert.Equal(shouldHeadBeDetached, repo.Info.IsHeadDetached); string expectedHeadName = expectedHeadNameRetriever(branch); - Assert.Equal(expectedHeadName, repo.Head.Name); + Assert.Equal(expectedHeadName, repo.Head.FriendlyName); Assert.Equal(branch.Tip.Sha, repo.Head.Tip.Sha); + var before = DateTimeOffset.Now.TruncateMilliseconds(); + /* Reset --soft the Head to a tag through its canonical name */ repo.Reset(ResetMode.Soft, tag.CanonicalName); - Assert.Equal(expectedHeadName, repo.Head.Name); + Assert.Equal(expectedHeadName, repo.Head.FriendlyName); Assert.Equal(tag.Target.Id, repo.Head.Tip.Id); - Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("a.txt")); + Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus("a.txt")); AssertRefLogEntry(repo, "HEAD", - tag.Target.Id, string.Format("reset: moving to {0}", tag.Target.Sha), - oldHeadId); + oldHeadId, + tag.Target.Id, + Constants.Identity, before); if (!shouldHeadBeDetached) { AssertRefLogEntry(repo, branch.CanonicalName, - tag.Target.Id, string.Format("reset: moving to {0}", tag.Target.Sha), - oldHeadId); + oldHeadId, + tag.Target.Id, + Constants.Identity, before); } + before = DateTimeOffset.Now.TruncateMilliseconds(); + /* Reset --soft the Head to a commit through its sha */ - repo.Reset(ResetMode.Soft, branch.Tip.Sha, Constants.Signature, "FOO"); - Assert.Equal(expectedHeadName, repo.Head.Name); + repo.Reset(ResetMode.Soft, branch.Tip.Sha); + Assert.Equal(expectedHeadName, repo.Head.FriendlyName); Assert.Equal(branch.Tip.Sha, repo.Head.Tip.Sha); Assert.Equal(FileStatus.Unaltered, repo.RetrieveStatus("a.txt")); AssertRefLogEntry(repo, "HEAD", - branch.Tip.Id, - "FOO", + string.Format("reset: moving to {0}", branch.Tip.Sha), tag.Target.Id, - Constants.Signature); + branch.Tip.Id, + Constants.Identity, before); if (!shouldHeadBeDetached) { AssertRefLogEntry(repo, branch.CanonicalName, - branch.Tip.Id, - "FOO", + string.Format("reset: moving to {0}", branch.Tip.Sha), tag.Target.Id, - Constants.Signature); + branch.Tip.Id, + Constants.Identity, before); } } } @@ -178,7 +184,7 @@ public void MixedResetRefreshesTheIndex() { string repoPath = InitNewRepository(); - using (var repo = new Repository(repoPath)) + using (var repo = new Repository(repoPath, new RepositoryOptions { Identity = Constants.Identity })) { FeedTheRepository(repo); @@ -186,19 +192,23 @@ public void MixedResetRefreshesTheIndex() Tag tag = repo.Tags["mytag"]; + var before = DateTimeOffset.Now.TruncateMilliseconds(); + repo.Reset(ResetMode.Mixed, tag.CanonicalName); - Assert.Equal(FileStatus.Modified, repo.RetrieveStatus("a.txt")); + Assert.Equal(FileStatus.ModifiedInWorkdir, repo.RetrieveStatus("a.txt")); AssertRefLogEntry(repo, "HEAD", - tag.Target.Id, string.Format("reset: moving to {0}", tag.Target.Sha), - oldHeadId); + oldHeadId, + tag.Target.Id, + Constants.Identity, before); AssertRefLogEntry(repo, "refs/heads/mybranch", - tag.Target.Id, string.Format("reset: moving to {0}", tag.Target.Sha), - oldHeadId); + oldHeadId, + tag.Target.Id, + Constants.Identity, before); } } @@ -225,6 +235,8 @@ public void HardResetInABareRepositoryThrows() [Fact] public void HardResetUpdatesTheContentOfTheWorkingDirectory() { + bool progressCalled = false; + string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { @@ -235,11 +247,16 @@ public void HardResetUpdatesTheContentOfTheWorkingDirectory() Assert.True(names.Count > 4); - repo.Reset(ResetMode.Hard, "HEAD~3"); + var commit = repo.Lookup("HEAD~3"); + repo.Reset(ResetMode.Hard, commit, new CheckoutOptions() + { + OnCheckoutProgress = (_path, _completed, _total) => { progressCalled = true; }, + }); names = new DirectoryInfo(repo.Info.WorkingDirectory).GetFileSystemInfos().Select(fsi => fsi.Name).ToList(); names.Sort(StringComparer.Ordinal); + Assert.Equal(true, progressCalled); Assert.Equal(new[] { ".git", "README", "WillNotBeRemoved.txt", "branch_file.txt", "new.txt", "new_untracked_file.txt" }, names); } } diff --git a/LibGit2Sharp.Tests/ResetIndexFixture.cs b/LibGit2Sharp.Tests/ResetIndexFixture.cs index a87c4ef23..082566218 100644 --- a/LibGit2Sharp.Tests/ResetIndexFixture.cs +++ b/LibGit2Sharp.Tests/ResetIndexFixture.cs @@ -14,18 +14,7 @@ public void ResetANewlyInitializedBareRepositoryThrows() using (var repo = new Repository(repoPath)) { - Assert.Throws(() => repo.Reset()); - } - } - - [Fact] - public void ResetANewlyInitializedNonBareRepositoryThrows() - { - string repoPath = InitNewRepository(false); - - using (var repo = new Repository(repoPath)) - { - Assert.Throws(() => repo.Reset()); + Assert.Throws(() => repo.Index.Replace(repo.Head.Tip)); } } @@ -35,23 +24,23 @@ public void ResettingInABareRepositoryThrows() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Throws(() => repo.Reset()); + Assert.Throws(() => repo.Index.Replace(repo.Head.Tip)); } } private static bool IsStaged(StatusEntry entry) { - if ((entry.State & FileStatus.Added) == FileStatus.Added) + if ((entry.State & FileStatus.NewInIndex) == FileStatus.NewInIndex) { return true; } - if ((entry.State & FileStatus.Staged) == FileStatus.Staged) + if ((entry.State & FileStatus.ModifiedInIndex) == FileStatus.ModifiedInIndex) { return true; } - if ((entry.State & FileStatus.Removed) == FileStatus.Removed) + if ((entry.State & FileStatus.DeletedFromIndex) == FileStatus.DeletedFromIndex) { return true; } @@ -70,7 +59,7 @@ public void ResetTheIndexWithTheHeadUnstagesEverything() var reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count(); - repo.Reset(); + repo.Index.Replace(repo.Head.Tip); RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.Where(IsStaged).Count()); @@ -80,31 +69,13 @@ public void ResetTheIndexWithTheHeadUnstagesEverything() } } - [Fact] - public void CanResetTheIndexToTheContentOfACommitWithCommittishAsArgument() - { - string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) - { - repo.Reset("be3563a"); - - RepositoryStatus newStatus = repo.RetrieveStatus(); - - var expected = new[] { "1.txt", Path.Combine("1", "branch_file.txt"), "deleted_staged_file.txt", - "deleted_unstaged_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt" }; - - Assert.Equal(expected.Length, newStatus.Where(IsStaged).Count()); - Assert.Equal(expected, newStatus.Removed.Select(s => s.FilePath)); - } - } - [Fact] public void CanResetTheIndexToTheContentOfACommitWithCommitAsArgument() { string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { - repo.Reset(repo.Lookup("be3563a")); + repo.Index.Replace(repo.Lookup("be3563a")); RepositoryStatus newStatus = repo.RetrieveStatus(); @@ -116,26 +87,13 @@ public void CanResetTheIndexToTheContentOfACommitWithCommitAsArgument() } } - [Fact] - public void CanResetTheIndexToASubsetOfTheContentOfACommitWithCommittishAsArgument() - { - string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path)) - { - repo.Reset("5b5b025", new[]{ "new.txt" }); - - Assert.Equal("a8233120f6ad708f843d861ce2b7228ec4e3dec6", repo.Index["README"].Id.Sha); - Assert.Equal("fa49b077972391ad58037050f2a75f74e3671e92", repo.Index["new.txt"].Id.Sha); - } - } - [Fact] public void CanResetTheIndexToASubsetOfTheContentOfACommitWithCommitAsArgumentAndLaxUnmatchedExplicitPathsValidation() { string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { - repo.Reset(repo.Lookup("5b5b025"), new[] { "new.txt", "non-existent-path-28.txt" }, + repo.Index.Replace(repo.Lookup("5b5b025"), new[] { "new.txt", "non-existent-path-28.txt" }, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); Assert.Equal("a8233120f6ad708f843d861ce2b7228ec4e3dec6", repo.Index["README"].Id.Sha); @@ -149,7 +107,7 @@ public void ResettingTheIndexToASubsetOfTheContentOfACommitWithCommitAsArgumentA using (var repo = new Repository(SandboxStandardTestRepo())) { Assert.Throws(() => - repo.Reset(repo.Lookup("5b5b025"), new[] { "new.txt", "non-existent-path-28.txt" }, new ExplicitPathsOptions())); + repo.Index.Replace(repo.Lookup("5b5b025"), new[] { "new.txt", "non-existent-path-28.txt" }, new ExplicitPathsOptions())); } } @@ -159,7 +117,7 @@ public void CanResetTheIndexWhenARenameExists() using (var repo = new Repository(SandboxStandardTestRepo())) { repo.Move("branch_file.txt", "renamed_branch_file.txt"); - repo.Reset(repo.Lookup("32eab9c")); + repo.Index.Replace(repo.Lookup("32eab9c")); RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(0, status.Where(IsStaged).Count()); @@ -178,12 +136,12 @@ public void CanResetSourceOfARenameInIndex() Assert.Equal(FileStatus.Nonexistent, oldStatus["branch_file.txt"].State); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); - repo.Reset(repo.Lookup("32eab9c"), new string[] { "branch_file.txt" }); + repo.Index.Replace(repo.Lookup("32eab9c"), new string[] { "branch_file.txt" }); RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); - Assert.Equal(FileStatus.Missing, newStatus["branch_file.txt"].State); - Assert.Equal(FileStatus.Added, newStatus["renamed_branch_file.txt"].State); + Assert.Equal(FileStatus.DeletedFromWorkdir, newStatus["branch_file.txt"].State); + Assert.Equal(FileStatus.NewInIndex, newStatus["renamed_branch_file.txt"].State); } } @@ -198,12 +156,12 @@ public void CanResetTargetOfARenameInIndex() Assert.Equal(1, oldStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); - repo.Reset(repo.Lookup("32eab9c"), new string[] { "renamed_branch_file.txt" }); + repo.Index.Replace(repo.Lookup("32eab9c"), new string[] { "renamed_branch_file.txt" }); RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); - Assert.Equal(FileStatus.Untracked, newStatus["renamed_branch_file.txt"].State); - Assert.Equal(FileStatus.Removed, newStatus["branch_file.txt"].State); + Assert.Equal(FileStatus.NewInWorkdir, newStatus["renamed_branch_file.txt"].State); + Assert.Equal(FileStatus.DeletedFromIndex, newStatus["branch_file.txt"].State); } } } diff --git a/LibGit2Sharp.Tests/Resources/.gitattributes b/LibGit2Sharp.Tests/Resources/.gitattributes index 23d9bdb29..48eb3141c 100644 --- a/LibGit2Sharp.Tests/Resources/.gitattributes +++ b/LibGit2Sharp.Tests/Resources/.gitattributes @@ -1 +1,4 @@ * binary +.gitattributes diff +.gitignore diff +config diff diff --git a/LibGit2Sharp.Tests/Resources/.gitignore b/LibGit2Sharp.Tests/Resources/.gitignore index 29ebe04b9..6b70b2273 100644 --- a/LibGit2Sharp.Tests/Resources/.gitignore +++ b/LibGit2Sharp.Tests/Resources/.gitignore @@ -1 +1,6 @@ *.sample +COMMIT_EDITMSG +exclude +logs/ +!testrepo_wd/dot_git/logs/ +description diff --git a/LibGit2Sharp.Tests/Resources/assume_unchanged_wd/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/assume_unchanged_wd/dot_git/logs/HEAD deleted file mode 100644 index 8a423a7ae..000000000 --- a/LibGit2Sharp.Tests/Resources/assume_unchanged_wd/dot_git/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 872129051d644790636b416d1ef1ec830c5f6b90 nulltoken 1422023333 +0100 commit (initial): Initial commit diff --git a/LibGit2Sharp.Tests/Resources/assume_unchanged_wd/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/assume_unchanged_wd/dot_git/logs/refs/heads/master deleted file mode 100644 index 8a423a7ae..000000000 --- a/LibGit2Sharp.Tests/Resources/assume_unchanged_wd/dot_git/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 872129051d644790636b416d1ef1ec830c5f6b90 nulltoken 1422023333 +0100 commit (initial): Initial commit diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/description b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/HEAD deleted file mode 100644 index 9e2a5c74a..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/HEAD +++ /dev/null @@ -1,9 +0,0 @@ -0000000000000000000000000000000000000000 a3ccc66533922b02858da9ed489cd19d573d6378 Jameson Miller 1393902455 -0500 commit (initial): Initial commit -a3ccc66533922b02858da9ed489cd19d573d6378 83cebf5389a4adbcb80bda6b68513caee4559802 Jameson Miller 1393902574 -0500 commit: Second commit on master -83cebf5389a4adbcb80bda6b68513caee4559802 83cebf5389a4adbcb80bda6b68513caee4559802 Jameson Miller 1393902595 -0500 checkout: moving from master to fast_forward -83cebf5389a4adbcb80bda6b68513caee4559802 4dfaa1500526214ae7b33f9b2c1144ca8b6b1f53 Jameson Miller 1393902643 -0500 commit: Commit on fast_forward -4dfaa1500526214ae7b33f9b2c1144ca8b6b1f53 a3ccc66533922b02858da9ed489cd19d573d6378 Jameson Miller 1393902655 -0500 checkout: moving from fast_forward to normal_merge -a3ccc66533922b02858da9ed489cd19d573d6378 625186280ed2a6ec9b65d250ed90cf2e4acef957 Jameson Miller 1393902725 -0500 commit: Commit on normal_merge -625186280ed2a6ec9b65d250ed90cf2e4acef957 a3ccc66533922b02858da9ed489cd19d573d6378 Jameson Miller 1393902735 -0500 checkout: moving from normal_merge to conflicts -a3ccc66533922b02858da9ed489cd19d573d6378 0771966a0d112d3787006a6fda5b6226a770e15a Jameson Miller 1393902760 -0500 commit: Commit on conflicts -0771966a0d112d3787006a6fda5b6226a770e15a 83cebf5389a4adbcb80bda6b68513caee4559802 Jameson Miller 1393907470 -0500 checkout: moving from conflicts to master diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/conflicts b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/conflicts deleted file mode 100644 index bf17c1056..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/conflicts +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 a3ccc66533922b02858da9ed489cd19d573d6378 Jameson Miller 1393902539 -0500 branch: Created from master -a3ccc66533922b02858da9ed489cd19d573d6378 0771966a0d112d3787006a6fda5b6226a770e15a Jameson Miller 1393902760 -0500 commit: Commit on conflicts diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/fast_forward b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/fast_forward deleted file mode 100644 index 9b3d80c28..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/fast_forward +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 83cebf5389a4adbcb80bda6b68513caee4559802 Jameson Miller 1393902591 -0500 branch: Created from master -83cebf5389a4adbcb80bda6b68513caee4559802 4dfaa1500526214ae7b33f9b2c1144ca8b6b1f53 Jameson Miller 1393902643 -0500 commit: Commit on fast_forward diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/master deleted file mode 100644 index 8201a1408..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/master +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 a3ccc66533922b02858da9ed489cd19d573d6378 Jameson Miller 1393902455 -0500 commit (initial): Initial commit -a3ccc66533922b02858da9ed489cd19d573d6378 83cebf5389a4adbcb80bda6b68513caee4559802 Jameson Miller 1393902574 -0500 commit: Second commit on master diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/normal_merge b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/normal_merge deleted file mode 100644 index e17413bdb..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/normal_merge +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 a3ccc66533922b02858da9ed489cd19d573d6378 Jameson Miller 1393902530 -0500 branch: Created from master -a3ccc66533922b02858da9ed489cd19d573d6378 625186280ed2a6ec9b65d250ed90cf2e4acef957 Jameson Miller 1393902725 -0500 commit: Commit on normal_merge diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/rename b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/rename deleted file mode 100644 index a2466a9d2..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/rename +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 83cebf5389a4adbcb80bda6b68513caee4559802 Jameson Miller 1398366152 -0400 branch: Created from HEAD -83cebf5389a4adbcb80bda6b68513caee4559802 9075c06ff9cd736610dea688bca7e912903ff2d1 Jameson Miller 1398366254 -0400 commit: Add content to b.txt -9075c06ff9cd736610dea688bca7e912903ff2d1 24434077dec097c1203ef9e1345c0545c190936a Jameson Miller 1398366641 -0400 commit: edit to b.txt diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/rename_source b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/rename_source deleted file mode 100644 index 1515dd26e..000000000 --- a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/logs/refs/heads/rename_source +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 9075c06ff9cd736610dea688bca7e912903ff2d1 Jameson Miller 1398366267 -0400 branch: Created from rename -9075c06ff9cd736610dea688bca7e912903ff2d1 2bc71d0e8acfbb9fd1cc2d9d48c23dbf8aea52c9 Jameson Miller 1398366593 -0400 commit: rename and edit b.txt diff --git a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config index 78387c50b..277bd5530 100644 --- a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config +++ b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config @@ -6,3 +6,4 @@ symlinks = false ignorecase = true hideDotFiles = dotGitOnly + autocrlf = false diff --git a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/description b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/HEAD deleted file mode 100644 index 839dfc811..000000000 --- a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/HEAD +++ /dev/null @@ -1,15 +0,0 @@ -0000000000000000000000000000000000000000 9107b30fe11ff310fec93876469147b263fb958c Edward Thomson 1360632480 -0600 commit (initial): ancestor -9107b30fe11ff310fec93876469147b263fb958c 9107b30fe11ff310fec93876469147b263fb958c Edward Thomson 1360632483 -0600 checkout: moving from master to branch -9107b30fe11ff310fec93876469147b263fb958c d4c107581ff54a41fd496e0a1f1b7b3354752103 Edward Thomson 1360632833 -0600 commit: branch -d4c107581ff54a41fd496e0a1f1b7b3354752103 9107b30fe11ff310fec93876469147b263fb958c Edward Thomson 1360632835 -0600 checkout: moving from branch to master -9107b30fe11ff310fec93876469147b263fb958c 8b19fd77de5995e6e502e7c3b82657248f510325 Edward Thomson 1360633224 -0600 commit: ours -8b19fd77de5995e6e502e7c3b82657248f510325 91116bad97c50a2daa278ca6cffe16d0118befe5 Edward Thomson 1360633337 -0600 commit: ours -91116bad97c50a2daa278ca6cffe16d0118befe5 8b19fd77de5995e6e502e7c3b82657248f510325 Edward Thomson 1360633358 -0600 checkout: moving from master to 8b19fd7 -8b19fd77de5995e6e502e7c3b82657248f510325 9b2f98b1c086d3929eac387d39fae94425e0115f Edward Thomson 1360633359 -0600 rebase -i (squash): ours -9b2f98b1c086d3929eac387d39fae94425e0115f 9b2f98b1c086d3929eac387d39fae94425e0115f Edward Thomson 1360633362 -0600 rebase -i (finish): returning to refs/heads/master -9b2f98b1c086d3929eac387d39fae94425e0115f d4c107581ff54a41fd496e0a1f1b7b3354752103 Edward Thomson 1360633367 -0600 checkout: moving from master to branch -d4c107581ff54a41fd496e0a1f1b7b3354752103 493209f496e504d63fd78e8c5b800083b442ea34 Edward Thomson 1360633416 -0600 commit: branch -493209f496e504d63fd78e8c5b800083b442ea34 d4c107581ff54a41fd496e0a1f1b7b3354752103 Edward Thomson 1360633427 -0600 checkout: moving from branch to d4c1075 -d4c107581ff54a41fd496e0a1f1b7b3354752103 8eefb6f8061e4d10a7a59aac8771dc17958d93eb Edward Thomson 1360633429 -0600 rebase -i (squash): branch -8eefb6f8061e4d10a7a59aac8771dc17958d93eb 8eefb6f8061e4d10a7a59aac8771dc17958d93eb Edward Thomson 1360633436 -0600 rebase -i (finish): returning to refs/heads/branch -8eefb6f8061e4d10a7a59aac8771dc17958d93eb 9b2f98b1c086d3929eac387d39fae94425e0115f Edward Thomson 1360633440 -0600 checkout: moving from branch to master diff --git a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/refs/heads/branch b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/refs/heads/branch deleted file mode 100644 index 13819577f..000000000 --- a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/refs/heads/branch +++ /dev/null @@ -1,4 +0,0 @@ -0000000000000000000000000000000000000000 9107b30fe11ff310fec93876469147b263fb958c Edward Thomson 1360632483 -0600 branch: Created from HEAD -9107b30fe11ff310fec93876469147b263fb958c d4c107581ff54a41fd496e0a1f1b7b3354752103 Edward Thomson 1360632833 -0600 commit: branch -d4c107581ff54a41fd496e0a1f1b7b3354752103 493209f496e504d63fd78e8c5b800083b442ea34 Edward Thomson 1360633416 -0600 commit: branch -493209f496e504d63fd78e8c5b800083b442ea34 8eefb6f8061e4d10a7a59aac8771dc17958d93eb Edward Thomson 1360633436 -0600 rebase -i (finish): refs/heads/branch onto d4c1075 diff --git a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/refs/heads/master deleted file mode 100644 index 167ae3e56..000000000 --- a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/logs/refs/heads/master +++ /dev/null @@ -1,4 +0,0 @@ -0000000000000000000000000000000000000000 9107b30fe11ff310fec93876469147b263fb958c Edward Thomson 1360632480 -0600 commit (initial): ancestor -9107b30fe11ff310fec93876469147b263fb958c 8b19fd77de5995e6e502e7c3b82657248f510325 Edward Thomson 1360633224 -0600 commit: ours -8b19fd77de5995e6e502e7c3b82657248f510325 91116bad97c50a2daa278ca6cffe16d0118befe5 Edward Thomson 1360633337 -0600 commit: ours -91116bad97c50a2daa278ca6cffe16d0118befe5 9b2f98b1c086d3929eac387d39fae94425e0115f Edward Thomson 1360633362 -0600 rebase -i (finish): refs/heads/master onto 8b19fd7 diff --git a/LibGit2Sharp.Tests/Resources/mergerenames_wd/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/mergerenames_wd/dot_git/logs/HEAD deleted file mode 100644 index 7b5293fd3..000000000 --- a/LibGit2Sharp.Tests/Resources/mergerenames_wd/dot_git/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1365715620 -0500 moving to bd59328 diff --git a/LibGit2Sharp.Tests/Resources/mergerenames_wd/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/mergerenames_wd/dot_git/logs/refs/heads/master deleted file mode 100644 index a46518da8..000000000 --- a/LibGit2Sharp.Tests/Resources/mergerenames_wd/dot_git/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/HEAD b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/config b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/config new file mode 100644 index 000000000..78387c50b --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/index b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/index new file mode 100644 index 000000000..1af67f2d4 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/index differ diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/87/2129051d644790636b416d1ef1ec830c5f6b90 b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/87/2129051d644790636b416d1ef1ec830c5f6b90 new file mode 100644 index 000000000..3c800a797 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/87/2129051d644790636b416d1ef1ec830c5f6b90 @@ -0,0 +1,3 @@ +xI +B1]>'[ oU/Zni"5SQhC.n=? \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/88/e38705fdbd3608cddbe904b67c731f3234c45b b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/88/e38705fdbd3608cddbe904b67c731f3234c45b new file mode 100644 index 000000000..783449ff9 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/88/e38705fdbd3608cddbe904b67c731f3234c45b differ diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/cc/628ccd10742baea8241c5924df992b5c019f71 b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/cc/628ccd10742baea8241c5924df992b5c019f71 new file mode 100644 index 000000000..6b011038d Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/cc/628ccd10742baea8241c5924df992b5c019f71 differ diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/ce/013625030ba8dba906f756967f9e9ca394464a b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/ce/013625030ba8dba906f756967f9e9ca394464a new file mode 100644 index 000000000..6802d4949 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/objects/ce/013625030ba8dba906f756967f9e9ca394464a differ diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/refs/heads/master b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/refs/heads/master new file mode 100644 index 000000000..2ed6cd9fa --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/dot_git/refs/heads/master @@ -0,0 +1 @@ +872129051d644790636b416d1ef1ec830c5f6b90 diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/hello.txt b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/hello.txt new file mode 100644 index 000000000..e965047ad --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/hello.txt @@ -0,0 +1 @@ +Hello diff --git a/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/world.txt b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/world.txt new file mode 100644 index 000000000..216e97ce0 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/packbuilder_testrepo_wd/world.txt @@ -0,0 +1 @@ +World diff --git a/LibGit2Sharp.Tests/Resources/submodule_small_wd/dot_git/description b/LibGit2Sharp.Tests/Resources/submodule_small_wd/dot_git/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_small_wd/dot_git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/description b/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/logs/HEAD deleted file mode 100644 index 0ecd1113f..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/logs/HEAD +++ /dev/null @@ -1,4 +0,0 @@ -0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer 1342559662 -0700 commit (initial): Initial commit -6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer 1342559709 -0700 commit: Adding test file -41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer 1342559726 -0700 commit: Updating test file -5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342559925 -0700 commit: One more update diff --git a/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/logs/refs/heads/master deleted file mode 100644 index 0ecd1113f..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_target_wd/dot_git/logs/refs/heads/master +++ /dev/null @@ -1,4 +0,0 @@ -0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer 1342559662 -0700 commit (initial): Initial commit -6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer 1342559709 -0700 commit: Adding test file -41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer 1342559726 -0700 commit: Updating test file -5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342559925 -0700 commit: One more update diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/config index d8535d3ac..7eada4ff5 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/config @@ -18,3 +18,5 @@ url = ../submodule_target_wd [submodule "sm_added_and_uncommited"] url = ../submodule_target_wd +[submodule "sm_branch_only"] + url = ../submodule_target_wd diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/logs/HEAD deleted file mode 100644 index 2cf2ca74d..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/logs/HEAD +++ /dev/null @@ -1,4 +0,0 @@ -0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer 1342559771 -0700 commit (initial): Initial commit -14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer 1342559831 -0700 commit: Adding a submodule -a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer 1342560036 -0700 commit: Updating submodule -5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer 1342560288 -0700 commit: Adding a bunch more test content diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/logs/refs/heads/master deleted file mode 100644 index 2cf2ca74d..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/logs/refs/heads/master +++ /dev/null @@ -1,4 +0,0 @@ -0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer 1342559771 -0700 commit (initial): Initial commit -14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer 1342559831 -0700 commit: Adding a submodule -a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer 1342560036 -0700 commit: Updating submodule -5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer 1342560288 -0700 commit: Adding a bunch more test content diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/config index 2d0583e99..e37936d26 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/config @@ -7,7 +7,7 @@ ignorecase = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* - url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target + url = ../../../../submodule_target_wd [branch "master"] remote = origin merge = refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/HEAD deleted file mode 100644 index 53753e7dd..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/refs/heads/master deleted file mode 100644 index 53753e7dd..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD deleted file mode 100644 index 53753e7dd..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/config new file mode 100644 index 000000000..d00ee8884 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/config @@ -0,0 +1,16 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + worktree = ../../../sm_branch_only + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly +[remote "origin"] + url = ../../../../submodule_target_wd + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master + rebase = true diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/index b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/index new file mode 100644 index 000000000..a30232ec4 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/index differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 new file mode 100644 index 000000000..f4b7094c5 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 new file mode 100644 index 000000000..56c845e49 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 new file mode 100644 index 000000000..bd179b5f5 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/41/bd4bc3df978de695f67ace64c560913da11653 new file mode 100644 index 000000000..ccf49bd15 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/41/bd4bc3df978de695f67ace64c560913da11653 differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 new file mode 100644 index 000000000..53029069a Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/5e/4963595a9774b90524d35a807169049de8ccad b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/5e/4963595a9774b90524d35a807169049de8ccad new file mode 100644 index 000000000..38c791eba Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/5e/4963595a9774b90524d35a807169049de8ccad differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/6b/31c659545507c381e9cd34ec508f16c04e149e new file mode 100644 index 000000000..a26d29993 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/6b/31c659545507c381e9cd34ec508f16c04e149e @@ -0,0 +1,2 @@ +xQ +!Evoy*_@g#hOh^9wSòf1*[Ic Ԥpk Α\S߇l@.^QpF(:D5zr~ en8 \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/73/ba924a80437097795ae839e66e187c55d3babf b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/73/ba924a80437097795ae839e66e187c55d3babf new file mode 100644 index 000000000..83d1ba481 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/73/ba924a80437097795ae839e66e187c55d3babf differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a new file mode 100644 index 000000000..6d27af8a8 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a @@ -0,0 +1,2 @@ +x-10 Fa0p(N-ӡғq]>ks*? |m“i@mV'`).-1 x +uxt(+ \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/78/9efbdadaa4a582778d4584385495559ea0994b new file mode 100644 index 000000000..17458840b --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/78/9efbdadaa4a582778d4584385495559ea0994b @@ -0,0 +1,2 @@ +x 0 )?= NlOkj8&r +qJW7B<fK8#Q1C-"e̫>'@ \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e new file mode 100644 index 000000000..83cc29fb1 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 new file mode 100644 index 000000000..55bda40ef Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/packed-refs b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/packed-refs new file mode 100644 index 000000000..def303a5f --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/packed-refs @@ -0,0 +1,2 @@ +# pack-refs with: peeled fully-peeled +480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/refs/heads/master new file mode 100644 index 000000000..e12c44d7a --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/refs/heads/master @@ -0,0 +1 @@ +480095882d281ed676fe5b863569520e54a7d5c0 diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/refs/remotes/origin/HEAD new file mode 100644 index 000000000..6efe28fff --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_branch_only/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/config index 10cc2508e..9812c64b3 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/config @@ -7,7 +7,7 @@ ignorecase = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* - url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target + url = ../../../../submodule_target_wd [branch "master"] remote = origin merge = refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/HEAD deleted file mode 100644 index e5cb63f8d..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/refs/heads/master deleted file mode 100644 index e5cb63f8d..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/refs/remotes/origin/HEAD deleted file mode 100644 index e5cb63f8d..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_file/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/COMMIT_EDITMSG b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/COMMIT_EDITMSG deleted file mode 100644 index 6b8d1e3fc..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/COMMIT_EDITMSG +++ /dev/null @@ -1 +0,0 @@ -Making a change in a submodule diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/config index 7d002536a..6d46e37ac 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/config @@ -7,7 +7,7 @@ ignorecase = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* - url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target + url = ../../../../submodule_target_wd [branch "master"] remote = origin merge = refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/HEAD deleted file mode 100644 index cabdeb2b5..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/HEAD +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target -480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer 1342560431 -0700 commit: Making a change in a submodule diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/refs/heads/master deleted file mode 100644 index cabdeb2b5..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/refs/heads/master +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target -480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer 1342560431 -0700 commit: Making a change in a submodule diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/refs/remotes/origin/HEAD deleted file mode 100644 index 257ca21d1..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_head/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/config index 0274ff7e3..94490e330 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/config @@ -7,7 +7,7 @@ ignorecase = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* - url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target + url = ../../../../submodule_target_wd [branch "master"] remote = origin merge = refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/HEAD deleted file mode 100644 index 80eb54102..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/refs/heads/master deleted file mode 100644 index 80eb54102..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/refs/remotes/origin/HEAD deleted file mode 100644 index 80eb54102..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_index/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/config index 7f2584476..e5ff0780a 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/config @@ -7,7 +7,7 @@ ignorecase = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* - url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target + url = ../../../../submodule_target_wd [branch "master"] remote = origin merge = refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/HEAD deleted file mode 100644 index d1beafbd6..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/refs/heads/master deleted file mode 100644 index d1beafbd6..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD deleted file mode 100644 index d1beafbd6..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/config index 45fbb30cf..376a475b5 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/config @@ -7,7 +7,7 @@ ignorecase = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* - url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target + url = ../../../../submodule_target_wd [branch "master"] remote = origin merge = refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/HEAD deleted file mode 100644 index ee08c9706..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/refs/heads/master deleted file mode 100644 index ee08c9706..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD deleted file mode 100644 index ee08c9706..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/config b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/config index fc706c9dd..ea79f0e26 100644 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/config +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/config @@ -7,7 +7,7 @@ ignorecase = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* - url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target + url = ../../../../submodule_target_wd [branch "master"] remote = origin merge = refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/description b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/info/exclude b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/HEAD deleted file mode 100644 index 72653286a..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/refs/heads/master deleted file mode 100644 index 72653286a..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/refs/remotes/origin/HEAD deleted file mode 100644 index 72653286a..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/modules/sm_unchanged/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/13/921096a46cb66610badba272f8211346eaf8f3 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/13/921096a46cb66610badba272f8211346eaf8f3 new file mode 100644 index 000000000..6f03cbf00 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/13/921096a46cb66610badba272f8211346eaf8f3 differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/6b/94d06e586d4ed904d8c00a9de7d0afe0fc9c3c b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/6b/94d06e586d4ed904d8c00a9de7d0afe0fc9c3c new file mode 100644 index 000000000..21bbe6d2b Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/6b/94d06e586d4ed904d8c00a9de7d0afe0fc9c3c differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/87/aa3a079302a662a9226af3c6e7f444815e3faf b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/87/aa3a079302a662a9226af3c6e7f444815e3faf new file mode 100644 index 000000000..666341811 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/87/aa3a079302a662a9226af3c6e7f444815e3faf differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/a4/aab482be687d2facec638781ded4aa1a92687a b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/a4/aab482be687d2facec638781ded4aa1a92687a new file mode 100644 index 000000000..34675b82e Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/a4/aab482be687d2facec638781ded4aa1a92687a differ diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/cf/27c0500009f6d12fd82d841ecf6a17b18ff812 b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/cf/27c0500009f6d12fd82d841ecf6a17b18ff812 new file mode 100644 index 000000000..d620874b9 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/objects/cf/27c0500009f6d12fd82d841ecf6a17b18ff812 @@ -0,0 +1,2 @@ +xK `)HP“Cz{jS-?fHCf]\ jJ8'4ǝQJxG{U2&mr֙t׃挦x\-|4Yf +Mmej dНчNY4ڠ)c6"܍\9l}2X;7N?Y h \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/refs/heads/dev b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/refs/heads/dev new file mode 100644 index 000000000..9cbe3f817 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/submodule_wd/dot_git/refs/heads/dev @@ -0,0 +1 @@ +6b94d06e586d4ed904d8c00a9de7d0afe0fc9c3c diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/description b/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/description deleted file mode 100644 index 498b267a8..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/logs/HEAD deleted file mode 100644 index 1749e7dff..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer 1342560358 -0700 commit (initial): Initial commit diff --git a/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/logs/refs/heads/master deleted file mode 100644 index 1749e7dff..000000000 --- a/LibGit2Sharp.Tests/Resources/submodule_wd/not_submodule/dot_git/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer 1342560358 -0700 commit (initial): Initial commit diff --git a/LibGit2Sharp.Tests/Resources/testrepo_wd/dot_git/description b/LibGit2Sharp.Tests/Resources/testrepo_wd/dot_git/description deleted file mode 100644 index 893e5cad2..000000000 --- a/LibGit2Sharp.Tests/Resources/testrepo_wd/dot_git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/RevertFixture.cs b/LibGit2Sharp.Tests/RevertFixture.cs index 5c52cc6c7..b57728764 100644 --- a/LibGit2Sharp.Tests/RevertFixture.cs +++ b/LibGit2Sharp.Tests/RevertFixture.cs @@ -1,9 +1,9 @@ +using System; using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; using Xunit.Extensions; -using System; namespace LibGit2Sharp.Tests { @@ -67,8 +67,6 @@ public void CanRevertAndNotCommit() string path = SandboxRevertTestRepo(); using (var repo = new Repository(path)) { - string modifiedFileFullPath = Path.Combine(repo.Info.WorkingDirectory, revertedFile); - // Checkout the revert branch. Branch branch = repo.Checkout(revertBranchName); Assert.NotNull(branch); @@ -83,7 +81,7 @@ public void CanRevertAndNotCommit() // Verify workspace is dirty. FileStatus fileStatus = repo.RetrieveStatus(revertedFile); - Assert.Equal(FileStatus.Staged, fileStatus); + Assert.Equal(FileStatus.ModifiedInIndex, fileStatus); // This is the ID of the blob containing the expected content. Blob expectedBlob = repo.Lookup("bc90ea420cf6c5ae3db7dcdffa0d79df567f219b"); @@ -131,8 +129,8 @@ public void RevertWithConflictDoesNotCommit() Assert.NotNull(repo.Index.Conflicts["a.txt"]); // Verify the non-conflicting paths are staged. - Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("b.txt")); - Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("c.txt")); + Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus("b.txt")); + Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus("c.txt")); } } @@ -160,6 +158,7 @@ public void RevertWithFileConflictStrategyOption(CheckoutFileConflictStrategy co }; RevertResult result = repo.Revert(repo.Head.Tip.Parents.First(), Constants.Signature, options); + Assert.Equal(RevertStatus.Conflicts, result.Status); // Verify there is a conflict. Assert.False(repo.Index.IsFullyMerged); @@ -242,6 +241,7 @@ public void RevertReportsCheckoutNotification() repo.Revert(repo.Head.Tip, Constants.Signature, options); Assert.True(wasCalled); + Assert.Equal(CheckoutNotifyFlags.Updated, actualNotifyFlags); } } diff --git a/LibGit2Sharp.Tests/SetErrorFixture.cs b/LibGit2Sharp.Tests/SetErrorFixture.cs new file mode 100644 index 000000000..b10a54108 --- /dev/null +++ b/LibGit2Sharp.Tests/SetErrorFixture.cs @@ -0,0 +1,182 @@ +using System; +using System.IO; +using System.Text; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class SetErrorFixture : BaseFixture + { + + private const string simpleExceptionMessage = "This is a simple exception message."; + private const string aggregateExceptionMessage = "This is aggregate exception."; + private const string outerExceptionMessage = "This is an outer exception."; + private const string innerExceptionMessage = "This is an inner exception."; + private const string innerExceptionMessage2 = "This is inner exception #2."; + + private const string expectedInnerExceptionHeaderText = "Inner Exception:"; + private const string expectedAggregateExceptionHeaderText = "Contained Exception:"; + private const string expectedAggregateExceptionsHeaderText = "Contained Exceptions:"; + + [Fact] + public void FormatSimpleException() + { + Exception exceptionToThrow = new Exception(simpleExceptionMessage); + string expectedMessage = simpleExceptionMessage; + + AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow); + } + + [Fact] + public void FormatExceptionWithInnerException() + { + Exception exceptionToThrow = new Exception(outerExceptionMessage, new Exception(innerExceptionMessage)); + + StringBuilder sb = new StringBuilder(); + sb.AppendLine(outerExceptionMessage); + sb.AppendLine(); + AppendIndentedLine(sb, expectedInnerExceptionHeaderText, 0); + AppendIndentedText(sb, innerExceptionMessage, 1); + string expectedMessage = sb.ToString(); + + AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow); + } + + [Fact] + public void FormatAggregateException() + { + Exception exceptionToThrow = new AggregateException(aggregateExceptionMessage, new Exception(innerExceptionMessage), new Exception(innerExceptionMessage2)); + + StringBuilder sb = new StringBuilder(); + sb.AppendLine(aggregateExceptionMessage); + sb.AppendLine(); + + AppendIndentedLine(sb, expectedAggregateExceptionsHeaderText, 0); + + AppendIndentedLine(sb, innerExceptionMessage, 1); + sb.AppendLine(); + + AppendIndentedText(sb, innerExceptionMessage2, 1); + + string expectedMessage = sb.ToString(); + + AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow); + } + + private void AssertExpectedExceptionMessage(string expectedMessage, Exception exceptionToThrow) + { + Exception thrownException = null; + + ObjectId id = new ObjectId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + + string repoPath = InitNewRepository(); + using (var repo = new Repository(repoPath)) + { + repo.ObjectDatabase.AddBackend(new ThrowingOdbBackend(exceptionToThrow), priority: 1); + + try + { + repo.Lookup(id); + } + catch (Exception ex) + { + thrownException = ex; + } + } + + Assert.NotNull(thrownException); + Assert.Equal(expectedMessage, thrownException.Message); + } + + private void AppendIndentedText(StringBuilder sb, string text, int indentLevel) + { + sb.AppendFormat("{0}{1}", IndentString(indentLevel), text); + } + + private void AppendIndentedLine(StringBuilder sb, string text, int indentLevel) + { + sb.AppendFormat("{0}{1}{2}", IndentString(indentLevel), text, Environment.NewLine); + } + + private string IndentString(int level) + { + return new string(' ', level * 4); + } + + #region ThrowingOdbBackend + + private class ThrowingOdbBackend : OdbBackend + { + private Exception exceptionToThrow; + + public ThrowingOdbBackend(Exception exceptionToThrow) + { + this.exceptionToThrow = exceptionToThrow; + } + + protected override OdbBackendOperations SupportedOperations + { + get + { + return OdbBackendOperations.Read | + OdbBackendOperations.ReadPrefix | + OdbBackendOperations.Write | + OdbBackendOperations.WriteStream | + OdbBackendOperations.Exists | + OdbBackendOperations.ExistsPrefix | + OdbBackendOperations.ForEach | + OdbBackendOperations.ReadHeader; + } + } + + public override int Read(ObjectId oid, out UnmanagedMemoryStream data, out ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int ReadPrefix(string shortSha, out ObjectId id, out UnmanagedMemoryStream data, out ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int Write(ObjectId oid, Stream dataStream, long length, ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int WriteStream(long length, ObjectType objectType, out OdbBackendStream stream) + { + throw this.exceptionToThrow; + } + + public override bool Exists(ObjectId oid) + { + throw this.exceptionToThrow; + } + + public override int ExistsPrefix(string shortSha, out ObjectId found) + { + throw this.exceptionToThrow; + } + + public override int ReadHeader(ObjectId oid, out int length, out ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int ReadStream(ObjectId oid, out OdbBackendStream stream) + { + throw this.exceptionToThrow; + } + + public override int ForEach(ForEachCallback callback) + { + throw this.exceptionToThrow; + } + } + + #endregion + + } +} diff --git a/LibGit2Sharp.Tests/ShadowCopyFixture.cs b/LibGit2Sharp.Tests/ShadowCopyFixture.cs index 5eaf4dced..f394e987e 100644 --- a/LibGit2Sharp.Tests/ShadowCopyFixture.cs +++ b/LibGit2Sharp.Tests/ShadowCopyFixture.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Reflection; using System.Security; @@ -17,7 +17,7 @@ public void CanProbeForNativeBinariesFromAShadowCopiedAssembly() Assembly assembly = type.Assembly; // Build a new domain which will shadow copy assemblies - string cachePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + string cachePath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); Directory.CreateDirectory(cachePath); var setup = new AppDomainSetup @@ -51,18 +51,13 @@ public void CanProbeForNativeBinariesFromAShadowCopiedAssembly() // ...but are currently loaded from different locations... string cachedAssemblyLocation = wrapper.AssemblyLocation; - if (cachedAssemblyLocation.StartsWith("/private")) - { - // On OS X, sometimes you get /private/var/… instead of /var/…, but they map to the same place. - cachedAssemblyLocation = cachedAssemblyLocation.Substring("/private".Length); - } Assert.NotEqual(sourceAssembly.Location, cachedAssemblyLocation); // ...that the assembly in the other domain is stored in the shadow copy cache... string cachedAssembliesPath = Path.Combine(setup.CachePath, setup.ApplicationName); Assert.True(cachedAssemblyLocation.StartsWith(cachedAssembliesPath)); - if (!IsRunningOnLinux()) + if (!Constants.IsRunningOnUnix) { // ...that this cache doesn't contain the `NativeBinaries` folder string cachedAssemblyParentPath = Path.GetDirectoryName(cachedAssemblyLocation); diff --git a/LibGit2Sharp.Tests/SmartSubtransportFixture.cs b/LibGit2Sharp.Tests/SmartSubtransportFixture.cs index 50f19ecc0..6d4e0fe40 100644 --- a/LibGit2Sharp.Tests/SmartSubtransportFixture.cs +++ b/LibGit2Sharp.Tests/SmartSubtransportFixture.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Security; using LibGit2Sharp.Tests.TestHelpers; +using LibGit2Sharp.Core; using Xunit; using Xunit.Extensions; @@ -39,7 +39,7 @@ public void CustomSmartSubtransportTest(string scheme, string url) registration = GlobalSettings.RegisterSmartSubtransport(scheme); Assert.NotNull(registration); - using (var repo = new Repository(scd.DirectoryPath)) + using (var repo = new Repository(repoPath)) { Remote remote = repo.Network.Remotes.Add(remoteName, url); @@ -77,6 +77,58 @@ public void CustomSmartSubtransportTest(string scheme, string url) } } + [Theory] + [InlineData("https", "https://bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3")] + public void CanUseCredentials(string scheme, string url, string user, string pass) + { + string remoteName = "testRemote"; + + var scd = BuildSelfCleaningDirectory(); + var repoPath = Repository.Init(scd.RootedDirectoryPath); + + SmartSubtransportRegistration registration = null; + + try + { + // Disable server certificate validation for testing. + // Do *NOT* enable this in production. + ServicePointManager.ServerCertificateValidationCallback = certificateValidationCallback; + + registration = GlobalSettings.RegisterSmartSubtransport(scheme); + Assert.NotNull(registration); + + using (var repo = new Repository(scd.DirectoryPath)) + { + Remote remote = repo.Network.Remotes.Add(remoteName, url); + + // Set up structures for the expected results + // and verifying the RemoteUpdateTips callback. + TestRemoteInfo expectedResults = TestRemoteInfo.TestRemoteInstance; + ExpectedFetchState expectedFetchState = new ExpectedFetchState(remoteName); + + // Add expected branch objects + foreach (KeyValuePair kvp in expectedResults.BranchTips) + { + expectedFetchState.AddExpectedBranch(kvp.Key, ObjectId.Zero, kvp.Value); + } + + // Perform the actual fetch + repo.Network.Fetch(remote, new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto, + CredentialsProvider = (_user, _valid, _hostname) => new UsernamePasswordCredentials() { Username = "libgit3", Password = "libgit3" }, + }); + + // Verify the expected + expectedFetchState.CheckUpdatedReferences(repo); + } + } + finally + { + GlobalSettings.UnregisterSmartSubtransport(registration); + + ServicePointManager.ServerCertificateValidationCallback -= certificateValidationCallback; + } + } + [Fact] public void CannotReregisterScheme() { @@ -235,7 +287,31 @@ private HttpWebResponse GetResponseWithRedirects() } } - response = (HttpWebResponse)request.GetResponse(); + try + { + response = (HttpWebResponse)request.GetResponse(); + } + catch (WebException ex) + { + response = ex.Response as HttpWebResponse; + if (response.StatusCode == HttpStatusCode.Unauthorized) + { + Credentials cred; + int ret = SmartTransport.AcquireCredentials(out cred, null, typeof(UsernamePasswordCredentials)); + if (ret != 0) + { + throw new InvalidOperationException("dunno"); + } + + request = CreateWebRequest(EndpointUrl, IsPost, ContentType); + UsernamePasswordCredentials userpass = (UsernamePasswordCredentials)cred; + request.Credentials = new NetworkCredential(userpass.Username, userpass.Password); + continue; + } + + // rethrow if it's not 401 + throw ex; + } if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect) { @@ -243,6 +319,7 @@ private HttpWebResponse GetResponseWithRedirects() continue; } + break; } diff --git a/LibGit2Sharp.Tests/StageFixture.cs b/LibGit2Sharp.Tests/StageFixture.cs index 679f20486..9a6c6248a 100644 --- a/LibGit2Sharp.Tests/StageFixture.cs +++ b/LibGit2Sharp.Tests/StageFixture.cs @@ -11,11 +11,11 @@ public class StageFixture : BaseFixture [Theory] [InlineData("1/branch_file.txt", FileStatus.Unaltered, true, FileStatus.Unaltered, true, 0)] [InlineData("README", FileStatus.Unaltered, true, FileStatus.Unaltered, true, 0)] - [InlineData("deleted_unstaged_file.txt", FileStatus.Missing, true, FileStatus.Removed, false, -1)] - [InlineData("modified_unstaged_file.txt", FileStatus.Modified, true, FileStatus.Staged, true, 0)] - [InlineData("new_untracked_file.txt", FileStatus.Untracked, false, FileStatus.Added, true, 1)] - [InlineData("modified_staged_file.txt", FileStatus.Staged, true, FileStatus.Staged, true, 0)] - [InlineData("new_tracked_file.txt", FileStatus.Added, true, FileStatus.Added, true, 0)] + [InlineData("deleted_unstaged_file.txt", FileStatus.DeletedFromWorkdir, true, FileStatus.DeletedFromIndex, false, -1)] + [InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInWorkdir, true, FileStatus.ModifiedInIndex, true, 0)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir, false, FileStatus.NewInIndex, true, 1)] + [InlineData("modified_staged_file.txt", FileStatus.ModifiedInIndex, true, FileStatus.ModifiedInIndex, true, 0)] + [InlineData("new_tracked_file.txt", FileStatus.NewInIndex, true, FileStatus.NewInIndex, true, 0)] public void CanStage(string relativePath, FileStatus currentStatus, bool doesCurrentlyExistInTheIndex, FileStatus expectedStatusOnceStaged, bool doesExistInTheIndexOnceStaged, int expectedIndexCountVariation) { string path = SandboxStandardTestRepo(); @@ -43,23 +43,23 @@ public void CanStageTheUpdationOfAStagedFile() const string filename = "new_tracked_file.txt"; IndexEntry blob = repo.Index[filename]; - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); Touch(repo.Info.WorkingDirectory, filename, "brand new content"); - Assert.Equal(FileStatus.Added | FileStatus.Modified, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex | FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(filename)); repo.Stage(filename); IndexEntry newBlob = repo.Index[filename]; Assert.Equal(count, repo.Index.Count); Assert.NotEqual(newBlob.Id, blob.Id); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); } } [Theory] [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] - [InlineData("deleted_staged_file.txt", FileStatus.Removed)] + [InlineData("deleted_staged_file.txt", FileStatus.DeletedFromIndex)] public void StagingAnUnknownFileThrowsIfExplicitPath(string relativePath, FileStatus status) { var path = SandboxStandardTestRepoGitDir(); @@ -74,7 +74,7 @@ public void StagingAnUnknownFileThrowsIfExplicitPath(string relativePath, FileSt [Theory] [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] - [InlineData("deleted_staged_file.txt", FileStatus.Removed)] + [InlineData("deleted_staged_file.txt", FileStatus.DeletedFromIndex)] public void CanStageAnUnknownFileWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus status) { var path = SandboxStandardTestRepoGitDir(); @@ -92,7 +92,7 @@ public void CanStageAnUnknownFileWithLaxUnmatchedExplicitPathsValidation(string [Theory] [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] - [InlineData("deleted_staged_file.txt", FileStatus.Removed)] + [InlineData("deleted_staged_file.txt", FileStatus.DeletedFromIndex)] public void StagingAnUnknownFileWithLaxExplicitPathsValidationDoesntThrow(string relativePath, FileStatus status) { var path = SandboxStandardTestRepoGitDir(); @@ -116,10 +116,10 @@ public void CanStageTheRemovalOfAStagedFile() const string filename = "new_tracked_file.txt"; Assert.NotNull(repo.Index[filename]); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); File.Delete(Path.Combine(repo.Info.WorkingDirectory, filename)); - Assert.Equal(FileStatus.Added | FileStatus.Missing, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex | FileStatus.DeletedFromWorkdir, repo.RetrieveStatus(filename)); repo.Stage(filename); Assert.Null(repo.Index[filename]); @@ -142,18 +142,18 @@ public void CanStageANewFileInAPersistentManner(string filename) Assert.Null(repo.Index[filename]); Touch(repo.Info.WorkingDirectory, filename, "some contents"); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(filename)); Assert.Null(repo.Index[filename]); repo.Stage(filename); Assert.NotNull(repo.Index[filename]); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); } using (var repo = new Repository(path)) { Assert.NotNull(repo.Index[filename]); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); } } @@ -194,9 +194,9 @@ private static void AssertStage(bool? ignorecase, IRepository repo, string path) try { repo.Stage(path); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(path)); - repo.Reset(); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(path)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(path)); + repo.Index.Replace(repo.Head.Tip); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(path)); } catch (ArgumentException) { @@ -331,7 +331,65 @@ public void CanStageIgnoredPaths(string path) Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(path)); repo.Stage(path, new StageOptions { IncludeIgnored = true }); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(path)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(path)); + } + } + + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Ignored)] + [InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInIndex)] + public void IgnoredFilesAreOnlyStagedIfTheyreInTheRepo(string filename, FileStatus expected) + { + var path = SandboxStandardTestRepoGitDir(); + using (var repo = new Repository(path)) + { + File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"), + String.Format("{0}\n", filename)); + + repo.Stage(filename); + Assert.Equal(expected, repo.RetrieveStatus(filename)); + } + } + + [Theory] + [InlineData("ancestor-and-ours.txt", FileStatus.Unaltered)] + [InlineData("ancestor-and-theirs.txt", FileStatus.NewInIndex)] + [InlineData("ancestor-only.txt", FileStatus.Nonexistent)] + [InlineData("conflicts-one.txt", FileStatus.ModifiedInIndex)] + [InlineData("conflicts-two.txt", FileStatus.ModifiedInIndex)] + [InlineData("ours-only.txt", FileStatus.Unaltered)] + [InlineData("ours-and-theirs.txt", FileStatus.ModifiedInIndex)] + [InlineData("theirs-only.txt", FileStatus.NewInIndex)] + public void CanStageConflictedIgnoredFiles(string filename, FileStatus expected) + { + var path = SandboxMergedTestRepo(); + using (var repo = new Repository(path)) + { + File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"), + String.Format("{0}\n", filename)); + + repo.Stage(filename); + Assert.Equal(expected, repo.RetrieveStatus(filename)); + } + } + + [Fact] + public void CanSuccessfullyStageTheContentOfAModifiedFileOfTheSameSizeWithinTheSameSecond() + { + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + for (int i = 0; i < 10; i++) + { + Touch(repo.Info.WorkingDirectory, "test.txt", + Guid.NewGuid().ToString()); + + repo.Stage("test.txt"); + + Assert.DoesNotThrow(() => repo.Commit( + "Commit", Constants.Signature, Constants.Signature)); + } } } } diff --git a/LibGit2Sharp.Tests/StashFixture.cs b/LibGit2Sharp.Tests/StashFixture.cs index c9f94064f..369ac0994 100644 --- a/LibGit2Sharp.Tests/StashFixture.cs +++ b/LibGit2Sharp.Tests/StashFixture.cs @@ -150,7 +150,7 @@ public void CanStashWithoutOptions() Assert.NotNull(stash.Index[staged]); //It should leave untracked files untracked - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(untracked)); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(untracked)); Assert.Null(stash.Untracked); } } @@ -171,7 +171,7 @@ public void CanStashAndKeepIndex() Assert.NotNull(stash); Assert.NotNull(stash.Index[filename]); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); Assert.Null(stash.Untracked); } } @@ -204,6 +204,156 @@ public void CanStashIgnoredFiles() } } + [Fact] + public void CanStashAndApplyWithOptions() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + const string filename = "staged_file_path.txt"; + Touch(repo.Info.WorkingDirectory, filename, "I'm staged\n"); + repo.Stage(filename); + + repo.Stashes.Add(stasher, "This stash with default options"); + Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Apply(0)); + + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); + Assert.Equal(1, repo.Stashes.Count()); + + repo.Stage(filename); + + repo.Stashes.Add(stasher, "This stash with default options"); + Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Apply( + 0, + new StashApplyOptions + { + ApplyModifiers = StashApplyModifiers.ReinstateIndex, + })); + + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); + Assert.Equal(2, repo.Stashes.Count()); + } + } + + [Fact] + public void CanStashAndPop() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + Assert.Equal(0, repo.Stashes.Count()); + + const string filename = "staged_file_path.txt"; + const string contents = "I'm staged"; + Touch(repo.Info.WorkingDirectory, filename, contents); + repo.Stage(filename); + + repo.Stashes.Add(stasher, "This stash with default options"); + Assert.Equal(1, repo.Stashes.Count()); + + Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Pop(0)); + Assert.Equal(0, repo.Stashes.Count()); + + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); + Assert.Equal(contents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename))); + } + } + + [Fact] + public void StashFailsWithUncommittedChangesIntheIndex() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + const string filename = "staged_file_path.txt"; + const string originalContents = "I'm pre-stash."; + const string filename2 = "unstaged_file_path.txt"; + const string newContents = "I'm post-stash."; + + Touch(repo.Info.WorkingDirectory, filename, originalContents); + repo.Stage(filename); + Touch(repo.Info.WorkingDirectory, filename2, originalContents); + + repo.Stashes.Add(stasher, "This stash with default options"); + + Touch(repo.Info.WorkingDirectory, filename, newContents); + repo.Stage(filename); + Touch(repo.Info.WorkingDirectory, filename2, newContents); + + Assert.Equal(StashApplyStatus.UncommittedChanges, repo.Stashes.Pop(0, new StashApplyOptions + { + ApplyModifiers = StashApplyModifiers.ReinstateIndex, + })); + Assert.Equal(1, repo.Stashes.Count()); + Assert.Equal(newContents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename))); + Assert.Equal(newContents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename2))); + } + } + + [Fact] + public void StashCallsTheCallback() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + bool called; + + const string filename = "staged_file_path.txt"; + const string filename2 = "unstaged_file_path.txt"; + const string originalContents = "I'm pre-stash."; + + Touch(repo.Info.WorkingDirectory, filename, originalContents); + repo.Stage(filename); + Touch(repo.Info.WorkingDirectory, filename2, originalContents); + + repo.Stashes.Add(stasher, "This stash with default options"); + + called = false; + repo.Stashes.Apply(0, new StashApplyOptions + { + ProgressHandler = (progress) => { called = true; return true; } + }); + + Assert.Equal(true, called); + + repo.Reset(ResetMode.Hard); + + called = false; + repo.Stashes.Pop(0, new StashApplyOptions + { + ProgressHandler = (progress) => { called = true; return true; } + }); + + Assert.Equal(true, called); + } + } + + [Fact] + public void StashApplyReportsNotFound() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + const string filename = "unstaged_file_path.txt"; + Touch(repo.Info.WorkingDirectory, filename, "I'm unstaged\n"); + + repo.Stashes.Add(stasher, "This stash with default options", StashModifiers.IncludeUntracked); + Touch(repo.Info.WorkingDirectory, filename, "I'm another unstaged\n"); + + Assert.Equal(StashApplyStatus.NotFound, repo.Stashes.Pop(1)); + Assert.Throws(() => repo.Stashes.Pop(-1)); + } + } + [Theory] [InlineData(-1)] [InlineData(-42)] diff --git a/LibGit2Sharp.Tests/StatusFixture.cs b/LibGit2Sharp.Tests/StatusFixture.cs index e8f9d7314..3c9bc0023 100644 --- a/LibGit2Sharp.Tests/StatusFixture.cs +++ b/LibGit2Sharp.Tests/StatusFixture.cs @@ -17,13 +17,13 @@ public void CanRetrieveTheStatusOfAFile() using (var repo = new Repository(path)) { FileStatus status = repo.RetrieveStatus("new_tracked_file.txt"); - Assert.Equal(FileStatus.Added, status); + Assert.Equal(FileStatus.NewInIndex, status); } } [Theory] - [InlineData(StatusShowOption.IndexAndWorkDir, FileStatus.Untracked)] - [InlineData(StatusShowOption.WorkDirOnly, FileStatus.Untracked)] + [InlineData(StatusShowOption.IndexAndWorkDir, FileStatus.NewInWorkdir)] + [InlineData(StatusShowOption.WorkDirOnly, FileStatus.NewInWorkdir)] [InlineData(StatusShowOption.IndexOnly, FileStatus.Nonexistent)] public void CanLimitStatusToWorkDirOnly(StatusShowOption show, FileStatus expected) { @@ -39,9 +39,9 @@ public void CanLimitStatusToWorkDirOnly(StatusShowOption show, FileStatus expect } [Theory] - [InlineData(StatusShowOption.IndexAndWorkDir, FileStatus.Added)] + [InlineData(StatusShowOption.IndexAndWorkDir, FileStatus.NewInIndex)] [InlineData(StatusShowOption.WorkDirOnly, FileStatus.Nonexistent)] - [InlineData(StatusShowOption.IndexOnly, FileStatus.Added)] + [InlineData(StatusShowOption.IndexOnly, FileStatus.NewInIndex)] public void CanLimitStatusToIndexOnly(StatusShowOption show, FileStatus expected) { var clone = SandboxStandardTestRepo(); @@ -89,7 +89,7 @@ public void CanRetrieveTheStatusOfAnUntrackedFile(string filePath) Touch(repo.Info.WorkingDirectory, filePath, "content"); FileStatus status = repo.RetrieveStatus(filePath); - Assert.Equal(FileStatus.Untracked, status); + Assert.Equal(FileStatus.NewInWorkdir, status); } } @@ -99,24 +99,26 @@ public void RetrievingTheStatusOfADirectoryThrows() var path = SandboxStandardTestRepoGitDir(); using (var repo = new Repository(path)) { - Assert.Throws(() => { FileStatus status = repo.RetrieveStatus("1"); }); + Assert.Throws(() => { repo.RetrieveStatus("1"); }); } } - [Fact] - public void CanRetrieveTheStatusOfTheWholeWorkingDirectory() + [Theory] + [InlineData(false, 0)] + [InlineData(true, 5)] + public void CanRetrieveTheStatusOfTheWholeWorkingDirectory(bool includeUnaltered, int unalteredCount) { string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { const string file = "modified_staged_file.txt"; - RepositoryStatus status = repo.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = includeUnaltered }); - Assert.Equal(FileStatus.Staged, status[file].State); + Assert.Equal(FileStatus.ModifiedInIndex, status[file].State); Assert.NotNull(status); - Assert.Equal(6, status.Count()); + Assert.Equal(6 + unalteredCount, status.Count()); Assert.True(status.IsDirty); Assert.Equal("new_untracked_file.txt", status.Untracked.Select(s => s.FilePath).Single()); @@ -129,13 +131,13 @@ public void CanRetrieveTheStatusOfTheWholeWorkingDirectory() File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, file), "Tclem's favorite commit message: boom"); - Assert.Equal(FileStatus.Staged | FileStatus.Modified, repo.RetrieveStatus(file)); + Assert.Equal(FileStatus.ModifiedInIndex | FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(file)); - RepositoryStatus status2 = repo.RetrieveStatus(); - Assert.Equal(FileStatus.Staged | FileStatus.Modified, status2[file].State); + RepositoryStatus status2 = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = includeUnaltered }); + Assert.Equal(FileStatus.ModifiedInIndex | FileStatus.ModifiedInWorkdir, status2[file].State); Assert.NotNull(status2); - Assert.Equal(6, status2.Count()); + Assert.Equal(6 + unalteredCount, status2.Count()); Assert.True(status2.IsDirty); Assert.Equal("new_untracked_file.txt", status2.Untracked.Select(s => s.FilePath).Single()); @@ -171,7 +173,7 @@ public void CanRetrieveTheStatusOfRenamedFilesInWorkDir() DetectRenamesInWorkDir = true }); - Assert.Equal(FileStatus.Added | FileStatus.RenamedInWorkDir, status["rename_target.txt"].State); + Assert.Equal(FileStatus.NewInIndex | FileStatus.RenamedInWorkdir, status["rename_target.txt"].State); Assert.Equal(100, status["rename_target.txt"].IndexToWorkDirRenameDetails.Similarity); } } @@ -223,7 +225,7 @@ public void CanDetectedVariousKindsOfRenaming() RepositoryStatus status = repo.RetrieveStatus(opts); // This passes as expected - Assert.Equal(FileStatus.RenamedInWorkDir, status.Single().State); + Assert.Equal(FileStatus.RenamedInWorkdir, status.Single().State); repo.Stage("file.txt"); repo.Stage("renamed.txt"); @@ -237,19 +239,21 @@ public void CanDetectedVariousKindsOfRenaming() status = repo.RetrieveStatus(opts); - Assert.Equal(FileStatus.RenamedInWorkDir | FileStatus.RenamedInIndex, + Assert.Equal(FileStatus.RenamedInWorkdir | FileStatus.RenamedInIndex, status.Single().State); } } - [Fact] - public void CanRetrieveTheStatusOfANewRepository() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void CanRetrieveTheStatusOfANewRepository(bool includeUnaltered) { string repoPath = InitNewRepository(); using (var repo = new Repository(repoPath)) { - RepositoryStatus status = repo.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = includeUnaltered }); Assert.NotNull(status); Assert.Equal(0, status.Count()); Assert.False(status.IsDirty); @@ -405,7 +409,7 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectives() [Theory] [InlineData(true, FileStatus.Unaltered, FileStatus.Unaltered)] - [InlineData(false, FileStatus.Missing, FileStatus.Untracked)] + [InlineData(false, FileStatus.DeletedFromWorkdir, FileStatus.NewInWorkdir)] public void RetrievingTheStatusOfAFilePathHonorsTheIgnoreCaseConfigurationSetting( bool shouldIgnoreCase, FileStatus expectedlowerCasedFileStatus, @@ -480,7 +484,7 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectivesThroug Touch(repo.Info.WorkingDirectory, gitIgnore, sb.ToString()); Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus("bin/look-ma.txt")); - Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus("bin/what-about-me.txt")); + Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus("bin/what-about-me.txt")); newStatus = repo.RetrieveStatus(); @@ -592,5 +596,43 @@ public void RetrievingTheStatusHonorsAssumedUnchangedMarkedIndexEntries() Assert.Equal("hello.txt", status.Modified.Single().FilePath); } } + + [Fact] + public void CanIncludeStatusOfUnalteredFiles() + { + var path = SandboxStandardTestRepo(); + string[] unalteredPaths = { + "1.txt", + "1" + Path.DirectorySeparatorChar + "branch_file.txt", + "branch_file.txt", + "new.txt", + "README", + }; + + using (var repo = new Repository(path)) + { + RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = true }); + + Assert.Equal(unalteredPaths.Length, status.Unaltered.Count()); + Assert.Equal(unalteredPaths, status.Unaltered.OrderBy(s => s.FilePath).Select(s => s.FilePath).ToArray()); + } + } + + [Fact] + public void UnalteredFilesDontMarkIndexAsDirty() + { + var path = SandboxStandardTestRepo(); + + using (var repo = new Repository(path)) + { + repo.Reset(ResetMode.Hard); + repo.RemoveUntrackedFiles(); + + RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = true }); + + Assert.Equal(false, status.IsDirty); + Assert.Equal(9, status.Count()); + } + } } } diff --git a/LibGit2Sharp.Tests/SubmoduleFixture.cs b/LibGit2Sharp.Tests/SubmoduleFixture.cs index d60e61f3e..58c8a830a 100644 --- a/LibGit2Sharp.Tests/SubmoduleFixture.cs +++ b/LibGit2Sharp.Tests/SubmoduleFixture.cs @@ -20,6 +20,26 @@ public void RetrievingSubmoduleForNormalDirectoryReturnsNull() } } + [Fact] + public void RetrievingSubmoduleInBranchShouldWork() + { + var path = SandboxSubmoduleTestRepo(); + using (var repo = new Repository(path)) + { + var submodule = repo.Submodules["sm_branch_only"]; + Assert.Null(submodule); + + repo.Checkout("dev", new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force }); + submodule = repo.Submodules["sm_branch_only"]; + Assert.NotNull(submodule); + Assert.NotEqual(SubmoduleStatus.Unmodified, submodule.RetrieveStatus()); + + repo.Checkout("master", new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force }); + submodule = repo.Submodules["sm_branch_only"]; + Assert.Null(submodule); + } + } + [Theory] [InlineData("sm_added_and_uncommited", SubmoduleStatus.InConfig | SubmoduleStatus.InIndex | SubmoduleStatus.InWorkDir | SubmoduleStatus.IndexAdded)] [InlineData("sm_changed_file", SubmoduleStatus.InConfig | SubmoduleStatus.InHead | SubmoduleStatus.InIndex | SubmoduleStatus.InWorkDir | SubmoduleStatus.WorkDirFilesModified)] @@ -29,12 +49,20 @@ public void RetrievingSubmoduleForNormalDirectoryReturnsNull() [InlineData("sm_gitmodules_only", SubmoduleStatus.InConfig)] [InlineData("sm_missing_commits", SubmoduleStatus.InConfig | SubmoduleStatus.InHead | SubmoduleStatus.InIndex | SubmoduleStatus.InWorkDir | SubmoduleStatus.WorkDirModified)] [InlineData("sm_unchanged", SubmoduleStatus.InConfig | SubmoduleStatus.InHead | SubmoduleStatus.InIndex | SubmoduleStatus.InWorkDir)] - public void CanRetrieveTheStatusOfASubmodule(string name, SubmoduleStatus expectedStatus) + [InlineData("sm_branch_only", null)] + public void CanRetrieveTheStatusOfASubmodule(string name, SubmoduleStatus? expectedStatus) { var path = SandboxSubmoduleTestRepo(); using (var repo = new Repository(path)) { var submodule = repo.Submodules[name]; + + if (expectedStatus == null) + { + Assert.Null(submodule); + return; + } + Assert.NotNull(submodule); Assert.Equal(name, submodule.Name); Assert.Equal(name, submodule.Path); diff --git a/LibGit2Sharp.Tests/TagFixture.cs b/LibGit2Sharp.Tests/TagFixture.cs index 2291a1266..48284a021 100644 --- a/LibGit2Sharp.Tests/TagFixture.cs +++ b/LibGit2Sharp.Tests/TagFixture.cs @@ -4,6 +4,7 @@ using LibGit2Sharp.Core; using LibGit2Sharp.Tests.TestHelpers; using Xunit; +using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -105,7 +106,7 @@ public void CanAddATagWithNameContainingASlash() Assert.NotNull(lwTag); Assert.False(lwTag.IsAnnotated); Assert.Equal(commitE90810BSha, lwTag.Target.Sha); - Assert.Equal(lwTagName, lwTag.Name); + Assert.Equal(lwTagName, lwTag.FriendlyName); const string anTagName = lwTagName + "_as_well"; Tag anTag = repo.Tags.Add(anTagName, commitE90810BSha, signatureNtk, "a nice message"); @@ -113,7 +114,7 @@ public void CanAddATagWithNameContainingASlash() Assert.True(anTag.IsAnnotated); Assert.Equal(commitE90810BSha, anTag.Target.Sha); Assert.Equal(anTag.Target, anTag.Annotation.Target); - Assert.Equal(anTagName, anTag.Name); + Assert.Equal(anTagName, anTag.FriendlyName); } } @@ -370,7 +371,7 @@ public void CanAddATagPointingToATree() Assert.Equal(tree.Id, tag.Target.Id); Assert.Equal(tree, repo.Lookup(tag.Target.Id)); - Assert.Equal(tag, repo.Tags[tag.Name]); + Assert.Equal(tag, repo.Tags[tag.FriendlyName]); } } @@ -408,7 +409,7 @@ public void CanAddATagPointingToABlob() Assert.Equal(blob.Id, tag.Target.Id); Assert.Equal(blob, repo.Lookup(tag.Target.Id)); - Assert.Equal(tag, repo.Tags[tag.Name]); + Assert.Equal(tag, repo.Tags[tag.FriendlyName]); } } @@ -428,7 +429,7 @@ public void CreatingALightweightTagPointingToATagAnnotationGeneratesAnAnnotatedT Assert.Equal(annotation, tag.Annotation); Assert.Equal(annotation, repo.Lookup(tag.Annotation.Id)); - Assert.Equal(tag, repo.Tags[tag.Name]); + Assert.Equal(tag, repo.Tags[tag.FriendlyName]); } } @@ -447,7 +448,7 @@ public void CanAddAnAnnotatedTagPointingToATagAnnotation() Assert.Equal(annotation.Id, tag.Annotation.Target.Id); Assert.NotEqual(annotation, tag.Annotation); - Assert.Equal(tag, repo.Tags[tag.Name]); + Assert.Equal(tag, repo.Tags[tag.FriendlyName]); } } @@ -604,12 +605,12 @@ public void RemovingATagDecreasesTheTagsCount() { const string tagName = "e90810b"; - List tags = repo.Tags.Select(r => r.Name).ToList(); + List tags = repo.Tags.Select(r => r.FriendlyName).ToList(); Assert.True(tags.Contains(tagName)); repo.Tags.Remove(tagName); - List tags2 = repo.Tags.Select(r => r.Name).ToList(); + List tags2 = repo.Tags.Select(r => r.FriendlyName).ToList(); Assert.False(tags2.Contains(tagName)); Assert.Equal(tags.Count - 1, tags2.Count); @@ -645,7 +646,7 @@ public void CanListTags() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(expectedTags, SortedTags(repo.Tags, t => t.Name)); + Assert.Equal(expectedTags, SortedTags(repo.Tags, t => t.FriendlyName)); Assert.Equal(5, repo.Tags.Count()); } @@ -672,7 +673,7 @@ public void CanLookupALightweightTag() { Tag tag = repo.Tags["lw"]; Assert.NotNull(tag); - Assert.Equal("lw", tag.Name); + Assert.Equal("lw", tag.FriendlyName); Assert.Equal(commitE90810BSha, tag.Target.Sha); Assert.False(tag.IsAnnotated); @@ -688,11 +689,11 @@ public void CanLookupATagByItsCanonicalName() { Tag tag = repo.Tags["refs/tags/lw"]; Assert.NotNull(tag); - Assert.Equal("lw", tag.Name); + Assert.Equal("lw", tag.FriendlyName); Tag tag2 = repo.Tags["refs/tags/lw"]; Assert.NotNull(tag2); - Assert.Equal("lw", tag2.Name); + Assert.Equal("lw", tag2.FriendlyName); Assert.Equal(tag, tag2); Assert.True((tag2 == tag)); @@ -707,7 +708,7 @@ public void CanLookupAnAnnotatedTag() { Tag tag = repo.Tags["e90810b"]; Assert.NotNull(tag); - Assert.Equal("e90810b", tag.Name); + Assert.Equal("e90810b", tag.FriendlyName); Assert.Equal(commitE90810BSha, tag.Target.Sha); Assert.True(tag.IsAnnotated); @@ -740,6 +741,35 @@ public void LookupNullTagNameThrows() } } + [Fact] + public void CanRetrieveThePeeledTargetOfATagPointingToATag() + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + Tag tag = repo.Tags["test"]; + + Assert.True(tag.Target is TagAnnotation); + Assert.True(tag.PeeledTarget is Commit); + } + } + + [Theory] + [InlineData("e90810b")] + [InlineData("lw")] + [InlineData("point_to_blob")] + [InlineData("tag_without_tagger")] + public void PeeledTargetAndTargetAreEqualWhenTagIsNotChained(string tagName) + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + Tag tag = repo.Tags[tagName]; + + Assert.Equal(tag.Target, tag.PeeledTarget); + } + } + private static T[] SortedTags(IEnumerable tags, Func selector) { return tags.OrderBy(t => t.CanonicalName, StringComparer.Ordinal).Select(selector).ToArray(); diff --git a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs index 08f068c81..c1fbefb7f 100644 --- a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs +++ b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -40,7 +40,8 @@ static BaseFixture() public static string SubmoduleTestRepoWorkingDirPath { get; private set; } private static string SubmoduleTargetTestRepoWorkingDirPath { get; set; } private static string AssumeUnchangedRepoWorkingDirPath { get; set; } - private static string SubmoduleSmallTestRepoWorkingDirPath { get; set; } + public static string SubmoduleSmallTestRepoWorkingDirPath { get; set; } + public static string PackBuilderTestRepoPath { get; private set; } public static DirectoryInfo ResourcesDirectory { get; private set; } @@ -49,15 +50,17 @@ static BaseFixture() protected static DateTimeOffset TruncateSubSeconds(DateTimeOffset dto) { int seconds = dto.ToSecondsSinceEpoch(); - return Epoch.ToDateTimeOffset(seconds, (int) dto.Offset.TotalMinutes); + return Epoch.ToDateTimeOffset(seconds, (int)dto.Offset.TotalMinutes); } private static void SetUpTestEnvironment() { IsFileSystemCaseSensitive = IsFileSystemCaseSensitiveInternal(); + string initialAssemblyParentFolder = Directory.GetParent(new Uri(typeof(BaseFixture).Assembly.EscapedCodeBase).LocalPath).FullName; + const string sourceRelativePath = @"../../Resources"; - ResourcesDirectory = new DirectoryInfo(sourceRelativePath); + ResourcesDirectory = new DirectoryInfo(Path.Combine(initialAssemblyParentFolder, sourceRelativePath)); // Setup standard paths to our test repositories BareTestRepoPath = Path.Combine(sourceRelativePath, "testrepo.git"); @@ -71,7 +74,23 @@ private static void SetUpTestEnvironment() SubmoduleTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "submodule_wd"); SubmoduleTargetTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "submodule_target_wd"); AssumeUnchangedRepoWorkingDirPath = Path.Combine(sourceRelativePath, "assume_unchanged_wd"); - SubmoduleSmallTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "submodule_small_wd"); + SubmoduleSmallTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "submodule_small_wd"); + PackBuilderTestRepoPath = Path.Combine(sourceRelativePath, "packbuilder_testrepo_wd"); + + CleanupTestReposOlderThan(TimeSpan.FromMinutes(15)); + } + + private static void CleanupTestReposOlderThan(TimeSpan olderThan) + { + var oldTestRepos = new DirectoryInfo(Constants.TemporaryReposPath) + .EnumerateDirectories() + .Where(di => di.CreationTimeUtc < DateTimeOffset.Now.Subtract(olderThan)) + .Select(di => di.FullName); + + foreach (var dir in oldTestRepos) + { + DirectoryHelper.DeleteDirectory(dir); + } } private static bool IsFileSystemCaseSensitiveInternal() @@ -91,14 +110,6 @@ private static bool IsFileSystemCaseSensitiveInternal() return !isInsensitive; } - // Should match LibGit2Sharp.Core.NativeMethods.IsRunningOnLinux() - protected static bool IsRunningOnLinux() - { - // see http://mono-project.com/FAQ%3a_Technical#Mono_Platforms - var p = (int)Environment.OSVersion.Platform; - return (p == 4) || (p == 6) || (p == 128); - } - protected void CreateCorruptedDeadBeefHead(string repoPath) { const string deadbeef = "deadbeef"; @@ -156,16 +167,20 @@ public string SandboxAssumeUnchangedTestRepo() { return Sandbox(AssumeUnchangedRepoWorkingDirPath); } - + public string SandboxSubmoduleSmallTestRepo() { - var submoduleTarget = Path.Combine(ResourcesDirectory.FullName, "submodule_target_wd"); - var path = Sandbox(SubmoduleSmallTestRepoWorkingDirPath, submoduleTarget); + var path = Sandbox(SubmoduleSmallTestRepoWorkingDirPath, SubmoduleTargetTestRepoWorkingDirPath); Directory.CreateDirectory(Path.Combine(path, "submodule_target_wd")); return path; } + protected string SandboxPackBuilderTestRepo() + { + return Sandbox(PackBuilderTestRepoPath); + } + protected string Sandbox(string sourceDirectoryPath, params string[] additionalSourcePaths) { var scd = BuildSelfCleaningDirectory(); @@ -252,7 +267,7 @@ protected void RequiresDotNetOrMonoGreaterThanOrEqualTo(System.Version minimumVe throw new InvalidOperationException("Cannot access Mono.RunTime.GetDisplayName() method."); } - var version = (string) displayName.Invoke(null, null); + var version = (string)displayName.Invoke(null, null); System.Version current; @@ -320,29 +335,29 @@ private static RepositoryOptions BuildFakeRepositoryOptions(SelfCleaningDirector /// Creates a configuration file with user.name and user.email set to signature /// /// The configuration file will be removed automatically when the tests are finished - /// The signature to use for user.name and user.email + /// The identity to use for user.name and user.email /// The path to the configuration file - protected string CreateConfigurationWithDummyUser(Signature signature) + protected string CreateConfigurationWithDummyUser(Identity identity) { - return CreateConfigurationWithDummyUser(signature.Name, signature.Email); + return CreateConfigurationWithDummyUser(identity.Name, identity.Email); } protected string CreateConfigurationWithDummyUser(string name, string email) { SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); - Directory.CreateDirectory(scd.DirectoryPath); - string configFilePath = Path.Combine(scd.DirectoryPath, "global-config"); - using (Configuration config = new Configuration(configFilePath)) + string configFilePath = Touch(scd.DirectoryPath, "fake-config"); + + using (Configuration config = Configuration.BuildFrom(configFilePath)) { if (name != null) { - config.Set("user.name", name, ConfigurationLevel.Global); + config.Set("user.name", name); } if (email != null) { - config.Set("user.email", email, ConfigurationLevel.Global); + config.Set("user.email", email); } } @@ -353,13 +368,13 @@ protected string CreateConfigurationWithDummyUser(string name, string email) /// Asserts that the commit has been authored and committed by the specified signature /// /// The commit - /// The signature to compare author and commiter to - protected void AssertCommitSignaturesAre(Commit commit, Signature signature) + /// The identity to compare author and commiter to + protected void AssertCommitIdentitiesAre(Commit commit, Identity identity) { - Assert.Equal(signature.Name, commit.Author.Name); - Assert.Equal(signature.Email, commit.Author.Email); - Assert.Equal(signature.Name, commit.Committer.Name); - Assert.Equal(signature.Email, commit.Committer.Email); + Assert.Equal(identity.Name, commit.Author.Name); + Assert.Equal(identity.Email, commit.Author.Email); + Assert.Equal(identity.Name, commit.Committer.Name); + Assert.Equal(identity.Email, commit.Committer.Email); } protected static string Touch(string parent, string file, string content = null, Encoding encoding = null) @@ -405,8 +420,8 @@ protected string Expected(string filenameFormat, params object[] args) } protected static void AssertRefLogEntry(IRepository repo, string canonicalName, - ObjectId to, string message, ObjectId @from = null, - Signature committer = null) + string message, ObjectId @from, ObjectId to, + Identity committer, DateTimeOffset before) { var reflogEntry = repo.Refs.Log(canonicalName).First(); @@ -414,9 +429,8 @@ protected static void AssertRefLogEntry(IRepository repo, string canonicalName, Assert.Equal(message, reflogEntry.Message); Assert.Equal(@from ?? ObjectId.Zero, reflogEntry.From); - committer = committer ?? repo.Config.BuildSignature(DateTimeOffset.Now); - Assert.Equal(committer.Email, reflogEntry.Commiter.Email); - Assert.InRange(reflogEntry.Commiter.When, committer.When - TimeSpan.FromSeconds(5), committer.When); + Assert.Equal(committer.Email, reflogEntry.Committer.Email); + Assert.InRange(reflogEntry.Committer.When, before, DateTimeOffset.Now); } protected static void EnableRefLog(IRepository repository, bool enable = true) @@ -455,5 +469,10 @@ public void AssertBelongsToARepository(IRepository repo, T instance) { Assert.Same(repo, ((IBelongToARepository)instance).Repository); } + + protected void CreateAttributesFile(IRepository repo, string attributeEntry) + { + Touch(repo.Info.WorkingDirectory, ".gitattributes", attributeEntry); + } } } diff --git a/LibGit2Sharp.Tests/TestHelpers/Constants.cs b/LibGit2Sharp.Tests/TestHelpers/Constants.cs index 71f942a53..334b61dc1 100644 --- a/LibGit2Sharp.Tests/TestHelpers/Constants.cs +++ b/LibGit2Sharp.Tests/TestHelpers/Constants.cs @@ -1,12 +1,21 @@ using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Security; +using LibGit2Sharp.Core; namespace LibGit2Sharp.Tests.TestHelpers { public static class Constants { - public const string TemporaryReposPath = "TestRepos"; + public static readonly string TemporaryReposPath = BuildPath(); public const string UnknownSha = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; - public static readonly Signature Signature = new Signature("A. U. Thor", "thor@valhalla.asgard.com", new DateTimeOffset(2011, 06, 16, 10, 58, 27, TimeSpan.FromHours(2))); + public static readonly Identity Identity = new Identity("A. U. Thor", "thor@valhalla.asgard.com"); + public static readonly Identity Identity2 = new Identity("nulltoken", "emeric.fermas@gmail.com"); + + public static readonly Signature Signature = new Signature(Identity, new DateTimeOffset(2011, 06, 16, 10, 58, 27, TimeSpan.FromHours(2))); + public static readonly Signature Signature2 = new Signature(Identity2, DateTimeOffset.Parse("Wed, Dec 14 2011 08:29:03 +0100")); // Populate these to turn on live credential tests: set the // PrivateRepoUrl to the URL of a repository that requires @@ -19,15 +28,85 @@ public static class Constants // ... return new UsernamePasswordCredentials { Username = "username", Password = "swordfish" }; // // Or: + // ... return new SecureUsernamePasswordCredentials() { Username = "username", Password = StringToSecureString("swordfish") }; + // + // Or: // public const string PrivateRepoUrl = "https://tfs.contoso.com/tfs/DefaultCollection/project/_git/project"; // ... return new DefaultCredentials(); public const string PrivateRepoUrl = ""; + public static bool IsRunningOnUnix + { + get + { + return Platform.OperatingSystem == OperatingSystemType.MacOSX || + Platform.OperatingSystem == OperatingSystemType.Unix; + } + } + public static Credentials PrivateRepoCredentials(string url, string usernameFromUrl, SupportedCredentialTypes types) { return null; } + + public static string BuildPath() + { + string tempPath = null; + + if (IsRunningOnUnix) + { + // We're running on Mono/*nix. Let's unwrap the path + tempPath = UnwrapUnixTempPath(); + } + else + { + const string LibGit2TestPath = "LibGit2TestPath"; + + // We're running on .Net/Windows + if (Environment.GetEnvironmentVariables().Contains(LibGit2TestPath)) + { + Trace.TraceInformation("{0} environment variable detected", LibGit2TestPath); + tempPath = Environment.GetEnvironmentVariables()[LibGit2TestPath] as String; + } + + if (String.IsNullOrWhiteSpace(tempPath) || !Directory.Exists(tempPath)) + { + Trace.TraceInformation("Using default test path value"); + tempPath = Path.GetTempPath(); + } + } + + string testWorkingDirectory = Path.Combine(tempPath, "LibGit2Sharp-TestRepos"); + Trace.TraceInformation("Test working directory set to '{0}'", testWorkingDirectory); + return testWorkingDirectory; + } + + private static string UnwrapUnixTempPath() + { + var type = Type.GetType("Mono.Unix.UnixPath, Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"); + + return (string)type.InvokeMember("GetCompleteRealPath", + BindingFlags.Static | BindingFlags.FlattenHierarchy | + BindingFlags.InvokeMethod | BindingFlags.Public, + null, type, new object[] { Path.GetTempPath() }); + } + + // To help with creating secure strings to test with. + internal static SecureString StringToSecureString(string str) + { + var chars = str.ToCharArray(); + + var secure = new SecureString(); + for (var i = 0; i < chars.Length; i++) + { + secure.AppendChar(chars[i]); + } + + secure.MakeReadOnly(); + + return secure; + } } } diff --git a/LibGit2Sharp.Tests/TestHelpers/DateTimeOffsetExtensions.cs b/LibGit2Sharp.Tests/TestHelpers/DateTimeOffsetExtensions.cs new file mode 100644 index 000000000..d1ff4024a --- /dev/null +++ b/LibGit2Sharp.Tests/TestHelpers/DateTimeOffsetExtensions.cs @@ -0,0 +1,14 @@ +using System; + +namespace LibGit2Sharp.Tests.TestHelpers +{ + public static class DateTimeOffsetExtensions + { + public static DateTimeOffset TruncateMilliseconds(this DateTimeOffset dto) + { + // From http://stackoverflow.com/a/1005222/335418 + + return dto.AddTicks( - (dto.Ticks % TimeSpan.TicksPerSecond)); + } + } +} diff --git a/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs b/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs index 44bc9bd9d..66c1d594a 100644 --- a/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs +++ b/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; +using System.Threading; namespace LibGit2Sharp.Tests.TestHelpers { @@ -13,6 +15,8 @@ public static class DirectoryHelper { "gitmodules", ".gitmodules" }, }; + private static readonly Type[] whitelist = { typeof(IOException), typeof(UnauthorizedAccessException) }; + public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) { // From http://stackoverflow.com/questions/58744/best-way-to-copy-the-entire-contents-of-a-directory-in-c/58779#58779 @@ -32,55 +36,67 @@ private static string Rename(string name) return toRename.ContainsKey(name) ? toRename[name] : name; } - public static void DeleteSubdirectories(string parentPath) - { - string[] dirs = Directory.GetDirectories(parentPath); - foreach (string dir in dirs) - { - DeleteDirectory(dir); - } - } - public static void DeleteDirectory(string directoryPath) { // From http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true/329502#329502 if (!Directory.Exists(directoryPath)) { - Trace.WriteLine( - string.Format("Directory '{0}' is missing and can't be removed.", - directoryPath)); - + Trace.WriteLine(string.Format("Directory '{0}' is missing and can't be removed.", directoryPath)); return; } + NormalizeAttributes(directoryPath); + DeleteDirectory(directoryPath, maxAttempts: 5, initialTimeout: 16, timeoutFactor: 2); + } - string[] files = Directory.GetFiles(directoryPath); - string[] dirs = Directory.GetDirectories(directoryPath); + private static void NormalizeAttributes(string directoryPath) + { + string[] filePaths = Directory.GetFiles(directoryPath); + string[] subdirectoryPaths = Directory.GetDirectories(directoryPath); - foreach (string file in files) + foreach (string filePath in filePaths) { - File.SetAttributes(file, FileAttributes.Normal); - File.Delete(file); + File.SetAttributes(filePath, FileAttributes.Normal); } - - foreach (string dir in dirs) + foreach (string subdirectoryPath in subdirectoryPaths) { - DeleteDirectory(dir); + NormalizeAttributes(subdirectoryPath); } - File.SetAttributes(directoryPath, FileAttributes.Normal); - try - { - Directory.Delete(directoryPath, false); - } - catch (IOException) + } + + private static void DeleteDirectory(string directoryPath, int maxAttempts, int initialTimeout, int timeoutFactor) + { + for (int attempt = 1; attempt <= maxAttempts; attempt++) { - Trace.WriteLine(string.Format("{0}The directory '{1}' could not be deleted!" + - "{0}Most of the time, this is due to an external process accessing the files in the temporary repositories created during the test runs, and keeping a handle on the directory, thus preventing the deletion of those files." + - "{0}Known and common causes include:" + - "{0}- Windows Search Indexer (go to the Indexing Options, in the Windows Control Panel, and exclude the bin folder of LibGit2Sharp.Tests)" + - "{0}- Antivirus (exclude the bin folder of LibGit2Sharp.Tests from the paths scanned by your real-time antivirus){0}", - Environment.NewLine, Path.GetFullPath(directoryPath))); + try + { + Directory.Delete(directoryPath, true); + return; + } + catch (Exception ex) + { + var caughtExceptionType = ex.GetType(); + + if (!whitelist.Any(knownExceptionType => knownExceptionType.IsAssignableFrom(caughtExceptionType))) + { + throw; + } + + if (attempt < maxAttempts) + { + Thread.Sleep(initialTimeout * (int)Math.Pow(timeoutFactor, attempt - 1)); + continue; + } + + Trace.WriteLine(string.Format("{0}The directory '{1}' could not be deleted ({2} attempts were made) due to a {3}: {4}" + + "{0}Most of the time, this is due to an external process accessing the files in the temporary repositories created during the test runs, and keeping a handle on the directory, thus preventing the deletion of those files." + + "{0}Known and common causes include:" + + "{0}- Windows Search Indexer (go to the Indexing Options, in the Windows Control Panel, and exclude the bin folder of LibGit2Sharp.Tests)" + + "{0}- Antivirus (exclude the bin folder of LibGit2Sharp.Tests from the paths scanned by your real-time antivirus)" + + "{0}- TortoiseGit (change the 'Icon Overlays' settings, e.g., adding the bin folder of LibGit2Sharp.Tests to 'Exclude paths' and appending an '*' to exclude all subfolders as well)", + Environment.NewLine, Path.GetFullPath(directoryPath), maxAttempts, caughtExceptionType, ex.Message)); + } } } } diff --git a/LibGit2Sharp.Tests/TestHelpers/FileExportFilter.cs b/LibGit2Sharp.Tests/TestHelpers/FileExportFilter.cs new file mode 100644 index 000000000..b03b8d2c1 --- /dev/null +++ b/LibGit2Sharp.Tests/TestHelpers/FileExportFilter.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace LibGit2Sharp.Tests.TestHelpers +{ + class FileExportFilter : Filter + { + public int CleanCalledCount = 0; + public int CompleteCalledCount = 0; + public int SmudgeCalledCount = 0; + public readonly HashSet FilesFiltered; + + private bool clean; + + public FileExportFilter(string name, IEnumerable attributes) + : base(name, attributes) + { + FilesFiltered = new HashSet(); + } + + protected override void Create(string path, string root, FilterMode mode) + { + if (mode == FilterMode.Clean) + { + string filename = Path.GetFileName(path); + string cachePath = Path.Combine(root, ".git", filename); + + if (File.Exists(cachePath)) + { + File.Delete(cachePath); + } + } + } + + protected override void Clean(string path, string root, Stream input, Stream output) + { + CleanCalledCount++; + + string filename = Path.GetFileName(path); + string cachePath = Path.Combine(root, ".git", filename); + + using (var file = File.Exists(cachePath) ? File.Open(cachePath, FileMode.Append, FileAccess.Write, FileShare.None) : File.Create(cachePath)) + { + input.CopyTo(file); + } + + clean = true; + } + + protected override void Complete(string path, string root, Stream output) + { + CompleteCalledCount++; + + string filename = Path.GetFileName(path); + string cachePath = Path.Combine(root, ".git", filename); + + if (clean) + { + byte[] bytes = Encoding.UTF8.GetBytes(path); + output.Write(bytes, 0, bytes.Length); + FilesFiltered.Add(path); + } + else + { + if (File.Exists(cachePath)) + { + using (var file = File.Open(cachePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None)) + { + file.CopyTo(output); + } + } + } + } + + protected override void Smudge(string path, string root, Stream input, Stream output) + { + SmudgeCalledCount++; + + StringBuilder text = new StringBuilder(); + + byte[] buffer = new byte[64 * 1024]; + int read; + while ((read = input.Read(buffer, 0, buffer.Length)) > 0) + { + string decoded = Encoding.UTF8.GetString(buffer, 0, read); + text.Append(decoded); + } + + if (!FilesFiltered.Contains(text.ToString())) + throw new FileNotFoundException(); + + clean = false; + } + } +} diff --git a/LibGit2Sharp.Tests/TestHelpers/SkippableFactAttribute.cs b/LibGit2Sharp.Tests/TestHelpers/SkippableFactAttribute.cs index b16d823f1..b9904dba3 100644 --- a/LibGit2Sharp.Tests/TestHelpers/SkippableFactAttribute.cs +++ b/LibGit2Sharp.Tests/TestHelpers/SkippableFactAttribute.cs @@ -1,4 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using Xunit; +using Xunit.Extensions; +using Xunit.Sdk; //********************************************************************** //* This file is based on the DynamicSkipExample.cs in xUnit which is //* provided under the following Ms-PL license: @@ -58,12 +64,6 @@ //* portion of the software in compiled or object code form, you may //* only do so under a license that complies with this license. //********************************************************************** -using System.Collections.Generic; -using System.Linq; -using System.Xml; -using Xunit; -using Xunit.Extensions; -using Xunit.Sdk; namespace LibGit2Sharp.Tests.TestHelpers { diff --git a/LibGit2Sharp.Tests/TestHelpers/SubstitutionCipherFilter.cs b/LibGit2Sharp.Tests/TestHelpers/SubstitutionCipherFilter.cs new file mode 100644 index 000000000..2cba06d49 --- /dev/null +++ b/LibGit2Sharp.Tests/TestHelpers/SubstitutionCipherFilter.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.IO; + +namespace LibGit2Sharp.Tests.TestHelpers +{ + public class SubstitutionCipherFilter : Filter + { + public int CleanCalledCount = 0; + public int SmudgeCalledCount = 0; + + public SubstitutionCipherFilter(string name, IEnumerable attributes) + : base(name, attributes) + { + } + + protected override void Clean(string path, string root, Stream input, Stream output) + { + CleanCalledCount++; + RotateByThirteenPlaces(input, output); + } + + protected override void Smudge(string path, string root, Stream input, Stream output) + { + SmudgeCalledCount++; + RotateByThirteenPlaces(input, output); + } + + public static void RotateByThirteenPlaces(Stream input, Stream output) + { + int value; + + while ((value = input.ReadByte()) != -1) + { + if ((value >= 'a' && value <= 'm') || (value >= 'A' && value <= 'M')) + { + value += 13; + } + else if ((value >= 'n' && value <= 'z') || (value >= 'N' && value <= 'Z')) + { + value -= 13; + } + + output.WriteByte((byte)value); + } + } + } +} diff --git a/LibGit2Sharp.Tests/TestHelpers/TestRemoteRefs.cs b/LibGit2Sharp.Tests/TestHelpers/TestRemoteRefs.cs new file mode 100644 index 000000000..a3e1e58c4 --- /dev/null +++ b/LibGit2Sharp.Tests/TestHelpers/TestRemoteRefs.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace LibGit2Sharp.Tests.TestHelpers +{ + public class TestRemoteRefs + { + /* + * git ls-remote http://github.com/libgit2/TestGitRepository + * 49322bb17d3acc9146f98c97d078513228bbf3c0 HEAD + * 0966a434eb1a025db6b71485ab63a3bfbea520b6 refs/heads/first-merge + * 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master + * 42e4e7c5e507e113ebbb7801b16b52cf867b7ce1 refs/heads/no-parent + * d96c4e80345534eccee5ac7b07fc7603b56124cb refs/tags/annotated_tag + * c070ad8c08840c8116da865b2d65593a6bb9cd2a refs/tags/annotated_tag^{} + * 55a1a760df4b86a02094a904dfa511deb5655905 refs/tags/blob + * 8f50ba15d49353813cc6e20298002c0d17b0a9ee refs/tags/commit_tree + * 6e0c7bdb9b4ed93212491ee778ca1c65047cab4e refs/tags/nearly-dangling + */ + /// + /// Expected references on http://github.com/libgit2/TestGitRepository + /// + public static List> ExpectedRemoteRefs = new List>() + { + new Tuple("HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0"), + new Tuple("refs/heads/first-merge", "0966a434eb1a025db6b71485ab63a3bfbea520b6"), + new Tuple("refs/heads/master", "49322bb17d3acc9146f98c97d078513228bbf3c0"), + new Tuple("refs/heads/no-parent", "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1"), + new Tuple("refs/tags/annotated_tag", "d96c4e80345534eccee5ac7b07fc7603b56124cb"), + new Tuple("refs/tags/annotated_tag^{}", "c070ad8c08840c8116da865b2d65593a6bb9cd2a"), + new Tuple("refs/tags/blob", "55a1a760df4b86a02094a904dfa511deb5655905"), + new Tuple("refs/tags/commit_tree", "8f50ba15d49353813cc6e20298002c0d17b0a9ee"), + new Tuple("refs/tags/nearly-dangling", "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e"), + }; + } +} diff --git a/LibGit2Sharp.Tests/UnstageFixture.cs b/LibGit2Sharp.Tests/UnstageFixture.cs index b511d6513..ac73cf381 100644 --- a/LibGit2Sharp.Tests/UnstageFixture.cs +++ b/LibGit2Sharp.Tests/UnstageFixture.cs @@ -51,7 +51,7 @@ public void CanStageAndUnstageAnIgnoredFile() Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); repo.Stage(relativePath, new StageOptions { IncludeIgnored = true }); - Assert.Equal(FileStatus.Added, repo.RetrieveStatus(relativePath)); + Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(relativePath)); repo.Unstage(relativePath); Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); @@ -60,11 +60,11 @@ public void CanStageAndUnstageAnIgnoredFile() [Theory] [InlineData("1/branch_file.txt", FileStatus.Unaltered, true, FileStatus.Unaltered, true, 0)] - [InlineData("deleted_unstaged_file.txt", FileStatus.Missing, true, FileStatus.Missing, true, 0)] - [InlineData("modified_unstaged_file.txt", FileStatus.Modified, true, FileStatus.Modified, true, 0)] - [InlineData("modified_staged_file.txt", FileStatus.Staged, true, FileStatus.Modified, true, 0)] - [InlineData("new_tracked_file.txt", FileStatus.Added, true, FileStatus.Untracked, false, -1)] - [InlineData("deleted_staged_file.txt", FileStatus.Removed, false, FileStatus.Missing, true, 1)] + [InlineData("deleted_unstaged_file.txt", FileStatus.DeletedFromWorkdir, true, FileStatus.DeletedFromWorkdir, true, 0)] + [InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInWorkdir, true, FileStatus.ModifiedInWorkdir, true, 0)] + [InlineData("modified_staged_file.txt", FileStatus.ModifiedInIndex, true, FileStatus.ModifiedInWorkdir, true, 0)] + [InlineData("new_tracked_file.txt", FileStatus.NewInIndex, true, FileStatus.NewInWorkdir, false, -1)] + [InlineData("deleted_staged_file.txt", FileStatus.DeletedFromIndex, false, FileStatus.DeletedFromWorkdir, true, 1)] public void CanUnstage( string relativePath, FileStatus currentStatus, bool doesCurrentlyExistInTheIndex, FileStatus expectedStatusOnceStaged, bool doesExistInTheIndexOnceStaged, int expectedIndexCountVariation) @@ -85,7 +85,7 @@ public void CanUnstage( } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir)] [InlineData("where-am-I.txt", FileStatus.Nonexistent)] public void UnstagingUnknownPathsWithStrictUnmatchedExplicitPathsValidationThrows(string relativePath, FileStatus currentStatus) { @@ -98,7 +98,7 @@ public void UnstagingUnknownPathsWithStrictUnmatchedExplicitPathsValidationThrow } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir)] [InlineData("where-am-I.txt", FileStatus.Nonexistent)] public void CanUnstageUnknownPathsWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus currentStatus) { @@ -124,12 +124,12 @@ public void CanUnstageTheRemovalOfAFile() string fullPath = Path.Combine(repo.Info.WorkingDirectory, filename); Assert.False(File.Exists(fullPath)); - Assert.Equal(FileStatus.Removed, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.DeletedFromIndex, repo.RetrieveStatus(filename)); repo.Unstage(filename); Assert.Equal(count + 1, repo.Index.Count); - Assert.Equal(FileStatus.Missing, repo.RetrieveStatus(filename)); + Assert.Equal(FileStatus.DeletedFromWorkdir, repo.RetrieveStatus(filename)); } } @@ -155,7 +155,7 @@ public void CanUnstageUntrackedFileAgainstAnOrphanedHead() } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir)] [InlineData("where-am-I.txt", FileStatus.Nonexistent)] public void UnstagingUnknownPathsAgainstAnOrphanedHeadWithStrictUnmatchedExplicitPathsValidationThrows(string relativePath, FileStatus currentStatus) { @@ -171,7 +171,7 @@ public void UnstagingUnknownPathsAgainstAnOrphanedHeadWithStrictUnmatchedExplici } [Theory] - [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("new_untracked_file.txt", FileStatus.NewInWorkdir)] [InlineData("where-am-I.txt", FileStatus.Nonexistent)] public void CanUnstageUnknownPathsAgainstAnOrphanedHeadWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus currentStatus) { @@ -251,8 +251,8 @@ public void CanUnstageSourceOfARename() RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); - Assert.Equal(FileStatus.Missing, newStatus["branch_file.txt"].State); - Assert.Equal(FileStatus.Added, newStatus["renamed_branch_file.txt"].State); + Assert.Equal(FileStatus.DeletedFromWorkdir, newStatus["branch_file.txt"].State); + Assert.Equal(FileStatus.NewInIndex, newStatus["renamed_branch_file.txt"].State); } } @@ -271,8 +271,8 @@ public void CanUnstageTargetOfARename() RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); - Assert.Equal(FileStatus.Untracked, newStatus["renamed_branch_file.txt"].State); - Assert.Equal(FileStatus.Removed, newStatus["branch_file.txt"].State); + Assert.Equal(FileStatus.NewInWorkdir, newStatus["renamed_branch_file.txt"].State); + Assert.Equal(FileStatus.DeletedFromIndex, newStatus["branch_file.txt"].State); } } @@ -285,8 +285,8 @@ public void CanUnstageBothSidesOfARename() repo.Unstage(new string[] { "branch_file.txt", "renamed_branch_file.txt" }); RepositoryStatus status = repo.RetrieveStatus(); - Assert.Equal(FileStatus.Missing, status["branch_file.txt"].State); - Assert.Equal(FileStatus.Untracked, status["renamed_branch_file.txt"].State); + Assert.Equal(FileStatus.DeletedFromWorkdir, status["branch_file.txt"].State); + Assert.Equal(FileStatus.NewInWorkdir, status["renamed_branch_file.txt"].State); } } } diff --git a/LibGit2Sharp.Tests/packages.config b/LibGit2Sharp.Tests/packages.config index 973dbbaa8..532dff7af 100644 --- a/LibGit2Sharp.Tests/packages.config +++ b/LibGit2Sharp.Tests/packages.config @@ -1,7 +1,7 @@ - + - + - + diff --git a/LibGit2Sharp.sln.DotSettings b/LibGit2Sharp.sln.DotSettings index 1b6065f39..8bc2282a8 100644 --- a/LibGit2Sharp.sln.DotSettings +++ b/LibGit2Sharp.sln.DotSettings @@ -10,5 +10,8 @@ False True True + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + True True + True diff --git a/LibGit2Sharp/AfterRebaseStepInfo.cs b/LibGit2Sharp/AfterRebaseStepInfo.cs new file mode 100644 index 000000000..8e6e78e2d --- /dev/null +++ b/LibGit2Sharp/AfterRebaseStepInfo.cs @@ -0,0 +1,61 @@ +namespace LibGit2Sharp +{ + /// + /// Information about a rebase step that was just completed. + /// + public class AfterRebaseStepInfo + { + /// + /// Needed for mocking. + /// + protected AfterRebaseStepInfo() + { } + + internal AfterRebaseStepInfo(RebaseStepInfo stepInfo, Commit commit, long completedStepIndex, long totalStepCount) + { + StepInfo = stepInfo; + Commit = commit; + WasPatchAlreadyApplied = false; + CompletedStepIndex = completedStepIndex; + TotalStepCount = totalStepCount; + } + + /// + /// Constructor to call when the patch has already been applied for this step. + /// + /// + /// + /// + internal AfterRebaseStepInfo(RebaseStepInfo stepInfo, long completedStepIndex, long totalStepCount) + : this (stepInfo, null, completedStepIndex, totalStepCount) + { + WasPatchAlreadyApplied = true; + } + + /// + /// The info on the completed step. + /// + public virtual RebaseStepInfo StepInfo { get; private set; } + + /// + /// The commit generated by the step, if any. + /// + public virtual Commit Commit { get; private set; } + + /// + /// Was the changes for this step already applied. If so, + /// will be null. + /// + public virtual bool WasPatchAlreadyApplied { get; private set; } + + /// + /// The index of the step that was just completed. + /// + public virtual long CompletedStepIndex { get; private set; } + + /// + /// The total number of steps in the rebase operation. + /// + public virtual long TotalStepCount { get; private set; } + } +} diff --git a/LibGit2Sharp/AmbiguousSpecificationException.cs b/LibGit2Sharp/AmbiguousSpecificationException.cs index 3b9024cf8..bad7af141 100644 --- a/LibGit2Sharp/AmbiguousSpecificationException.cs +++ b/LibGit2Sharp/AmbiguousSpecificationException.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Runtime.Serialization; namespace LibGit2Sharp @@ -13,8 +14,7 @@ public class AmbiguousSpecificationException : LibGit2SharpException /// Initializes a new instance of the class. /// public AmbiguousSpecificationException() - { - } + { } /// /// Initializes a new instance of the class with a specified error message. @@ -22,6 +22,15 @@ public AmbiguousSpecificationException() /// A message that describes the error. public AmbiguousSpecificationException(string message) : base(message) + { } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A composite format string for use in . + /// An object array that contains zero or more objects to format. + public AmbiguousSpecificationException(string format, params object[] args) + : base(String.Format(format, args)) { } @@ -32,8 +41,7 @@ public AmbiguousSpecificationException(string message) /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. public AmbiguousSpecificationException(string message, Exception innerException) : base(message, innerException) - { - } + { } /// /// Initializes a new instance of the class with a serialized data. @@ -42,7 +50,6 @@ public AmbiguousSpecificationException(string message, Exception innerException) /// The that contains contextual information about the source or destination. protected AmbiguousSpecificationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + { } } } diff --git a/LibGit2Sharp/BareRepositoryException.cs b/LibGit2Sharp/BareRepositoryException.cs index 00b61a04b..75ad9695c 100644 --- a/LibGit2Sharp/BareRepositoryException.cs +++ b/LibGit2Sharp/BareRepositoryException.cs @@ -15,8 +15,7 @@ public class BareRepositoryException : LibGit2SharpException /// Initializes a new instance of the class. /// public BareRepositoryException() - { - } + { } /// /// Initializes a new instance of the class with a specified error message. @@ -24,8 +23,16 @@ public BareRepositoryException() /// A message that describes the error. public BareRepositoryException(string message) : base(message) - { - } + { } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A composite format string for use in . + /// An object array that contains zero or more objects to format. + public BareRepositoryException(string format, params object[] args) + : base(format, args) + { } /// /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. @@ -34,8 +41,7 @@ public BareRepositoryException(string message) /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. public BareRepositoryException(string message, Exception innerException) : base(message, innerException) - { - } + { } /// /// Initializes a new instance of the class with a serialized data. @@ -44,12 +50,10 @@ public BareRepositoryException(string message, Exception innerException) /// The that contains contextual information about the source or destination. protected BareRepositoryException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + { } internal BareRepositoryException(string message, GitErrorCode code, GitErrorCategory category) : base(message, code, category) - { - } + { } } } diff --git a/LibGit2Sharp/BeforeRebaseStepInfo.cs b/LibGit2Sharp/BeforeRebaseStepInfo.cs new file mode 100644 index 000000000..e01175c08 --- /dev/null +++ b/LibGit2Sharp/BeforeRebaseStepInfo.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace LibGit2Sharp +{ + /// + /// Information about a rebase step that is about to be performed. + /// + public class BeforeRebaseStepInfo + { + /// + /// Needed for mocking. + /// + protected BeforeRebaseStepInfo() + { } + + internal BeforeRebaseStepInfo(RebaseStepInfo stepInfo, long stepIndex, long totalStepCount) + { + StepInfo = stepInfo; + StepIndex = stepIndex; + TotalStepCount = totalStepCount; + } + + /// + /// Information on the step that is about to be performed. + /// + public virtual RebaseStepInfo StepInfo { get; private set; } + + /// + /// The index of the step that is to be run. + /// + public virtual long StepIndex { get; private set; } + + /// + /// The total number of steps in the rebase operation. + /// + public virtual long TotalStepCount { get; private set; } + } +} diff --git a/LibGit2Sharp/BlameHunk.cs b/LibGit2Sharp/BlameHunk.cs index 79dbf3945..7f05a0b30 100644 --- a/LibGit2Sharp/BlameHunk.cs +++ b/LibGit2Sharp/BlameHunk.cs @@ -29,17 +29,18 @@ internal BlameHunk(IRepository repository, GitBlameHunk rawHunk) { InitialPath = LaxUtf8Marshaler.FromNative(rawHunk.OrigPath); } - LineCount = rawHunk.LinesInHunk; + LineCount = rawHunk.LinesInHunk.ConvertToInt(); // Libgit2's line numbers are 1-based - FinalStartLineNumber = rawHunk.FinalStartLineNumber - 1; - InitialStartLineNumber = rawHunk.OrigStartLineNumber - 1; + FinalStartLineNumber = rawHunk.FinalStartLineNumber.ConvertToInt() - 1; + InitialStartLineNumber = rawHunk.OrigStartLineNumber.ConvertToInt() - 1; // Signature objects need to have ownership of their native pointers if (rawHunk.FinalSignature != IntPtr.Zero) { FinalSignature = new Signature(Proxy.git_signature_dup(rawHunk.FinalSignature)); } + if (rawHunk.OrigSignature != IntPtr.Zero) { InitialSignature = new Signature(Proxy.git_signature_dup(rawHunk.OrigSignature)); @@ -49,7 +50,8 @@ internal BlameHunk(IRepository repository, GitBlameHunk rawHunk) /// /// For easier mocking /// - protected BlameHunk() { } + protected BlameHunk() + { } /// /// Determine if this hunk contains a given line. diff --git a/LibGit2Sharp/BlameHunkCollection.cs b/LibGit2Sharp/BlameHunkCollection.cs index f487915cd..ef647eb37 100644 --- a/LibGit2Sharp/BlameHunkCollection.cs +++ b/LibGit2Sharp/BlameHunkCollection.cs @@ -28,13 +28,15 @@ internal BlameHunkCollection(Repository repo, RepositorySafeHandle repoHandle, s { version = 1, flags = options.Strategy.ToGitBlameOptionFlags(), - MinLine = (uint)options.MinLine, - MaxLine = (uint)options.MaxLine, + MinLine = new UIntPtr((uint)options.MinLine), + MaxLine = new UIntPtr((uint)options.MaxLine), }; + if (options.StartingAt != null) { rawopts.NewestCommit = repo.Committish(options.StartingAt).Oid; } + if (options.StoppingAt != null) { rawopts.OldestCommit = repo.Committish(options.StoppingAt).Oid; diff --git a/LibGit2Sharp/BlameOptions.cs b/LibGit2Sharp/BlameOptions.cs index 1fa41a1d2..c39a3f536 100644 --- a/LibGit2Sharp/BlameOptions.cs +++ b/LibGit2Sharp/BlameOptions.cs @@ -1,6 +1,4 @@ -using System; - -namespace LibGit2Sharp +namespace LibGit2Sharp { /// /// Strategy used for blaming. diff --git a/LibGit2Sharp/Blob.cs b/LibGit2Sharp/Blob.cs index 187532b7f..9b14cb50f 100644 --- a/LibGit2Sharp/Blob.cs +++ b/LibGit2Sharp/Blob.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -28,7 +29,7 @@ internal Blob(Repository repo, ObjectId id) /// /// Gets the size in bytes of the raw content of a blob. /// Please note that this would load entire blob content in the memory to compute the Size. - /// In order to read blob size from header, Repository.ObjectMetadata.RetrieveObjectMetadata(Blob.Id).Size + /// In order to read blob size from header, Repository.ObjectDatabase.RetrieveObjectMetadata(Blob.Id).Size /// can be used. /// /// @@ -55,7 +56,64 @@ public virtual Stream GetContentStream() public virtual Stream GetContentStream(FilteringOptions filteringOptions) { Ensure.ArgumentNotNull(filteringOptions, "filteringOptions"); + return Proxy.git_blob_filtered_content_stream(repo.Handle, Id, filteringOptions.HintPath, false); } + + /// + /// Gets the blob content, decoded with UTF8 encoding if the encoding cannot be detected from the byte order mark + /// + /// Blob content as text. + public virtual string GetContentText() + { + return ReadToEnd(GetContentStream(), null); + } + + /// + /// Gets the blob content decoded with the specified encoding, + /// or according to byte order marks, or the specified encoding as a fallback + /// + /// The encoding of the text to use, if it cannot be detected + /// Blob content as text. + public virtual string GetContentText(Encoding encoding) + { + Ensure.ArgumentNotNull(encoding, "encoding"); + + return ReadToEnd(GetContentStream(), encoding); + } + + /// + /// Gets the blob content, decoded with UTF8 encoding if the encoding cannot be detected + /// + /// Parameter controlling content filtering behavior + /// Blob content as text. + public virtual string GetContentText(FilteringOptions filteringOptions) + { + return GetContentText(filteringOptions, null); + } + + /// + /// Gets the blob content as it would be checked out to the + /// working directory, decoded with the specified encoding, + /// or according to byte order marks, with UTF8 as fallback, + /// if is null. + /// + /// Parameter controlling content filtering behavior + /// The encoding of the text. (default: detected or UTF8) + /// Blob content as text. + public virtual string GetContentText(FilteringOptions filteringOptions, Encoding encoding) + { + Ensure.ArgumentNotNull(filteringOptions, "filteringOptions"); + + return ReadToEnd(GetContentStream(filteringOptions), encoding); + } + + private static string ReadToEnd(Stream stream, Encoding encoding) + { + using (var reader = new StreamReader(stream, encoding ?? LaxUtf8Marshaler.Encoding, encoding == null)) + { + return reader.ReadToEnd(); + } + } } } diff --git a/LibGit2Sharp/BlobExtensions.cs b/LibGit2Sharp/BlobExtensions.cs deleted file mode 100644 index c9e15bf4c..000000000 --- a/LibGit2Sharp/BlobExtensions.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.IO; -using System.Text; -using LibGit2Sharp.Core; - -namespace LibGit2Sharp -{ - /// - /// Provides helper overloads to a . - /// - public static class BlobExtensions - { - /// - /// Gets the blob content decoded with the specified encoding, - /// or according to byte order marks, with UTF8 as fallback, - /// if is null. - /// - /// The blob for which the content will be returned. - /// The encoding of the text. (default: detected or UTF8) - /// Blob content as text. - public static string GetContentText(this Blob blob, Encoding encoding = null) - { - Ensure.ArgumentNotNull(blob, "blob"); - - using (var reader = new StreamReader(blob.GetContentStream(), encoding ?? Encoding.UTF8, encoding == null)) - { - return reader.ReadToEnd(); - } - } - - /// - /// Gets the blob content as it would be checked out to the - /// working directory, decoded with the specified encoding, - /// or according to byte order marks, with UTF8 as fallback, - /// if is null. - /// - /// The blob for which the content will be returned. - /// Parameter controlling content filtering behavior - /// The encoding of the text. (default: detected or UTF8) - /// Blob content as text. - public static string GetContentText(this Blob blob, FilteringOptions filteringOptions, Encoding encoding = null) - { - Ensure.ArgumentNotNull(blob, "blob"); - Ensure.ArgumentNotNull(filteringOptions, "filteringOptions"); - - using (var reader = new StreamReader(blob.GetContentStream(filteringOptions), encoding ?? Encoding.UTF8, encoding == null)) - { - return reader.ReadToEnd(); - } - } - } -} diff --git a/LibGit2Sharp/Branch.cs b/LibGit2Sharp/Branch.cs index 79f15b078..31fe33d89 100644 --- a/LibGit2Sharp/Branch.cs +++ b/LibGit2Sharp/Branch.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using LibGit2Sharp.Core; -using LibGit2Sharp.Handlers; namespace LibGit2Sharp { @@ -26,8 +25,7 @@ protected Branch() /// The full name of the reference internal Branch(Repository repo, Reference reference, string canonicalName) : this(repo, reference, _ => canonicalName) - { - } + { } /// /// Initializes a new instance of an orphaned class. @@ -39,8 +37,7 @@ internal Branch(Repository repo, Reference reference, string canonicalName) /// The reference. internal Branch(Repository repo, Reference reference) : this(repo, reference, r => r.TargetIdentifier) - { - } + { } private Branch(Repository repo, Reference reference, Func canonicalNameSelector) : base(repo, reference, canonicalNameSelector) @@ -125,7 +122,7 @@ public virtual Commit Tip /// public virtual ICommitLog Commits { - get { return repo.Commits.QueryBy(new CommitFilter { Since = this }); } + get { return repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = this }); } } /// @@ -183,7 +180,7 @@ public virtual Remote Remote private string UpstreamBranchCanonicalNameFromLocalBranch() { - ConfigurationEntry mergeRefEntry = repo.Config.Get("branch", Name, "merge"); + ConfigurationEntry mergeRefEntry = repo.Config.Get("branch", FriendlyName, "merge"); if (mergeRefEntry == null) { @@ -195,7 +192,7 @@ private string UpstreamBranchCanonicalNameFromLocalBranch() private string RemoteNameFromLocalBranch() { - ConfigurationEntry remoteEntry = repo.Config.Get("branch", Name, "remote"); + ConfigurationEntry remoteEntry = repo.Config.Get("branch", FriendlyName, "remote"); if (remoteEntry == null) { @@ -219,34 +216,6 @@ private string RemoteNameFromRemoteTrackingBranch() return Proxy.git_branch_remote_name(repo.Handle, CanonicalName, false); } - /// - /// Checkout the tip commit of this object. - /// If this commit is the current tip of the branch, will checkout - /// the named branch. Otherwise, will checkout the tip commit as a - /// detached HEAD. - /// - [Obsolete("This method will be removed in the next release. Please use IRepository.Checkout() instead.")] - public virtual void Checkout() - { - repo.Checkout(this); - } - - /// - /// Checkout the tip commit of this object with - /// parameter specifying checkout - /// behavior. If this commit is the current tip of the branch, will - /// checkout the named branch. Otherwise, will checkout the tip - /// commit as a detached HEAD. - /// - /// controlling checkout behavior. - /// Identity for use when updating the reflog. - [Obsolete("This method will be removed in the next release. Please use IRepository.Checkout() instead.")] - public virtual void Checkout(CheckoutOptions options, Signature signature = null) - { - Ensure.ArgumentNotNull(options, "options"); - repo.Checkout(this, options, signature); - } - private Branch ResolveTrackedBranch() { if (IsRemote) @@ -293,9 +262,9 @@ protected override string Shorten() return CanonicalName.Substring(Reference.RemoteTrackingBranchPrefix.Length); } - throw new ArgumentException( - string.Format(CultureInfo.InvariantCulture, - "'{0}' does not look like a valid branch name.", CanonicalName)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, + "'{0}' does not look like a valid branch name.", + CanonicalName)); } } } diff --git a/LibGit2Sharp/BranchCollection.cs b/LibGit2Sharp/BranchCollection.cs index 929661fea..803e28d00 100644 --- a/LibGit2Sharp/BranchCollection.cs +++ b/LibGit2Sharp/BranchCollection.cs @@ -92,7 +92,8 @@ private Branch BuildFromReferenceName(string canonicalName) public virtual IEnumerator GetEnumerator() { return Proxy.git_branch_iterator(repo, GitBranchType.GIT_BRANCH_ALL) - .ToList().GetEnumerator(); + .ToList() + .GetEnumerator(); } /// @@ -106,43 +107,90 @@ IEnumerator IEnumerable.GetEnumerator() #endregion + /// + /// Create a new local branch with the specified name + /// + /// The name of the branch. + /// Revparse spec for the target commit. + /// A new . + public virtual Branch Add(string name, string committish) + { + return Add(name, committish, false); + } + + /// + /// Create a new local branch with the specified name + /// + /// The name of the branch. + /// The target commit. + /// A new . + public virtual Branch Add(string name, Commit commit) + { + return Add(name, commit, false); + } + /// /// Create a new local branch with the specified name /// /// The name of the branch. /// The target commit. - /// Identity used for updating the reflog - /// Message added to the reflog. If null, the default is "branch: Created from [sha]". /// True to allow silent overwriting a potentially existing branch, false otherwise. /// A new . - public virtual Branch Add(string name, Commit commit, Signature signature, string logMessage = null, bool allowOverwrite = false) + public virtual Branch Add(string name, Commit commit, bool allowOverwrite) { - Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNull(commit, "commit"); - if (logMessage == null) - { - logMessage = "branch: Created from " + commit.Id; - } + return Add(name, commit.Sha, allowOverwrite); + } + + /// + /// Create a new local branch with the specified name + /// + /// The name of the branch. + /// Revparse spec for the target commit. + /// True to allow silent overwriting a potentially existing branch, false otherwise. + /// A new . + public virtual Branch Add(string name, string committish, bool allowOverwrite) + { + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(committish, "committish"); - using (Proxy.git_branch_create(repo.Handle, name, commit.Id, allowOverwrite, signature.OrDefault(repo.Config), logMessage)) {} + using (Proxy.git_branch_create_from_annotated(repo.Handle, name, committish, allowOverwrite)) + { } var branch = this[ShortToLocalName(name)]; return branch; } /// - /// Create a new local branch with the specified name, using the default reflog message + /// Deletes the branch with the specified name. /// - /// The name of the branch. - /// The target commit. - /// True to allow silent overwriting a potentially existing branch, false otherwise. - /// A new . - public virtual Branch Add(string name, Commit commit, bool allowOverwrite = false) + /// The name of the branch to delete. + public virtual void Remove(string name) { - return Add(name, commit, null, null, allowOverwrite); + Remove(name, false); } + /// + /// Deletes the branch with the specified name. + /// + /// The name of the branch to delete. + /// True if the provided is the name of a remote branch, false otherwise. + public virtual void Remove(string name, bool isRemote) + { + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + string branchName = isRemote ? Reference.RemoteTrackingBranchPrefix + name : name; + + Branch branch = this[branchName]; + + if (branch == null) + { + return; + } + + Remove(branch); + } /// /// Deletes the specified branch. /// @@ -158,53 +206,75 @@ public virtual void Remove(Branch branch) } /// - /// Rename an existing local branch + /// Rename an existing local branch, using the default reflog message /// - /// The current local branch. + /// The current branch name. + /// The new name the existing branch should bear. + /// A new . + public virtual Branch Rename(string currentName, string newName) + { + return Rename(currentName, newName, false); + } + + /// + /// Rename an existing local branch, using the default reflog message + /// + /// The current branch name. /// The new name the existing branch should bear. - /// Identity used for updating the reflog - /// Message added to the reflog. If null, the default is "branch: renamed [old] to [new]". /// True to allow silent overwriting a potentially existing branch, false otherwise. /// A new . - public virtual Branch Rename(Branch branch, string newName, Signature signature, string logMessage = null, bool allowOverwrite = false) + public virtual Branch Rename(string currentName, string newName, bool allowOverwrite) { - Ensure.ArgumentNotNull(branch, "branch"); + Ensure.ArgumentNotNullOrEmptyString(currentName, "currentName"); Ensure.ArgumentNotNullOrEmptyString(newName, "newName"); - if (branch.IsRemote) - { - throw new LibGit2SharpException( - string.Format(CultureInfo.InvariantCulture, - "Cannot rename branch '{0}'. It's a remote tracking branch.", branch.Name)); - } + Branch branch = this[currentName]; - if (logMessage == null) + if (branch == null) { - logMessage = string.Format(CultureInfo.InvariantCulture, - "branch: renamed {0} to {1}", branch.CanonicalName, Reference.LocalBranchPrefix + newName); + throw new LibGit2SharpException("No branch named '{0}' exists in the repository."); } - using (ReferenceSafeHandle referencePtr = repo.Refs.RetrieveReferencePtr(Reference.LocalBranchPrefix + branch.Name)) - { - using (Proxy.git_branch_move(referencePtr, newName, allowOverwrite, signature.OrDefault(repo.Config), logMessage)) - { - } - } + return Rename(branch, newName, allowOverwrite); + } - var newBranch = this[newName]; - return newBranch; + /// + /// Rename an existing local branch + /// + /// The current local branch. + /// The new name the existing branch should bear. + /// A new . + public virtual Branch Rename(Branch branch, string newName) + { + return Rename(branch, newName, false); } /// - /// Rename an existing local branch, using the default reflog message + /// Rename an existing local branch /// /// The current local branch. /// The new name the existing branch should bear. /// True to allow silent overwriting a potentially existing branch, false otherwise. /// A new . - public virtual Branch Rename(Branch branch, string newName, bool allowOverwrite = false) + public virtual Branch Rename(Branch branch, string newName, bool allowOverwrite) { - return Rename(branch, newName, null, null, allowOverwrite); + Ensure.ArgumentNotNull(branch, "branch"); + Ensure.ArgumentNotNullOrEmptyString(newName, "newName"); + + if (branch.IsRemote) + { + throw new LibGit2SharpException("Cannot rename branch '{0}'. It's a remote tracking branch.", + branch.FriendlyName); + } + + using (ReferenceSafeHandle referencePtr = repo.Refs.RetrieveReferencePtr(Reference.LocalBranchPrefix + branch.FriendlyName)) + { + using (Proxy.git_branch_move(referencePtr, newName, allowOverwrite)) + { } + } + + var newBranch = this[newName]; + return newBranch; } /// @@ -222,7 +292,7 @@ public virtual Branch Update(Branch branch, params Action[] actio action(updater); } - return this[branch.Name]; + return this[branch.FriendlyName]; } private static bool LooksLikeABranchName(string referenceName) @@ -234,11 +304,7 @@ private static bool LooksLikeABranchName(string referenceName) private string DebuggerDisplay { - get - { - return string.Format(CultureInfo.InvariantCulture, - "Count = {0}", this.Count()); - } + get { return string.Format(CultureInfo.InvariantCulture, "Count = {0}", this.Count()); } } } } diff --git a/LibGit2Sharp/BranchCollectionExtensions.cs b/LibGit2Sharp/BranchCollectionExtensions.cs deleted file mode 100644 index 7008eb6af..000000000 --- a/LibGit2Sharp/BranchCollectionExtensions.cs +++ /dev/null @@ -1,101 +0,0 @@ -using LibGit2Sharp.Core; -using System; - -namespace LibGit2Sharp -{ - /// - /// Provides helper overloads to a . - /// - public static class BranchCollectionExtensions - { - /// - /// Create a new local branch with the specified name, using the default reflog message - /// - /// The name of the branch. - /// Revparse spec for the target commit. - /// True to allow silent overwriting a potentially existing branch, false otherwise. - /// The being worked with. - /// A new . - public static Branch Add(this BranchCollection branches, string name, string committish, bool allowOverwrite = false) - { - return Add(branches, name, committish, null, null, allowOverwrite); - } - - /// - /// Create a new local branch with the specified name - /// - /// The being worked with. - /// The name of the branch. - /// Revparse spec for the target commit. - /// The identity used for updating the reflog - /// The optional message to log in the - /// True to allow silent overwriting a potentially existing branch, false otherwise. - /// A new . - public static Branch Add(this BranchCollection branches, string name, string committish, Signature signature, - string logMessage = null, bool allowOverwrite = false) - { - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNullOrEmptyString(committish, "committish"); - - var commit = branches.repo.LookupCommit(committish); - - if (logMessage == null) - { - var createdFrom = committish != "HEAD" - ? committish - : branches.repo.Info.IsHeadDetached - ? commit.Sha - : branches.repo.Head.Name; - - logMessage = "branch: Created from " + createdFrom; - } - - return branches.Add(name, commit, signature, logMessage, allowOverwrite); - } - - /// - /// Deletes the branch with the specified name. - /// - /// The name of the branch to delete. - /// True if the provided is the name of a remote branch, false otherwise. - /// The being worked with. - public static void Remove(this BranchCollection branches, string name, bool isRemote = false) - { - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - - string branchName = isRemote ? Reference.RemoteTrackingBranchPrefix + name : name; - - Branch branch = branches[branchName]; - - if (branch == null) - { - return; - } - - branches.Remove(branch); - } - - /// - /// Rename an existing local branch, using the default reflog message - /// - /// The current branch name. - /// The new name the existing branch should bear. - /// True to allow silent overwriting a potentially existing branch, false otherwise. - /// The being worked with. - /// A new . - public static Branch Rename(this BranchCollection branches, string currentName, string newName, bool allowOverwrite = false) - { - Ensure.ArgumentNotNullOrEmptyString(currentName, "currentName"); - Ensure.ArgumentNotNullOrEmptyString(newName, "newName"); - - Branch branch = branches[currentName]; - - if (branch == null) - { - throw new LibGit2SharpException("No branch named '{0}' exists in the repository."); - } - - return branches.Rename(branch, newName, allowOverwrite); - } - } -} diff --git a/LibGit2Sharp/BranchTrackingDetails.cs b/LibGit2Sharp/BranchTrackingDetails.cs index ad7153c88..e3d99bd45 100644 --- a/LibGit2Sharp/BranchTrackingDetails.cs +++ b/LibGit2Sharp/BranchTrackingDetails.cs @@ -1,6 +1,4 @@ -using System; - -namespace LibGit2Sharp +namespace LibGit2Sharp { /// /// Tracking information for a diff --git a/LibGit2Sharp/BranchUpdater.cs b/LibGit2Sharp/BranchUpdater.cs index f51332eb7..032bf6cf3 100644 --- a/LibGit2Sharp/BranchUpdater.cs +++ b/LibGit2Sharp/BranchUpdater.cs @@ -125,7 +125,7 @@ private void SetUpstream(string upstreamBranchName) /// The merge branch in the upstream remote's namespace. private void SetUpstreamBranch(string mergeBranchName) { - string configKey = string.Format(CultureInfo.InvariantCulture, "branch.{0}.merge", branch.Name); + string configKey = string.Format(CultureInfo.InvariantCulture, "branch.{0}.merge", branch.FriendlyName); if (string.IsNullOrEmpty(mergeBranchName)) { @@ -143,7 +143,7 @@ private void SetUpstreamBranch(string mergeBranchName) /// The name of the remote to set as the upstream branch. private void SetUpstreamRemote(string remoteName) { - string configKey = string.Format(CultureInfo.InvariantCulture, "branch.{0}.remote", branch.Name); + string configKey = string.Format(CultureInfo.InvariantCulture, "branch.{0}.remote", branch.FriendlyName); if (string.IsNullOrEmpty(remoteName)) { @@ -189,7 +189,8 @@ private void GetUpstreamInformation(string canonicalName, out string remoteName, else { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, - "'{0}' does not look like a valid canonical branch name.", canonicalName)); + "'{0}' does not look like a valid canonical branch name.", + canonicalName)); } } } diff --git a/LibGit2Sharp/Certificate.cs b/LibGit2Sharp/Certificate.cs new file mode 100644 index 000000000..95472a24c --- /dev/null +++ b/LibGit2Sharp/Certificate.cs @@ -0,0 +1,9 @@ +namespace LibGit2Sharp +{ + /// + /// Top-level certificate type. The usable certificates inherit from this class. + /// + public abstract class Certificate + { + } +} diff --git a/LibGit2Sharp/CertificateSsh.cs b/LibGit2Sharp/CertificateSsh.cs new file mode 100644 index 000000000..763a9f0b4 --- /dev/null +++ b/LibGit2Sharp/CertificateSsh.cs @@ -0,0 +1,81 @@ +using LibGit2Sharp.Core; +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp +{ + /// + /// This class represents the hostkey which is avaiable when connecting to a SSH host. + /// + public class CertificateSsh : Certificate + { + /// + /// For mocking purposes + /// + protected CertificateSsh() + { } + + /// + /// The MD5 hash of the host. Meaningful if is true + /// + public readonly byte[] HashMD5; + + /// + /// The SHA1 hash of the host. Meaningful if is true + /// + public readonly byte[] HashSHA1; + + /// + /// True if we have the MD5 hostkey hash from the server + /// + public readonly bool HasMD5; + + /// + /// True if we have the SHA1 hostkey hash from the server + /// + public readonly bool HasSHA1; + + /// + /// True if we have the SHA1 hostkey hash from the server + /// public readonly bool HasSHA1; + + internal CertificateSsh(GitCertificateSsh cert) + { + + HasMD5 = cert.type.HasFlag(GitCertificateSshType.MD5); + HasSHA1 = cert.type.HasFlag(GitCertificateSshType.SHA1); + + HashMD5 = new byte[16]; + cert.HashMD5.CopyTo(HashMD5, 0); + + HashSHA1 = new byte[20]; + cert.HashSHA1.CopyTo(HashSHA1, 0); + } + + internal IntPtr ToPointer() + { + GitCertificateSshType sshCertType = 0; + if (HasMD5) + { + sshCertType |= GitCertificateSshType.MD5; + } + if (HasSHA1) + { + sshCertType |= GitCertificateSshType.SHA1; + } + + var gitCert = new GitCertificateSsh + { + cert_type = GitCertificateType.Hostkey, + type = sshCertType, + }; + HashMD5.CopyTo(gitCert.HashMD5, 0); + HashSHA1.CopyTo(gitCert.HashSHA1, 0); + + var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(gitCert)); + Marshal.StructureToPtr(gitCert, ptr, false); + + return ptr; + } + } +} diff --git a/LibGit2Sharp/CertificateX509.cs b/LibGit2Sharp/CertificateX509.cs new file mode 100644 index 000000000..8c65cfa68 --- /dev/null +++ b/LibGit2Sharp/CertificateX509.cs @@ -0,0 +1,51 @@ +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// Conains a X509 certificate + /// + public class CertificateX509 : Certificate + { + + /// + /// For mocking purposes + /// + protected CertificateX509() + { } + + /// + /// The certificate. + /// + public virtual X509Certificate Certificate { get; private set; } + + internal CertificateX509(GitCertificateX509 cert) + { + int len = checked((int) cert.len.ToUInt32()); + byte[] data = new byte[len]; + Marshal.Copy(cert.data, data, 0, len); + Certificate = new X509Certificate(data); + } + + internal IntPtr ToPointers(out IntPtr dataPtr) + { + var certData = Certificate.Export(X509ContentType.Cert); + dataPtr = Marshal.AllocHGlobal(certData.Length); + Marshal.Copy(certData, 0, dataPtr, certData.Length); + var gitCert = new GitCertificateX509() + { + cert_type = GitCertificateType.X509, + data = dataPtr, + len = (UIntPtr)certData.LongLength, + }; + + var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(gitCert)); + Marshal.StructureToPtr(gitCert, ptr, false); + + return ptr; + } + } +} diff --git a/LibGit2Sharp/ChangeKind.cs b/LibGit2Sharp/ChangeKind.cs index c95095a37..304438be8 100644 --- a/LibGit2Sharp/ChangeKind.cs +++ b/LibGit2Sharp/ChangeKind.cs @@ -55,5 +55,10 @@ public enum ChangeKind /// Entry is unreadable. /// Unreadable = 9, + + /// + /// Entry is currently in conflict. + /// + Conflicted = 10, } } diff --git a/LibGit2Sharp/CheckoutConflictException.cs b/LibGit2Sharp/CheckoutConflictException.cs new file mode 100644 index 000000000..811e2183a --- /dev/null +++ b/LibGit2Sharp/CheckoutConflictException.cs @@ -0,0 +1,24 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// The exception that is thrown when a checkout cannot be performed + /// because of a conflicting change staged in the index, or unstaged + /// in the working directory. + /// + [Serializable] + public class CheckoutConflictException : MergeConflictException + { + /// + /// Initializes a new instance of the class. + /// + public CheckoutConflictException() + { } + + internal CheckoutConflictException(string message, GitErrorCode code, GitErrorCategory category) + : base(message, code, category) + { } + } +} diff --git a/LibGit2Sharp/CheckoutFileConflictStrategy.cs b/LibGit2Sharp/CheckoutFileConflictStrategy.cs index 578ebe03e..9d53745e7 100644 --- a/LibGit2Sharp/CheckoutFileConflictStrategy.cs +++ b/LibGit2Sharp/CheckoutFileConflictStrategy.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LibGit2Sharp +namespace LibGit2Sharp { /// /// Enum specifying what content checkout should write to disk diff --git a/LibGit2Sharp/CheckoutNotificationOptions.cs b/LibGit2Sharp/CheckoutNotificationOptions.cs index 261727154..649695797 100644 --- a/LibGit2Sharp/CheckoutNotificationOptions.cs +++ b/LibGit2Sharp/CheckoutNotificationOptions.cs @@ -1,5 +1,4 @@ using System; -using LibGit2Sharp.Handlers; namespace LibGit2Sharp { diff --git a/LibGit2Sharp/CheckoutOptions.cs b/LibGit2Sharp/CheckoutOptions.cs index 9e297cd47..010502007 100644 --- a/LibGit2Sharp/CheckoutOptions.cs +++ b/LibGit2Sharp/CheckoutOptions.cs @@ -34,8 +34,9 @@ CheckoutStrategy IConvertableToGitCheckoutOpts.CheckoutStrategy { get { - return CheckoutModifiers.HasFlag(CheckoutModifiers.Force) ? - CheckoutStrategy.GIT_CHECKOUT_FORCE : CheckoutStrategy.GIT_CHECKOUT_SAFE; + return CheckoutModifiers.HasFlag(CheckoutModifiers.Force) + ? CheckoutStrategy.GIT_CHECKOUT_FORCE + : CheckoutStrategy.GIT_CHECKOUT_SAFE; } } diff --git a/LibGit2Sharp/CherryPickOptions.cs b/LibGit2Sharp/CherryPickOptions.cs index 05619f37b..065e79bbb 100644 --- a/LibGit2Sharp/CherryPickOptions.cs +++ b/LibGit2Sharp/CherryPickOptions.cs @@ -1,113 +1,27 @@ -using LibGit2Sharp.Core; -using LibGit2Sharp.Handlers; - -namespace LibGit2Sharp +namespace LibGit2Sharp { /// /// Options controlling CherryPick behavior. /// - public sealed class CherryPickOptions : IConvertableToGitCheckoutOpts + public sealed class CherryPickOptions : MergeAndCheckoutOptionsBase { /// /// Initializes a new instance of the class. /// By default the cherry pick will be committed if there are no conflicts. /// public CherryPickOptions() - { - CommitOnSuccess = true; - - FindRenames = true; - - // TODO: libgit2 should provide reasonable defaults for these - // values, but it currently does not. - RenameThreshold = 50; - TargetLimit = 200; - } - - /// - /// The Flags specifying what conditions are - /// reported through the OnCheckoutNotify delegate. - /// - public CheckoutNotifyFlags CheckoutNotifyFlags { get; set; } - - /// - /// Delegate that checkout progress will be reported through. - /// - public CheckoutProgressHandler OnCheckoutProgress { get; set; } - - /// - /// Delegate that checkout will notify callers of - /// certain conditions. The conditions that are reported is - /// controlled with the CheckoutNotifyFlags property. - /// - public CheckoutNotifyHandler OnCheckoutNotify { get; set; } - - /// - /// Commit the cherry pick if the cherry pick is successful. - /// - public bool CommitOnSuccess { get; set; } + { } /// /// When cherry picking a merge commit, the parent number to consider as /// mainline, starting from offset 1. /// /// As a merge commit has multiple parents, cherry picking a merge commit - /// will reverse all the changes brought in by the merge except for - /// one parent's line of commits. The parent to preserve is called the - /// mainline, and must be specified by its number (i.e. offset). + /// will take only the changes relative to the given parent. The parent + /// to consider changes based on is called the mainline, and must be + /// specified by its number (i.e. offset). /// /// public int Mainline { get; set; } - - /// - /// How to handle conflicts encountered during a merge. - /// - public MergeFileFavor MergeFileFavor { get; set; } - - /// - /// How Checkout should handle writing out conflicting index entries. - /// - public CheckoutFileConflictStrategy FileConflictStrategy { get; set; } - - /// - /// Find renames. Default is true. - /// - public bool FindRenames { get; set; } - - /// - /// Similarity to consider a file renamed (default 50). If - /// `FindRenames` is enabled, added files will be compared - /// with deleted files to determine their similarity. Files that are - /// more similar than the rename threshold (percentage-wise) will be - /// treated as a rename. - /// - public int RenameThreshold; - - /// - /// Maximum similarity sources to examine for renames (default 200). - /// If the number of rename candidates (add / delete pairs) is greater - /// than this value, inexact rename detection is aborted. - /// - /// This setting overrides the `merge.renameLimit` configuration value. - /// - public int TargetLimit; - - #region IConvertableToGitCheckoutOpts - - CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks() - { - return CheckoutCallbacks.From(OnCheckoutProgress, OnCheckoutNotify); - } - - CheckoutStrategy IConvertableToGitCheckoutOpts.CheckoutStrategy - { - get - { - return CheckoutStrategy.GIT_CHECKOUT_SAFE | - GitCheckoutOptsWrapper.CheckoutStrategyFromFileConflictStrategy(FileConflictStrategy); - } - } - - #endregion IConvertableToGitCheckoutOpts } } diff --git a/LibGit2Sharp/CherryPickResult.cs b/LibGit2Sharp/CherryPickResult.cs index b93de8df5..7e944baf7 100644 --- a/LibGit2Sharp/CherryPickResult.cs +++ b/LibGit2Sharp/CherryPickResult.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace LibGit2Sharp +namespace LibGit2Sharp { /// /// Class to report the result of a cherry picked. diff --git a/LibGit2Sharp/CloneOptions.cs b/LibGit2Sharp/CloneOptions.cs index 779fef990..8d260db74 100644 --- a/LibGit2Sharp/CloneOptions.cs +++ b/LibGit2Sharp/CloneOptions.cs @@ -1,5 +1,4 @@ -using System; -using LibGit2Sharp.Core; +using LibGit2Sharp.Core; using LibGit2Sharp.Handlers; namespace LibGit2Sharp @@ -34,6 +33,11 @@ public CloneOptions() /// public string BranchName { get; set; } + /// + /// Recursively clone submodules. + /// + public bool RecurseSubmodules { get; set; } + /// /// Handler for checkout progress information. /// @@ -50,9 +54,9 @@ CheckoutStrategy IConvertableToGitCheckoutOpts.CheckoutStrategy { get { - return this.Checkout ? - CheckoutStrategy.GIT_CHECKOUT_SAFE_CREATE : - CheckoutStrategy.GIT_CHECKOUT_NONE; + return this.Checkout + ? CheckoutStrategy.GIT_CHECKOUT_SAFE + : CheckoutStrategy.GIT_CHECKOUT_NONE; } } diff --git a/LibGit2Sharp/Commit.cs b/LibGit2Sharp/Commit.cs index 6ea9d0608..ea7844477 100644 --- a/LibGit2Sharp/Commit.cs +++ b/LibGit2Sharp/Commit.cs @@ -54,7 +54,7 @@ internal Commit(Repository repo, ObjectId id) /// /// Gets the pointed at by the in the . /// - /// The relative path to the from the working directory. + /// Path to the from the tree in this /// null if nothing has been found, the otherwise. public virtual TreeEntry this[string relativePath] { @@ -118,7 +118,9 @@ private string DebuggerDisplay get { return string.Format(CultureInfo.InvariantCulture, - "{0} {1}", Id.ToString(7), MessageShort); + "{0} {1}", + Id.ToString(7), + MessageShort); } } diff --git a/LibGit2Sharp/CommitFilter.cs b/LibGit2Sharp/CommitFilter.cs index 5021c1260..fe0bfd127 100644 --- a/LibGit2Sharp/CommitFilter.cs +++ b/LibGit2Sharp/CommitFilter.cs @@ -1,4 +1,5 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; @@ -15,7 +16,7 @@ public sealed class CommitFilter public CommitFilter() { SortBy = CommitSortStrategies.Time; - Since = "HEAD"; + IncludeReachableFrom = "HEAD"; FirstParentOnly = false; } @@ -36,11 +37,42 @@ public CommitFilter() /// By default, the will be used as boundary. /// /// - public object Since { get; set; } + [Obsolete("This property will be removed in the next release. Please use IncludeReachableFrom instead.")] + public object Since + { + get { return IncludeReachableFrom; } + set { IncludeReachableFrom = value; } + } + + /// + /// A pointer to a commit object or a list of pointers to consider as starting points. + /// + /// Can be either a containing the sha or reference canonical name to use, + /// a , a , a , a , + /// a , an or even a mixed collection of all of the above. + /// By default, the will be used as boundary. + /// + /// + public object IncludeReachableFrom { get; set; } internal IList SinceList { - get { return ToList(Since); } + get { return ToList(IncludeReachableFrom); } + } + + /// + /// A pointer to a commit object or a list of pointers which will be excluded (along with ancestors) from the enumeration. + /// + /// Can be either a containing the sha or reference canonical name to use, + /// a , a , a , a , + /// a , an or even a mixed collection of all of the above. + /// + /// + [Obsolete("This property will be removed in the next release. Please use ExcludeReachableFrom instead.")] + public object Until + { + get { return ExcludeReachableFrom; } + set { ExcludeReachableFrom = value; } } /// @@ -51,11 +83,11 @@ internal IList SinceList /// a , an or even a mixed collection of all of the above. /// /// - public object Until { get; set; } + public object ExcludeReachableFrom { get; set; } internal IList UntilList { - get { return ToList(Until); } + get { return ToList(ExcludeReachableFrom); } } /// @@ -73,12 +105,12 @@ private static IList ToList(object obj) } var types = new[] - { - typeof(string), typeof(ObjectId), - typeof(Commit), typeof(TagAnnotation), - typeof(Tag), typeof(Branch), typeof(DetachedHead), - typeof(Reference), typeof(DirectReference), typeof(SymbolicReference) - }; + { + typeof(string), typeof(ObjectId), + typeof(Commit), typeof(TagAnnotation), + typeof(Tag), typeof(Branch), typeof(DetachedHead), + typeof(Reference), typeof(DirectReference), typeof(SymbolicReference) + }; if (types.Contains(obj.GetType())) { diff --git a/LibGit2Sharp/CommitLog.cs b/LibGit2Sharp/CommitLog.cs index 1056437af..7568181d3 100644 --- a/LibGit2Sharp/CommitLog.cs +++ b/LibGit2Sharp/CommitLog.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Globalization; using System.Linq; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -23,8 +22,7 @@ public sealed class CommitLog : IQueryableCommitLog /// The repository. internal CommitLog(Repository repo) : this(repo, new CommitFilter()) - { - } + { } /// /// Initializes a new instance of the class. @@ -75,24 +73,48 @@ IEnumerator IEnumerable.GetEnumerator() public ICommitLog QueryBy(CommitFilter filter) { Ensure.ArgumentNotNull(filter, "filter"); - Ensure.ArgumentNotNull(filter.Since, "filter.Since"); - Ensure.ArgumentNotNullOrEmptyString(filter.Since.ToString(), "filter.Since"); + Ensure.ArgumentNotNull(filter.IncludeReachableFrom, "filter.IncludeReachableFrom"); + Ensure.ArgumentNotNullOrEmptyString(filter.IncludeReachableFrom.ToString(), "filter.IncludeReachableFrom"); return new CommitLog(repo, filter); } + /// + /// Returns the list of commits of the repository representing the history of a file beyond renames. + /// + /// The file's path. + /// A list of file history entries, ready to be enumerated. + public IEnumerable QueryBy(string path) + { + Ensure.ArgumentNotNull(path, "path"); + + return new FileHistory(repo, path); + } + + /// + /// Returns the list of commits of the repository representing the history of a file beyond renames. + /// + /// The file's path. + /// The options used to control which commits will be returned. + /// A list of file history entries, ready to be enumerated. + public IEnumerable QueryBy(string path, FollowFilter filter) + { + Ensure.ArgumentNotNull(path, "path"); + Ensure.ArgumentNotNull(filter, "filter"); + + return new FileHistory(repo, path, new CommitFilter { SortBy = filter.SortBy }); + } + /// /// Find the best possible merge base given two s. /// /// The first . /// The second . /// The merge base or null if none found. + [Obsolete("This method will be removed in the next release. Please use ObjectDatabase.FindMergeBase() instead.")] public Commit FindMergeBase(Commit first, Commit second) { - Ensure.ArgumentNotNull(first, "first"); - Ensure.ArgumentNotNull(second, "second"); - - return FindMergeBase(new[] { first, second }, MergeBaseFindingStrategy.Standard); + return repo.ObjectDatabase.FindMergeBase(first, second); } /// @@ -101,42 +123,10 @@ public Commit FindMergeBase(Commit first, Commit second) /// The s for which to find the merge base. /// The strategy to leverage in order to find the merge base. /// The merge base or null if none found. + [Obsolete("This method will be removed in the next release. Please use ObjectDatabase.FindMergeBase() instead.")] public Commit FindMergeBase(IEnumerable commits, MergeBaseFindingStrategy strategy) { - Ensure.ArgumentNotNull(commits, "commits"); - - ObjectId id; - List ids = new List(8); - int count = 0; - - foreach (var commit in commits) - { - if (commit == null) - { - throw new ArgumentException("Enumerable contains null at position: " + count.ToString(CultureInfo.InvariantCulture), "commits"); - } - ids.Add(commit.Id.Oid); - count++; - } - - if (count < 2) - { - throw new ArgumentException("The enumerable must contains at least two commits.", "commits"); - } - - switch (strategy) - { - case MergeBaseFindingStrategy.Standard: - id = Proxy.git_merge_base_many(repo.Handle, ids.ToArray()); - break; - case MergeBaseFindingStrategy.Octopus: - id = Proxy.git_merge_base_octopus(repo.Handle, ids.ToArray()); - break; - default: - throw new ArgumentException("", "strategy"); - } - - return id == null ? null : repo.Lookup(id); + return repo.ObjectDatabase.FindMergeBase(commits, strategy); } private class CommitEnumerator : IEnumerator @@ -241,7 +231,6 @@ private void FirstParentOnly(bool firstParent) } } } - } /// diff --git a/LibGit2Sharp/CommitOptions.cs b/LibGit2Sharp/CommitOptions.cs index c36fb4334..ffd4c23d2 100644 --- a/LibGit2Sharp/CommitOptions.cs +++ b/LibGit2Sharp/CommitOptions.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LibGit2Sharp +namespace LibGit2Sharp { /// /// Provides optional additional information to commit creation. diff --git a/LibGit2Sharp/CommitRewriteInfo.cs b/LibGit2Sharp/CommitRewriteInfo.cs index 5e0a5caaa..ca7399578 100644 --- a/LibGit2Sharp/CommitRewriteInfo.cs +++ b/LibGit2Sharp/CommitRewriteInfo.cs @@ -28,11 +28,51 @@ public sealed class CommitRewriteInfo public static CommitRewriteInfo From(Commit commit) { return new CommitRewriteInfo - { - Author = commit.Author, - Committer = commit.Committer, - Message = commit.Message - }; + { + Author = commit.Author, + Committer = commit.Committer, + Message = commit.Message + }; + } + + /// + /// Build a from the passed in, + /// optionally overriding some of its properties + /// + /// The whose information is to be copied + /// Optional override for the author + /// A new object that matches the info for the + /// with the optional parameters replaced.. + public static CommitRewriteInfo From(Commit commit, Signature author) + { + return From(commit, author, null, null); + } + + /// + /// Build a from the passed in, + /// optionally overriding some of its properties + /// + /// The whose information is to be copied + /// Optional override for the message + /// A new object that matches the info for the + /// with the optional parameters replaced.. + public static CommitRewriteInfo From(Commit commit, string message) + { + return From(commit, null, null, message); + } + + /// + /// Build a from the passed in, + /// optionally overriding some of its properties + /// + /// The whose information is to be copied + /// Optional override for the author + /// Optional override for the committer + /// A new object that matches the info for the + /// with the optional parameters replaced.. + public static CommitRewriteInfo From(Commit commit, Signature author, Signature committer) + { + return From(commit, author, committer, null); } /// @@ -45,10 +85,11 @@ public static CommitRewriteInfo From(Commit commit) /// Optional override for the message /// A new object that matches the info for the /// with the optional parameters replaced.. - public static CommitRewriteInfo From(Commit commit, - Signature author = null, - Signature committer = null, - string message = null) + public static CommitRewriteInfo From( + Commit commit, + Signature author, + Signature committer, + string message) { var cri = From(commit); cri.Author = author ?? cri.Author; diff --git a/LibGit2Sharp/CompareOptions.cs b/LibGit2Sharp/CompareOptions.cs index 09f47a2ec..6b3acdfe6 100644 --- a/LibGit2Sharp/CompareOptions.cs +++ b/LibGit2Sharp/CompareOptions.cs @@ -14,6 +14,7 @@ public CompareOptions() { ContextLines = 3; InterhunkLines = 0; + Algorithm = DiffAlgorithm.Myers; } /// @@ -37,5 +38,17 @@ public CompareOptions() /// Include "unmodified" entries in the results. /// public bool IncludeUnmodified { get; set; } + + /// + /// Use the "patience diff" algorithm. + /// + [Obsolete("This property will be removed in the next release. Please use Algorithm instead.")] + public bool UsePatienceAlgorithm { get; set; } + + /// + /// Algorithm to be used when performing a Diff. + /// By default, will be used. + /// + public DiffAlgorithm Algorithm { get; set; } } } diff --git a/LibGit2Sharp/Configuration.cs b/LibGit2Sharp/Configuration.cs index 89059b63e..4dbab2412 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -14,12 +15,11 @@ namespace LibGit2Sharp public class Configuration : IDisposable, IEnumerable> { + private readonly FilePath repoConfigPath; private readonly FilePath globalConfigPath; private readonly FilePath xdgConfigPath; private readonly FilePath systemConfigPath; - private readonly Repository repository; - private ConfigurationSafeHandle configHandle; /// @@ -28,19 +28,26 @@ public class Configuration : IDisposable, protected Configuration() { } - internal Configuration(Repository repository, string globalConfigurationFileLocation, - string xdgConfigurationFileLocation, string systemConfigurationFileLocation) + internal Configuration( + Repository repository, + string repositoryConfigurationFileLocation, + string globalConfigurationFileLocation, + string xdgConfigurationFileLocation, + string systemConfigurationFileLocation) { - this.repository = repository; + if (repositoryConfigurationFileLocation != null) + { + repoConfigPath = NormalizeConfigPath(repositoryConfigurationFileLocation); + } globalConfigPath = globalConfigurationFileLocation ?? Proxy.git_config_find_global(); xdgConfigPath = xdgConfigurationFileLocation ?? Proxy.git_config_find_xdg(); systemConfigPath = systemConfigurationFileLocation ?? Proxy.git_config_find_system(); - Init(); + Init(repository); } - private void Init() + private void Init(Repository repository) { configHandle = Proxy.git_config_new(); @@ -55,6 +62,10 @@ private void Init() Proxy.git_repository_set_config(repository.Handle, configHandle); } + else if (repoConfigPath != null) + { + Proxy.git_config_add_file_ondisk(configHandle, repoConfigPath, ConfigurationLevel.Local); + } if (globalConfigPath != null) { @@ -72,23 +83,154 @@ private void Init() } } + private FilePath NormalizeConfigPath(FilePath path) + { + if (File.Exists(path.Native)) + { + return path; + } + + if (!Directory.Exists(path.Native)) + { + throw new FileNotFoundException("Cannot find repository configuration file", path.Native); + } + + var configPath = Path.Combine(path.Native, "config"); + + if (File.Exists(configPath)) + { + return configPath; + } + + var gitConfigPath = Path.Combine(path.Native, ".git", "config"); + + if (File.Exists(gitConfigPath)) + { + return gitConfigPath; + } + + throw new FileNotFoundException("Cannot find repository configuration file", path.Native); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// An instance of . + public static Configuration BuildFrom(string repositoryConfigurationFileLocation) + { + return BuildFrom(repositoryConfigurationFileLocation, null, null, null); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// Path to a Global configuration file. If null, the default path for a Global configuration file will be probed. + /// An instance of . + public static Configuration BuildFrom( + string repositoryConfigurationFileLocation, + string globalConfigurationFileLocation) + { + return BuildFrom(repositoryConfigurationFileLocation, globalConfigurationFileLocation, null, null); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// Path to a Global configuration file. If null, the default path for a Global configuration file will be probed. + /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. + /// An instance of . + public static Configuration BuildFrom( + string repositoryConfigurationFileLocation, + string globalConfigurationFileLocation, + string xdgConfigurationFileLocation) + { + return BuildFrom(repositoryConfigurationFileLocation, globalConfigurationFileLocation, xdgConfigurationFileLocation, null); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// Path to a Global configuration file. If null, the default path for a Global configuration file will be probed. + /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. + /// Path to a System configuration file. If null, the default path for a System configuration file will be probed. + /// An instance of . + public static Configuration BuildFrom( + string repositoryConfigurationFileLocation, + string globalConfigurationFileLocation, + string xdgConfigurationFileLocation, + string systemConfigurationFileLocation) + { + return new Configuration(null, repositoryConfigurationFileLocation, globalConfigurationFileLocation, xdgConfigurationFileLocation, systemConfigurationFileLocation); + } + + /// + /// Access configuration values without a repository. Generally you want to access configuration via an instance of instead. + /// + /// Path to a Global configuration file. If null, the default path for a global configuration file will be probed. + [Obsolete("This method will be removed in the next release. Please use Configuration.BuildFrom(string, string) instead.")] + public Configuration(string globalConfigurationFileLocation) + : this(null, null, globalConfigurationFileLocation, null, null) + { } + + /// + /// Access configuration values without a repository. Generally you want to access configuration via an instance of instead. + /// + /// Path to a Global configuration file. If null, the default path for a global configuration file will be probed. + /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. + [Obsolete("This method will be removed in the next release. Please use Configuration.BuildFrom(string, string, string) instead.")] + public Configuration(string globalConfigurationFileLocation, string xdgConfigurationFileLocation) + : this(null, null, globalConfigurationFileLocation, xdgConfigurationFileLocation, null) + { } + /// /// Access configuration values without a repository. Generally you want to access configuration via an instance of instead. /// /// Path to a Global configuration file. If null, the default path for a global configuration file will be probed. /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. /// Path to a System configuration file. If null, the default path for a system configuration file will be probed. - public Configuration(string globalConfigurationFileLocation = null, string xdgConfigurationFileLocation = null, string systemConfigurationFileLocation = null) - : this(null, globalConfigurationFileLocation, xdgConfigurationFileLocation, systemConfigurationFileLocation) - { - } + [Obsolete("This method will be removed in the next release. Please use Configuration.BuildFrom(string, string, string, string) instead.")] + public Configuration(string globalConfigurationFileLocation, string xdgConfigurationFileLocation, string systemConfigurationFileLocation) + : this(null, null, globalConfigurationFileLocation, xdgConfigurationFileLocation, systemConfigurationFileLocation) + { } /// /// Determines which configuration file has been found. /// public virtual bool HasConfig(ConfigurationLevel level) { - using (ConfigurationSafeHandle snapshot = Snapshot ()) + using (ConfigurationSafeHandle snapshot = Snapshot()) using (ConfigurationSafeHandle handle = RetrieveConfigurationHandle(level, false, snapshot)) { return handle != null; @@ -109,12 +251,21 @@ public void Dispose() #endregion + /// + /// Unset a configuration variable (key and value) in the local configuration. + /// + /// The key to unset. + public virtual void Unset(string key) + { + Unset(key, ConfigurationLevel.Local); + } + /// /// Unset a configuration variable (key and value). /// /// The key to unset. /// The configuration file which should be considered as the target of this operation - public virtual void Unset(string key, ConfigurationLevel level = ConfigurationLevel.Local) + public virtual void Unset(string key, ConfigurationLevel level) { Ensure.ArgumentNotNullOrEmptyString(key, "key"); @@ -124,6 +275,16 @@ public virtual void Unset(string key, ConfigurationLevel level = ConfigurationLe } } + internal void UnsetMultivar(string key, ConfigurationLevel level) + { + Ensure.ArgumentNotNullOrEmptyString(key, "key"); + + using (ConfigurationSafeHandle h = RetrieveConfigurationHandle(level, true, configHandle)) + { + Proxy.git_config_delete_multivar(h, key); + } + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -132,6 +293,64 @@ protected virtual void Dispose(bool disposing) configHandle.SafeDispose(); } + /// + /// Get a configuration value for the given key parts. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [core] + /// bare = true + /// + /// + /// You would call: + /// + /// + /// bool isBare = repo.Config.Get<bool>(new []{ "core", "bare" }).Value; + /// + /// + /// + /// The configuration value type + /// The key parts + /// The , or null if not set + public virtual ConfigurationEntry Get(string[] keyParts) + { + Ensure.ArgumentNotNull(keyParts, "keyParts"); + + return Get(string.Join(".", keyParts)); + } + + /// + /// Get a configuration value for the given key parts. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [difftool "kdiff3"] + /// path = c:/Program Files/KDiff3/kdiff3.exe + /// + /// + /// You would call: + /// + /// + /// string where = repo.Config.Get<string>("difftool", "kdiff3", "path").Value; + /// + /// + /// + /// The configuration value type + /// The first key part + /// The second key part + /// The third key part + /// The , or null if not set + public virtual ConfigurationEntry Get(string firstKeyPart, string secondKeyPart, string thirdKeyPart) + { + Ensure.ArgumentNotNullOrEmptyString(firstKeyPart, "firstKeyPart"); + Ensure.ArgumentNotNullOrEmptyString(secondKeyPart, "secondKeyPart"); + Ensure.ArgumentNotNullOrEmptyString(thirdKeyPart, "thirdKeyPart"); + + return Get(new[] { firstKeyPart, secondKeyPart, thirdKeyPart }); + } + /// /// Get a configuration value for a key. Keys are in the form 'section.name'. /// @@ -208,6 +427,202 @@ public virtual ConfigurationEntry Get(string key, ConfigurationLevel level } } + /// + /// Get a configuration value for the given key. + /// + /// The configuration value type. + /// The key + /// The configuration value, or the default value for the selected if not found + public virtual T GetValueOrDefault(string key) + { + return ValueOrDefault(Get(key), default(T)); + } + + /// + /// Get a configuration value for the given key, + /// or if the key is not set. + /// + /// The configuration value type. + /// The key + /// The default value if the key is not set. + /// The configuration value, or the default value + public virtual T GetValueOrDefault(string key, T defaultValue) + { + return ValueOrDefault(Get(key), defaultValue); + } + + /// + /// Get a configuration value for the given key + /// + /// The configuration value type. + /// The key. + /// The configuration file into which the key should be searched for. + /// The configuration value, or the default value for if not found + public virtual T GetValueOrDefault(string key, ConfigurationLevel level) + { + return ValueOrDefault(Get(key, level), default(T)); + } + + /// + /// Get a configuration value for the given key, + /// or if the key is not set. + /// + /// The configuration value type. + /// The key. + /// The configuration file into which the key should be searched for. + /// The selector used to generate a default value if the key is not set. + /// The configuration value, or the default value. + public virtual T GetValueOrDefault(string key, ConfigurationLevel level, T defaultValue) + { + return ValueOrDefault(Get(key, level), defaultValue); + } + + /// + /// Get a configuration value for the given key parts + /// + /// The configuration value type. + /// The key parts. + /// The configuration value, or the default value for if not found + public virtual T GetValueOrDefault(string[] keyParts) + { + return ValueOrDefault(Get(keyParts), default(T)); + } + + /// + /// Get a configuration value for the given key parts, + /// or if the key is not set. + /// + /// The configuration value type. + /// The key parts. + /// The default value if the key is not set. + /// The configuration value, or the default value. + public virtual T GetValueOrDefault(string[] keyParts, T defaultValue) + { + return ValueOrDefault(Get(keyParts), defaultValue); + } + + /// + /// Get a configuration value for the given key parts. + /// + /// The configuration value type. + /// The first key part. + /// The second key part. + /// The third key part. + /// The configuration value, or the default value for the selected if not found + public virtual T GetValueOrDefault(string firstKeyPart, string secondKeyPart, string thirdKeyPart) + { + return ValueOrDefault(Get(firstKeyPart, secondKeyPart, thirdKeyPart), default(T)); + } + + /// + /// Get a configuration value for the given key parts, + /// or if the key is not set. + /// + /// The configuration value type. + /// The first key part. + /// The second key part. + /// The third key part. + /// The default value if the key is not set. + /// The configuration value, or the default. + public virtual T GetValueOrDefault(string firstKeyPart, string secondKeyPart, string thirdKeyPart, T defaultValue) + { + return ValueOrDefault(Get(firstKeyPart, secondKeyPart, thirdKeyPart), defaultValue); + } + + /// + /// Get a configuration value for the given key, + /// or a value generated by + /// if the key is not set. + /// + /// The configuration value type. + /// The key + /// The selector used to generate a default value if the key is not set. + /// The configuration value, or a generated default. + public virtual T GetValueOrDefault(string key, Func defaultValueSelector) + { + return ValueOrDefault(Get(key), defaultValueSelector); + } + + /// + /// Get a configuration value for the given key, + /// or a value generated by + /// if the key is not set. + /// + /// The configuration value type. + /// The key. + /// The configuration file into which the key should be searched for. + /// The selector used to generate a default value if the key is not set. + /// The configuration value, or a generated default. + public virtual T GetValueOrDefault(string key, ConfigurationLevel level, Func defaultValueSelector) + { + return ValueOrDefault(Get(key, level), defaultValueSelector); + } + + /// + /// Get a configuration value for the given key parts, + /// or a value generated by + /// if the key is not set. + /// + /// The configuration value type. + /// The key parts. + /// The selector used to generate a default value if the key is not set. + /// The configuration value, or a generated default. + public virtual T GetValueOrDefault(string[] keyParts, Func defaultValueSelector) + { + return ValueOrDefault(Get(keyParts), defaultValueSelector); + } + + /// + /// Get a configuration value for the given key parts, + /// or a value generated by + /// if the key is not set. + /// + /// The configuration value type. + /// The first key part. + /// The second key part. + /// The third key part. + /// The selector used to generate a default value if the key is not set. + /// The configuration value, or a generated default. + public virtual T GetValueOrDefault(string firstKeyPart, string secondKeyPart, string thirdKeyPart, Func defaultValueSelector) + { + return ValueOrDefault(Get(firstKeyPart, secondKeyPart, thirdKeyPart), defaultValueSelector); + } + + private static T ValueOrDefault(ConfigurationEntry value, T defaultValue) + { + return value == null ? defaultValue : value.Value; + } + + private static T ValueOrDefault(ConfigurationEntry value, Func defaultValueSelector) + { + Ensure.ArgumentNotNull(defaultValueSelector, "defaultValueSelector"); + + return value == null + ? defaultValueSelector() + : value.Value; + } + + /// + /// Set a configuration value for a key in the local configuration. Keys are in the form 'section.name'. + /// + /// For example in order to set the value for this in a .git\config file: + /// + /// [test] + /// boolsetting = true + /// + /// You would call: + /// + /// repo.Config.Set("test.boolsetting", true); + /// + /// + /// The configuration value type + /// The key parts + /// The value + public virtual void Set(string key, T value) + { + Set(key, value, ConfigurationLevel.Local); + } + /// /// Set a configuration value for a key. Keys are in the form 'section.name'. /// @@ -225,7 +640,7 @@ public virtual ConfigurationEntry Get(string key, ConfigurationLevel level /// The key parts /// The value /// The configuration file which should be considered as the target of this operation - public virtual void Set(string key, T value, ConfigurationLevel level = ConfigurationLevel.Local) + public virtual void Set(string key, T value, ConfigurationLevel level) { Ensure.ArgumentNotNull(value, "value"); Ensure.ArgumentNotNullOrEmptyString(key, "key"); @@ -241,14 +656,23 @@ public virtual void Set(string key, T value, ConfigurationLevel level = Confi } } + /// + /// Find configuration entries matching . + /// + /// A regular expression. + /// Matching entries. + public virtual IEnumerable> Find(string regexp) + { + return Find(regexp, ConfigurationLevel.Local); + } + /// /// Find configuration entries matching . /// /// A regular expression. /// The configuration file into which the key should be searched for. /// Matching entries. - public virtual IEnumerable> Find(string regexp, - ConfigurationLevel level = ConfigurationLevel.Local) + public virtual IEnumerable> Find(string regexp, ConfigurationLevel level) { Ensure.ArgumentNotNullOrEmptyString(regexp, "regexp"); @@ -269,9 +693,8 @@ private ConfigurationSafeHandle RetrieveConfigurationHandle(ConfigurationLevel l if (handle == null && throwIfStoreHasNotBeenFound) { - throw new LibGit2SharpException( - string.Format(CultureInfo.InvariantCulture, "No {0} configuration file has been found.", - Enum.GetName(typeof(ConfigurationLevel), level))); + throw new LibGit2SharpException("No {0} configuration file has been found.", + Enum.GetName(typeof(ConfigurationLevel), level)); } return handle; @@ -299,7 +722,7 @@ public virtual IEnumerator> GetEnumerator() return BuildConfigEntries().GetEnumerator(); } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable>)this).GetEnumerator(); } @@ -319,12 +742,8 @@ private static ConfigurationEntry BuildConfigEntry(IntPtr entryPtr) } /// - /// Builds a based on current configuration. - /// - /// Name is populated from the user.name setting, and is "unknown" if unspecified. - /// Email is populated from the user.email setting, and is built from - /// and if unspecified. - /// + /// Builds a based on current configuration. If it is not found or + /// some configuration is missing, null is returned. /// /// The same escalation logic than in git.git will be used when looking for the key in the config files: /// - local: the Git file in the current repository @@ -334,45 +753,30 @@ private static ConfigurationEntry BuildConfigEntry(IntPtr entryPtr) /// /// /// The timestamp to use for the . - /// The signature. + /// The signature or null if no user identity can be found in the configuration. public virtual Signature BuildSignature(DateTimeOffset now) { - return BuildSignature(now, false); - } - - internal Signature BuildSignature(DateTimeOffset now, bool shouldThrowIfNotFound) - { - const string userNameKey = "user.name"; - var name = this.GetValueOrDefault(userNameKey); - var normalizedName = NormalizeUserSetting(shouldThrowIfNotFound, userNameKey, name, - () => "unknown"); + var name = this.GetValueOrDefault("user.name"); + var email = this.GetValueOrDefault("user.email"); - const string userEmailKey = "user.email"; - var email = this.GetValueOrDefault(userEmailKey); - var normalizedEmail = NormalizeUserSetting(shouldThrowIfNotFound, userEmailKey, email, - () => string.Format( - CultureInfo.InvariantCulture, "{0}@{1}", Environment.UserName, Environment.UserDomainName)); - - return new Signature(normalizedName, normalizedEmail, now); - } - - private string NormalizeUserSetting(bool shouldThrowIfNotFound, string entryName, string currentValue, Func defaultValue) - { - if (!string.IsNullOrEmpty(currentValue)) + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(email)) { - return currentValue; + return null; } - string message = string.Format("Configuration value '{0}' is missing or invalid.", entryName); + return new Signature(name, email, now); + } - if (shouldThrowIfNotFound) + internal Signature BuildSignatureOrThrow(DateTimeOffset now) + { + var signature = BuildSignature(now); + if (signature == null) { - throw new LibGit2SharpException(message); + throw new LibGit2SharpException("This overload requires 'user.name' and 'user.email' to be set. " + + "Use a different overload or set those variables in the configuation"); } - Log.Write(LogLevel.Warning, message); - - return defaultValue(); + return signature; } private ConfigurationSafeHandle Snapshot() diff --git a/LibGit2Sharp/ConfigurationEntry.cs b/LibGit2Sharp/ConfigurationEntry.cs index 7456be3c4..13c153a2a 100644 --- a/LibGit2Sharp/ConfigurationEntry.cs +++ b/LibGit2Sharp/ConfigurationEntry.cs @@ -48,8 +48,7 @@ private string DebuggerDisplay { get { - return string.Format(CultureInfo.InvariantCulture, - "{0} = \"{1}\"", Key, Value); + return string.Format(CultureInfo.InvariantCulture, "{0} = \"{1}\"", Key, Value); } } } diff --git a/LibGit2Sharp/ConfigurationExtensions.cs b/LibGit2Sharp/ConfigurationExtensions.cs deleted file mode 100644 index 5e79a2898..000000000 --- a/LibGit2Sharp/ConfigurationExtensions.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using LibGit2Sharp.Core; - -namespace LibGit2Sharp -{ - /// - /// Provides helper overloads to a . - /// - public static class ConfigurationExtensions - { - /// - /// Get a configuration value for the given key parts. - /// - /// For example in order to get the value for this in a .git\config file: - /// - /// - /// [core] - /// bare = true - /// - /// - /// You would call: - /// - /// - /// bool isBare = repo.Config.Get<bool>(new []{ "core", "bare" }).Value; - /// - /// - /// - /// The configuration value type - /// The configuration being worked with. - /// The key parts - /// The , or null if not set - public static ConfigurationEntry Get(this Configuration config, string[] keyParts) - { - Ensure.ArgumentNotNull(keyParts, "keyParts"); - - return config.Get(string.Join(".", keyParts)); - } - - /// - /// Get a configuration value for the given key parts. - /// - /// For example in order to get the value for this in a .git\config file: - /// - /// - /// [difftool "kdiff3"] - /// path = c:/Program Files/KDiff3/kdiff3.exe - /// - /// - /// You would call: - /// - /// - /// string where = repo.Config.Get<string>("difftool", "kdiff3", "path").Value; - /// - /// - /// - /// The configuration value type - /// The configuration being worked with. - /// The first key part - /// The second key part - /// The third key part - /// The , or null if not set - public static ConfigurationEntry Get(this Configuration config, string firstKeyPart, string secondKeyPart, string thirdKeyPart) - { - Ensure.ArgumentNotNullOrEmptyString(firstKeyPart, "firstKeyPart"); - Ensure.ArgumentNotNullOrEmptyString(secondKeyPart, "secondKeyPart"); - Ensure.ArgumentNotNullOrEmptyString(thirdKeyPart, "thirdKeyPart"); - - return config.Get(new[] { firstKeyPart, secondKeyPart, thirdKeyPart }); - } - - /// - /// Get a configuration value for the given key, - /// or if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The key - /// The default value if the key is not set. - /// The configuration value, or the default. - public static T GetValueOrDefault(this Configuration config, string key, T defaultValue = default(T)) - { - return ValueOrDefault(config.Get(key), defaultValue); - } - - /// - /// Get a configuration value for the given key, - /// or if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The key. - /// The configuration file into which the key should be searched for. - /// The selector used to generate a default value if the key is not set. - /// The configuration value, or the default. - public static T GetValueOrDefault(this Configuration config, string key, ConfigurationLevel level, T defaultValue = default(T)) - { - return ValueOrDefault(config.Get(key, level), defaultValue); - } - - /// - /// Get a configuration value for the given key parts, - /// or if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The key parts. - /// The default value if the key is not set. - /// The configuration value, or the default. - public static T GetValueOrDefault(this Configuration config, string[] keyParts, T defaultValue = default(T)) - { - return ValueOrDefault(config.Get(keyParts), defaultValue); - } - - /// - /// Get a configuration value for the given key parts, - /// or if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The first key part. - /// The second key part. - /// The third key part. - /// The default value if the key is not set. - /// The configuration value, or the default. - public static T GetValueOrDefault(this Configuration config, string firstKeyPart, string secondKeyPart, string thirdKeyPart, T defaultValue = default(T)) - { - return ValueOrDefault(config.Get(firstKeyPart, secondKeyPart, thirdKeyPart), defaultValue); - } - - /// - /// Get a configuration value for the given key, - /// or a value generated by - /// if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The key - /// The selector used to generate a default value if the key is not set. - /// The configuration value, or a generated default. - public static T GetValueOrDefault(this Configuration config, string key, Func defaultValueSelector) - { - return ValueOrDefault(config.Get(key), defaultValueSelector); - } - - /// - /// Get a configuration value for the given key, - /// or a value generated by - /// if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The key. - /// The configuration file into which the key should be searched for. - /// The selector used to generate a default value if the key is not set. - /// The configuration value, or a generated default. - public static T GetValueOrDefault(this Configuration config, string key, ConfigurationLevel level, Func defaultValueSelector) - { - return ValueOrDefault(config.Get(key, level), defaultValueSelector); - } - - /// - /// Get a configuration value for the given key parts, - /// or a value generated by - /// if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The key parts. - /// The selector used to generate a default value if the key is not set. - /// The configuration value, or a generated default. - public static T GetValueOrDefault(this Configuration config, string[] keyParts, Func defaultValueSelector) - { - return ValueOrDefault(config.Get(keyParts), defaultValueSelector); - } - - /// - /// Get a configuration value for the given key parts, - /// or a value generated by - /// if the key is not set. - /// - /// The configuration value type. - /// The configuration being worked with. - /// The first key part. - /// The second key part. - /// The third key part. - /// The selector used to generate a default value if the key is not set. - /// The configuration value, or a generated default. - public static T GetValueOrDefault(this Configuration config, string firstKeyPart, string secondKeyPart, string thirdKeyPart, Func defaultValueSelector) - { - return ValueOrDefault(config.Get(firstKeyPart, secondKeyPart, thirdKeyPart), defaultValueSelector); - } - - private static T ValueOrDefault(ConfigurationEntry value, T defaultValue) - { - return value == null ? defaultValue : value.Value; - } - - private static T ValueOrDefault(ConfigurationEntry value, Func defaultValueSelector) - { - Ensure.ArgumentNotNull(defaultValueSelector, "defaultValueSelector"); - - return value == null - ? defaultValueSelector() - : value.Value; - } - } -} diff --git a/LibGit2Sharp/ConfigurationLevel.cs b/LibGit2Sharp/ConfigurationLevel.cs index b8ab12097..92122727a 100644 --- a/LibGit2Sharp/ConfigurationLevel.cs +++ b/LibGit2Sharp/ConfigurationLevel.cs @@ -8,21 +8,21 @@ public enum ConfigurationLevel /// /// The local .git/config of the current repository. /// - Local = 4, + Local = 5, /// /// The global ~/.gitconfig of the current user. /// - Global = 3, + Global = 4, /// /// The global ~/.config/git/config of the current user. /// - Xdg = 2, + Xdg = 3, /// /// The system wide .gitconfig. /// - System = 1, + System = 2, } } diff --git a/LibGit2Sharp/ConflictCollection.cs b/LibGit2Sharp/ConflictCollection.cs index ba8bb8c37..90d48fa33 100644 --- a/LibGit2Sharp/ConflictCollection.cs +++ b/LibGit2Sharp/ConflictCollection.cs @@ -13,7 +13,7 @@ namespace LibGit2Sharp /// public class ConflictCollection : IEnumerable { - private readonly Repository repo; + private readonly Index index; /// /// Needed for mocking purposes. @@ -21,9 +21,9 @@ public class ConflictCollection : IEnumerable protected ConflictCollection() { } - internal ConflictCollection(Repository repo) + internal ConflictCollection(Index index) { - this.repo = repo; + this.index = index; } /// @@ -36,7 +36,7 @@ public virtual Conflict this[string path] { get { - return Proxy.git_index_conflict_get(repo.Index.Handle, repo, path); + return Proxy.git_index_conflict_get(index.Handle, path); } } @@ -48,7 +48,7 @@ public virtual IndexReucEntryCollection ResolvedConflicts { get { - return new IndexReucEntryCollection(repo); + return new IndexReucEntryCollection(index); } } @@ -60,7 +60,7 @@ public virtual IndexNameEntryCollection Names { get { - return new IndexNameEntryCollection(repo); + return new IndexNameEntryCollection(index); } } @@ -72,7 +72,7 @@ private List AllConflicts() IndexEntry ancestor = null, ours = null, theirs = null; string currentPath = null; - foreach (IndexEntry entry in repo.Index) + foreach (IndexEntry entry in index) { if (entry.StageLevel == StageLevel.Staged) { @@ -102,10 +102,10 @@ private List AllConflicts() theirs = entry; break; default: - throw new InvalidOperationException(string.Format( - CultureInfo.InvariantCulture, - "Entry '{0}' bears an unexpected StageLevel '{1}'", - entry.Path, entry.StageLevel)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + "Entry '{0}' bears an unexpected StageLevel '{1}'", + entry.Path, + entry.StageLevel)); } } diff --git a/LibGit2Sharp/ContentChanges.cs b/LibGit2Sharp/ContentChanges.cs index e5d84503e..dee15abee 100644 --- a/LibGit2Sharp/ContentChanges.cs +++ b/LibGit2Sharp/ContentChanges.cs @@ -25,7 +25,10 @@ internal ContentChanges(Repository repo, Blob oldBlob, Blob newBlob, GitDiffOpti Proxy.git_diff_blobs(repo.Handle, oldBlob != null ? oldBlob.Id : null, newBlob != null ? newBlob.Id : null, - options, FileCallback, HunkCallback, LineCallback); + options, + FileCallback, + HunkCallback, + LineCallback); } internal ContentChanges(bool isBinaryComparison) @@ -120,7 +123,9 @@ private string DebuggerDisplay get { return string.Format(CultureInfo.InvariantCulture, - @"{{+{0}, -{1}}}", LinesAdded, LinesDeleted); + @"{{+{0}, -{1}}}", + LinesAdded, + LinesDeleted); } } } diff --git a/LibGit2Sharp/CopyWindowsNativeDependencies.targets b/LibGit2Sharp/CopyWindowsNativeDependencies.targets deleted file mode 100644 index 3a7d61997..000000000 --- a/LibGit2Sharp/CopyWindowsNativeDependencies.targets +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/LibGit2Sharp/Core/EncodingMarshaler.cs b/LibGit2Sharp/Core/EncodingMarshaler.cs index ad85c5ec9..fadc15610 100644 --- a/LibGit2Sharp/Core/EncodingMarshaler.cs +++ b/LibGit2Sharp/Core/EncodingMarshaler.cs @@ -43,8 +43,9 @@ public virtual IntPtr MarshalManagedToNative(Object managedObj) if (str == null) { - throw new MarshalDirectiveException( - string.Format(CultureInfo.InvariantCulture, "{0} must be used on a string.", GetType().Name)); + throw new MarshalDirectiveException(string.Format(CultureInfo.InvariantCulture, + "{0} must be used on a string.", + GetType().Name)); } return FromManaged(encoding, str); @@ -59,7 +60,7 @@ public virtual Object MarshalNativeToManaged(IntPtr pNativeData) public static unsafe IntPtr FromManaged(Encoding encoding, String value) { - if (value == null) + if (encoding == null || value == null) { return IntPtr.Zero; } diff --git a/LibGit2Sharp/Core/Ensure.cs b/LibGit2Sharp/Core/Ensure.cs index 1205f8313..b85cf712c 100644 --- a/LibGit2Sharp/Core/Ensure.cs +++ b/LibGit2Sharp/Core/Ensure.cs @@ -1,9 +1,8 @@ using System; -using System.Linq; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Linq; namespace LibGit2Sharp.Core { @@ -88,6 +87,33 @@ public static void ArgumentDoesNotContainZeroByte(string argumentValue, string a "Zero bytes ('\\0') are not allowed. A zero byte has been found at position {0}.", zeroPos), argumentName); } + /// + /// Checks an argument to ensure it isn't a IntPtr.Zero (aka null). + /// + /// The argument value to check. + /// The name of the argument. + public static void ArgumentNotZeroIntPtr(IntPtr argumentValue, string argumentName) + { + if (argumentValue == IntPtr.Zero) + { + throw new ArgumentNullException(argumentName); + } + } + + /// + /// Checks a pointer argument to ensure it is the expected pointer value. + /// + /// The argument value to check. + /// The expected value. + /// The name of the argument. + public static void ArgumentIsExpectedIntPtr(IntPtr argumentValue, IntPtr expectedValue, string argumentName) + { + if (argumentValue != expectedValue) + { + throw new ArgumentException("Unexpected IntPtr value", argumentName); + } + } + private static readonly Dictionary> GitErrorsToLibGit2SharpExceptions = new Dictionary> @@ -98,7 +124,7 @@ private static readonly Dictionary new InvalidSpecificationException(m, r, c) }, { GitErrorCode.UnmergedEntries, (m, r, c) => new UnmergedIndexEntriesException(m, r, c) }, { GitErrorCode.NonFastForward, (m, r, c) => new NonFastForwardException(m, r, c) }, - { GitErrorCode.MergeConflict, (m, r, c) => new MergeConflictException(m, r, c) }, + { GitErrorCode.Conflict, (m, r, c) => new CheckoutConflictException(m, r, c) }, { GitErrorCode.LockedFile, (m, r, c) => new LockedFileException(m, r, c) }, { GitErrorCode.NotFound, (m, r, c) => new NotFoundException(m, r, c) }, { GitErrorCode.Peel, (m, r, c) => new PeelException(m, r, c) }, @@ -107,7 +133,13 @@ private static readonly DictionaryThe identifier to examine. public static void GitObjectIsNotNull(GitObject gitObject, string identifier) { - Func exceptionBuilder; - - if (string.Equals("HEAD", identifier, StringComparison.Ordinal)) - { - exceptionBuilder = m => new UnbornBranchException(m); - } - else + if (gitObject != null) { - exceptionBuilder = m => new NotFoundException(m); + return; } - GitObjectIsNotNull(gitObject, identifier, exceptionBuilder); - } - - - /// - /// Check that the result of a C call that returns a non-null GitObject - /// using the default exception builder. - /// - /// The native function is expected to return a valid object value. - /// - /// - /// The to examine. - /// The identifier to examine. - /// The builder which constructs an from a message. - public static void GitObjectIsNotNull( - GitObject gitObject, - string identifier, - Func exceptionBuilder) - { - if (gitObject != null) + var messageFormat = "No valid git object identified by '{0}' exists in the repository."; + + if (string.Equals("HEAD", identifier, StringComparison.Ordinal)) { - return; + throw new UnbornBranchException(messageFormat, identifier); } - throw exceptionBuilder(string.Format(CultureInfo.InvariantCulture, - "No valid git object identified by '{0}' exists in the repository.", - identifier)); + throw new NotFoundException(messageFormat, identifier); } } } diff --git a/LibGit2Sharp/Core/Epoch.cs b/LibGit2Sharp/Core/Epoch.cs index 963314d08..0f2657267 100644 --- a/LibGit2Sharp/Core/Epoch.cs +++ b/LibGit2Sharp/Core/Epoch.cs @@ -5,7 +5,7 @@ namespace LibGit2Sharp.Core /// /// Provides helper methods to help converting between Epoch (unix timestamp) and . /// - public static class Epoch + internal static class Epoch { private static readonly DateTimeOffset epochDateTimeOffset = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); diff --git a/LibGit2Sharp/Core/FetchPruneStrategy.cs b/LibGit2Sharp/Core/FetchPruneStrategy.cs new file mode 100644 index 000000000..695709421 --- /dev/null +++ b/LibGit2Sharp/Core/FetchPruneStrategy.cs @@ -0,0 +1,25 @@ +namespace LibGit2Sharp.Core +{ + /// + /// Specify how the remote tracking branches should be locally dealt with + /// when their upstream countepart doesn't exist anymore. + /// + internal enum FetchPruneStrategy + { + /// + /// Use the setting from the configuration + /// or, when there isn't any, fallback to default behavior. + /// + FromConfigurationOrDefault = 0, + + /// + /// Force pruning on + /// + Prune, + + /// + /// Force pruning off + /// + NoPrune, + } +} diff --git a/LibGit2Sharp/Core/FileHistory.cs b/LibGit2Sharp/Core/FileHistory.cs new file mode 100644 index 000000000..477717c79 --- /dev/null +++ b/LibGit2Sharp/Core/FileHistory.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace LibGit2Sharp.Core +{ + /// + /// Represents a file-related log of commits beyond renames. + /// + internal class FileHistory : IEnumerable + { + #region Fields + + /// + /// The allowed commit sort strategies. + /// + private static readonly List AllowedSortStrategies = new List + { + CommitSortStrategies.Topological, + CommitSortStrategies.Time, + CommitSortStrategies.Topological | CommitSortStrategies.Time + }; + + /// + /// The repository. + /// + private readonly Repository _repo; + + /// + /// The file's path relative to the repository's root. + /// + private readonly string _path; + + /// + /// The filter to be used in querying the commit log. + /// + private readonly CommitFilter _queryFilter; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// The commits will be enumerated in reverse chronological order. + /// + /// The repository. + /// The file's path relative to the repository's root. + /// If any of the parameters is null. + internal FileHistory(Repository repo, string path) + : this(repo, path, new CommitFilter()) + { } + + /// + /// Initializes a new instance of the class. + /// The given instance specifies the commit + /// sort strategies and range of commits to be considered. + /// Only the time (corresponding to --date-order) and topological + /// (coresponding to --topo-order) sort strategies are supported. + /// + /// The repository. + /// The file's path relative to the repository's root. + /// The filter to be used in querying the commit log. + /// If any of the parameters is null. + /// When an unsupported commit sort strategy is specified. + internal FileHistory(Repository repo, string path, CommitFilter queryFilter) + { + Ensure.ArgumentNotNull(repo, "repo"); + Ensure.ArgumentNotNull(path, "path"); + Ensure.ArgumentNotNull(queryFilter, "queryFilter"); + + // Ensure the commit sort strategy makes sense. + if (!AllowedSortStrategies.Contains(queryFilter.SortBy)) + { + throw new ArgumentException("Unsupported sort strategy. Only 'Topological', 'Time', or 'Topological | Time' are allowed.", + "queryFilter"); + } + + _repo = repo; + _path = path; + _queryFilter = queryFilter; + } + + #endregion + + #region IEnumerable Members + + /// + /// Gets the that enumerates the + /// instances representing the file's history, + /// including renames (as in git log --follow). + /// + /// A . + public IEnumerator GetEnumerator() + { + return FullHistory(_repo, _path, _queryFilter).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + /// + /// Gets the relevant commits in which the given file was created, changed, or renamed. + /// + /// The repository. + /// The file's path relative to the repository's root. + /// The filter to be used in querying the commits log. + /// A collection of instances. + private static IEnumerable FullHistory(IRepository repo, string path, CommitFilter filter) + { + var map = new Dictionary(); + + foreach (var currentCommit in repo.Commits.QueryBy(filter)) + { + var currentPath = map.Keys.Count > 0 ? map[currentCommit] : path; + var currentTreeEntry = currentCommit.Tree[currentPath]; + + if (currentTreeEntry == null) + { + yield break; + } + + var parentCount = currentCommit.Parents.Count(); + if (parentCount == 0) + { + yield return new LogEntry { Path = currentPath, Commit = currentCommit }; + } + else + { + DetermineParentPaths(repo, currentCommit, currentPath, map); + + if (parentCount != 1) + { + continue; + } + + var parentCommit = currentCommit.Parents.Single(); + var parentPath = map[parentCommit]; + var parentTreeEntry = parentCommit.Tree[parentPath]; + + if (parentTreeEntry == null || + parentTreeEntry.Target.Id != currentTreeEntry.Target.Id || + parentPath != currentPath) + { + yield return new LogEntry { Path = currentPath, Commit = currentCommit }; + } + } + } + } + + private static void DetermineParentPaths(IRepository repo, Commit currentCommit, string currentPath, IDictionary map) + { + foreach (var parentCommit in currentCommit.Parents.Where(parentCommit => !map.ContainsKey(parentCommit))) + { + map.Add(parentCommit, ParentPath(repo, currentCommit, currentPath, parentCommit)); + } + } + + private static string ParentPath(IRepository repo, Commit currentCommit, string currentPath, Commit parentCommit) + { + var treeChanges = repo.Diff.Compare(parentCommit.Tree, currentCommit.Tree); + var treeEntryChanges = treeChanges.FirstOrDefault(c => c.Path == currentPath); + return treeEntryChanges != null && treeEntryChanges.Status == ChangeKind.Renamed + ? treeEntryChanges.OldPath + : currentPath; + } + } +} diff --git a/LibGit2Sharp/Core/FilePath.cs b/LibGit2Sharp/Core/FilePath.cs index 3d547aab2..580138cd0 100644 --- a/LibGit2Sharp/Core/FilePath.cs +++ b/LibGit2Sharp/Core/FilePath.cs @@ -60,7 +60,9 @@ private static string Replace(string path, char oldChar, char newChar) public bool Equals(FilePath other) { - return other == null ? posix == null : string.Equals(posix, other.posix, StringComparison.Ordinal); + return other == null + ? posix == null + : string.Equals(posix, other.posix, StringComparison.Ordinal); } public override bool Equals(object obj) diff --git a/LibGit2Sharp/Core/FilePathExtensions.cs b/LibGit2Sharp/Core/FilePathExtensions.cs index 930f38ed9..66118492c 100644 --- a/LibGit2Sharp/Core/FilePathExtensions.cs +++ b/LibGit2Sharp/Core/FilePathExtensions.cs @@ -2,14 +2,14 @@ namespace LibGit2Sharp.Core { internal static class FilePathExtensions { - internal static FilePath Combine(this FilePath @this, string childPath) + internal static FilePath Combine(this FilePath filePath, string childPath) { - return @this.IsNullOrEmpty() ? childPath : @this.Posix + "/" + childPath; + return filePath.IsNullOrEmpty() ? childPath : filePath.Posix + "/" + childPath; } - internal static bool IsNullOrEmpty(this FilePath @this) + internal static bool IsNullOrEmpty(this FilePath filePath) { - return ReferenceEquals(@this, null) || @this.Posix.Length == 0; + return ReferenceEquals(filePath, null) || filePath.Posix.Length == 0; } } -} \ No newline at end of file +} diff --git a/LibGit2Sharp/Core/FilePathMarshaler.cs b/LibGit2Sharp/Core/FilePathMarshaler.cs index 6b17df4cb..db28409c1 100644 --- a/LibGit2Sharp/Core/FilePathMarshaler.cs +++ b/LibGit2Sharp/Core/FilePathMarshaler.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using System.Runtime.InteropServices; -using System.Text; namespace LibGit2Sharp.Core { @@ -69,8 +68,9 @@ public override IntPtr MarshalManagedToNative(Object managedObj) if (null == filePath) { - throw new MarshalDirectiveException( - string.Format(CultureInfo.InvariantCulture, "{0} must be used on a FilePath.", GetType().Name)); + throw new MarshalDirectiveException(string.Format(CultureInfo.InvariantCulture, + "{0} must be used on a FilePath.", + GetType().Name)); } return FromManaged(filePath); diff --git a/LibGit2Sharp/Core/GitBlame.cs b/LibGit2Sharp/Core/GitBlame.cs index fd08b991c..b14632c40 100644 --- a/LibGit2Sharp/Core/GitBlame.cs +++ b/LibGit2Sharp/Core/GitBlame.cs @@ -37,7 +37,7 @@ internal enum GitBlameOptionFlags /// Restrict the search of commits to those reachable /// following only the first parents. /// - GIT_BLAME_FIRST_PARENT = (1<<4), + GIT_BLAME_FIRST_PARENT = (1 << 4), } [StructLayout(LayoutKind.Sequential)] @@ -48,22 +48,22 @@ internal class GitBlameOptions public UInt16 MinMatchCharacters; public GitOid NewestCommit; public GitOid OldestCommit; - public uint MinLine; - public uint MaxLine; + public UIntPtr MinLine; + public UIntPtr MaxLine; } [StructLayout(LayoutKind.Sequential)] internal class GitBlameHunk { - public ushort LinesInHunk; + public UIntPtr LinesInHunk; public GitOid FinalCommitId; - public ushort FinalStartLineNumber; + public UIntPtr FinalStartLineNumber; public IntPtr FinalSignature; public GitOid OrigCommitId; public IntPtr OrigPath; - public ushort OrigStartLineNumber; + public UIntPtr OrigStartLineNumber; public IntPtr OrigSignature; public byte Boundary; @@ -79,8 +79,9 @@ public static GitBlameOptionFlags ToGitBlameOptionFlags(this BlameStrategy strat return GitBlameOptionFlags.GIT_BLAME_NORMAL; default: - throw new NotSupportedException( - string.Format(CultureInfo.InvariantCulture, "{0} is not supported at this time", strategy)); + throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, + "{0} is not supported at this time", + strategy)); } } } diff --git a/LibGit2Sharp/Core/GitCertificate.cs b/LibGit2Sharp/Core/GitCertificate.cs new file mode 100644 index 000000000..2a237fb22 --- /dev/null +++ b/LibGit2Sharp/Core/GitCertificate.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal struct GitCertificate + { + public GitCertificateType type; + } +} diff --git a/LibGit2Sharp/Core/GitCertificateSsh.cs b/LibGit2Sharp/Core/GitCertificateSsh.cs new file mode 100644 index 000000000..4bb88a0d1 --- /dev/null +++ b/LibGit2Sharp/Core/GitCertificateSsh.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal struct GitCertificateSsh + { + public GitCertificateType cert_type; + public GitCertificateSshType type; + + /// + /// The MD5 hash (if appropriate) + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] HashMD5; + + /// + /// The MD5 hash (if appropriate) + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public byte[] HashSHA1; + } +} diff --git a/LibGit2Sharp/Core/GitCertificateSshType.cs b/LibGit2Sharp/Core/GitCertificateSshType.cs new file mode 100644 index 000000000..a5151123c --- /dev/null +++ b/LibGit2Sharp/Core/GitCertificateSshType.cs @@ -0,0 +1,11 @@ +using System; + +namespace LibGit2Sharp.Core +{ + [Flags] + internal enum GitCertificateSshType + { + MD5 = (1 << 0), + SHA1 = (1 << 1), + } +} diff --git a/LibGit2Sharp/Core/GitCertificateType.cs b/LibGit2Sharp/Core/GitCertificateType.cs new file mode 100644 index 000000000..1b06b1af3 --- /dev/null +++ b/LibGit2Sharp/Core/GitCertificateType.cs @@ -0,0 +1,29 @@ +namespace LibGit2Sharp.Core +{ + /// + /// Git certificate types to present to the user + /// + internal enum GitCertificateType + { + /// + /// No information about the certificate is available. + /// + None = 0, + + /// + /// The certificate is a x509 certificate + /// + X509 = 1, + + /// + /// The "certificate" is in fact a hostkey identification for ssh. + /// + Hostkey = 2, + + /// + /// The "certificate" is in fact a collection of `name:content` strings + /// containing information about the certificate. + /// + StrArray = 3, + } +} diff --git a/LibGit2Sharp/Core/GitCertificateX509.cs b/LibGit2Sharp/Core/GitCertificateX509.cs new file mode 100644 index 000000000..36df3c3ca --- /dev/null +++ b/LibGit2Sharp/Core/GitCertificateX509.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal struct GitCertificateX509 + { + /// + /// Type of the certificate, in this case, GitCertificateType.X509 + /// + public GitCertificateType cert_type; + /// + /// Pointer to the X509 certificate data + /// + public IntPtr data; + /// + /// The size of the certificate data + /// + public UIntPtr len; + } +} diff --git a/LibGit2Sharp/Core/GitCheckoutOpts.cs b/LibGit2Sharp/Core/GitCheckoutOpts.cs index b3f2bd965..3424094be 100644 --- a/LibGit2Sharp/Core/GitCheckoutOpts.cs +++ b/LibGit2Sharp/Core/GitCheckoutOpts.cs @@ -17,14 +17,14 @@ internal enum CheckoutStrategy GIT_CHECKOUT_SAFE = (1 << 0), /// - /// Allow safe updates plus creation of missing files. + /// Allow update of entries in working dir that are modified from HEAD. /// - GIT_CHECKOUT_SAFE_CREATE = (1 << 1), + GIT_CHECKOUT_FORCE = (1 << 1), /// - /// Allow update of entries in working dir that are modified from HEAD. + /// Allow checkout to recreate missing files. /// - GIT_CHECKOUT_FORCE = (1 << 2), + GIT_CHECKOUT_RECREATE_MISSING = (1 << 2), /// /// Allow checkout to make safe updates even if conflicts are found @@ -48,6 +48,7 @@ internal enum CheckoutStrategy /// /// Normally checkout updates index entries as it goes; this stops that + /// Implies `GIT_CHECKOUT_DONT_WRITE_INDEX`. /// GIT_CHECKOUT_DONT_UPDATE_INDEX = (1 << 8), @@ -94,6 +95,18 @@ internal enum CheckoutStrategy /// GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1 << 21), + /// + /// Don't overwrite existing files or folders + /// + GIT_CHECKOUT_DONT_REMOVE_EXISTING = (1 << 22), + + /// + /// Normally checkout writes the index upon completion; this prevents that. + /// + GIT_CHECKOUT_DONT_WRITE_INDEX = (1 << 23), + + // THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED + /// /// Recursively checkout submodules with same options (NOT IMPLEMENTED) /// @@ -145,6 +158,7 @@ internal struct GitCheckoutOpts public GitStrArray paths; public IntPtr baseline; + public IntPtr baseline_index; public IntPtr target_directory; public IntPtr ancestor_label; diff --git a/LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs b/LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs index 7fda697a4..0fba82754 100644 --- a/LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs +++ b/LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace LibGit2Sharp.Core { @@ -69,12 +66,15 @@ internal static CheckoutStrategy CheckoutStrategyFromFileConflictStrategy(Checko case CheckoutFileConflictStrategy.Ours: flags = CheckoutStrategy.GIT_CHECKOUT_USE_OURS; break; + case CheckoutFileConflictStrategy.Theirs: flags = CheckoutStrategy.GIT_CHECKOUT_USE_THEIRS; break; + case CheckoutFileConflictStrategy.Merge: flags = CheckoutStrategy.GIT_CHECKOUT_CONFLICT_STYLE_MERGE; break; + case CheckoutFileConflictStrategy.Diff3: flags = CheckoutStrategy.GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; break; diff --git a/LibGit2Sharp/Core/GitCherryPickOptions.cs b/LibGit2Sharp/Core/GitCherryPickOptions.cs index b06f22a7d..287ecacf0 100644 --- a/LibGit2Sharp/Core/GitCherryPickOptions.cs +++ b/LibGit2Sharp/Core/GitCherryPickOptions.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace LibGit2Sharp.Core { diff --git a/LibGit2Sharp/Core/GitCloneOptions.cs b/LibGit2Sharp/Core/GitCloneOptions.cs index 803bc8b3c..bed647e31 100644 --- a/LibGit2Sharp/Core/GitCloneOptions.cs +++ b/LibGit2Sharp/Core/GitCloneOptions.cs @@ -17,7 +17,7 @@ internal struct GitCloneOptions public uint Version; public GitCheckoutOpts CheckoutOpts; - public GitRemoteCallbacks RemoteCallbacks; + public GitFetchOptions FetchOpts; public int Bare; public GitCloneLocal Local; diff --git a/LibGit2Sharp/Core/GitConfigEntry.cs b/LibGit2Sharp/Core/GitConfigEntry.cs index d8bd3751f..85d1669a8 100644 --- a/LibGit2Sharp/Core/GitConfigEntry.cs +++ b/LibGit2Sharp/Core/GitConfigEntry.cs @@ -9,5 +9,7 @@ internal struct GitConfigEntry public IntPtr namePtr; public IntPtr valuePtr; public uint level; + public IntPtr freePtr; + public IntPtr payloadPtr; } } diff --git a/LibGit2Sharp/Core/GitCredential.cs b/LibGit2Sharp/Core/GitCredential.cs new file mode 100644 index 000000000..b3fefcaf1 --- /dev/null +++ b/LibGit2Sharp/Core/GitCredential.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitCredential + { + public GitCredentialType credtype; + public IntPtr free; + } +} + diff --git a/LibGit2Sharp/Core/GitCredentialType.cs b/LibGit2Sharp/Core/GitCredentialType.cs index 6ae636588..0ab1273e2 100644 --- a/LibGit2Sharp/Core/GitCredentialType.cs +++ b/LibGit2Sharp/Core/GitCredentialType.cs @@ -6,7 +6,7 @@ namespace LibGit2Sharp.Core /// Authentication type requested. /// [Flags] - public enum GitCredentialType + internal enum GitCredentialType { /// /// A plaintext username and password. @@ -32,5 +32,20 @@ public enum GitCredentialType /// TODO /// SshInteractive = (1 << 4), + + /// + /// Username-only information + /// + /// If the SSH transport does not know which username to use, + /// it will ask via this credential type. + /// + Username = (1 << 5), + + /// + /// Credentials read from memory. + /// + /// Only available for libssh2+OpenSSL for now. + /// + SshMemory = (1 << 6), } } diff --git a/LibGit2Sharp/Core/GitCredentialUserpass.cs b/LibGit2Sharp/Core/GitCredentialUserpass.cs new file mode 100644 index 000000000..5d807b12e --- /dev/null +++ b/LibGit2Sharp/Core/GitCredentialUserpass.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitCredentialUserpass + { + public GitCredential parent; + public IntPtr username; + public IntPtr password; + } +} + diff --git a/LibGit2Sharp/Core/GitDiff.cs b/LibGit2Sharp/Core/GitDiff.cs index 7ce4eafdf..8236a5b85 100644 --- a/LibGit2Sharp/Core/GitDiff.cs +++ b/LibGit2Sharp/Core/GitDiff.cs @@ -80,6 +80,13 @@ internal enum GitDiffOptionFlags /// GIT_DIFF_IGNORE_CASE = (1 << 10), + + /// + /// May be combined with `GIT_DIFF_IGNORE_CASE` to specify that a file + /// that has changed case will be returned as an add/delete pair. + /// + GIT_DIFF_INCLUDE_CASECHANGE = (1 << 11), + /// /// If the pathspec is set in the diff options, this flags means to /// apply it as an exact match instead of as an fnmatch pattern. @@ -190,6 +197,12 @@ internal delegate int diff_notify_cb( IntPtr matched_pathspec, IntPtr payload); + internal delegate int diff_progress_cb( + IntPtr diff_so_far, + IntPtr old_path, + IntPtr new_path, + IntPtr payload); + [StructLayout(LayoutKind.Sequential)] internal class GitDiffOptions : IDisposable { @@ -201,7 +214,8 @@ internal class GitDiffOptions : IDisposable public SubmoduleIgnore IgnoreSubmodules; public GitStrArrayManaged PathSpec; public diff_notify_cb NotifyCallback; - public IntPtr NotifyPayload; + public diff_progress_cb ProgressCallback; + public IntPtr Payload; /* options controlling how to diff text is generated */ @@ -224,6 +238,7 @@ internal enum GitDiffFlags GIT_DIFF_FLAG_BINARY = (1 << 0), GIT_DIFF_FLAG_NOT_BINARY = (1 << 1), GIT_DIFF_FLAG_VALID_ID = (1 << 2), + GIT_DIFF_FLAG_EXISTS = (1 << 3), } [StructLayout(LayoutKind.Sequential)] @@ -355,4 +370,33 @@ internal class GitDiffFindOptions // TODO public IntPtr SimilarityMetric; } + + [Flags] + enum GitDiffBinaryType + { + // There is no binary delta. + GIT_DIFF_BINARY_NONE = 0, + + // The binary data is the literal contents of the file. */ + GIT_DIFF_BINARY_LITERAL, + + // The binary data is the delta from one side to the other. */ + GIT_DIFF_BINARY_DELTA, + } + + [StructLayout(LayoutKind.Sequential)] + internal class GitDiffBinaryFile + { + public GitDiffBinaryType Type; + public IntPtr Data; + public UIntPtr DataLen; + public UIntPtr InflatedLen; + } + + [StructLayout(LayoutKind.Sequential)] + internal class GitDiffBinary + { + public GitDiffBinaryFile OldFile; + public GitDiffBinaryFile NewFile; + } } diff --git a/LibGit2Sharp/Core/GitErrorCategory.cs b/LibGit2Sharp/Core/GitErrorCategory.cs index 66e10cbaf..0fa80ddc3 100644 --- a/LibGit2Sharp/Core/GitErrorCategory.cs +++ b/LibGit2Sharp/Core/GitErrorCategory.cs @@ -30,5 +30,9 @@ internal enum GitErrorCategory Filter, Revert, Callback, + CherryPick, + Describe, + Rebase, + Filesystem, } } diff --git a/LibGit2Sharp/Core/GitErrorCode.cs b/LibGit2Sharp/Core/GitErrorCode.cs index 59ba1b15a..1ef7af7b0 100644 --- a/LibGit2Sharp/Core/GitErrorCode.cs +++ b/LibGit2Sharp/Core/GitErrorCode.cs @@ -56,9 +56,10 @@ internal enum GitErrorCode InvalidSpecification = -12, /// - /// A conflicting change has been detected. + /// A conflicting change has been detected in the index + /// or working directory. /// - MergeConflict = -13, + Conflict = -13, /// /// A file operation failed because the file was locked. @@ -90,6 +91,31 @@ internal enum GitErrorCode /// Peel = -19, + /// + /// Unexpected EOF. + /// + EndOfFile = -20, + + /// + /// Invalid operation or input. + /// + Invalid = -21, + + /// + /// Uncommitted changes in index prevented operation. + /// + Uncommitted = -22, + + /// + /// The operation is not valid for a directory. + /// + Directory = -23, + + /// + /// A merge conflict exists and cannot continue + /// + MergeConflict = -24, + /// /// Skip and passthrough the given ODB backend. /// diff --git a/LibGit2Sharp/Core/GitFetchOptions.cs b/LibGit2Sharp/Core/GitFetchOptions.cs new file mode 100644 index 000000000..7b284bf51 --- /dev/null +++ b/LibGit2Sharp/Core/GitFetchOptions.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitFetchOptions + { + public int Version = 1; + public GitRemoteCallbacks RemoteCallbacks; + public FetchPruneStrategy Prune; + public bool UpdateFetchHead = true; + public TagFetchMode download_tags; + public GitStrArrayManaged custom_headers; + } +} diff --git a/LibGit2Sharp/Core/GitFilter.cs b/LibGit2Sharp/Core/GitFilter.cs new file mode 100644 index 000000000..648cf47ec --- /dev/null +++ b/LibGit2Sharp/Core/GitFilter.cs @@ -0,0 +1,121 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + /// + /// A git filter + /// + [StructLayout(LayoutKind.Sequential)] + internal class GitFilter + { + public uint version = 1; + + public IntPtr attributes; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public git_filter_init_fn init; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public git_filter_shutdown_fn shutdown; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public git_filter_check_fn check; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public git_filter_apply_fn apply; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public git_filter_stream_fn stream; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public git_filter_cleanup_fn cleanup; + + /* The libgit2 structure definition ends here. Subsequent fields are for libgit2sharp bookkeeping. */ + + /// + /// Initialize callback on filter + /// + /// Specified as `filter.initialize`, this is an optional callback invoked + /// before a filter is first used. It will be called once at most. + /// + /// If non-NULL, the filter's `initialize` callback will be invoked right + /// before the first use of the filter, so you can defer expensive + /// initialization operations (in case libgit2 is being used in a way that doesn't need the filter). + /// + public delegate int git_filter_init_fn(IntPtr filter); + + /// + /// Shutdown callback on filter + /// + /// Specified as `filter.shutdown`, this is an optional callback invoked + /// when the filter is unregistered or when libgit2 is shutting down. It + /// will be called once at most and should release resources as needed. + /// Typically this function will free the `git_filter` object itself. + /// + public delegate void git_filter_shutdown_fn(IntPtr filter); + + /// + /// Callback to decide if a given source needs this filter + /// Specified as `filter.check`, this is an optional callback that checks if filtering is needed for a given source. + /// + /// It should return 0 if the filter should be applied (i.e. success), GIT_PASSTHROUGH if the filter should + /// not be applied, or an error code to fail out of the filter processing pipeline and return to the caller. + /// + /// The `attr_values` will be set to the values of any attributes given in the filter definition. See `git_filter` below for more detail. + /// + /// The `payload` will be a pointer to a reference payload for the filter. This will start as NULL, but `check` can assign to this + /// pointer for later use by the `apply` callback. Note that the value should be heap allocated (not stack), so that it doesn't go + /// away before the `apply` callback can use it. If a filter allocates and assigns a value to the `payload`, it will need a `cleanup` + /// callback to free the payload. + /// + public delegate int git_filter_check_fn( + GitFilter gitFilter, + IntPtr payload, + IntPtr filterSource, + IntPtr attributeValues); + + /// + /// Callback to actually perform the data filtering + /// + /// Specified as `filter.apply`, this is the callback that actually filters data. + /// If it successfully writes the output, it should return 0. Like `check`, + /// it can return GIT_PASSTHROUGH to indicate that the filter doesn't want to run. + /// Other error codes will stop filter processing and return to the caller. + /// + /// The `payload` value will refer to any payload that was set by the `check` callback. It may be read from or written to as needed. + /// + public delegate int git_filter_apply_fn( + GitFilter gitFilter, + IntPtr payload, + IntPtr gitBufTo, + IntPtr gitBufFrom, + IntPtr filterSource); + + public delegate int git_filter_stream_fn( + out IntPtr git_writestream_out, + GitFilter self, + IntPtr payload, + IntPtr filterSource, + IntPtr git_writestream_next); + + /// + /// Callback to clean up after filtering has been applied. Specified as `filter.cleanup`, this is an optional callback invoked + /// after the filter has been applied. If the `check` or `apply` callbacks allocated a `payload` + /// to keep per-source filter state, use this callback to free that payload and release resources as required. + /// + public delegate void git_filter_cleanup_fn(IntPtr gitFilter, IntPtr payload); + } + /// + /// The file source being filtered + /// + [StructLayout(LayoutKind.Sequential)] + internal class GitFilterSource + { + public IntPtr repository; + + public IntPtr path; + + public GitOid oid; + } +} diff --git a/LibGit2Sharp/Core/GitIndexEntry.cs b/LibGit2Sharp/Core/GitIndexEntry.cs index 05904c84d..11bee09ea 100644 --- a/LibGit2Sharp/Core/GitIndexEntry.cs +++ b/LibGit2Sharp/Core/GitIndexEntry.cs @@ -15,7 +15,7 @@ internal class GitIndexEntry public uint Mode; public uint Uid; public uint Gid; - public Int64 file_size; + public uint file_size; public GitOid Id; public ushort Flags; public ushort ExtendedFlags; diff --git a/LibGit2Sharp/Core/GitIndexTime.cs b/LibGit2Sharp/Core/GitIndexTime.cs index fb47f2944..9f16c5121 100644 --- a/LibGit2Sharp/Core/GitIndexTime.cs +++ b/LibGit2Sharp/Core/GitIndexTime.cs @@ -5,7 +5,7 @@ namespace LibGit2Sharp.Core [StructLayout(LayoutKind.Sequential)] internal class GitIndexTime { - public long seconds; + public int seconds; public uint nanoseconds; } } diff --git a/LibGit2Sharp/Core/GitMergeOpts.cs b/LibGit2Sharp/Core/GitMergeOpts.cs index a2ebe979d..59a9040df 100644 --- a/LibGit2Sharp/Core/GitMergeOpts.cs +++ b/LibGit2Sharp/Core/GitMergeOpts.cs @@ -8,7 +8,7 @@ internal struct GitMergeOpts { public uint Version; - public GitMergeTreeFlags MergeTreeFlags; + public GitMergeFlag MergeTreeFlags; /// /// Similarity to consider a file renamed. @@ -27,10 +27,23 @@ internal struct GitMergeOpts /// public IntPtr SimilarityMetric; + /// + /// Maximum number of times to merge common ancestors to build a + /// virtual merge base when faced with criss-cross merges. When this + /// limit is reached, the next ancestor will simply be used instead of + /// attempting to merge it. The default is unlimited. + /// + public uint RecursionLimit; + /// /// Flags for automerging content. /// public MergeFileFavor MergeFileFavorFlags; + + /// + /// File merging flags. + /// + public GitMergeFileFlag FileFlags; } /// @@ -63,11 +76,11 @@ internal enum GitMergeAnalysis /// GIT_MERGE_ANALYSIS_FASTFORWARD = (1 << 2), - /** - * The HEAD of the current repository is "unborn" and does not point to - * a valid commit. No merge can be performed, but the caller may wish - * to simply set HEAD to the target commit(s). - */ + /// + /// The HEAD of the current repository is "unborn" and does not point to + /// a valid commit. No merge can be performed, but the caller may wish + /// to simply set HEAD to the target commit(s). + /// GIT_MERGE_ANALYSIS_UNBORN = (1 << 3), } @@ -93,16 +106,87 @@ internal enum GitMergePreference } [Flags] - internal enum GitMergeTreeFlags + internal enum GitMergeFlag { /// /// No options. /// - GIT_MERGE_TREE_NORMAL = 0, + GIT_MERGE_NORMAL = 0, + + /// + /// Detect renames that occur between the common ancestor and the "ours" + /// side or the common ancestor and the "theirs" side. This will enable + /// the ability to merge between a modified and renamed file. + /// + GIT_MERGE_FIND_RENAMES = (1 << 0), + + /// + /// If a conflict occurs, exit immediately instead of attempting to + /// continue resolving conflicts. The merge operation will fail with + /// GIT_EMERGECONFLICT and no index will be returned. + /// + GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1), + + /// + /// Do not write the REUC extension on the generated index + /// + GIT_MERGE_SKIP_REUC = (1 << 2), + + /// + /// If the commits being merged have multiple merge bases, do not build + /// a recursive merge base (by merging the multiple merge bases), + /// instead simply use the first base. This flag provides a similar + /// merge base to `git-merge-resolve`. + /// + GIT_MERGE_NO_RECURSIVE = (1 << 3), + } + + [Flags] + internal enum GitMergeFileFlag + { + /// + /// Defaults + /// + GIT_MERGE_FILE_DEFAULT = 0, + + /// + /// Create standard conflicted merge files + /// + GIT_MERGE_FILE_STYLE_MERGE = (1 << 0), + + /// + /// Create diff3-style files + /// + GIT_MERGE_FILE_STYLE_DIFF3 = (1 << 1), + + /// + /// Condense non-alphanumeric regions for simplified diff file + /// + GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 2), + + /// + /// Ignore all whitespace + /// + GIT_MERGE_FILE_IGNORE_WHITESPACE = (1 << 3), + + /// + /// Ignore changes in amount of whitespace + /// + GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = (1 << 4), + + /// + /// Ignore whitespace at end of line + /// + GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = (1 << 5), + + /// + /// Use the "patience diff" algorithm + /// + GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6), /// - /// GIT_MERGE_TREE_FIND_RENAMES in libgit2 + /// Take extra time to find minimal diff /// - GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), + GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7), } } diff --git a/LibGit2Sharp/Core/GitObjectLazyGroup.cs b/LibGit2Sharp/Core/GitObjectLazyGroup.cs index 7d4429166..824cc92cc 100644 --- a/LibGit2Sharp/Core/GitObjectLazyGroup.cs +++ b/LibGit2Sharp/Core/GitObjectLazyGroup.cs @@ -26,7 +26,9 @@ public static ILazy Singleton(Repository repo, ObjectId id, Fu return Singleton(() => { using (var osw = new ObjectSafeWrapper(id, repo.Handle)) + { return resultSelector(osw.ObjectPtr); + } }); } } diff --git a/LibGit2Sharp/Core/GitObjectType.cs b/LibGit2Sharp/Core/GitObjectType.cs index a38523f2c..50d8412c2 100644 --- a/LibGit2Sharp/Core/GitObjectType.cs +++ b/LibGit2Sharp/Core/GitObjectType.cs @@ -75,8 +75,9 @@ public static TreeEntryTargetType ToTreeEntryTargetType(this GitObjectType type) return TreeEntryTargetType.Blob; default: - throw new InvalidOperationException( - string.Format(CultureInfo.InvariantCulture, "Cannot map {0} to a TreeEntryTargetType.", type)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + "Cannot map {0} to a TreeEntryTargetType.", + type)); } } @@ -97,8 +98,9 @@ public static ObjectType ToObjectType(this GitObjectType type) return ObjectType.Tag; default: - throw new InvalidOperationException( - string.Format(CultureInfo.InvariantCulture, "Cannot map {0} to a ObjectType.", type)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + "Cannot map {0} to a ObjectType.", + type)); } } } diff --git a/LibGit2Sharp/Core/GitOdbBackend.cs b/LibGit2Sharp/Core/GitOdbBackend.cs index a7b0acf41..e36cdc531 100644 --- a/LibGit2Sharp/Core/GitOdbBackend.cs +++ b/LibGit2Sharp/Core/GitOdbBackend.cs @@ -130,7 +130,7 @@ public delegate int write_callback( public delegate int writestream_callback( out IntPtr stream_out, IntPtr backend, - UIntPtr length, + Int64 length, GitObjectType type); /// diff --git a/LibGit2Sharp/Core/GitOdbBackendStream.cs b/LibGit2Sharp/Core/GitOdbBackendStream.cs index 82f6ce89c..30477ecbd 100644 --- a/LibGit2Sharp/Core/GitOdbBackendStream.cs +++ b/LibGit2Sharp/Core/GitOdbBackendStream.cs @@ -22,8 +22,8 @@ static GitOdbBackendStream() public GitOdbBackendStreamMode Mode; public IntPtr HashCtx; - public UIntPtr DeclaredSize; - public UIntPtr ReceivedBytes; + public Int64 DeclaredSize; + public Int64 ReceivedBytes; public read_callback Read; public write_callback Write; @@ -48,11 +48,8 @@ public delegate int write_callback( IntPtr buffer, UIntPtr len); - public delegate int finalize_write_callback( - IntPtr stream, - ref GitOid oid); + public delegate int finalize_write_callback(IntPtr stream, ref GitOid oid); - public delegate void free_callback( - IntPtr stream); + public delegate void free_callback(IntPtr stream); } } diff --git a/LibGit2Sharp/Core/GitOid.cs b/LibGit2Sharp/Core/GitOid.cs index 9c5930908..0f1be6743 100644 --- a/LibGit2Sharp/Core/GitOid.cs +++ b/LibGit2Sharp/Core/GitOid.cs @@ -27,5 +27,13 @@ public static implicit operator ObjectId(GitOid? oid) { return oid == null ? null : new ObjectId(oid.Value); } + + /// + /// Static convenience property to return an id (all zeros). + /// + public static GitOid Empty + { + get { return new GitOid(); } + } } } diff --git a/LibGit2Sharp/Core/GitPushOptions.cs b/LibGit2Sharp/Core/GitPushOptions.cs index bf6058d67..075a62b6b 100644 --- a/LibGit2Sharp/Core/GitPushOptions.cs +++ b/LibGit2Sharp/Core/GitPushOptions.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace LibGit2Sharp.Core { @@ -8,5 +7,7 @@ internal class GitPushOptions { public int Version = 1; public int PackbuilderDegreeOfParallelism; + public GitRemoteCallbacks RemoteCallbacks; + public GitStrArrayManaged CustomHeaders; } } diff --git a/LibGit2Sharp/Core/GitPushUpdate.cs b/LibGit2Sharp/Core/GitPushUpdate.cs new file mode 100644 index 000000000..5e5246622 --- /dev/null +++ b/LibGit2Sharp/Core/GitPushUpdate.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal struct GitPushUpdate + { + public IntPtr src_refname; + public IntPtr dst_refname; + public GitOid src; + public GitOid dst; + } +} diff --git a/LibGit2Sharp/Core/GitRebaseOperation.cs b/LibGit2Sharp/Core/GitRebaseOperation.cs new file mode 100644 index 000000000..660676edb --- /dev/null +++ b/LibGit2Sharp/Core/GitRebaseOperation.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitRebaseOperation + { + internal RebaseStepOperation type; + internal GitOid id; + internal IntPtr exec; + } +} diff --git a/LibGit2Sharp/Core/GitRebaseOptions.cs b/LibGit2Sharp/Core/GitRebaseOptions.cs new file mode 100644 index 000000000..3ae4e0ed1 --- /dev/null +++ b/LibGit2Sharp/Core/GitRebaseOptions.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitRebaseOptions + { + public uint version = 1; + + public int quiet; + + public int inmemory; + + public IntPtr rewrite_notes_ref; + + public GitMergeOpts merge_options = new GitMergeOpts { Version = 1 }; + + public GitCheckoutOpts checkout_options = new GitCheckoutOpts { version = 1 }; + } +} diff --git a/LibGit2Sharp/Core/GitRemoteCallbacks.cs b/LibGit2Sharp/Core/GitRemoteCallbacks.cs index 805ba8426..4c797b596 100644 --- a/LibGit2Sharp/Core/GitRemoteCallbacks.cs +++ b/LibGit2Sharp/Core/GitRemoteCallbacks.cs @@ -17,7 +17,7 @@ internal struct GitRemoteCallbacks internal NativeMethods.git_cred_acquire_cb acquire_credentials; - internal IntPtr certificate_check; + internal NativeMethods.git_transport_certificate_check_cb certificate_check; internal NativeMethods.git_transfer_progress_callback download_progress; @@ -27,7 +27,11 @@ internal struct GitRemoteCallbacks internal NativeMethods.git_push_transfer_progress push_transfer_progress; - internal IntPtr push_update_reference; + internal NativeMethods.push_update_reference_callback push_update_reference; + + internal NativeMethods.push_negotiation_callback push_negotiation; + + internal IntPtr transport; internal IntPtr payload; } diff --git a/LibGit2Sharp/Core/GitRepositoryInitOptions.cs b/LibGit2Sharp/Core/GitRepositoryInitOptions.cs index c42bc83f4..f639a0d8d 100644 --- a/LibGit2Sharp/Core/GitRepositoryInitOptions.cs +++ b/LibGit2Sharp/Core/GitRepositoryInitOptions.cs @@ -19,10 +19,10 @@ internal class GitRepositoryInitOptions : IDisposable public static GitRepositoryInitOptions BuildFrom(FilePath workdirPath, bool isBare) { var opts = new GitRepositoryInitOptions - { - Flags = GitRepositoryInitFlags.GIT_REPOSITORY_INIT_MKPATH, - Mode = 0 /* GIT_REPOSITORY_INIT_SHARED_UMASK */ - }; + { + Flags = GitRepositoryInitFlags.GIT_REPOSITORY_INIT_MKPATH, + Mode = 0 /* GIT_REPOSITORY_INIT_SHARED_UMASK */ + }; if (workdirPath != null) { diff --git a/LibGit2Sharp/Core/GitRevertOpts.cs b/LibGit2Sharp/Core/GitRevertOpts.cs index 7976243c2..3d6583a81 100644 --- a/LibGit2Sharp/Core/GitRevertOpts.cs +++ b/LibGit2Sharp/Core/GitRevertOpts.cs @@ -12,6 +12,6 @@ internal class GitRevertOpts public GitMergeOpts MergeOpts = new GitMergeOpts { Version = 1 }; - public GitCheckoutOpts CheckoutOpts = new GitCheckoutOpts {version = 1}; + public GitCheckoutOpts CheckoutOpts = new GitCheckoutOpts { version = 1 }; } } diff --git a/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs index 724c6c414..e721c1e79 100644 --- a/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs +++ b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs @@ -8,9 +8,11 @@ internal class GitSmartSubtransportRegistration { public IntPtr SubtransportCallback; public uint Rpc; + public uint Param; public delegate int create_callback( out IntPtr subtransport, - IntPtr transport); + IntPtr owner, + IntPtr param); } } diff --git a/LibGit2Sharp/Core/GitSmartSubtransportStream.cs b/LibGit2Sharp/Core/GitSmartSubtransportStream.cs index f73218c09..16155aeba 100644 --- a/LibGit2Sharp/Core/GitSmartSubtransportStream.cs +++ b/LibGit2Sharp/Core/GitSmartSubtransportStream.cs @@ -36,7 +36,6 @@ public delegate int write_callback( IntPtr buffer, UIntPtr len); - public delegate void free_callback( - IntPtr stream); + public delegate void free_callback(IntPtr stream); } } diff --git a/LibGit2Sharp/Core/GitStashApplyOpts.cs b/LibGit2Sharp/Core/GitStashApplyOpts.cs new file mode 100644 index 000000000..e7f2be19c --- /dev/null +++ b/LibGit2Sharp/Core/GitStashApplyOpts.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + internal delegate int stash_apply_progress_cb(StashApplyProgress progress, IntPtr payload); + + [StructLayout(LayoutKind.Sequential)] + internal class GitStashApplyOpts + { + public uint Version = 1; + + public StashApplyModifiers Flags; + public GitCheckoutOpts CheckoutOptions; + + public stash_apply_progress_cb ApplyProgressCallback; + public IntPtr ProgressPayload; + } +} diff --git a/LibGit2Sharp/Core/GitStatusEntry.cs b/LibGit2Sharp/Core/GitStatusEntry.cs index 2e13a186a..3e46c7b23 100644 --- a/LibGit2Sharp/Core/GitStatusEntry.cs +++ b/LibGit2Sharp/Core/GitStatusEntry.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; namespace LibGit2Sharp.Core { diff --git a/LibGit2Sharp/Core/GitStatusOptions.cs b/LibGit2Sharp/Core/GitStatusOptions.cs index 4b4089f6e..3e9dbd5d9 100644 --- a/LibGit2Sharp/Core/GitStatusOptions.cs +++ b/LibGit2Sharp/Core/GitStatusOptions.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; namespace LibGit2Sharp.Core { diff --git a/LibGit2Sharp/Core/GitStrArray.cs b/LibGit2Sharp/Core/GitStrArray.cs index a94187e1b..d2efca8b7 100644 --- a/LibGit2Sharp/Core/GitStrArray.cs +++ b/LibGit2Sharp/Core/GitStrArray.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; -using System.Text; namespace LibGit2Sharp.Core { diff --git a/LibGit2Sharp/Core/GitStrArrayNative.cs b/LibGit2Sharp/Core/GitStrArrayNative.cs index 2db550c0f..8813f8e6e 100644 --- a/LibGit2Sharp/Core/GitStrArrayNative.cs +++ b/LibGit2Sharp/Core/GitStrArrayNative.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; namespace LibGit2Sharp.Core diff --git a/LibGit2Sharp/Core/GitSubmoduleIgnore.cs b/LibGit2Sharp/Core/GitSubmoduleIgnore.cs new file mode 100644 index 000000000..5550864a6 --- /dev/null +++ b/LibGit2Sharp/Core/GitSubmoduleIgnore.cs @@ -0,0 +1,11 @@ +namespace LibGit2Sharp.Core +{ + internal enum GitSubmoduleIgnore + { + Unspecified = -1, + None = 1, + Untracked = 2, + Dirty = 3, + All = 4, + } +} diff --git a/LibGit2Sharp/Core/GitSubmoduleOptions.cs b/LibGit2Sharp/Core/GitSubmoduleOptions.cs index b26244e8a..4fb07411a 100644 --- a/LibGit2Sharp/Core/GitSubmoduleOptions.cs +++ b/LibGit2Sharp/Core/GitSubmoduleOptions.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace LibGit2Sharp.Core { @@ -10,10 +9,8 @@ internal struct GitSubmoduleOptions public GitCheckoutOpts CheckoutOptions; - public GitRemoteCallbacks RemoteCallbacks; + public GitFetchOptions FetchOptions; public CheckoutStrategy CloneCheckoutStrategy; - - public IntPtr Signature; } } diff --git a/LibGit2Sharp/Core/GitWriteStream.cs b/LibGit2Sharp/Core/GitWriteStream.cs new file mode 100644 index 000000000..6739fd32e --- /dev/null +++ b/LibGit2Sharp/Core/GitWriteStream.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitWriteStream + { + [MarshalAs(UnmanagedType.FunctionPtr)] + public write_fn write; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public close_fn close; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public free_fn free; + + public delegate int write_fn(IntPtr stream, IntPtr buffer, UIntPtr len); + public delegate int close_fn(IntPtr stream); + public delegate void free_fn(IntPtr stream); + } +} diff --git a/LibGit2Sharp/Core/Handles/ConflictIteratorSafeHandle.cs b/LibGit2Sharp/Core/Handles/ConflictIteratorSafeHandle.cs new file mode 100644 index 000000000..255563af5 --- /dev/null +++ b/LibGit2Sharp/Core/Handles/ConflictIteratorSafeHandle.cs @@ -0,0 +1,11 @@ +namespace LibGit2Sharp.Core.Handles +{ + internal class ConflictIteratorSafeHandle : SafeHandleBase + { + protected override bool ReleaseHandleImpl() + { + Proxy.git_index_conflict_iterator_free(handle); + return true; + } + } +} diff --git a/LibGit2Sharp/Core/Handles/GitAnnotatedCommitHandle.cs b/LibGit2Sharp/Core/Handles/GitAnnotatedCommitHandle.cs index 519874d03..f125772d0 100644 --- a/LibGit2Sharp/Core/Handles/GitAnnotatedCommitHandle.cs +++ b/LibGit2Sharp/Core/Handles/GitAnnotatedCommitHandle.cs @@ -1,5 +1,3 @@ -using System.Runtime.InteropServices; - namespace LibGit2Sharp.Core.Handles { internal class GitAnnotatedCommitHandle : SafeHandleBase diff --git a/LibGit2Sharp/Core/Handles/GitConfigEntryHandle.cs b/LibGit2Sharp/Core/Handles/GitConfigEntryHandle.cs index 5d9d4a2b6..677c5fbdc 100644 --- a/LibGit2Sharp/Core/Handles/GitConfigEntryHandle.cs +++ b/LibGit2Sharp/Core/Handles/GitConfigEntryHandle.cs @@ -1,12 +1,16 @@ -using System.Runtime.InteropServices; - namespace LibGit2Sharp.Core.Handles { - internal class GitConfigEntryHandle : NotOwnedSafeHandleBase + internal class GitConfigEntryHandle : SafeHandleBase { public GitConfigEntry MarshalAsGitConfigEntry() { return handle.MarshalAs(); } + + protected override bool ReleaseHandleImpl() + { + Proxy.git_config_entry_free(handle); + return true; + } } } diff --git a/LibGit2Sharp/Core/Handles/GitErrorSafeHandle.cs b/LibGit2Sharp/Core/Handles/GitErrorSafeHandle.cs index 8ae8c3c8c..d0010a635 100644 --- a/LibGit2Sharp/Core/Handles/GitErrorSafeHandle.cs +++ b/LibGit2Sharp/Core/Handles/GitErrorSafeHandle.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.InteropServices; namespace LibGit2Sharp.Core.Handles { diff --git a/LibGit2Sharp/Core/Handles/IndexEntrySafeHandle.cs b/LibGit2Sharp/Core/Handles/IndexEntrySafeHandle.cs index 87e0f1dc4..f09e01c00 100644 --- a/LibGit2Sharp/Core/Handles/IndexEntrySafeHandle.cs +++ b/LibGit2Sharp/Core/Handles/IndexEntrySafeHandle.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace LibGit2Sharp.Core.Handles +namespace LibGit2Sharp.Core.Handles { internal class IndexEntrySafeHandle : NotOwnedSafeHandleBase { diff --git a/LibGit2Sharp/Core/Handles/IndexNameEntrySafeHandle.cs b/LibGit2Sharp/Core/Handles/IndexNameEntrySafeHandle.cs index 549d41e6a..9c5421a58 100644 --- a/LibGit2Sharp/Core/Handles/IndexNameEntrySafeHandle.cs +++ b/LibGit2Sharp/Core/Handles/IndexNameEntrySafeHandle.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace LibGit2Sharp.Core.Handles +namespace LibGit2Sharp.Core.Handles { internal class IndexNameEntrySafeHandle : NotOwnedSafeHandleBase { diff --git a/LibGit2Sharp/Core/Handles/IndexReucEntrySafeHandle.cs b/LibGit2Sharp/Core/Handles/IndexReucEntrySafeHandle.cs index ec006b39a..5150081d1 100644 --- a/LibGit2Sharp/Core/Handles/IndexReucEntrySafeHandle.cs +++ b/LibGit2Sharp/Core/Handles/IndexReucEntrySafeHandle.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace LibGit2Sharp.Core.Handles +namespace LibGit2Sharp.Core.Handles { internal class IndexReucEntrySafeHandle : NotOwnedSafeHandleBase { diff --git a/LibGit2Sharp/Core/Handles/OidSafeHandle.cs b/LibGit2Sharp/Core/Handles/OidSafeHandle.cs index f6e00ec31..ced01e50a 100644 --- a/LibGit2Sharp/Core/Handles/OidSafeHandle.cs +++ b/LibGit2Sharp/Core/Handles/OidSafeHandle.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace LibGit2Sharp.Core.Handles { @@ -6,10 +7,10 @@ internal class OidSafeHandle : NotOwnedSafeHandleBase { private GitOid? MarshalAsGitOid() { - return IsInvalid ? null : (GitOid?)MarshalAsGitOid(handle); + return IsZero || IsInvalid ? null : (GitOid?)MarshalAsGitOid(handle); } - private static GitOid MarshalAsGitOid(System.IntPtr data) + private static GitOid MarshalAsGitOid(IntPtr data) { var gitOid = new GitOid { Id = new byte[GitOid.Size] }; Marshal.Copy(data, gitOid.Id, 0, GitOid.Size); diff --git a/LibGit2Sharp/Core/Handles/PackBuilderSafeHandle.cs b/LibGit2Sharp/Core/Handles/PackBuilderSafeHandle.cs new file mode 100644 index 000000000..060fefc10 --- /dev/null +++ b/LibGit2Sharp/Core/Handles/PackBuilderSafeHandle.cs @@ -0,0 +1,11 @@ +namespace LibGit2Sharp.Core.Handles +{ + internal class PackBuilderSafeHandle : SafeHandleBase + { + protected override bool ReleaseHandleImpl() + { + Proxy.git_packbuilder_free(handle); + return true; + } + } +} diff --git a/LibGit2Sharp/Core/Handles/RebaseSafeHandle.cs b/LibGit2Sharp/Core/Handles/RebaseSafeHandle.cs new file mode 100644 index 000000000..e5698fcab --- /dev/null +++ b/LibGit2Sharp/Core/Handles/RebaseSafeHandle.cs @@ -0,0 +1,13 @@ +using LibGit2Sharp.Core.Handles; + +namespace LibGit2Sharp.Core +{ + internal class RebaseSafeHandle : SafeHandleBase + { + protected override bool ReleaseHandleImpl() + { + Proxy.git_rebase_free(handle); + return true; + } + } +} diff --git a/LibGit2Sharp/Core/Handles/SafeHandleBase.cs b/LibGit2Sharp/Core/Handles/SafeHandleBase.cs index b4ad98047..e16e30917 100644 --- a/LibGit2Sharp/Core/Handles/SafeHandleBase.cs +++ b/LibGit2Sharp/Core/Handles/SafeHandleBase.cs @@ -66,7 +66,15 @@ public static void Clear() /// public static IEnumerable TypeNames { - get { return _typeNames.ToArray(); } + get + { + string[] result = null; + lock (_lockpad) + { + result = _typeNames.ToArray(); + } + return result; + } } } } diff --git a/LibGit2Sharp/Core/Handles/StatusListSafeHandle.cs b/LibGit2Sharp/Core/Handles/StatusListSafeHandle.cs index 74316a3fa..3a51cd7b5 100644 --- a/LibGit2Sharp/Core/Handles/StatusListSafeHandle.cs +++ b/LibGit2Sharp/Core/Handles/StatusListSafeHandle.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LibGit2Sharp.Core.Handles +namespace LibGit2Sharp.Core.Handles { internal class StatusListSafeHandle : SafeHandleBase { diff --git a/LibGit2Sharp/Core/HistoryRewriter.cs b/LibGit2Sharp/Core/HistoryRewriter.cs index 33c35c6fd..c4cc2be8b 100644 --- a/LibGit2Sharp/Core/HistoryRewriter.cs +++ b/LibGit2Sharp/Core/HistoryRewriter.cs @@ -45,21 +45,25 @@ public void Execute() var filter = new CommitFilter { - Since = refsToRewrite, + IncludeReachableFrom = refsToRewrite, SortBy = CommitSortStrategies.Reverse | CommitSortStrategies.Topological }; var commits = repo.Commits.QueryBy(filter); foreach (var commit in commits) { - RewriteCommit(commit); + RewriteCommit(commit, options); } // Ordering matters. In the case of `A -> B -> commit`, we need to make sure B is rewritten // before A. foreach (var reference in refsToRewrite.OrderBy(ReferenceDepth)) { - // TODO: Check how rewriting of notes actually behaves + // TODO: Rewrite refs/notes/* properly + if (reference.CanonicalName.StartsWith("refs/notes/")) + { + continue; + } RewriteReference(reference); } @@ -106,24 +110,33 @@ private Reference RewriteReference(Reference reference) var sref = reference as SymbolicReference; if (sref != null) { - return RewriteReference( - sref, old => old.Target, RewriteReference, - (refs, old, target, sig, logMessage) => refs.UpdateTarget(old, target, sig, logMessage)); + return RewriteReference(sref, + old => old.Target, + RewriteReference, + (refs, old, target, logMessage) => refs.UpdateTarget(old, + target, + logMessage)); } var dref = reference as DirectReference; if (dref != null) { - return RewriteReference( - dref, old => old.Target, RewriteTarget, - (refs, old, target, sig, logMessage) => refs.UpdateTarget(old, target.Id, sig, logMessage)); + return RewriteReference(dref, + old => old.Target, + RewriteTarget, + (refs, old, target, logMessage) => refs.UpdateTarget(old, + target.Id, + logMessage)); } return reference; } private delegate Reference ReferenceUpdater( - ReferenceCollection refs, TRef origRef, TTarget origTarget, Signature signature, string logMessage) + ReferenceCollection refs, + TRef origRef, + TTarget origTarget, + string logMessage) where TRef : Reference where TTarget : class; @@ -135,14 +148,14 @@ private Reference RewriteReference( where TTarget : class { var oldRefTarget = selectTarget(oldRef); - var signature = repo.Config.BuildSignature(DateTimeOffset.Now); string newRefName = oldRef.CanonicalName; - if (oldRef.IsTag() && options.TagNameRewriter != null) + if (oldRef.IsTag && options.TagNameRewriter != null) { newRefName = Reference.TagPrefix + options.TagNameRewriter(oldRef.CanonicalName.Substring(Reference.TagPrefix.Length), - false, oldRef.TargetIdentifier); + false, + oldRef.TargetIdentifier); } var newTarget = rewriteTarget(oldRefTarget); @@ -157,36 +170,36 @@ private Reference RewriteReference( if (repo.Refs.Resolve(backupName) != null) { - throw new InvalidOperationException( - String.Format( - CultureInfo.InvariantCulture, "Can't back up reference '{0}' - '{1}' already exists", - oldRef.CanonicalName, backupName)); + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, + "Can't back up reference '{0}' - '{1}' already exists", + oldRef.CanonicalName, + backupName)); } - repo.Refs.Add(backupName, oldRef.TargetIdentifier, signature, "filter-branch: backup"); + repo.Refs.Add(backupName, oldRef.TargetIdentifier, "filter-branch: backup"); rollbackActions.Enqueue(() => repo.Refs.Remove(backupName)); if (newTarget == null) { repo.Refs.Remove(oldRef); - rollbackActions.Enqueue(() => repo.Refs.Add(oldRef.CanonicalName, oldRef, signature, "filter-branch: abort", true)); + rollbackActions.Enqueue(() => repo.Refs.Add(oldRef.CanonicalName, oldRef, "filter-branch: abort", true)); return refMap[oldRef] = null; } - Reference newRef = updateTarget(repo.Refs, oldRef, newTarget, signature, "filter-branch: rewrite"); - rollbackActions.Enqueue(() => updateTarget(repo.Refs, oldRef, oldRefTarget, signature, "filter-branch: abort")); + Reference newRef = updateTarget(repo.Refs, oldRef, newTarget, "filter-branch: rewrite"); + rollbackActions.Enqueue(() => updateTarget(repo.Refs, oldRef, oldRefTarget, "filter-branch: abort")); if (newRef.CanonicalName == newRefName) { return refMap[oldRef] = newRef; } - var movedRef = repo.Refs.Rename(newRef, newRefName); - rollbackActions.Enqueue(() => repo.Refs.Rename(newRef, oldRef.CanonicalName)); + var movedRef = repo.Refs.Rename(newRef, newRefName, false); + rollbackActions.Enqueue(() => repo.Refs.Rename(newRef, oldRef.CanonicalName, false)); return refMap[oldRef] = movedRef; } - private void RewriteCommit(Commit commit) + private void RewriteCommit(Commit commit, RewriteHistoryOptions options) { var newHeader = CommitRewriteInfo.From(commit); var newTree = commit.Tree; @@ -218,8 +231,7 @@ private void RewriteCommit(Commit commit) // Create the new commit var mappedNewParents = newParents - .Select(oldParent => - objectMap.ContainsKey(oldParent) + .Select(oldParent => objectMap.ContainsKey(oldParent) ? objectMap[oldParent] as Commit : oldParent) .Where(newParent => newParent != null) @@ -236,7 +248,7 @@ private void RewriteCommit(Commit commit) newHeader.Message, newTree, mappedNewParents, - true); + options.PrettifyMessages); // Record the rewrite objectMap[commit] = newCommit; @@ -290,8 +302,10 @@ private GitObject RewriteTarget(GitObject oldTarget) newName = options.TagNameRewriter(annotation.Name, true, annotation.Target.Sha); } - var newAnnotation = repo.ObjectDatabase.CreateTagAnnotation(newName, newTarget, annotation.Tagger, - annotation.Message); + var newAnnotation = repo.ObjectDatabase.CreateTagAnnotation(newName, + newTarget, + annotation.Tagger, + annotation.Message); objectMap[annotation] = newAnnotation; return newAnnotation; } @@ -300,8 +314,8 @@ private int ReferenceDepth(Reference reference) { var dref = reference as DirectReference; return dref == null - ? 1 + ReferenceDepth(((SymbolicReference)reference).Target) - : 1; + ? 1 + ReferenceDepth(((SymbolicReference)reference).Target) + : 1; } } } diff --git a/LibGit2Sharp/Core/LambdaEqualityHelper.cs b/LibGit2Sharp/Core/LambdaEqualityHelper.cs index 120e705de..80e826c02 100644 --- a/LibGit2Sharp/Core/LambdaEqualityHelper.cs +++ b/LibGit2Sharp/Core/LambdaEqualityHelper.cs @@ -48,7 +48,7 @@ public int GetHashCode(T instance) foreach (Func accessor in equalityContributorAccessors) { object item = accessor(instance); - hashCode = (hashCode*397) ^ (item != null ? item.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (item != null ? item.GetHashCode() : 0); } } diff --git a/LibGit2Sharp/Core/LazyGroup.cs b/LibGit2Sharp/Core/LazyGroup.cs index 3c82fa3ad..d8b13fa42 100644 --- a/LibGit2Sharp/Core/LazyGroup.cs +++ b/LibGit2Sharp/Core/LazyGroup.cs @@ -24,18 +24,19 @@ public ILazy AddLazy(Func func) public void Evaluate() { - if (evaluated) - return; - lock (@lock) { if (evaluated) + { return; + } EvaluateInternal(input => { foreach (var e in evaluators) + { e.Evaluate(input); + } }); evaluated = true; } @@ -93,8 +94,7 @@ protected class LazyWrapper : Lazy, ILazy { public LazyWrapper(Func evaluator) : base(evaluator) - { - } + { } } } } diff --git a/LibGit2Sharp/Core/NativeDllName.cs b/LibGit2Sharp/Core/NativeDllName.cs deleted file mode 100644 index 5127c4e4d..000000000 --- a/LibGit2Sharp/Core/NativeDllName.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace LibGit2Sharp.Core -{ - internal static class NativeDllName - { - public const string Name = "git2-e0902fb"; - } -} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 56a4d454d..8978abda4 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using System.IO; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; @@ -64,51 +63,28 @@ internal static void RemoveHandle() static NativeMethods() { - if (!IsRunningOnLinux()) + if (Platform.OperatingSystem == OperatingSystemType.Windows) { - string originalAssemblypath = new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath; + string nativeLibraryPath = GlobalSettings.GetAndLockNativeLibraryPath(); - string currentArchSubPath = "NativeBinaries/" + ProcessorArchitecture; - - string path = Path.Combine(Path.GetDirectoryName(originalAssemblypath), currentArchSubPath); + string path = Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture); const string pathEnvVariable = "PATH"; Environment.SetEnvironmentVariable(pathEnvVariable, - String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", path, Path.PathSeparator, Environment.GetEnvironmentVariable(pathEnvVariable))); + String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", path, Path.PathSeparator, Environment.GetEnvironmentVariable(pathEnvVariable))); } // See LibraryLifetimeObject description. lifetimeObject = new LibraryLifetimeObject(); } - public static string ProcessorArchitecture - { - get - { - if (Environment.Is64BitProcess) - { - return "amd64"; - } - - return "x86"; - } - } - - // Should match LibGit2Sharp.Tests.TestHelpers.BaseFixture.IsRunningOnLinux() - private static bool IsRunningOnLinux() - { - // see http://mono-project.com/FAQ%3a_Technical#Mono_Platforms - var p = (int)Environment.OSVersion.Platform; - return (p == 4) || (p == 6) || (p == 128); - } - [DllImport(libgit2)] internal static extern GitErrorSafeHandle giterr_last(); [DllImport(libgit2)] internal static extern void giterr_set_str( GitErrorCategory error_class, - string errorString); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string errorString); [DllImport(libgit2)] internal static extern void giterr_set_oom(); @@ -169,14 +145,12 @@ internal static extern int git_blob_filtered_content( internal static extern Int64 git_blob_rawsize(GitObjectSafeHandle blob); [DllImport(libgit2)] - internal static extern int git_branch_create( + internal static extern int git_branch_create_from_annotated( out ReferenceSafeHandle ref_out, RepositorySafeHandle repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string branch_name, - GitObjectSafeHandle target, // TODO: GitCommitSafeHandle? - [MarshalAs(UnmanagedType.Bool)] bool force, - SignatureSafeHandle signature, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); + GitAnnotatedCommitHandle target, + [MarshalAs(UnmanagedType.Bool)] bool force); [DllImport(libgit2)] internal static extern int git_branch_delete( @@ -202,9 +176,7 @@ internal static extern int git_branch_move( out ReferenceSafeHandle ref_out, ReferenceSafeHandle reference, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string new_branch_name, - [MarshalAs(UnmanagedType.Bool)] bool force, - SignatureSafeHandle signature, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); + [MarshalAs(UnmanagedType.Bool)] bool force); [DllImport(libgit2)] internal static extern int git_branch_next( @@ -218,6 +190,61 @@ internal static extern int git_branch_remote_name( RepositorySafeHandle repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string canonical_branch_name); + [DllImport(libgit2)] + internal static extern int git_rebase_init( + out RebaseSafeHandle rebase, + RepositorySafeHandle repo, + GitAnnotatedCommitHandle branch, + GitAnnotatedCommitHandle upstream, + GitAnnotatedCommitHandle onto, + GitRebaseOptions options); + + [DllImport(libgit2)] + internal static extern int git_rebase_open( + out RebaseSafeHandle rebase, + RepositorySafeHandle repo, + GitRebaseOptions options); + + [DllImport(libgit2)] + internal static extern UIntPtr git_rebase_operation_entrycount( + RebaseSafeHandle rebase); + + [DllImport(libgit2)] + internal static extern UIntPtr git_rebase_operation_current( + RebaseSafeHandle rebase); + + [DllImport(libgit2)] + internal static extern IntPtr git_rebase_operation_byindex( + RebaseSafeHandle rebase, + UIntPtr index); + + [DllImport(libgit2)] + internal static extern int git_rebase_next( + out IntPtr operation, + RebaseSafeHandle rebase); + + [DllImport(libgit2)] + internal static extern int git_rebase_commit( + ref GitOid id, + RebaseSafeHandle rebase, + SignatureSafeHandle author, + SignatureSafeHandle committer, + IntPtr message_encoding, + IntPtr message); + + [DllImport(libgit2)] + internal static extern int git_rebase_abort( + RebaseSafeHandle rebase); + + [DllImport(libgit2)] + internal static extern int git_rebase_finish( + RebaseSafeHandle repo, + SignatureSafeHandle signature); + + [DllImport(libgit2)] + internal static extern void git_rebase_free( + IntPtr rebase); + [DllImport(libgit2)] internal static extern int git_remote_rename( ref GitStrArray problems, @@ -229,7 +256,6 @@ internal delegate int git_remote_rename_problem_cb( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] string problematic_refspec, IntPtr payload); - [DllImport(libgit2)] internal static extern int git_branch_upstream_name( GitBuf buf, @@ -278,7 +304,7 @@ internal static extern int git_commit_create_from_ids( [MarshalAs(UnmanagedType.LPArray)] [In] IntPtr[] parents); [DllImport(libgit2)] - [return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern string git_commit_message(GitObjectSafeHandle commit); [DllImport(libgit2)] @@ -299,7 +325,15 @@ internal static extern int git_commit_create_from_ids( internal static extern OidSafeHandle git_commit_tree_id(GitObjectSafeHandle commit); [DllImport(libgit2)] - internal static extern int git_config_delete_entry(ConfigurationSafeHandle cfg, string name); + internal static extern int git_config_delete_entry( + ConfigurationSafeHandle cfg, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); + + [DllImport(libgit2)] + internal static extern int git_config_delete_multivar( + ConfigurationSafeHandle cfg, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp); [DllImport(libgit2)] internal static extern int git_config_find_global(GitBuf global_config_path); @@ -313,6 +347,9 @@ internal static extern int git_commit_create_from_ids( [DllImport(libgit2)] internal static extern void git_config_free(IntPtr cfg); + [DllImport(libgit2)] + internal static extern void git_config_entry_free(IntPtr entry); + [DllImport(libgit2)] internal static extern int git_config_get_entry( out GitConfigEntryHandle entry, @@ -419,8 +456,11 @@ internal delegate int git_cred_acquire_cb( [DllImport(libgit2)] internal static extern int git_cred_userpass_plaintext_new( out IntPtr cred, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string username, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string password); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string password); + + [DllImport(libgit2)] + internal static extern void git_cred_free(IntPtr cred); [DllImport(libgit2)] internal static extern int git_describe_commit( @@ -491,6 +531,11 @@ internal delegate int git_diff_line_cb( [In] GitDiffLine line, IntPtr payload); + internal delegate int git_diff_binary_cb( + [In] GitDiffDelta delta, + [In] GitDiffBinary binary, + IntPtr payload); + [DllImport(libgit2)] internal static extern int git_diff_blobs( GitObjectSafeHandle oldBlob, @@ -499,6 +544,7 @@ internal static extern int git_diff_blobs( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath new_as_path, GitDiffOptions options, git_diff_file_cb fileCallback, + git_diff_binary_cb binaryCallback, git_diff_hunk_cb hunkCallback, git_diff_line_cb lineCallback, IntPtr payload); @@ -507,6 +553,7 @@ internal static extern int git_diff_blobs( internal static extern int git_diff_foreach( DiffSafeHandle diff, git_diff_file_cb fileCallback, + git_diff_binary_cb binaryCallback, git_diff_hunk_cb hunkCallback, git_diff_line_cb lineCallback, IntPtr payload); @@ -522,9 +569,40 @@ internal static extern int git_diff_find_similar( [DllImport(libgit2)] internal static extern IntPtr git_diff_get_delta(DiffSafeHandle diff, UIntPtr idx); + [DllImport(libgit2)] + internal static extern int git_filter_register( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, + IntPtr gitFilter, int priority); + + [DllImport(libgit2)] + internal static extern int git_filter_unregister( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))]string name); + + [DllImport(libgit2)] + internal static extern int git_filter_source_mode(IntPtr source); + [DllImport(libgit2)] internal static extern int git_libgit2_features(); + #region git_libgit2_opts + + // Bindings for git_libgit2_opts(int option, ...): + // Currently only GIT_OPT_GET_SEARCH_PATH and GIT_OPT_SET_SEARCH_PATH are supported, + // but other overloads could be added using a similar pattern. + // CallingConvention.Cdecl is used to allow binding the the C varargs signature, and each possible call signature must be enumerated. + // __argslist was an option, but is an undocumented feature that should likely not be used here. + + // git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_libgit2_opts(int option, uint level, GitBuf buf); + + // git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_libgit2_opts(int option, uint level, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))]string path); + + #endregion + [DllImport(libgit2)] internal static extern int git_graph_ahead_behind(out UIntPtr ahead, out UIntPtr behind, RepositorySafeHandle repo, ref GitOid one, ref GitOid two); @@ -537,7 +615,7 @@ internal static extern int git_graph_descendant_of( [DllImport(libgit2)] internal static extern int git_ignore_add_rule( RepositorySafeHandle repo, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string rules); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string rules); [DllImport(libgit2)] internal static extern int git_ignore_clear_internal_rules(RepositorySafeHandle repo); @@ -566,6 +644,22 @@ internal static extern int git_index_conflict_get( IndexSafeHandle index, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); + [DllImport(libgit2)] + internal static extern int git_index_conflict_iterator_new( + out ConflictIteratorSafeHandle iterator, + IndexSafeHandle index); + + [DllImport(libgit2)] + internal static extern int git_index_conflict_next( + out IndexEntrySafeHandle ancestor, + out IndexEntrySafeHandle ours, + out IndexEntrySafeHandle theirs, + ConflictIteratorSafeHandle iterator); + + [DllImport(libgit2)] + internal static extern void git_index_conflict_iterator_free( + IntPtr iterator); + [DllImport(libgit2)] internal static extern UIntPtr git_index_entrycount(IndexSafeHandle index); @@ -588,7 +682,7 @@ internal static extern IndexEntrySafeHandle git_index_get_bypath( internal static extern int git_index_has_conflicts(IndexSafeHandle index); [DllImport(libgit2)] - internal static extern uint git_index_name_entrycount(IndexSafeHandle handle); + internal static extern UIntPtr git_index_name_entrycount(IndexSafeHandle handle); [DllImport(libgit2)] internal static extern IndexNameEntrySafeHandle git_index_name_get_byindex(IndexSafeHandle handle, UIntPtr n); @@ -610,7 +704,7 @@ internal static extern int git_index_remove_bypath( [DllImport(libgit2)] - internal static extern uint git_index_reuc_entrycount(IndexSafeHandle handle); + internal static extern UIntPtr git_index_reuc_entrycount(IndexSafeHandle handle); [DllImport(libgit2)] internal static extern IndexReucEntrySafeHandle git_index_reuc_get_byindex(IndexSafeHandle handle, UIntPtr n); @@ -626,6 +720,9 @@ internal static extern IndexReucEntrySafeHandle git_index_reuc_get_bypath( [DllImport(libgit2)] internal static extern int git_index_write_tree(out GitOid treeOid, IndexSafeHandle index); + [DllImport(libgit2)] + internal static extern int git_index_write_tree_to(out GitOid treeOid, IndexSafeHandle index, RepositorySafeHandle repo); + [DllImport(libgit2)] internal static extern int git_index_read_tree(IndexSafeHandle index, GitObjectSafeHandle tree); @@ -660,6 +757,12 @@ internal static extern int git_annotated_commit_from_fetchhead( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote_url, ref GitOid oid); + [DllImport(libgit2)] + internal static extern int git_annotated_commit_from_revspec( + out GitAnnotatedCommitHandle annotatedCommit, + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string revspec); + [DllImport(libgit2)] internal static extern int git_annotated_commit_lookup( out GitAnnotatedCommitHandle annotatedCommit, @@ -678,6 +781,14 @@ internal static extern int git_merge( ref GitMergeOpts merge_opts, ref GitCheckoutOpts checkout_opts); + [DllImport(libgit2)] + internal static extern int git_merge_commits( + out IndexSafeHandle index, + RepositorySafeHandle repo, + GitObjectSafeHandle our_commit, + GitObjectSafeHandle their_commit, + ref GitMergeOpts merge_opts); + [DllImport(libgit2)] internal static extern int git_merge_analysis( out GitMergeAnalysis status_out, @@ -735,7 +846,7 @@ internal static extern int git_note_remove( [DllImport(libgit2)] internal static extern int git_note_default_ref( - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] out string notes_ref, + GitBuf notes_ref, RepositorySafeHandle repo); internal delegate int git_note_foreach_cb( @@ -770,7 +881,7 @@ internal static extern int git_odb_foreach( IntPtr payload); [DllImport(libgit2)] - internal static extern int git_odb_open_wstream(out OdbStreamSafeHandle stream, ObjectDatabaseSafeHandle odb, UIntPtr size, GitObjectType type); + internal static extern int git_odb_open_wstream(out OdbStreamSafeHandle stream, ObjectDatabaseSafeHandle odb, Int64 size, GitObjectType type); [DllImport(libgit2)] internal static extern void git_odb_free(IntPtr odb); @@ -830,6 +941,51 @@ internal static extern int git_patch_line_stats( internal delegate int git_push_transfer_progress(uint current, uint total, UIntPtr bytes, IntPtr payload); internal delegate int git_packbuilder_progress(int stage, uint current, uint total, IntPtr payload); + [DllImport(libgit2)] + internal static extern void git_packbuilder_free(IntPtr packbuilder); + + [DllImport(libgit2)] + internal static extern int git_packbuilder_insert( + PackBuilderSafeHandle packbuilder, + ref GitOid id, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); + + [DllImport(libgit2)] + internal static extern int git_packbuilder_insert_commit( + PackBuilderSafeHandle packbuilder, + ref GitOid id); + + [DllImport(libgit2)] + internal static extern int git_packbuilder_insert_recur( + PackBuilderSafeHandle packbuilder, + ref GitOid id, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); + + [DllImport(libgit2)] + internal static extern int git_packbuilder_insert_tree( + PackBuilderSafeHandle packbuilder, + ref GitOid id); + + [DllImport(libgit2)] + internal static extern int git_packbuilder_new(out PackBuilderSafeHandle packbuilder, RepositorySafeHandle repo); + + [DllImport(libgit2)] + internal static extern UInt32 git_packbuilder_object_count(PackBuilderSafeHandle packbuilder); + + [DllImport(libgit2)] + internal static extern UInt32 git_packbuilder_set_threads(PackBuilderSafeHandle packbuilder, UInt32 numThreads); + + [DllImport(libgit2)] + internal static extern int git_packbuilder_write( + PackBuilderSafeHandle packbuilder, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path, + uint mode, + IntPtr progressCallback, + IntPtr payload); + + [DllImport(libgit2)] + internal static extern UInt32 git_packbuilder_written(PackBuilderSafeHandle packbuilder); + [DllImport(libgit2)] internal static extern int git_reference_create( out ReferenceSafeHandle reference, @@ -837,7 +993,6 @@ internal static extern int git_reference_create( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, ref GitOid oid, [MarshalAs(UnmanagedType.Bool)] bool force, - SignatureSafeHandle signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); [DllImport(libgit2)] @@ -847,7 +1002,6 @@ internal static extern int git_reference_symbolic_create( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string target, [MarshalAs(UnmanagedType.Bool)] bool force, - SignatureSafeHandle signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); internal delegate int ref_glob_callback( @@ -895,7 +1049,6 @@ internal static extern int git_reference_rename( ReferenceSafeHandle reference, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string newName, [MarshalAs(UnmanagedType.Bool)] bool force, - SignatureSafeHandle signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); [DllImport(libgit2)] @@ -903,7 +1056,6 @@ internal static extern int git_reference_set_target( out ReferenceSafeHandle ref_out, ReferenceSafeHandle reference, ref GitOid id, - SignatureSafeHandle signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); [DllImport(libgit2)] @@ -911,7 +1063,6 @@ internal static extern int git_reference_symbolic_set_target( out ReferenceSafeHandle ref_out, ReferenceSafeHandle reference, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string target, - SignatureSafeHandle signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); [DllImport(libgit2)] @@ -924,7 +1075,7 @@ internal static extern int git_reference_symbolic_set_target( [DllImport(libgit2)] internal static extern int git_reference_ensure_log( RepositorySafeHandle repo, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string refname); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refname); [DllImport(libgit2)] internal static extern void git_reflog_free( @@ -992,7 +1143,11 @@ internal static extern string git_refspec_src( internal static extern int git_remote_autotag(RemoteSafeHandle remote); [DllImport(libgit2)] - internal static extern int git_remote_connect(RemoteSafeHandle remote, GitDirection direction); + internal static extern int git_remote_connect( + RemoteSafeHandle remote, + GitDirection direction, + ref GitRemoteCallbacks callbacks, + ref GitStrArray custom_headers); [DllImport(libgit2)] internal static extern int git_remote_create( @@ -1005,8 +1160,7 @@ internal static extern int git_remote_create( internal static extern int git_remote_create_anonymous( out RemoteSafeHandle remote, RepositorySafeHandle repo, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refspec); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); [DllImport(libgit2)] @@ -1022,14 +1176,11 @@ internal static extern int git_remote_delete( RepositorySafeHandle repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] - internal static extern void git_remote_disconnect(RemoteSafeHandle remote); - [DllImport(libgit2)] internal static extern int git_remote_fetch( RemoteSafeHandle remote, ref GitStrArray refspecs, - SignatureSafeHandle signature, + GitFetchOptions fetch_opts, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); [DllImport(libgit2)] @@ -1048,9 +1199,7 @@ internal static extern int git_remote_fetch( internal static extern int git_remote_push( RemoteSafeHandle remote, ref GitStrArray refSpecs, - GitPushOptions opts, - SignatureSafeHandle signature, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string reflogMessage); + GitPushOptions opts); [DllImport(libgit2)] internal static extern UIntPtr git_remote_refspec_count(RemoteSafeHandle remote); @@ -1063,7 +1212,26 @@ internal static extern int git_remote_push( [DllImport(libgit2)] internal static extern int git_remote_set_url( - RemoteSafeHandle remote, + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); + + [DllImport(libgit2)] + internal static extern int git_remote_add_fetch( + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); + + [DllImport(libgit2)] + internal static extern int git_remote_set_pushurl( + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); + + [DllImport(libgit2)] + internal static extern int git_remote_add_push( + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); [DllImport(libgit2)] @@ -1088,20 +1256,19 @@ internal static extern int git_remote_lookup( [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern string git_remote_name(RemoteSafeHandle remote); - [DllImport(libgit2)] - internal static extern int git_remote_save(RemoteSafeHandle remote); - [DllImport(libgit2)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern string git_remote_url(RemoteSafeHandle remote); [DllImport(libgit2)] - internal static extern void git_remote_set_autotag(RemoteSafeHandle remote, TagFetchMode option); + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] + internal static extern string git_remote_pushurl(RemoteSafeHandle remote); [DllImport(libgit2)] - internal static extern int git_remote_set_callbacks( - RemoteSafeHandle remote, - ref GitRemoteCallbacks callbacks); + internal static extern void git_remote_set_autotag( + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, + TagFetchMode option); internal delegate int remote_progress_callback(IntPtr str, int len, IntPtr data); @@ -1113,6 +1280,17 @@ internal delegate int remote_update_tips_callback( ref GitOid newId, IntPtr data); + internal delegate int push_negotiation_callback( + IntPtr updates, + UIntPtr len, + IntPtr payload); + + internal delegate int push_update_reference_callback( + IntPtr refName, + IntPtr status, + IntPtr data + ); + [DllImport(libgit2)] internal static extern int git_repository_discover( GitBuf buf, @@ -1142,6 +1320,12 @@ internal static extern int git_repository_fetchhead_foreach( [DllImport(libgit2)] internal static extern int git_repository_head_unborn(RepositorySafeHandle repo); + [DllImport(libgit2)] + internal static extern int git_repository_ident( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] out string name, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] out string email, + RepositorySafeHandle repo); + [DllImport(libgit2)] internal static extern int git_repository_index(out IndexSafeHandle index, RepositorySafeHandle repo); @@ -1199,6 +1383,13 @@ internal static extern void git_repository_set_config( RepositorySafeHandle repository, ConfigurationSafeHandle config); + [DllImport(libgit2)] + internal static extern int git_repository_set_ident( + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string email); + + [DllImport(libgit2)] internal static extern void git_repository_set_index( RepositorySafeHandle repository, @@ -1213,16 +1404,17 @@ internal static extern int git_repository_set_workdir( [DllImport(libgit2)] internal static extern int git_repository_set_head_detached( RepositorySafeHandle repo, - ref GitOid commitish, - SignatureSafeHandle signature, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); + ref GitOid commitish); + + [DllImport(libgit2)] + internal static extern int git_repository_set_head_detached_from_annotated( + RepositorySafeHandle repo, + GitAnnotatedCommitHandle commit); [DllImport(libgit2)] internal static extern int git_repository_set_head( RepositorySafeHandle repo, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refname, - SignatureSafeHandle signature, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refname); [DllImport(libgit2)] internal static extern int git_repository_state( @@ -1232,14 +1424,19 @@ internal static extern int git_repository_state( [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))] internal static extern FilePath git_repository_workdir(RepositorySafeHandle repository); + [DllImport(libgit2)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))] + internal static extern FilePath git_repository_workdir(IntPtr repository); + + [DllImport(libgit2)] + internal static extern int git_repository_new(out RepositorySafeHandle repo); + [DllImport(libgit2)] internal static extern int git_reset( RepositorySafeHandle repo, GitObjectSafeHandle target, ResetMode reset_type, - ref GitCheckoutOpts opts, - SignatureSafeHandle signature, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); + ref GitCheckoutOpts opts); [DllImport(libgit2)] internal static extern int git_revert( @@ -1289,6 +1486,12 @@ internal static extern int git_signature_new( long time, int offset); + [DllImport(libgit2)] + internal static extern int git_signature_now( + out SignatureSafeHandle signature, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string email); + [DllImport(libgit2)] internal static extern int git_signature_dup(out IntPtr dest, IntPtr sig); @@ -1315,6 +1518,18 @@ internal static extern int git_stash_foreach( [DllImport(libgit2)] internal static extern int git_stash_drop(RepositorySafeHandle repo, UIntPtr index); + [DllImport(libgit2)] + internal static extern int git_stash_apply( + RepositorySafeHandle repo, + UIntPtr index, + GitStashApplyOpts opts); + + [DllImport(libgit2)] + internal static extern int git_stash_pop( + RepositorySafeHandle repo, + UIntPtr index, + GitStashApplyOpts opts); + [DllImport(libgit2)] internal static extern int git_status_file( out FileStatus statusflags, @@ -1351,6 +1566,12 @@ internal static extern int git_submodule_lookup( RepositorySafeHandle repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath name); + [DllImport(libgit2)] + internal static extern int git_submodule_resolve_url( + GitBuf buf, + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); + [DllImport(libgit2)] internal static extern int git_submodule_update( SubmoduleSafeHandle sm, @@ -1373,10 +1594,6 @@ internal static extern int git_submodule_add_to_index( SubmoduleSafeHandle submodule, [MarshalAs(UnmanagedType.Bool)] bool write_index); - [DllImport(libgit2)] - internal static extern int git_submodule_save( - SubmoduleSafeHandle submodule); - [DllImport(libgit2)] internal static extern void git_submodule_free( IntPtr submodule); @@ -1423,7 +1640,9 @@ internal static extern int git_submodule_reload( [DllImport(libgit2)] internal static extern int git_submodule_status( out SubmoduleStatus status, - SubmoduleSafeHandle submodule); + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath name, + GitSubmoduleIgnore ignore); [DllImport(libgit2)] internal static extern int git_submodule_init( @@ -1502,6 +1721,8 @@ internal static extern int git_tag_delete( internal delegate int git_transport_cb(out IntPtr transport, IntPtr remote, IntPtr payload); + internal delegate int git_transport_certificate_check_cb(IntPtr cert, int valid, IntPtr hostname, IntPtr payload); + [DllImport(libgit2)] internal static extern int git_transport_register( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string prefix, @@ -1514,6 +1735,20 @@ internal static extern int git_transport_smart( IntPtr remote, IntPtr definition); + [DllImport(libgit2)] + internal static extern int git_transport_smart_certificate_check( + IntPtr transport, + IntPtr cert, + int valid, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string hostname); + + [DllImport(libgit2)] + internal static extern int git_transport_smart_credentials( + out IntPtr cred_out, + IntPtr transport, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string user, + int methods); + [DllImport(libgit2)] internal static extern int git_transport_unregister( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string prefix); diff --git a/LibGit2Sharp/Core/PathCase.cs b/LibGit2Sharp/Core/PathCase.cs index a5fadbb48..600f693de 100644 --- a/LibGit2Sharp/Core/PathCase.cs +++ b/LibGit2Sharp/Core/PathCase.cs @@ -16,6 +16,7 @@ public PathCase(IRepository repo) comparer = StringComparer.OrdinalIgnoreCase; comparison = StringComparison.OrdinalIgnoreCase; break; + default: comparer = StringComparer.Ordinal; comparison = StringComparison.Ordinal; diff --git a/LibGit2Sharp/Core/Platform.cs b/LibGit2Sharp/Core/Platform.cs new file mode 100644 index 000000000..d18613d29 --- /dev/null +++ b/LibGit2Sharp/Core/Platform.cs @@ -0,0 +1,39 @@ +using System; + +namespace LibGit2Sharp.Core +{ + internal enum OperatingSystemType + { + Windows, + Unix, + MacOSX + } + + internal static class Platform + { + public static string ProcessorArchitecture + { + get { return Environment.Is64BitProcess ? "amd64" : "x86"; } + } + + public static OperatingSystemType OperatingSystem + { + get + { + // See http://www.mono-project.com/docs/faq/technical/#how-to-detect-the-execution-platform + switch ((int)Environment.OSVersion.Platform) + { + case 4: + case 128: + return OperatingSystemType.Unix; + + case 6: + return OperatingSystemType.MacOSX; + + default: + return OperatingSystemType.Windows; + } + } + } + } +} diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 2a3a1bb63..35371d886 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; -using System.Threading; +using System.Text; using LibGit2Sharp.Core.Handles; using LibGit2Sharp.Handlers; @@ -23,7 +24,7 @@ public static void giterr_set_str(GitErrorCategory error_class, Exception except } else { - NativeMethods.giterr_set_str(error_class, exception.Message); + NativeMethods.giterr_set_str(error_class, ErrorMessageFromException(exception)); } } @@ -32,6 +33,64 @@ public static void giterr_set_str(GitErrorCategory error_class, String errorStri NativeMethods.giterr_set_str(error_class, errorString); } + /// + /// This method will take an exception and try to generate an error message + /// that captures the important messages of the error. + /// The formatting is a bit subjective. + /// + /// + /// + public static string ErrorMessageFromException(Exception ex) + { + StringBuilder sb = new StringBuilder(); + BuildErrorMessageFromException(sb, 0, ex); + return sb.ToString(); + } + + private static void BuildErrorMessageFromException(StringBuilder sb, int level, Exception ex) + { + string indent = new string(' ', level * 4); + sb.AppendFormat("{0}{1}", indent, ex.Message); + + if (ex is AggregateException) + { + AggregateException aggregateException = ((AggregateException)ex).Flatten(); + + if (aggregateException.InnerExceptions.Count == 1) + { + sb.AppendLine(); + sb.AppendLine(); + + sb.AppendFormat("{0}Contained Exception:{1}", indent, Environment.NewLine); + BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerException); + } + else + { + sb.AppendLine(); + sb.AppendLine(); + + sb.AppendFormat("{0}Contained Exceptions:{1}", indent, Environment.NewLine); + for (int i = 0; i < aggregateException.InnerExceptions.Count; i++) + { + if (i != 0) + { + sb.AppendLine(); + sb.AppendLine(); + } + + BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerExceptions[i]); + } + } + } + else if (ex.InnerException != null) + { + sb.AppendLine(); + sb.AppendLine(); + sb.AppendFormat("{0}Inner Exception:{1}", indent, Environment.NewLine); + BuildErrorMessageFromException(sb, level + 1, ex.InnerException); + } + } + #endregion #region git_blame_ @@ -41,13 +100,10 @@ public static BlameSafeHandle git_blame_file( FilePath path, GitBlameOptions options) { - using (ThreadAffinity()) - { - BlameSafeHandle handle; - int res = NativeMethods.git_blame_file(out handle, repo, path, options); - Ensure.ZeroResult(res); - return handle; - } + BlameSafeHandle handle; + int res = NativeMethods.git_blame_file(out handle, repo, path, options); + Ensure.ZeroResult(res); + return handle; } public static GitBlameHunk git_blame_get_hunk_byindex(BlameSafeHandle blame, uint idx) @@ -66,44 +122,35 @@ public static void git_blame_free(IntPtr blame) public static ObjectId git_blob_create_fromchunks(RepositorySafeHandle repo, FilePath hintpath, NativeMethods.source_callback fileCallback) { - using (ThreadAffinity()) - { - var oid = new GitOid(); - int res = NativeMethods.git_blob_create_fromchunks(ref oid, repo, hintpath, fileCallback, IntPtr.Zero); + var oid = new GitOid(); + int res = NativeMethods.git_blob_create_fromchunks(ref oid, repo, hintpath, fileCallback, IntPtr.Zero); - if (res == (int)GitErrorCode.User) - { - throw new EndOfStreamException("The stream ended unexpectedly"); - } + if (res == (int)GitErrorCode.User) + { + throw new EndOfStreamException("The stream ended unexpectedly"); + } - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return oid; - } + return oid; } public static ObjectId git_blob_create_fromdisk(RepositorySafeHandle repo, FilePath path) { - using (ThreadAffinity()) - { - var oid = new GitOid(); - int res = NativeMethods.git_blob_create_fromdisk(ref oid, repo, path); - Ensure.ZeroResult(res); + var oid = new GitOid(); + int res = NativeMethods.git_blob_create_fromdisk(ref oid, repo, path); + Ensure.ZeroResult(res); - return oid; - } + return oid; } public static ObjectId git_blob_create_fromfile(RepositorySafeHandle repo, FilePath path) { - using (ThreadAffinity()) - { - var oid = new GitOid(); - int res = NativeMethods.git_blob_create_fromworkdir(ref oid, repo, path); - Ensure.ZeroResult(res); + var oid = new GitOid(); + int res = NativeMethods.git_blob_create_fromworkdir(ref oid, repo, path); + Ensure.ZeroResult(res); - return oid; - } + return oid; } public static UnmanagedMemoryStream git_blob_filtered_content_stream(RepositorySafeHandle repo, ObjectId id, FilePath path, bool check_for_binary_data) @@ -126,7 +173,7 @@ public static UnmanagedMemoryStream git_blob_rawcontent_stream(RepositorySafeHan return new RawContentStream(handle, NativeMethods.git_blob_rawcontent, h => size); } - public static Int64 git_blob_rawsize(GitObjectSafeHandle obj) + public static long git_blob_rawsize(GitObjectSafeHandle obj) { return NativeMethods.git_blob_rawsize(obj); } @@ -143,46 +190,40 @@ public static bool git_blob_is_binary(GitObjectSafeHandle obj) #region git_branch_ - public static ReferenceSafeHandle git_branch_create(RepositorySafeHandle repo, string branch_name, ObjectId targetId, bool force, - Signature signature, string logMessage) + public static ReferenceSafeHandle git_branch_create_from_annotated(RepositorySafeHandle repo, string branch_name, string targetIdentifier, bool force) { - using (ThreadAffinity()) - using (var osw = new ObjectSafeWrapper(targetId, repo)) - using (var sigHandle = signature.BuildHandle()) + ReferenceSafeHandle reference; + + using (var annotatedCommit = git_annotated_commit_from_revspec(repo, targetIdentifier)) { - ReferenceSafeHandle reference; - int res = NativeMethods.git_branch_create(out reference, repo, branch_name, osw.ObjectPtr, force, sigHandle, logMessage); + int res = NativeMethods.git_branch_create_from_annotated(out reference, repo, branch_name, annotatedCommit, force); Ensure.ZeroResult(res); - return reference; } + + return reference; } public static void git_branch_delete(ReferenceSafeHandle reference) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_branch_delete(reference); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_branch_delete(reference); + Ensure.ZeroResult(res); } public static IEnumerable git_branch_iterator(Repository repo, GitBranchType branchType) { - return git_iterator( - (out BranchIteratorSafeHandle iter_out) => - NativeMethods.git_branch_iterator_new(out iter_out, repo.Handle, branchType), - (BranchIteratorSafeHandle iter, out ReferenceSafeHandle ref_out, out int res) => - { - GitBranchType type_out; - res = NativeMethods.git_branch_next(out ref_out, out type_out, iter); - return new { BranchType = type_out }; - }, - (handle, payload) => - { - var reference = Reference.BuildFromPtr(handle, repo); - return new Branch(repo, reference, reference.CanonicalName); - } - ); + return git_iterator((out BranchIteratorSafeHandle iter_out) => + NativeMethods.git_branch_iterator_new(out iter_out, repo.Handle, branchType), + (BranchIteratorSafeHandle iter, out ReferenceSafeHandle ref_out, out int res) => + { + GitBranchType type_out; + res = NativeMethods.git_branch_next(out ref_out, out type_out, iter); + return new { BranchType = type_out }; + }, + (handle, payload) => + { + var reference = Reference.BuildFromPtr(handle, repo); + return new Branch(repo, reference, reference.CanonicalName); + }); } public static void git_branch_iterator_free(IntPtr iter) @@ -190,28 +231,22 @@ public static void git_branch_iterator_free(IntPtr iter) NativeMethods.git_branch_iterator_free(iter); } - public static ReferenceSafeHandle git_branch_move(ReferenceSafeHandle reference, string new_branch_name, bool force, - Signature signature, string logMessage) + public static ReferenceSafeHandle git_branch_move(ReferenceSafeHandle reference, string new_branch_name, bool force) { - using (ThreadAffinity()) - using (var sigHandle = signature.BuildHandle()) - { - ReferenceSafeHandle ref_out; - int res = NativeMethods.git_branch_move(out ref_out, reference, new_branch_name, force, sigHandle, logMessage); - Ensure.ZeroResult(res); - return ref_out; - } + ReferenceSafeHandle ref_out; + int res = NativeMethods.git_branch_move(out ref_out, reference, new_branch_name, force); + Ensure.ZeroResult(res); + return ref_out; } public static string git_branch_remote_name(RepositorySafeHandle repo, string canonical_branch_name, bool shouldThrowIfNotFound) { - using (ThreadAffinity()) using (var buf = new GitBuf()) { int res = NativeMethods.git_branch_remote_name(buf, repo, canonical_branch_name); if (!shouldThrowIfNotFound && - (res == (int) GitErrorCode.NotFound || res == (int) GitErrorCode.Ambiguous)) + (res == (int)GitErrorCode.NotFound || res == (int)GitErrorCode.Ambiguous)) { return null; } @@ -223,11 +258,10 @@ public static string git_branch_remote_name(RepositorySafeHandle repo, string ca public static string git_branch_upstream_name(RepositorySafeHandle handle, string canonicalReferenceName) { - using (ThreadAffinity()) using (var buf = new GitBuf()) { int res = NativeMethods.git_branch_upstream_name(buf, handle, canonicalReferenceName); - if (res == (int) GitErrorCode.NotFound) + if (res == (int)GitErrorCode.NotFound) { return null; } @@ -255,7 +289,6 @@ public static void git_checkout_tree( ObjectId treeId, ref GitCheckoutOpts opts) { - using (ThreadAffinity()) using (var osw = new ObjectSafeWrapper(treeId, repo)) { int res = NativeMethods.git_checkout_tree(repo, osw.ObjectPtr, ref opts); @@ -265,11 +298,8 @@ public static void git_checkout_tree( public static void git_checkout_index(RepositorySafeHandle repo, GitObjectSafeHandle treeish, ref GitCheckoutOpts opts) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_checkout_index(repo, treeish, ref opts); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_checkout_index(repo, treeish, ref opts); + Ensure.ZeroResult(res); } #endregion @@ -278,7 +308,6 @@ public static void git_checkout_index(RepositorySafeHandle repo, GitObjectSafeHa internal static void git_cherrypick(RepositorySafeHandle repo, ObjectId commit, GitCherryPickOptions options) { - using (ThreadAffinity()) using (var nativeCommit = git_object_lookup(repo, commit, GitObjectType.Commit)) { int res = NativeMethods.git_cherrypick(repo, nativeCommit, options); @@ -294,13 +323,10 @@ public static RepositorySafeHandle git_clone( string workdir, ref GitCloneOptions opts) { - using (ThreadAffinity()) - { - RepositorySafeHandle repo; - int res = NativeMethods.git_clone(out repo, url, workdir, ref opts); - Ensure.ZeroResult(res); - return repo; - } + RepositorySafeHandle repo; + int res = NativeMethods.git_clone(out repo, url, workdir, ref opts); + Ensure.ZeroResult(res); + return repo; } #endregion @@ -326,7 +352,6 @@ public static ObjectId git_commit_create( Tree tree, GitOid[] parentIds) { - using (ThreadAffinity()) using (SignatureSafeHandle authorHandle = author.BuildHandle()) using (SignatureSafeHandle committerHandle = committer.BuildHandle()) using (var parentPtrs = new ArrayMarshaler(parentIds)) @@ -335,10 +360,16 @@ public static ObjectId git_commit_create( var treeOid = tree.Id.Oid; - int res = NativeMethods.git_commit_create_from_ids( - out commitOid, repo, referenceName, authorHandle, - committerHandle, null, message, - ref treeOid, (UIntPtr)parentPtrs.Count, parentPtrs.ToArray()); + int res = NativeMethods.git_commit_create_from_ids(out commitOid, + repo, + referenceName, + authorHandle, + committerHandle, + null, + message, + ref treeOid, + (UIntPtr)parentPtrs.Count, + parentPtrs.ToArray()); Ensure.ZeroResult(res); @@ -390,27 +421,36 @@ public static ObjectId git_commit_tree_id(GitObjectSafeHandle obj) public static void git_config_add_file_ondisk(ConfigurationSafeHandle config, FilePath path, ConfigurationLevel level) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_config_add_file_ondisk(config, path, (uint)level, true); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_config_add_file_ondisk(config, path, (uint)level, true); + Ensure.ZeroResult(res); } public static bool git_config_delete(ConfigurationSafeHandle config, string name) { - using (ThreadAffinity()) + int res = NativeMethods.git_config_delete_entry(config, name); + + if (res == (int)GitErrorCode.NotFound) { - int res = NativeMethods.git_config_delete_entry(config, name); + return false; + } - if (res == (int)GitErrorCode.NotFound) - { - return false; - } + Ensure.ZeroResult(res); + return true; + } - Ensure.ZeroResult(res); - return true; + const string anyValue = ".*"; + + public static bool git_config_delete_multivar(ConfigurationSafeHandle config, string name) + { + int res = NativeMethods.git_config_delete_multivar(config, name, anyValue); + + if (res == (int)GitErrorCode.NotFound) + { + return false; } + + Ensure.ZeroResult(res); + return true; } public static FilePath git_config_find_global() @@ -433,16 +473,23 @@ public static void git_config_free(IntPtr config) NativeMethods.git_config_free(config); } + public static void git_config_entry_free(IntPtr entry) + { + NativeMethods.git_config_entry_free(entry); + } + public static ConfigurationEntry git_config_get_entry(ConfigurationSafeHandle config, string key) { - GitConfigEntryHandle handle; + GitConfigEntryHandle handle = null; if (!configurationParser.ContainsKey(typeof(T))) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Generic Argument of type '{0}' is not supported.", typeof(T).FullName)); } - using (ThreadAffinity()) + GitConfigEntry entry; + + try { var res = NativeMethods.git_config_get_entry(out handle, config, key); if (res == (int)GitErrorCode.NotFound) @@ -451,115 +498,92 @@ public static ConfigurationEntry git_config_get_entry(ConfigurationSafeHan } Ensure.ZeroResult(res); - } - GitConfigEntry entry = handle.MarshalAsGitConfigEntry(); + entry = handle.MarshalAsGitConfigEntry(); + } + finally + { + handle.SafeDispose(); + } return new ConfigurationEntry(LaxUtf8Marshaler.FromNative(entry.namePtr), - (T)configurationParser[typeof(T)](LaxUtf8Marshaler.FromNative(entry.valuePtr)), - (ConfigurationLevel)entry.level); + (T)configurationParser[typeof(T)](LaxUtf8Marshaler.FromNative(entry.valuePtr)), + (ConfigurationLevel)entry.level); } public static ConfigurationSafeHandle git_config_new() { - using (ThreadAffinity()) - { - ConfigurationSafeHandle handle; - int res = NativeMethods.git_config_new(out handle); - Ensure.ZeroResult(res); + ConfigurationSafeHandle handle; + int res = NativeMethods.git_config_new(out handle); + Ensure.ZeroResult(res); - return handle; - } + return handle; } public static ConfigurationSafeHandle git_config_open_level(ConfigurationSafeHandle parent, ConfigurationLevel level) { - using (ThreadAffinity()) - { - ConfigurationSafeHandle handle; - int res = NativeMethods.git_config_open_level(out handle, parent, (uint)level); + ConfigurationSafeHandle handle; + int res = NativeMethods.git_config_open_level(out handle, parent, (uint)level); - if (res == (int)GitErrorCode.NotFound) - { - return null; - } + if (res == (int)GitErrorCode.NotFound) + { + return null; + } - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return handle; - } + return handle; } public static bool git_config_parse_bool(string value) { - using (ThreadAffinity()) - { - bool outVal; - var res = NativeMethods.git_config_parse_bool(out outVal, value); + bool outVal; + var res = NativeMethods.git_config_parse_bool(out outVal, value); - Ensure.ZeroResult(res); - return outVal; - } + Ensure.ZeroResult(res); + return outVal; } public static int git_config_parse_int32(string value) { - using (ThreadAffinity()) - { - int outVal; - var res = NativeMethods.git_config_parse_int32(out outVal, value); + int outVal; + var res = NativeMethods.git_config_parse_int32(out outVal, value); - Ensure.ZeroResult(res); - return outVal; - } + Ensure.ZeroResult(res); + return outVal; } public static long git_config_parse_int64(string value) { - using (ThreadAffinity()) - { - long outVal; - var res = NativeMethods.git_config_parse_int64(out outVal, value); + long outVal; + var res = NativeMethods.git_config_parse_int64(out outVal, value); - Ensure.ZeroResult(res); - return outVal; - } + Ensure.ZeroResult(res); + return outVal; } public static void git_config_set_bool(ConfigurationSafeHandle config, string name, bool value) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_config_set_bool(config, name, value); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_config_set_bool(config, name, value); + Ensure.ZeroResult(res); } public static void git_config_set_int32(ConfigurationSafeHandle config, string name, int value) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_config_set_int32(config, name, value); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_config_set_int32(config, name, value); + Ensure.ZeroResult(res); } public static void git_config_set_int64(ConfigurationSafeHandle config, string name, long value) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_config_set_int64(config, name, value); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_config_set_int64(config, name, value); + Ensure.ZeroResult(res); } public static void git_config_set_string(ConfigurationSafeHandle config, string name, string value) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_config_set_string(config, name, value); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_config_set_string(config, name, value); + Ensure.ZeroResult(res); } public static ICollection git_config_foreach( @@ -574,19 +598,18 @@ public static IEnumerable> git_config_iterator_glob( string regexp, Func> resultSelector) { - return git_iterator( - (out ConfigurationIteratorSafeHandle iter) => - NativeMethods.git_config_iterator_glob_new(out iter, config, regexp), - (ConfigurationIteratorSafeHandle iter, out SafeHandleBase handle, out int res) => - { - handle = null; + return git_iterator((out ConfigurationIteratorSafeHandle iter) => + NativeMethods.git_config_iterator_glob_new(out iter, config, regexp), + (ConfigurationIteratorSafeHandle iter, out SafeHandleBase handle, out int res) => + { + handle = null; - IntPtr entry; - res = NativeMethods.git_config_next(out entry, iter); - return new { EntryPtr = entry }; - }, - (handle, payload) => resultSelector(payload.EntryPtr) - ); + IntPtr entry; + res = NativeMethods.git_config_next(out entry, iter); + return new { EntryPtr = entry }; + }, + (handle, payload) => + resultSelector(payload.EntryPtr)); } public static void git_config_iterator_free(IntPtr iter) @@ -596,14 +619,20 @@ public static void git_config_iterator_free(IntPtr iter) public static ConfigurationSafeHandle git_config_snapshot(ConfigurationSafeHandle config) { - using (ThreadAffinity()) - { - ConfigurationSafeHandle handle; - int res = NativeMethods.git_config_snapshot(out handle, config); - Ensure.ZeroResult(res); + ConfigurationSafeHandle handle; + int res = NativeMethods.git_config_snapshot(out handle, config); + Ensure.ZeroResult(res); - return handle; - } + return handle; + } + + #endregion + + #region git_cred_ + + public static void git_cred_free(IntPtr cred) + { + NativeMethods.git_cred_free(cred); } #endregion @@ -615,10 +644,8 @@ public static string git_describe_commit( ObjectId committishId, DescribeOptions options) { - Ensure.ArgumentPositiveInt32(options.MinimumCommitIdAbbreviatedSize, - "options.MinimumCommitIdAbbreviatedSize"); + Ensure.ArgumentPositiveInt32(options.MinimumCommitIdAbbreviatedSize, "options.MinimumCommitIdAbbreviatedSize"); - using (ThreadAffinity()) using (var osw = new ObjectSafeWrapper(committishId, repo)) { GitDescribeOptions opts = new GitDescribeOptions @@ -626,7 +653,7 @@ public static string git_describe_commit( Version = 1, DescribeStrategy = options.Strategy, MaxCandidatesTags = 10, - OnlyFollowFirstParent = false, + OnlyFollowFirstParent = options.OnlyFollowFirstParent, ShowCommitOidAsFallback = options.UseCommitIdAsFallback, }; @@ -681,13 +708,19 @@ public static void git_diff_blobs( NativeMethods.git_diff_hunk_cb hunkCallback, NativeMethods.git_diff_line_cb lineCallback) { - using (ThreadAffinity()) using (var osw1 = new ObjectSafeWrapper(oldBlob, repo, true)) using (var osw2 = new ObjectSafeWrapper(newBlob, repo, true)) { - int res = NativeMethods.git_diff_blobs( - osw1.ObjectPtr, null, osw2.ObjectPtr, null, - options, fileCallback, hunkCallback, lineCallback, IntPtr.Zero); + int res = NativeMethods.git_diff_blobs(osw1.ObjectPtr, + null, + osw2.ObjectPtr, + null, + options, + fileCallback, + null, + hunkCallback, + lineCallback, + IntPtr.Zero); Ensure.ZeroResult(res); } @@ -699,11 +732,8 @@ public static void git_diff_foreach( NativeMethods.git_diff_hunk_cb hunkCallback, NativeMethods.git_diff_line_cb lineCallback) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_diff_foreach(diff, fileCallback, hunkCallback, lineCallback, IntPtr.Zero); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_diff_foreach(diff, fileCallback, null, hunkCallback, lineCallback, IntPtr.Zero); + Ensure.ZeroResult(res); } public static DiffSafeHandle git_diff_tree_to_index( @@ -712,7 +742,6 @@ public static DiffSafeHandle git_diff_tree_to_index( ObjectId oldTree, GitDiffOptions options) { - using (ThreadAffinity()) using (var osw = new ObjectSafeWrapper(oldTree, repo, true)) { DiffSafeHandle diff; @@ -730,11 +759,8 @@ public static void git_diff_free(IntPtr diff) public static void git_diff_merge(DiffSafeHandle onto, DiffSafeHandle from) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_diff_merge(onto, from); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_diff_merge(onto, from); + Ensure.ZeroResult(res); } public static DiffSafeHandle git_diff_tree_to_tree( @@ -743,7 +769,6 @@ public static DiffSafeHandle git_diff_tree_to_tree( ObjectId newTree, GitDiffOptions options) { - using (ThreadAffinity()) using (var osw1 = new ObjectSafeWrapper(oldTree, repo, true)) using (var osw2 = new ObjectSafeWrapper(newTree, repo, true)) { @@ -760,14 +785,11 @@ public static DiffSafeHandle git_diff_index_to_workdir( IndexSafeHandle index, GitDiffOptions options) { - using (ThreadAffinity()) - { - DiffSafeHandle diff; - int res = NativeMethods.git_diff_index_to_workdir(out diff, repo, index, options); - Ensure.ZeroResult(res); + DiffSafeHandle diff; + int res = NativeMethods.git_diff_index_to_workdir(out diff, repo, index, options); + Ensure.ZeroResult(res); - return diff; - } + return diff; } public static DiffSafeHandle git_diff_tree_to_workdir( @@ -775,7 +797,6 @@ public static DiffSafeHandle git_diff_tree_to_workdir( ObjectId oldTree, GitDiffOptions options) { - using (ThreadAffinity()) using (var osw = new ObjectSafeWrapper(oldTree, repo, true)) { DiffSafeHandle diff; @@ -788,11 +809,8 @@ public static DiffSafeHandle git_diff_tree_to_workdir( public static void git_diff_find_similar(DiffSafeHandle diff, GitDiffFindOptions options) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_diff_find_similar(diff, options); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_diff_find_similar(diff, options); + Ensure.ZeroResult(res); } public static int git_diff_num_deltas(DiffSafeHandle diff) @@ -802,7 +820,33 @@ public static int git_diff_num_deltas(DiffSafeHandle diff) public static GitDiffDelta git_diff_get_delta(DiffSafeHandle diff, int idx) { - return NativeMethods.git_diff_get_delta(diff, (UIntPtr) idx).MarshalAs(false); + return NativeMethods.git_diff_get_delta(diff, (UIntPtr)idx).MarshalAs(false); + } + + #endregion + + #region git_filter_ + + public static void git_filter_register(string name, IntPtr filterPtr, int priority) + { + int res = NativeMethods.git_filter_register(name, filterPtr, priority); + if (res == (int)GitErrorCode.Exists) + { + throw new EntryExistsException("A filter with the name '{0}' is already registered", name); + } + Ensure.ZeroResult(res); + } + + public static void git_filter_unregister(string name) + { + int res = NativeMethods.git_filter_unregister(name); + Ensure.ZeroResult(res); + } + + public static FilterMode git_filter_source_mode(IntPtr filterSource) + { + var res = NativeMethods.git_filter_source_mode(filterSource); + return (FilterMode)res; } #endregion @@ -819,32 +863,25 @@ public static GitDiffDelta git_diff_get_delta(DiffSafeHandle diff, int idx) GitOid oid1 = first.Id.Oid; GitOid oid2 = second.Id.Oid; - using (ThreadAffinity()) - { - UIntPtr ahead; - UIntPtr behind; + UIntPtr ahead; + UIntPtr behind; - int res = NativeMethods.git_graph_ahead_behind(out ahead, out behind, repo, ref oid1, ref oid2); + int res = NativeMethods.git_graph_ahead_behind(out ahead, out behind, repo, ref oid1, ref oid2); - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return new Tuple((int)ahead, (int)behind); - } + return new Tuple((int)ahead, (int)behind); } public static bool git_graph_descendant_of(RepositorySafeHandle repo, ObjectId commitId, ObjectId ancestorId) { GitOid oid1 = commitId.Oid; GitOid oid2 = ancestorId.Oid; + int res = NativeMethods.git_graph_descendant_of(repo, ref oid1, ref oid2); - using (ThreadAffinity()) - { - int res = NativeMethods.git_graph_descendant_of(repo, ref oid1, ref oid2); - - Ensure.BooleanResult(res); + Ensure.BooleanResult(res); - return (res == 1); - } + return (res == 1); } #endregion @@ -853,32 +890,23 @@ public static bool git_graph_descendant_of(RepositorySafeHandle repo, ObjectId c public static void git_ignore_add_rule(RepositorySafeHandle repo, string rules) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_ignore_add_rule(repo, rules); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_ignore_add_rule(repo, rules); + Ensure.ZeroResult(res); } public static void git_ignore_clear_internal_rules(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_ignore_clear_internal_rules(repo); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_ignore_clear_internal_rules(repo); + Ensure.ZeroResult(res); } public static bool git_ignore_path_is_ignored(RepositorySafeHandle repo, string path) { - using (ThreadAffinity()) - { - int ignored; - int res = NativeMethods.git_ignore_path_is_ignored(out ignored, repo, path); - Ensure.ZeroResult(res); + int ignored; + int res = NativeMethods.git_ignore_path_is_ignored(out ignored, repo, path); + Ensure.ZeroResult(res); - return (ignored != 0); - } + return (ignored != 0); } #endregion @@ -887,31 +915,27 @@ public static bool git_ignore_path_is_ignored(RepositorySafeHandle repo, string public static void git_index_add(IndexSafeHandle index, GitIndexEntry entry) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_index_add(index, entry); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_index_add(index, entry); + Ensure.ZeroResult(res); } public static void git_index_add_bypath(IndexSafeHandle index, FilePath path) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_index_add_bypath(index, path); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_index_add_bypath(index, path); + Ensure.ZeroResult(res); } public static Conflict git_index_conflict_get( IndexSafeHandle index, - Repository repo, FilePath path) { IndexEntrySafeHandle ancestor, ours, theirs; - int res = NativeMethods.git_index_conflict_get( - out ancestor, out ours, out theirs, index, path); + int res = NativeMethods.git_index_conflict_get(out ancestor, + out ours, + out theirs, + index, + path); if (res == (int)GitErrorCode.NotFound) { @@ -920,20 +944,52 @@ public static Conflict git_index_conflict_get( Ensure.ZeroResult(res); - return new Conflict( - IndexEntry.BuildFromPtr(ancestor), - IndexEntry.BuildFromPtr(ours), - IndexEntry.BuildFromPtr(theirs)); + return new Conflict(IndexEntry.BuildFromPtr(ancestor), + IndexEntry.BuildFromPtr(ours), + IndexEntry.BuildFromPtr(theirs)); } - public static int git_index_entrycount(IndexSafeHandle index) + public static ConflictIteratorSafeHandle git_index_conflict_iterator_new(IndexSafeHandle index) + { + ConflictIteratorSafeHandle iter; + int res = NativeMethods.git_index_conflict_iterator_new(out iter, index); + Ensure.ZeroResult(res); + + return iter; + } + + public static Conflict git_index_conflict_next(ConflictIteratorSafeHandle iterator) { - UIntPtr count = NativeMethods.git_index_entrycount(index); - if ((long)count > int.MaxValue) + IndexEntrySafeHandle ancestor, ours, theirs; + + int res = NativeMethods.git_index_conflict_next(out ancestor, out ours, out theirs, iterator); + + if (res == (int)GitErrorCode.IterOver) + { + return null; + } + + Ensure.ZeroResult(res); + + using (ancestor) + using (ours) + using (theirs) { - throw new LibGit2SharpException("Index entry count exceeds size of int"); + return new Conflict(IndexEntry.BuildFromPtr(ancestor), + IndexEntry.BuildFromPtr(ours), + IndexEntry.BuildFromPtr(theirs)); } - return (int)count; + } + + public static void git_index_conflict_iterator_free(IntPtr iterator) + { + NativeMethods.git_index_conflict_iterator_free(iterator); + } + + public static int git_index_entrycount(IndexSafeHandle index) + { + return NativeMethods.git_index_entrycount(index) + .ConvertToInt(); } public static StageLevel git_index_entry_stage(IndexEntrySafeHandle index) @@ -968,12 +1024,8 @@ public static bool git_index_has_conflicts(IndexSafeHandle index) public static int git_index_name_entrycount(IndexSafeHandle index) { - uint count = NativeMethods.git_index_name_entrycount(index); - if ((long)count > int.MaxValue) - { - throw new LibGit2SharpException("Index name entry count exceeds size of int"); - } - return (int)count; + return NativeMethods.git_index_name_entrycount(index) + .ConvertToInt(); } public static IndexNameEntrySafeHandle git_index_name_get_byindex(IndexSafeHandle index, UIntPtr n) @@ -983,42 +1035,29 @@ public static IndexNameEntrySafeHandle git_index_name_get_byindex(IndexSafeHandl public static IndexSafeHandle git_index_open(FilePath indexpath) { - using (ThreadAffinity()) - { - IndexSafeHandle handle; - int res = NativeMethods.git_index_open(out handle, indexpath); - Ensure.ZeroResult(res); + IndexSafeHandle handle; + int res = NativeMethods.git_index_open(out handle, indexpath); + Ensure.ZeroResult(res); - return handle; - } + return handle; } public static void git_index_read(IndexSafeHandle index) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_index_read(index, false); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_index_read(index, false); + Ensure.ZeroResult(res); } public static void git_index_remove_bypath(IndexSafeHandle index, FilePath path) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_index_remove_bypath(index, path); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_index_remove_bypath(index, path); + Ensure.ZeroResult(res); } public static int git_index_reuc_entrycount(IndexSafeHandle index) { - uint count = NativeMethods.git_index_reuc_entrycount(index); - if ((long)count > int.MaxValue) - { - throw new LibGit2SharpException("Index REUC entry count exceeds size of int"); - } - return (int)count; + return NativeMethods.git_index_reuc_entrycount(index) + .ConvertToInt(); } public static IndexReucEntrySafeHandle git_index_reuc_get_byindex(IndexSafeHandle index, UIntPtr n) @@ -1033,123 +1072,133 @@ public static IndexReucEntrySafeHandle git_index_reuc_get_bypath(IndexSafeHandle public static void git_index_write(IndexSafeHandle index) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_index_write(index); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_index_write(index); + Ensure.ZeroResult(res); } - public static ObjectId git_tree_create_fromindex(Index index) + public static ObjectId git_index_write_tree(IndexSafeHandle index) { - using (ThreadAffinity()) - { - GitOid treeOid; - int res = NativeMethods.git_index_write_tree(out treeOid, index.Handle); - Ensure.ZeroResult(res); + GitOid treeOid; + int res = NativeMethods.git_index_write_tree(out treeOid, index); + Ensure.ZeroResult(res); - return treeOid; - } + return treeOid; + } + + public static ObjectId git_index_write_tree_to(IndexSafeHandle index, RepositorySafeHandle repo) + { + GitOid treeOid; + int res = NativeMethods.git_index_write_tree_to(out treeOid, index, repo); + Ensure.ZeroResult(res); + + return treeOid; } public static void git_index_read_fromtree(Index index, GitObjectSafeHandle tree) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_index_read_tree(index.Handle, tree); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_index_read_tree(index.Handle, tree); + Ensure.ZeroResult(res); } public static void git_index_clear(Index index) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_index_clear(index.Handle); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_index_clear(index.Handle); + Ensure.ZeroResult(res); } #endregion #region git_merge_ - public static ObjectId git_merge_base_many(RepositorySafeHandle repo, GitOid[] commitIds) + public static IndexSafeHandle git_merge_commits(RepositorySafeHandle repo, GitObjectSafeHandle ourCommit, GitObjectSafeHandle theirCommit, GitMergeOpts opts, out bool earlyStop) { - using (ThreadAffinity()) + IndexSafeHandle index; + int res = NativeMethods.git_merge_commits(out index, repo, ourCommit, theirCommit, ref opts); + if (res == (int)GitErrorCode.MergeConflict) + { + earlyStop = true; + } + else { - GitOid ret; - int res = NativeMethods.git_merge_base_many(out ret, repo, commitIds.Length, commitIds); + earlyStop = false; + Ensure.ZeroResult(res); + } - if (res == (int)GitErrorCode.NotFound) - { - return null; - } + return index; + } - Ensure.ZeroResult(res); + public static ObjectId git_merge_base_many(RepositorySafeHandle repo, GitOid[] commitIds) + { + GitOid ret; + int res = NativeMethods.git_merge_base_many(out ret, repo, commitIds.Length, commitIds); - return ret; + if (res == (int)GitErrorCode.NotFound) + { + return null; } + + Ensure.ZeroResult(res); + + return ret; } public static ObjectId git_merge_base_octopus(RepositorySafeHandle repo, GitOid[] commitIds) { - using (ThreadAffinity()) - { - GitOid ret; - int res = NativeMethods.git_merge_base_octopus(out ret, repo, commitIds.Length, commitIds); + GitOid ret; + int res = NativeMethods.git_merge_base_octopus(out ret, repo, commitIds.Length, commitIds); - if (res == (int)GitErrorCode.NotFound) - { - return null; - } + if (res == (int)GitErrorCode.NotFound) + { + return null; + } - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return ret; - } + return ret; } public static GitAnnotatedCommitHandle git_annotated_commit_from_fetchhead(RepositorySafeHandle repo, string branchName, string remoteUrl, GitOid oid) { - using (ThreadAffinity()) - { - GitAnnotatedCommitHandle merge_head; + GitAnnotatedCommitHandle merge_head; - int res = NativeMethods.git_annotated_commit_from_fetchhead(out merge_head, repo, branchName, remoteUrl, ref oid); + int res = NativeMethods.git_annotated_commit_from_fetchhead(out merge_head, repo, branchName, remoteUrl, ref oid); - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return merge_head; - } + return merge_head; } public static GitAnnotatedCommitHandle git_annotated_commit_lookup(RepositorySafeHandle repo, GitOid oid) { - using (ThreadAffinity()) - { - GitAnnotatedCommitHandle their_head; + GitAnnotatedCommitHandle their_head; - int res = NativeMethods.git_annotated_commit_lookup(out their_head, repo, ref oid); + int res = NativeMethods.git_annotated_commit_lookup(out their_head, repo, ref oid); - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return their_head; - } + return their_head; } public static GitAnnotatedCommitHandle git_annotated_commit_from_ref(RepositorySafeHandle repo, ReferenceSafeHandle reference) { - using (ThreadAffinity()) - { - GitAnnotatedCommitHandle their_head; + GitAnnotatedCommitHandle their_head; - int res = NativeMethods.git_annotated_commit_from_ref(out their_head, repo, reference); + int res = NativeMethods.git_annotated_commit_from_ref(out their_head, repo, reference); - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return their_head; - } + return their_head; + } + + public static GitAnnotatedCommitHandle git_annotated_commit_from_revspec(RepositorySafeHandle repo, string revspec) + { + GitAnnotatedCommitHandle their_head; + + int res = NativeMethods.git_annotated_commit_from_revspec(out their_head, repo, revspec); + + Ensure.ZeroResult(res); + + return their_head; } public static ObjectId git_annotated_commit_id(GitAnnotatedCommitHandle mergeHead) @@ -1157,19 +1206,23 @@ public static ObjectId git_annotated_commit_id(GitAnnotatedCommitHandle mergeHea return NativeMethods.git_annotated_commit_id(mergeHead).MarshalAsObjectId(); } - public static void git_merge(RepositorySafeHandle repo, GitAnnotatedCommitHandle[] heads, GitMergeOpts mergeOptions, GitCheckoutOpts checkoutOptions) + public static void git_merge(RepositorySafeHandle repo, GitAnnotatedCommitHandle[] heads, GitMergeOpts mergeOptions, GitCheckoutOpts checkoutOptions, out bool earlyStop) { - using (ThreadAffinity()) - { - IntPtr[] their_heads = heads.Select(head => head.DangerousGetHandle()).ToArray(); + IntPtr[] their_heads = heads.Select(head => head.DangerousGetHandle()).ToArray(); - int res = NativeMethods.git_merge( - repo, - their_heads, - (UIntPtr)their_heads.Length, - ref mergeOptions, - ref checkoutOptions); + int res = NativeMethods.git_merge(repo, + their_heads, + (UIntPtr)their_heads.Length, + ref mergeOptions, + ref checkoutOptions); + if (res == (int)GitErrorCode.MergeConflict) + { + earlyStop = true; + } + else + { + earlyStop = false; Ensure.ZeroResult(res); } } @@ -1180,19 +1233,15 @@ public static void git_merge_analysis( out GitMergeAnalysis analysis_out, out GitMergePreference preference_out) { - using (ThreadAffinity()) - { - IntPtr[] their_heads = heads.Select(head => head.DangerousGetHandle()).ToArray(); + IntPtr[] their_heads = heads.Select(head => head.DangerousGetHandle()).ToArray(); - int res = NativeMethods.git_merge_analysis( - out analysis_out, - out preference_out, - repo, - their_heads, - their_heads.Length); + int res = NativeMethods.git_merge_analysis(out analysis_out, + out preference_out, + repo, + their_heads, + their_heads.Length); - Ensure.ZeroResult(res); - } + Ensure.ZeroResult(res); } public static void git_annotated_commit_free(IntPtr handle) @@ -1217,7 +1266,6 @@ public static string git_message_prettify(string message, char? commentChar) throw new InvalidOperationException("Only single byte characters are allowed as commentary characters in a message (eg. '#')."); } - using (ThreadAffinity()) using (var buf = new GitBuf()) { int res = NativeMethods.git_message_prettify(buf, message, false, (sbyte)comment); @@ -1240,7 +1288,6 @@ public static ObjectId git_note_create( string note, bool force) { - using (ThreadAffinity()) using (SignatureSafeHandle authorHandle = author.BuildHandle()) using (SignatureSafeHandle committerHandle = committer.BuildHandle()) { @@ -1256,20 +1303,21 @@ public static ObjectId git_note_create( public static string git_note_default_ref(RepositorySafeHandle repo) { - using (ThreadAffinity()) + using (var buf = new GitBuf()) { - string notes_ref; - int res = NativeMethods.git_note_default_ref(out notes_ref, repo); + int res = NativeMethods.git_note_default_ref(buf, repo); Ensure.ZeroResult(res); - return notes_ref; + return LaxUtf8Marshaler.FromNative(buf.ptr); } } public static ICollection git_note_foreach(RepositorySafeHandle repo, string notes_ref, Func resultSelector) { - return git_foreach(resultSelector, c => NativeMethods.git_note_foreach(repo, notes_ref, - (ref GitOid x, ref GitOid y, IntPtr p) => c(x, y, p), IntPtr.Zero)); + return git_foreach(resultSelector, c => NativeMethods.git_note_foreach(repo, + notes_ref, + (ref GitOid x, ref GitOid y, IntPtr p) => c(x, y, p), + IntPtr.Zero)); } public static void git_note_free(IntPtr note) @@ -1289,27 +1337,23 @@ public static ObjectId git_note_id(NoteSafeHandle note) public static NoteSafeHandle git_note_read(RepositorySafeHandle repo, string notes_ref, ObjectId id) { - using (ThreadAffinity()) - { - GitOid oid = id.Oid; - NoteSafeHandle note; + GitOid oid = id.Oid; + NoteSafeHandle note; - int res = NativeMethods.git_note_read(out note, repo, notes_ref, ref oid); + int res = NativeMethods.git_note_read(out note, repo, notes_ref, ref oid); - if (res == (int)GitErrorCode.NotFound) - { - return null; - } + if (res == (int)GitErrorCode.NotFound) + { + return null; + } - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return note; - } + return note; } public static void git_note_remove(RepositorySafeHandle repo, string notes_ref, Signature author, Signature committer, ObjectId targetId) { - using (ThreadAffinity()) using (SignatureSafeHandle authorHandle = author.BuildHandle()) using (SignatureSafeHandle committerHandle = committer.BuildHandle()) { @@ -1342,52 +1386,46 @@ public static void git_object_free(IntPtr obj) public static GitObjectSafeHandle git_object_lookup(RepositorySafeHandle repo, ObjectId id, GitObjectType type) { - using (ThreadAffinity()) - { - GitObjectSafeHandle handle; - GitOid oid = id.Oid; - - int res = NativeMethods.git_object_lookup(out handle, repo, ref oid, type); - switch (res) - { - case (int)GitErrorCode.NotFound: - return null; + GitObjectSafeHandle handle; + GitOid oid = id.Oid; - default: - Ensure.ZeroResult(res); - break; - } + int res = NativeMethods.git_object_lookup(out handle, repo, ref oid, type); + switch (res) + { + case (int)GitErrorCode.NotFound: + return null; - return handle; + default: + Ensure.ZeroResult(res); + break; } + + return handle; } public static GitObjectSafeHandle git_object_peel(RepositorySafeHandle repo, ObjectId id, GitObjectType type, bool throwsIfCanNotPeel) { - using (ThreadAffinity()) - { - GitObjectSafeHandle peeled; - int res; - - using (var obj = new ObjectSafeWrapper(id, repo)) - { - res = NativeMethods.git_object_peel(out peeled, obj.ObjectPtr, type); - } + GitObjectSafeHandle peeled; + int res; - if (!throwsIfCanNotPeel && - (res == (int)GitErrorCode.NotFound || res == (int)GitErrorCode.Ambiguous || res == (int)GitErrorCode.InvalidSpecification || res == (int)GitErrorCode.Peel)) - { - return null; - } + using (var obj = new ObjectSafeWrapper(id, repo)) + { + res = NativeMethods.git_object_peel(out peeled, obj.ObjectPtr, type); + } - Ensure.ZeroResult(res); - return peeled; + if (!throwsIfCanNotPeel && + (res == (int)GitErrorCode.NotFound || res == (int)GitErrorCode.Ambiguous || + res == (int)GitErrorCode.InvalidSpecification || res == (int)GitErrorCode.Peel)) + { + return null; } + + Ensure.ZeroResult(res); + return peeled; } public static string git_object_short_id(RepositorySafeHandle repo, ObjectId id) { - using (ThreadAffinity()) using (var obj = new ObjectSafeWrapper(id, repo)) using (var buf = new GitBuf()) { @@ -1418,10 +1456,10 @@ public static IntPtr git_odb_backend_malloc(IntPtr backend, UIntPtr len) if (IntPtr.Zero == toReturn) { - throw new LibGit2SharpException(String.Format(CultureInfo.InvariantCulture, - "Unable to allocate {0} bytes; out of memory", - len), - GitErrorCode.Error, GitErrorCategory.NoMemory); + throw new LibGit2SharpException("Unable to allocate {0} bytes; out of memory", + len, + GitErrorCode.Error, + GitErrorCategory.NoMemory); } return toReturn; @@ -1429,54 +1467,43 @@ public static IntPtr git_odb_backend_malloc(IntPtr backend, UIntPtr len) public static bool git_odb_exists(ObjectDatabaseSafeHandle odb, ObjectId id) { - using (ThreadAffinity()) - { - GitOid oid = id.Oid; + GitOid oid = id.Oid; - int res = NativeMethods.git_odb_exists(odb, ref oid); - Ensure.BooleanResult(res); + int res = NativeMethods.git_odb_exists(odb, ref oid); + Ensure.BooleanResult(res); - return (res == 1); - } + return (res == 1); } public static GitObjectMetadata git_odb_read_header(ObjectDatabaseSafeHandle odb, ObjectId id) { - using (ThreadAffinity()) - { - GitOid oid = id.Oid; - UIntPtr length; - GitObjectType objectType; + GitOid oid = id.Oid; + UIntPtr length; + GitObjectType objectType; - int res = NativeMethods.git_odb_read_header(out length, out objectType, odb, ref oid); - Ensure.ZeroResult(res); + int res = NativeMethods.git_odb_read_header(out length, out objectType, odb, ref oid); + Ensure.ZeroResult(res); - return new GitObjectMetadata((long)length, objectType); - } + return new GitObjectMetadata((long)length, objectType); } public static ICollection git_odb_foreach( ObjectDatabaseSafeHandle odb, Func resultSelector) { - return git_foreach( - resultSelector, - c => NativeMethods.git_odb_foreach( - odb, - (x, p) => c(x, p), - IntPtr.Zero)); + return git_foreach(resultSelector, + c => NativeMethods.git_odb_foreach(odb, + (x, p) => c(x, p), + IntPtr.Zero)); } - public static OdbStreamSafeHandle git_odb_open_wstream(ObjectDatabaseSafeHandle odb, UIntPtr size, GitObjectType type) + public static OdbStreamSafeHandle git_odb_open_wstream(ObjectDatabaseSafeHandle odb, long size, GitObjectType type) { - using (ThreadAffinity()) - { - OdbStreamSafeHandle stream; - int res = NativeMethods.git_odb_open_wstream(out stream, odb, size, type); - Ensure.ZeroResult(res); + OdbStreamSafeHandle stream; + int res = NativeMethods.git_odb_open_wstream(out stream, odb, size, type); + Ensure.ZeroResult(res); - return stream; - } + return stream; } public static void git_odb_free(IntPtr odb) @@ -1486,31 +1513,25 @@ public static void git_odb_free(IntPtr odb) public static void git_odb_stream_write(OdbStreamSafeHandle stream, byte[] data, int len) { - using (ThreadAffinity()) + int res; + unsafe { - int res; - unsafe + fixed (byte* p = data) { - fixed (byte *p = data) - { - res = NativeMethods.git_odb_stream_write(stream, (IntPtr) p, (UIntPtr) len); - } + res = NativeMethods.git_odb_stream_write(stream, (IntPtr)p, (UIntPtr)len); } - - Ensure.ZeroResult(res); } + + Ensure.ZeroResult(res); } public static ObjectId git_odb_stream_finalize_write(OdbStreamSafeHandle stream) { - using (ThreadAffinity()) - { - GitOid id; - int res = NativeMethods.git_odb_stream_finalize_write(out id, stream); - Ensure.ZeroResult(res); + GitOid id; + int res = NativeMethods.git_odb_stream_finalize_write(out id, stream); + Ensure.ZeroResult(res); - return id; - } + return id; } public static void git_odb_stream_free(IntPtr stream) @@ -1529,235 +1550,442 @@ public static void git_patch_free(IntPtr patch) public static PatchSafeHandle git_patch_from_diff(DiffSafeHandle diff, int idx) { - using (ThreadAffinity()) - { - PatchSafeHandle handle; - int res = NativeMethods.git_patch_from_diff(out handle, diff, (UIntPtr) idx); - Ensure.ZeroResult(res); - return handle; - } + PatchSafeHandle handle; + int res = NativeMethods.git_patch_from_diff(out handle, diff, (UIntPtr)idx); + Ensure.ZeroResult(res); + return handle; } public static void git_patch_print(PatchSafeHandle patch, NativeMethods.git_diff_line_cb printCallback) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_patch_print(patch, printCallback, IntPtr.Zero); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_patch_print(patch, printCallback, IntPtr.Zero); + Ensure.ZeroResult(res); } public static Tuple git_patch_line_stats(PatchSafeHandle patch) { - using (ThreadAffinity()) - { - UIntPtr ctx, add, del; - int res = NativeMethods.git_patch_line_stats(out ctx, out add, out del, patch); - Ensure.ZeroResult(res); - return new Tuple((int)add, (int)del); - } + UIntPtr ctx, add, del; + int res = NativeMethods.git_patch_line_stats(out ctx, out add, out del, patch); + Ensure.ZeroResult(res); + return new Tuple((int)add, (int)del); } #endregion - #region git_reference_ + #region git_packbuilder_ - public static ReferenceSafeHandle git_reference_create(RepositorySafeHandle repo, string name, ObjectId targetId, bool allowOverwrite, - Signature signature, string logMessage) + public static void git_packbuilder_free(IntPtr packbuilder) { - using (ThreadAffinity()) - using (var sigHandle = signature.BuildHandle()) - { - GitOid oid = targetId.Oid; - ReferenceSafeHandle handle; - - int res = NativeMethods.git_reference_create(out handle, repo, name, ref oid, allowOverwrite, sigHandle, logMessage); - Ensure.ZeroResult(res); - - return handle; - } + NativeMethods.git_packbuilder_free(packbuilder); } - public static ReferenceSafeHandle git_reference_symbolic_create(RepositorySafeHandle repo, string name, string target, bool allowOverwrite, - Signature signature, string logMessage) + public static PackBuilderSafeHandle git_packbuilder_new(RepositorySafeHandle repo) { - using (ThreadAffinity()) - using (var sigHandle = signature.BuildHandle()) - { - ReferenceSafeHandle handle; - int res = NativeMethods.git_reference_symbolic_create(out handle, repo, name, target, allowOverwrite, sigHandle, logMessage); - Ensure.ZeroResult(res); + PackBuilderSafeHandle handle; - return handle; - } - } + int res = NativeMethods.git_packbuilder_new(out handle, repo); + Ensure.ZeroResult(res); - public static ICollection git_reference_foreach_glob( - RepositorySafeHandle repo, - string glob, - Func resultSelector) - { - return git_foreach(resultSelector, c => NativeMethods.git_reference_foreach_glob(repo, glob, (x, p) => c(x, p), IntPtr.Zero)); + return handle; } - public static void git_reference_free(IntPtr reference) + public static void git_packbuilder_insert(PackBuilderSafeHandle packbuilder, ObjectId targetId, string name) { - NativeMethods.git_reference_free(reference); + GitOid oid = targetId.Oid; + + int res = NativeMethods.git_packbuilder_insert(packbuilder, ref oid, name); + Ensure.ZeroResult(res); } - public static bool git_reference_is_valid_name(string refname) + internal static void git_packbuilder_insert_commit(PackBuilderSafeHandle packbuilder, ObjectId targetId) { - int res = NativeMethods.git_reference_is_valid_name(refname); - Ensure.BooleanResult(res); + GitOid oid = targetId.Oid; - return (res == 1); + int res = NativeMethods.git_packbuilder_insert_commit(packbuilder, ref oid); + Ensure.ZeroResult(res); } - public static IList git_reference_list(RepositorySafeHandle repo) + internal static void git_packbuilder_insert_tree(PackBuilderSafeHandle packbuilder, ObjectId targetId) { - using (ThreadAffinity()) - { - var array = new GitStrArrayNative(); - - try - { - int res = NativeMethods.git_reference_list(out array.Array, repo); - Ensure.ZeroResult(res); + GitOid oid = targetId.Oid; - return array.ReadStrings(); - } - finally - { - array.Dispose(); - } - } + int res = NativeMethods.git_packbuilder_insert_tree(packbuilder, ref oid); + Ensure.ZeroResult(res); } - public static ReferenceSafeHandle git_reference_lookup(RepositorySafeHandle repo, string name, bool shouldThrowIfNotFound) + public static void git_packbuilder_insert_recur(PackBuilderSafeHandle packbuilder, ObjectId targetId, string name) { - using (ThreadAffinity()) - { - ReferenceSafeHandle handle; - int res = NativeMethods.git_reference_lookup(out handle, repo, name); - - if (!shouldThrowIfNotFound && res == (int)GitErrorCode.NotFound) - { - return null; - } - - Ensure.ZeroResult(res); + GitOid oid = targetId.Oid; - return handle; - } + int res = NativeMethods.git_packbuilder_insert_recur(packbuilder, ref oid, name); + Ensure.ZeroResult(res); } - public static string git_reference_name(ReferenceSafeHandle reference) + public static uint git_packbuilder_set_threads(PackBuilderSafeHandle packbuilder, uint numThreads) { - return NativeMethods.git_reference_name(reference); + return NativeMethods.git_packbuilder_set_threads(packbuilder, numThreads); } - public static void git_reference_remove(RepositorySafeHandle repo, string name) + public static void git_packbuilder_write(PackBuilderSafeHandle packbuilder, FilePath path) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_reference_remove(repo, name); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_packbuilder_write(packbuilder, path, 0, IntPtr.Zero, IntPtr.Zero); + Ensure.ZeroResult(res); } - public static ObjectId git_reference_target(ReferenceSafeHandle reference) + public static uint git_packbuilder_object_count(PackBuilderSafeHandle packbuilder) { - return NativeMethods.git_reference_target(reference).MarshalAsObjectId(); + return NativeMethods.git_packbuilder_object_count(packbuilder); } - public static ReferenceSafeHandle git_reference_rename(ReferenceSafeHandle reference, string newName, bool allowOverwrite, - Signature signature, string logMessage) + public static uint git_packbuilder_written(PackBuilderSafeHandle packbuilder) { - using (ThreadAffinity()) - using (var sigHandle = signature.BuildHandle()) - { - ReferenceSafeHandle ref_out; - - int res = NativeMethods.git_reference_rename(out ref_out, reference, newName, allowOverwrite, sigHandle, logMessage); - Ensure.ZeroResult(res); - - return ref_out; - } + return NativeMethods.git_packbuilder_written(packbuilder); } + #endregion - public static ReferenceSafeHandle git_reference_set_target(ReferenceSafeHandle reference, ObjectId id, Signature signature, string logMessage) + #region git_rebase + + public static RebaseSafeHandle git_rebase_init( + RepositorySafeHandle repo, + GitAnnotatedCommitHandle branch, + GitAnnotatedCommitHandle upstream, + GitAnnotatedCommitHandle onto, + GitRebaseOptions options) { - using (ThreadAffinity()) - using (SignatureSafeHandle sigHandle = signature.BuildHandle()) - { - GitOid oid = id.Oid; - ReferenceSafeHandle ref_out; + RebaseSafeHandle rebase = null; - int res = NativeMethods.git_reference_set_target(out ref_out, reference, ref oid, sigHandle, logMessage); - Ensure.ZeroResult(res); + int result = NativeMethods.git_rebase_init(out rebase, repo, branch, upstream, onto, options); + Ensure.ZeroResult(result); - return ref_out; - } + return rebase; } - public static ReferenceSafeHandle git_reference_symbolic_set_target(ReferenceSafeHandle reference, string target, Signature signature, string logMessage) + public static RebaseSafeHandle git_rebase_open(RepositorySafeHandle repo, GitRebaseOptions options) { - using (ThreadAffinity()) - using (SignatureSafeHandle sigHandle = signature.BuildHandle()) - { - ReferenceSafeHandle ref_out; + RebaseSafeHandle rebase = null; - int res = NativeMethods.git_reference_symbolic_set_target(out ref_out, reference, target, sigHandle, logMessage); - Ensure.ZeroResult(res); + int result = NativeMethods.git_rebase_open(out rebase, repo, options); + Ensure.ZeroResult(result); - return ref_out; - } + return rebase; } - public static string git_reference_symbolic_target(ReferenceSafeHandle reference) + public static long git_rebase_operation_entrycount(RebaseSafeHandle rebase) { - return NativeMethods.git_reference_symbolic_target(reference); + return NativeMethods.git_rebase_operation_entrycount(rebase).ConvertToLong(); } - public static GitReferenceType git_reference_type(ReferenceSafeHandle reference) + public static long git_rebase_operation_current(RebaseSafeHandle rebase) { - return NativeMethods.git_reference_type(reference); + UIntPtr result = NativeMethods.git_rebase_operation_current(rebase); + + if (result == GIT_REBASE_NO_OPERATION) + { + return RebaseNoOperation; + } + else + { + return result.ConvertToLong(); + } } - public static void git_reference_ensure_log(RepositorySafeHandle repo, string refname) + /// + /// The value from the native layer indicating that no rebase operation is in progress. + /// + private static UIntPtr GIT_REBASE_NO_OPERATION { - using (ThreadAffinity()) + get { - int res = NativeMethods.git_reference_ensure_log(repo, refname); - Ensure.ZeroResult(res); + return UIntPtr.Size == 4 ? new UIntPtr(uint.MaxValue) : new UIntPtr(ulong.MaxValue); } } - #endregion - - #region git_reflog_ + public const long RebaseNoOperation = -1; - public static void git_reflog_free(IntPtr reflog) + public static GitRebaseOperation git_rebase_operation_byindex( + RebaseSafeHandle rebase, + long index) { - NativeMethods.git_reflog_free(reflog); + Debug.Assert(index >= 0); + IntPtr ptr = NativeMethods.git_rebase_operation_byindex(rebase, ((UIntPtr)index)); + GitRebaseOperation operation = ptr.MarshalAs(); + + return operation; } - public static ReflogSafeHandle git_reflog_read(RepositorySafeHandle repo, string canonicalName) + /// + /// Returns null when finished. + /// + /// + /// + public static GitRebaseOperation git_rebase_next(RebaseSafeHandle rebase) { - using (ThreadAffinity()) + GitRebaseOperation operation = null; + IntPtr ptr; + int result = NativeMethods.git_rebase_next(out ptr, rebase); + if (result == (int)GitErrorCode.IterOver) { - ReflogSafeHandle reflog_out; + return null; + } + Ensure.ZeroResult(result); - int res = NativeMethods.git_reflog_read(out reflog_out, repo, canonicalName); - Ensure.ZeroResult(res); + // If successsful, then marshal native struct to managed struct. + operation = ptr.MarshalAs(); - return reflog_out; - } + return operation; } - public static int git_reflog_entrycount(ReflogSafeHandle reflog) + public static GitRebaseCommitResult git_rebase_commit( + RebaseSafeHandle rebase, + Identity author, + Identity committer) { - return (int)NativeMethods.git_reflog_entrycount(reflog); + Ensure.ArgumentNotNull(rebase, "rebase"); + Ensure.ArgumentNotNull(committer, "committer"); + + using (SignatureSafeHandle committerHandle = committer.BuildNowSignatureHandle()) + using (SignatureSafeHandle authorHandle = author.SafeBuildNowSignatureHandle()) + { + GitRebaseCommitResult commitResult = new GitRebaseCommitResult(); + + int result = NativeMethods.git_rebase_commit(ref commitResult.CommitId, rebase, authorHandle, committerHandle, IntPtr.Zero, IntPtr.Zero); + + if (result == (int)GitErrorCode.Applied) + { + commitResult.CommitId = GitOid.Empty; + commitResult.WasPatchAlreadyApplied = true; + } + else + { + Ensure.ZeroResult(result); + } + + return commitResult; + } + } + + /// + /// Struct to report the result of calling git_rebase_commit. + /// + public struct GitRebaseCommitResult + { + /// + /// The ID of the commit that was generated, if any + /// + public GitOid CommitId; + + /// + /// bool to indicate if the patch was already applied. + /// If Patch was already applied, then CommitId will be empty (all zeros). + /// + public bool WasPatchAlreadyApplied; + } + + public static void git_rebase_abort( + RebaseSafeHandle rebase) + { + Ensure.ArgumentNotNull(rebase, "rebase"); + + int result = NativeMethods.git_rebase_abort(rebase); + Ensure.ZeroResult(result); + } + + public static void git_rebase_finish( + RebaseSafeHandle rebase, + Identity committer) + { + Ensure.ArgumentNotNull(rebase, "rebase"); + Ensure.ArgumentNotNull(committer, "committer"); + + using (var signatureHandle = committer.BuildNowSignatureHandle()) + { + int result = NativeMethods.git_rebase_finish(rebase, signatureHandle); + Ensure.ZeroResult(result); + } + } + + public static void git_rebase_free(IntPtr rebase) + { + NativeMethods.git_rebase_free(rebase); + } + + #endregion + + #region git_reference_ + + public static ReferenceSafeHandle git_reference_create( + RepositorySafeHandle repo, + string name, + ObjectId targetId, + bool allowOverwrite, + string logMessage) + { + GitOid oid = targetId.Oid; + ReferenceSafeHandle handle; + + int res = NativeMethods.git_reference_create(out handle, repo, name, ref oid, allowOverwrite, logMessage); + Ensure.ZeroResult(res); + + return handle; + } + + public static ReferenceSafeHandle git_reference_symbolic_create( + RepositorySafeHandle repo, + string name, + string target, + bool allowOverwrite, + string logMessage) + { + ReferenceSafeHandle handle; + int res = NativeMethods.git_reference_symbolic_create(out handle, repo, name, target, allowOverwrite, + logMessage); + Ensure.ZeroResult(res); + + return handle; + } + + public static ICollection git_reference_foreach_glob( + RepositorySafeHandle repo, + string glob, + Func resultSelector) + { + return git_foreach(resultSelector, c => NativeMethods.git_reference_foreach_glob(repo, glob, (x, p) => c(x, p), IntPtr.Zero)); + } + + public static void git_reference_free(IntPtr reference) + { + NativeMethods.git_reference_free(reference); + } + + public static bool git_reference_is_valid_name(string refname) + { + int res = NativeMethods.git_reference_is_valid_name(refname); + Ensure.BooleanResult(res); + + return (res == 1); + } + + public static IList git_reference_list(RepositorySafeHandle repo) + { + var array = new GitStrArrayNative(); + + try + { + int res = NativeMethods.git_reference_list(out array.Array, repo); + Ensure.ZeroResult(res); + + return array.ReadStrings(); + } + finally + { + array.Dispose(); + } + } + + public static ReferenceSafeHandle git_reference_lookup(RepositorySafeHandle repo, string name, bool shouldThrowIfNotFound) + { + ReferenceSafeHandle handle; + int res = NativeMethods.git_reference_lookup(out handle, repo, name); + + if (!shouldThrowIfNotFound && res == (int)GitErrorCode.NotFound) + { + return null; + } + + Ensure.ZeroResult(res); + + return handle; + } + + public static string git_reference_name(ReferenceSafeHandle reference) + { + return NativeMethods.git_reference_name(reference); + } + + public static void git_reference_remove(RepositorySafeHandle repo, string name) + { + int res = NativeMethods.git_reference_remove(repo, name); + Ensure.ZeroResult(res); + } + + public static ObjectId git_reference_target(ReferenceSafeHandle reference) + { + return NativeMethods.git_reference_target(reference).MarshalAsObjectId(); + } + + public static ReferenceSafeHandle git_reference_rename( + ReferenceSafeHandle reference, + string newName, + bool allowOverwrite, + string logMessage) + { + ReferenceSafeHandle ref_out; + + int res = NativeMethods.git_reference_rename(out ref_out, reference, newName, allowOverwrite, logMessage); + Ensure.ZeroResult(res); + + return ref_out; + } + + public static ReferenceSafeHandle git_reference_set_target(ReferenceSafeHandle reference, ObjectId id, string logMessage) + { + GitOid oid = id.Oid; + ReferenceSafeHandle ref_out; + + int res = NativeMethods.git_reference_set_target(out ref_out, reference, ref oid, logMessage); + Ensure.ZeroResult(res); + + return ref_out; + } + + public static ReferenceSafeHandle git_reference_symbolic_set_target(ReferenceSafeHandle reference, string target, string logMessage) + { + ReferenceSafeHandle ref_out; + + int res = NativeMethods.git_reference_symbolic_set_target(out ref_out, reference, target, logMessage); + Ensure.ZeroResult(res); + + return ref_out; + } + + public static string git_reference_symbolic_target(ReferenceSafeHandle reference) + { + return NativeMethods.git_reference_symbolic_target(reference); + } + + public static GitReferenceType git_reference_type(ReferenceSafeHandle reference) + { + return NativeMethods.git_reference_type(reference); + } + + public static void git_reference_ensure_log(RepositorySafeHandle repo, string refname) + { + int res = NativeMethods.git_reference_ensure_log(repo, refname); + Ensure.ZeroResult(res); + } + + #endregion + + #region git_reflog_ + + public static void git_reflog_free(IntPtr reflog) + { + NativeMethods.git_reflog_free(reflog); + } + + public static ReflogSafeHandle git_reflog_read(RepositorySafeHandle repo, string canonicalName) + { + ReflogSafeHandle reflog_out; + + int res = NativeMethods.git_reflog_read(out reflog_out, repo, canonicalName); + Ensure.ZeroResult(res); + + return reflog_out; + } + + public static int git_reflog_entrycount(ReflogSafeHandle reflog) + { + return (int)NativeMethods.git_reflog_entrycount(reflog); } public static ReflogEntrySafeHandle git_reflog_entry_byindex(ReflogSafeHandle reflog, int idx) @@ -1791,7 +2019,6 @@ public static string git_reflog_entry_message(SafeHandle entry) public static string git_refspec_rtransform(GitRefSpecHandle refSpecPtr, string name) { - using (ThreadAffinity()) using (var buf = new GitBuf()) { int res = NativeMethods.git_refspec_rtransform(buf, refSpecPtr, name); @@ -1832,75 +2059,61 @@ public static bool git_refspec_force(GitRefSpecHandle refSpec) public static TagFetchMode git_remote_autotag(RemoteSafeHandle remote) { - return (TagFetchMode) NativeMethods.git_remote_autotag(remote); + return (TagFetchMode)NativeMethods.git_remote_autotag(remote); } public static RemoteSafeHandle git_remote_create(RepositorySafeHandle repo, string name, string url) { - using (ThreadAffinity()) - { - RemoteSafeHandle handle; - int res = NativeMethods.git_remote_create(out handle, repo, name, url); - Ensure.ZeroResult(res); + RemoteSafeHandle handle; + int res = NativeMethods.git_remote_create(out handle, repo, name, url); + Ensure.ZeroResult(res); - return handle; - } + return handle; } public static RemoteSafeHandle git_remote_create_with_fetchspec(RepositorySafeHandle repo, string name, string url, string refspec) { - using (ThreadAffinity()) - { - RemoteSafeHandle handle; - int res = NativeMethods.git_remote_create_with_fetchspec(out handle, repo, name, url, refspec); - Ensure.ZeroResult(res); + RemoteSafeHandle handle; + int res = NativeMethods.git_remote_create_with_fetchspec(out handle, repo, name, url, refspec); + Ensure.ZeroResult(res); - return handle; - } + return handle; } - public static RemoteSafeHandle git_remote_create_anonymous(RepositorySafeHandle repo, string url, string refspec) + public static RemoteSafeHandle git_remote_create_anonymous(RepositorySafeHandle repo, string url) { - using (ThreadAffinity()) - { - RemoteSafeHandle handle; - int res = NativeMethods.git_remote_create_anonymous(out handle, repo, url, refspec); - Ensure.ZeroResult(res); + RemoteSafeHandle handle; + int res = NativeMethods.git_remote_create_anonymous(out handle, repo, url); + Ensure.ZeroResult(res); - return handle; - } + return handle; } - public static void git_remote_connect(RemoteSafeHandle remote, GitDirection direction) + public static void git_remote_connect(RemoteSafeHandle remote, GitDirection direction, ref GitRemoteCallbacks remoteCallbacks) { - using (ThreadAffinity()) + GitStrArrayManaged customHeaders = new GitStrArrayManaged(); + + try { - int res = NativeMethods.git_remote_connect(remote, direction); + int res = NativeMethods.git_remote_connect(remote, direction, ref remoteCallbacks, ref customHeaders.Array); Ensure.ZeroResult(res); } - } - - public static void git_remote_delete(RepositorySafeHandle repo, string name) - { - using (ThreadAffinity()) + catch (Exception) { - int res = NativeMethods.git_remote_delete(repo, name); - - if (res == (int)GitErrorCode.NotFound) - { - return; - } - - Ensure.ZeroResult(res); + customHeaders.Dispose(); } } - public static void git_remote_disconnect(RemoteSafeHandle remote) + public static void git_remote_delete(RepositorySafeHandle repo, string name) { - using (ThreadAffinity()) + int res = NativeMethods.git_remote_delete(repo, name); + + if (res == (int)GitErrorCode.NotFound) { - NativeMethods.git_remote_disconnect(remote); + return; } + + Ensure.ZeroResult(res); } public static GitRefSpecHandle git_remote_get_refspec(RemoteSafeHandle remote, int n) @@ -1915,132 +2128,95 @@ public static int git_remote_refspec_count(RemoteSafeHandle remote) public static IList git_remote_get_fetch_refspecs(RemoteSafeHandle remote) { - using (ThreadAffinity()) - { - var array = new GitStrArrayNative(); + var array = new GitStrArrayNative(); - try - { - int res = NativeMethods.git_remote_get_fetch_refspecs(out array.Array, remote); - Ensure.ZeroResult(res); + try + { + int res = NativeMethods.git_remote_get_fetch_refspecs(out array.Array, remote); + Ensure.ZeroResult(res); - return array.ReadStrings(); - } - finally - { - array.Dispose(); - } + return array.ReadStrings(); + } + finally + { + array.Dispose(); } } public static IList git_remote_get_push_refspecs(RemoteSafeHandle remote) { - using (ThreadAffinity()) - { - var array = new GitStrArrayNative(); + var array = new GitStrArrayNative(); - try - { - int res = NativeMethods.git_remote_get_push_refspecs(out array.Array, remote); - Ensure.ZeroResult(res); + try + { + int res = NativeMethods.git_remote_get_push_refspecs(out array.Array, remote); + Ensure.ZeroResult(res); - return array.ReadStrings(); - } - finally - { - array.Dispose(); - } + return array.ReadStrings(); } - } - - public static void git_remote_push(RemoteSafeHandle remote, IEnumerable refSpecs, GitPushOptions opts, Signature signature, string reflogMessage) - { - using (ThreadAffinity()) + finally { - using (var sigHandle = signature.BuildHandle()) - { - var array = new GitStrArrayManaged(); - - try - { - array = GitStrArrayManaged.BuildFrom(refSpecs.ToArray()); - - int res = NativeMethods.git_remote_push(remote, ref array.Array, opts, sigHandle, reflogMessage); - Ensure.ZeroResult(res); - } - finally - { - array.Dispose(); - } - } + array.Dispose(); } } - public static void git_remote_set_fetch_refspecs(RemoteSafeHandle remote, IEnumerable refSpecs) + public static void git_remote_push(RemoteSafeHandle remote, IEnumerable refSpecs, GitPushOptions opts) { - using (ThreadAffinity()) - { - var array = new GitStrArrayManaged(); + var array = new GitStrArrayManaged(); - try - { - array = GitStrArrayManaged.BuildFrom(refSpecs.ToArray()); + try + { + array = GitStrArrayManaged.BuildFrom(refSpecs.ToArray()); - int res = NativeMethods.git_remote_set_fetch_refspecs(remote, ref array.Array); - Ensure.ZeroResult(res); - } - finally - { - array.Dispose(); - } + int res = NativeMethods.git_remote_push(remote, ref array.Array, opts); + Ensure.ZeroResult(res); + } + finally + { + array.Dispose(); } } - public static void git_remote_set_push_refspecs(RemoteSafeHandle remote, IEnumerable refSpecs) + public static void git_remote_set_url(RepositorySafeHandle repo, string remote, string url) { - using (ThreadAffinity()) - { - var array = new GitStrArrayManaged(); + int res = NativeMethods.git_remote_set_url(repo, remote, url); + Ensure.ZeroResult(res); + } - try - { - array = GitStrArrayManaged.BuildFrom(refSpecs.ToArray()); + public static void git_remote_add_fetch(RepositorySafeHandle repo, string remote, string url) + { + int res = NativeMethods.git_remote_add_fetch(repo, remote, url); + Ensure.ZeroResult(res); + } - int res = NativeMethods.git_remote_set_push_refspecs(remote, ref array.Array); - Ensure.ZeroResult(res); - } - finally - { - array.Dispose(); - } - } + public static void git_remote_set_pushurl(RepositorySafeHandle repo, string remote, string url) + { + int res = NativeMethods.git_remote_set_pushurl(repo, remote, url); + Ensure.ZeroResult(res); } - public static void git_remote_set_url(RemoteSafeHandle remote, string url) + public static void git_remote_add_push(RepositorySafeHandle repo, string remote, string url) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_remote_set_url(remote, url); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_remote_add_push(repo, remote, url); + Ensure.ZeroResult(res); } - public static void git_remote_fetch(RemoteSafeHandle remote, Signature signature, string logMessage) + public static void git_remote_fetch( + RemoteSafeHandle remote, IEnumerable refSpecs, + GitFetchOptions fetchOptions, string logMessage) { - using (ThreadAffinity()) - using (var sigHandle = signature.BuildHandle()) + var array = new GitStrArrayManaged(); + + try { - var array = new GitStrArrayNative(); + array = GitStrArrayManaged.BuildFrom(refSpecs.ToArray()); - try - { - int res = NativeMethods.git_remote_fetch(remote, ref array.Array, sigHandle, logMessage); - Ensure.ZeroResult(res); - } - finally - { - array.Dispose(); - } + int res = NativeMethods.git_remote_fetch(remote, ref array.Array, fetchOptions, logMessage); + Ensure.ZeroResult(res); + } + finally + { + array.Dispose(); } } @@ -2059,34 +2235,28 @@ public static bool git_remote_is_valid_name(string refname) public static IList git_remote_list(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - var array = new GitStrArrayNative(); + var array = new GitStrArrayNative(); - try - { - int res = NativeMethods.git_remote_list(out array.Array, repo); - Ensure.ZeroResult(res); + try + { + int res = NativeMethods.git_remote_list(out array.Array, repo); + Ensure.ZeroResult(res); - return array.ReadStrings(); - } - finally - { - array.Dispose(); - } + return array.ReadStrings(); + } + finally + { + array.Dispose(); } } - public static IEnumerable git_remote_ls(Repository repository, RemoteSafeHandle remote) + public static IEnumerable git_remote_ls(Repository repository, RemoteSafeHandle remote) { IntPtr heads; UIntPtr count; - using (ThreadAffinity()) - { - int res = NativeMethods.git_remote_ls(out heads, out count, remote); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_remote_ls(out heads, out count, remote); + Ensure.ZeroResult(res); var intCount = (int)count.ToUInt32(); @@ -2095,44 +2265,67 @@ public static IEnumerable git_remote_ls(Repository repository, throw new OverflowException(); } - var refs = new List(); + var directRefs = new Dictionary(); + var symRefs = new Dictionary(); + IntPtr currentHead = heads; for (int i = 0; i < intCount; i++) { var remoteHead = Marshal.ReadIntPtr(currentHead).MarshalAs(); + string name = LaxUtf8Marshaler.FromNative(remoteHead.NamePtr); + string symRefTarget = LaxUtf8Marshaler.FromNative(remoteHead.SymRefTargetPtr); + // The name pointer should never be null - if it is, // this indicates a bug somewhere (libgit2, server, etc). - if (remoteHead.NamePtr == IntPtr.Zero) + if (string.IsNullOrEmpty(name)) { throw new InvalidOperationException("Not expecting null value for reference name."); } - string name = LaxUtf8Marshaler.FromNative(remoteHead.NamePtr); - refs.Add(new DirectReference(name, repository, remoteHead.Oid)); + if (!string.IsNullOrEmpty(symRefTarget)) + { + symRefs.Add(name, symRefTarget); + } + else + { + directRefs.Add(name, new DirectReference(name, repository, remoteHead.Oid)); + } currentHead = IntPtr.Add(currentHead, IntPtr.Size); } + for (int i = 0; i < symRefs.Count; i++) + { + var symRef = symRefs.ElementAt(i); + + if (!directRefs.ContainsKey(symRef.Value)) + { + throw new InvalidOperationException("Symbolic reference target not found in direct reference results."); + } + + directRefs.Add(symRef.Key, new SymbolicReference(repository, symRef.Key, symRef.Value, directRefs[symRef.Value])); + } + + var refs = directRefs.Values.ToList(); + refs.Sort((r1, r2) => String.CompareOrdinal(r1.CanonicalName, r2.CanonicalName)); + return refs; } public static RemoteSafeHandle git_remote_lookup(RepositorySafeHandle repo, string name, bool throwsIfNotFound) { - using (ThreadAffinity()) - { - RemoteSafeHandle handle; - int res = NativeMethods.git_remote_lookup(out handle, repo, name); - - if (res == (int)GitErrorCode.NotFound && !throwsIfNotFound) - { - return null; - } + RemoteSafeHandle handle; + int res = NativeMethods.git_remote_lookup(out handle, repo, name); - Ensure.ZeroResult(res); - return handle; + if (res == (int)GitErrorCode.NotFound && !throwsIfNotFound) + { + return null; } + + Ensure.ZeroResult(res); + return handle; } public static string git_remote_name(RemoteSafeHandle remote) @@ -2142,69 +2335,51 @@ public static string git_remote_name(RemoteSafeHandle remote) public static void git_remote_rename(RepositorySafeHandle repo, string name, string new_name, RemoteRenameFailureHandler callback) { - using (ThreadAffinity()) + if (callback == null) { - if (callback == null) - { - callback = problem => {}; - } + callback = problem => { }; + } - var array = new GitStrArrayNative(); + var array = new GitStrArrayNative(); - try - { - int res = NativeMethods.git_remote_rename( - ref array.Array, - repo, - name, - new_name); + try + { + int res = NativeMethods.git_remote_rename(ref array.Array, + repo, + name, + new_name); - if (res == (int)GitErrorCode.NotFound) - { - throw new NotFoundException( - string.Format("Remote '{0}' does not exist and cannot be renamed.", name)); - } + if (res == (int)GitErrorCode.NotFound) + { + throw new NotFoundException("Remote '{0}' does not exist and cannot be renamed.", name); + } - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - foreach (var item in array.ReadStrings()) - { - callback(item); - } - } - finally + foreach (var item in array.ReadStrings()) { - array.Dispose(); + callback(item); } } - } - - public static void git_remote_save(RemoteSafeHandle remote) - { - using (ThreadAffinity()) + finally { - int res = NativeMethods.git_remote_save(remote); - Ensure.ZeroResult(res); + array.Dispose(); } } - public static void git_remote_set_autotag(RemoteSafeHandle remote, TagFetchMode value) + public static void git_remote_set_autotag(RepositorySafeHandle repo, string remote, TagFetchMode value) { - NativeMethods.git_remote_set_autotag(remote, value); + NativeMethods.git_remote_set_autotag(repo, remote, value); } - public static void git_remote_set_callbacks(RemoteSafeHandle remote, ref GitRemoteCallbacks callbacks) + public static string git_remote_url(RemoteSafeHandle remote) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_remote_set_callbacks(remote, ref callbacks); - Ensure.ZeroResult(res); - } + return NativeMethods.git_remote_url(remote); } - public static string git_remote_url(RemoteSafeHandle remote) + public static string git_remote_pushurl(RemoteSafeHandle remote) { - return NativeMethods.git_remote_url(remote); + return NativeMethods.git_remote_pushurl(remote); } #endregion @@ -2225,13 +2400,12 @@ public static ICollection git_repository_fetchhead_foreach( RepositorySafeHandle repo, Func resultSelector) { - return git_foreach( - resultSelector, - c => NativeMethods.git_repository_fetchhead_foreach( - repo, - (IntPtr w, IntPtr x, ref GitOid y, bool z, IntPtr p) - => c(LaxUtf8Marshaler.FromNative(w), LaxUtf8Marshaler.FromNative(x), y, z, p), IntPtr.Zero), - GitErrorCode.NotFound); + return git_foreach(resultSelector, + c => NativeMethods.git_repository_fetchhead_foreach(repo, + (IntPtr w, IntPtr x, ref GitOid y, bool z, IntPtr p) + => c(LaxUtf8Marshaler.FromNative(w), LaxUtf8Marshaler.FromNative(x), y, z, p), + IntPtr.Zero), + GitErrorCode.NotFound); } public static void git_repository_free(IntPtr repo) @@ -2246,14 +2420,11 @@ public static bool git_repository_head_unborn(RepositorySafeHandle repo) public static IndexSafeHandle git_repository_index(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - IndexSafeHandle handle; - int res = NativeMethods.git_repository_index(out handle, repo); - Ensure.ZeroResult(res); + IndexSafeHandle handle; + int res = NativeMethods.git_repository_index(out handle, repo); + Ensure.ZeroResult(res); - return handle; - } + return handle; } public static RepositorySafeHandle git_repository_init_ext( @@ -2261,7 +2432,6 @@ public static RepositorySafeHandle git_repository_init_ext( FilePath gitdirPath, bool isBare) { - using (ThreadAffinity()) using (var opts = GitRepositoryInitOptions.BuildFrom(workdirPath, isBare)) { RepositorySafeHandle repo; @@ -2284,27 +2454,22 @@ public static bool git_repository_is_shallow(RepositorySafeHandle repo) public static void git_repository_state_cleanup(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_repository_state_cleanup(repo); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_repository_state_cleanup(repo); + Ensure.ZeroResult(res); } public static ICollection git_repository_mergehead_foreach( RepositorySafeHandle repo, Func resultSelector) { - return git_foreach( - resultSelector, - c => NativeMethods.git_repository_mergehead_foreach( - repo, (ref GitOid x, IntPtr p) => c(x, p), IntPtr.Zero), - GitErrorCode.NotFound); + return git_foreach(resultSelector, + c => NativeMethods.git_repository_mergehead_foreach(repo, + (ref GitOid x, IntPtr p) => c(x, p), IntPtr.Zero), + GitErrorCode.NotFound); } public static string git_repository_message(RepositorySafeHandle repo) { - using (ThreadAffinity()) using (var buf = new GitBuf()) { int res = NativeMethods.git_repository_message(buf, repo); @@ -2320,52 +2485,55 @@ public static string git_repository_message(RepositorySafeHandle repo) public static ObjectDatabaseSafeHandle git_repository_odb(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - ObjectDatabaseSafeHandle handle; - int res = NativeMethods.git_repository_odb(out handle, repo); - Ensure.ZeroResult(res); + ObjectDatabaseSafeHandle handle; + int res = NativeMethods.git_repository_odb(out handle, repo); + Ensure.ZeroResult(res); - return handle; - } + return handle; } public static RepositorySafeHandle git_repository_open(string path) { - using (ThreadAffinity()) + RepositorySafeHandle repo; + int res = NativeMethods.git_repository_open(out repo, path); + + if (res == (int)GitErrorCode.NotFound) { - RepositorySafeHandle repo; - int res = NativeMethods.git_repository_open(out repo, path); + throw new RepositoryNotFoundException("Path '{0}' doesn't point at a valid Git repository or workdir.", + path); + } - if (res == (int)GitErrorCode.NotFound) - { - throw new RepositoryNotFoundException(String.Format(CultureInfo.InvariantCulture, "Path '{0}' doesn't point at a valid Git repository or workdir.", path)); - } + Ensure.ZeroResult(res); - Ensure.ZeroResult(res); + return repo; + } - return repo; - } + public static RepositorySafeHandle git_repository_new() + { + RepositorySafeHandle repo; + int res = NativeMethods.git_repository_new(out repo); + + Ensure.ZeroResult(res); + + return repo; } public static void git_repository_open_ext(string path, RepositoryOpenFlags flags, string ceilingDirs) { - using (ThreadAffinity()) - { - int res; - - using (var repo = new NullRepositorySafeHandle()) - { - res = NativeMethods.git_repository_open_ext(repo, path, flags, ceilingDirs); - } + int res; - if (res == (int)GitErrorCode.NotFound) - { - throw new RepositoryNotFoundException(String.Format(CultureInfo.InvariantCulture, "Path '{0}' doesn't point at a valid Git repository or workdir.", path)); - } + using (var repo = new NullRepositorySafeHandle()) + { + res = NativeMethods.git_repository_open_ext(repo, path, flags, ceilingDirs); + } - Ensure.ZeroResult(res); + if (res == (int)GitErrorCode.NotFound) + { + throw new RepositoryNotFoundException("Path '{0}' doesn't point at a valid Git repository or workdir.", + path); } + + Ensure.ZeroResult(res); } public static FilePath git_repository_path(RepositorySafeHandle repo) @@ -2378,6 +2546,12 @@ public static void git_repository_set_config(RepositorySafeHandle repo, Configur NativeMethods.git_repository_set_config(repo, config); } + public static void git_repository_set_ident(RepositorySafeHandle repo, string name, string email) + { + int res = NativeMethods.git_repository_set_ident(repo, name, email); + Ensure.ZeroResult(res); + } + public static void git_repository_set_index(RepositorySafeHandle repo, IndexSafeHandle index) { NativeMethods.git_repository_set_index(repo, index); @@ -2385,21 +2559,15 @@ public static void git_repository_set_index(RepositorySafeHandle repo, IndexSafe public static void git_repository_set_workdir(RepositorySafeHandle repo, FilePath workdir) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_repository_set_workdir(repo, workdir, false); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_repository_set_workdir(repo, workdir, false); + Ensure.ZeroResult(res); } public static CurrentOperation git_repository_state(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_repository_state(repo); - Ensure.Int32Result(res); - return (CurrentOperation)res; - } + int res = NativeMethods.git_repository_state(repo); + Ensure.Int32Result(res); + return (CurrentOperation)res; } public static FilePath git_repository_workdir(RepositorySafeHandle repo) @@ -2407,27 +2575,28 @@ public static FilePath git_repository_workdir(RepositorySafeHandle repo) return NativeMethods.git_repository_workdir(repo); } - public static void git_repository_set_head_detached(RepositorySafeHandle repo, ObjectId commitish, - Signature signature, string logMessage) + public static FilePath git_repository_workdir(IntPtr repo) { - using (ThreadAffinity()) - using (var sigHandle = signature.BuildHandle()) - { - GitOid oid = commitish.Oid; - int res = NativeMethods.git_repository_set_head_detached(repo, ref oid, sigHandle, logMessage); - Ensure.ZeroResult(res); - } + return NativeMethods.git_repository_workdir(repo); } - public static void git_repository_set_head(RepositorySafeHandle repo, string refname, - Signature signature, string logMessage) + public static void git_repository_set_head_detached(RepositorySafeHandle repo, ObjectId commitish) { - using (ThreadAffinity()) - using (var sigHandle = signature.BuildHandle()) - { - int res = NativeMethods.git_repository_set_head(repo, refname, sigHandle, logMessage); - Ensure.ZeroResult(res); - } + GitOid oid = commitish.Oid; + int res = NativeMethods.git_repository_set_head_detached(repo, ref oid); + Ensure.ZeroResult(res); + } + + public static void git_repository_set_head_detached_from_annotated(RepositorySafeHandle repo, GitAnnotatedCommitHandle commit) + { + int res = NativeMethods.git_repository_set_head_detached_from_annotated(repo, commit); + Ensure.ZeroResult(res); + } + + public static void git_repository_set_head(RepositorySafeHandle repo, string refname) + { + int res = NativeMethods.git_repository_set_head(repo, refname); + Ensure.ZeroResult(res); } #endregion @@ -2438,15 +2607,11 @@ public static void git_reset( RepositorySafeHandle repo, ObjectId committishId, ResetMode resetKind, - ref GitCheckoutOpts checkoutOptions, - Signature signature, - string logMessage) + ref GitCheckoutOpts checkoutOptions) { - using (ThreadAffinity()) using (var osw = new ObjectSafeWrapper(committishId, repo)) - using (var sigHandle = signature.BuildHandle()) { - int res = NativeMethods.git_reset(repo, osw.ObjectPtr, resetKind, ref checkoutOptions, sigHandle, logMessage); + int res = NativeMethods.git_reset(repo, osw.ObjectPtr, resetKind, ref checkoutOptions); Ensure.ZeroResult(res); } } @@ -2460,7 +2625,6 @@ public static void git_revert( ObjectId commit, GitRevertOpts opts) { - using (ThreadAffinity()) using (var nativeCommit = git_object_lookup(repo, commit, GitObjectType.Commit)) { int res = NativeMethods.git_revert(repo, nativeCommit, opts); @@ -2474,27 +2638,25 @@ public static void git_revert( public static Tuple git_revparse_ext(RepositorySafeHandle repo, string objectish) { - using (ThreadAffinity()) - { - GitObjectSafeHandle obj; - ReferenceSafeHandle reference; - int res = NativeMethods.git_revparse_ext(out obj, out reference, repo, objectish); - - switch (res) - { - case (int)GitErrorCode.NotFound: - return null; + GitObjectSafeHandle obj; + ReferenceSafeHandle reference; + int res = NativeMethods.git_revparse_ext(out obj, out reference, repo, objectish); - case (int)GitErrorCode.Ambiguous: - throw new AmbiguousSpecificationException(string.Format(CultureInfo.InvariantCulture, "Provided abbreviated ObjectId '{0}' is too short.", objectish)); + switch (res) + { + case (int)GitErrorCode.NotFound: + return null; - default: - Ensure.ZeroResult(res); - break; - } + case (int)GitErrorCode.Ambiguous: + throw new AmbiguousSpecificationException("Provided abbreviated ObjectId '{0}' is too short.", + objectish); - return new Tuple(obj, reference); + default: + Ensure.ZeroResult(res); + break; } + + return new Tuple(obj, reference); } public static GitObjectSafeHandle git_revparse_single(RepositorySafeHandle repo, string objectish) @@ -2522,52 +2684,40 @@ public static void git_revwalk_free(IntPtr walker) public static void git_revwalk_hide(RevWalkerSafeHandle walker, ObjectId commit_id) { - using (ThreadAffinity()) - { - GitOid oid = commit_id.Oid; - int res = NativeMethods.git_revwalk_hide(walker, ref oid); - Ensure.ZeroResult(res); - } + GitOid oid = commit_id.Oid; + int res = NativeMethods.git_revwalk_hide(walker, ref oid); + Ensure.ZeroResult(res); } public static RevWalkerSafeHandle git_revwalk_new(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - RevWalkerSafeHandle handle; - int res = NativeMethods.git_revwalk_new(out handle, repo); - Ensure.ZeroResult(res); + RevWalkerSafeHandle handle; + int res = NativeMethods.git_revwalk_new(out handle, repo); + Ensure.ZeroResult(res); - return handle; - } + return handle; } public static ObjectId git_revwalk_next(RevWalkerSafeHandle walker) { - using (ThreadAffinity()) - { - GitOid ret; - int res = NativeMethods.git_revwalk_next(out ret, walker); + GitOid ret; + int res = NativeMethods.git_revwalk_next(out ret, walker); - if (res == (int)GitErrorCode.IterOver) - { - return null; - } + if (res == (int)GitErrorCode.IterOver) + { + return null; + } - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - return ret; - } + return ret; } public static void git_revwalk_push(RevWalkerSafeHandle walker, ObjectId id) { - using (ThreadAffinity()) - { - GitOid oid = id.Oid; - int res = NativeMethods.git_revwalk_push(walker, ref oid); - Ensure.ZeroResult(res); - } + GitOid oid = id.Oid; + int res = NativeMethods.git_revwalk_push(walker, ref oid); + Ensure.ZeroResult(res); } public static void git_revwalk_reset(RevWalkerSafeHandle walker) @@ -2596,26 +2746,30 @@ public static void git_signature_free(IntPtr signature) public static SignatureSafeHandle git_signature_new(string name, string email, DateTimeOffset when) { - using (ThreadAffinity()) - { - SignatureSafeHandle handle; - int res = NativeMethods.git_signature_new(out handle, name, email, when.ToSecondsSinceEpoch(), - (int)when.Offset.TotalMinutes); - Ensure.ZeroResult(res); + SignatureSafeHandle handle; - return handle; - } + int res = NativeMethods.git_signature_new(out handle, name, email, when.ToSecondsSinceEpoch(), + (int)when.Offset.TotalMinutes); + Ensure.ZeroResult(res); + + return handle; + } + + public static SignatureSafeHandle git_signature_now(string name, string email) + { + SignatureSafeHandle handle; + int res = NativeMethods.git_signature_now(out handle, name, email); + Ensure.ZeroResult(res); + + return handle; } public static IntPtr git_signature_dup(IntPtr sig) { - using (ThreadAffinity()) - { - IntPtr handle; - int res = NativeMethods.git_signature_dup(out handle, sig); - Ensure.ZeroResult(res); - return handle; - } + IntPtr handle; + int res = NativeMethods.git_signature_dup(out handle, sig); + Ensure.ZeroResult(res); + return handle; } #endregion @@ -2628,7 +2782,6 @@ public static ObjectId git_stash_save( string prettifiedMessage, StashModifiers options) { - using (ThreadAffinity()) using (SignatureSafeHandle sigHandle = stasher.BuildHandle()) { GitOid stashOid; @@ -2650,20 +2803,55 @@ public static ICollection git_stash_foreach( RepositorySafeHandle repo, Func resultSelector) { - return git_foreach( - resultSelector, - c => NativeMethods.git_stash_foreach( - repo, (UIntPtr i, IntPtr m, ref GitOid x, IntPtr p) => c((int)i, m, x, p), IntPtr.Zero), - GitErrorCode.NotFound); + return git_foreach(resultSelector, + c => NativeMethods.git_stash_foreach(repo, + (UIntPtr i, IntPtr m, ref GitOid x, IntPtr p) + => c((int)i, m, x, p), + IntPtr.Zero), + GitErrorCode.NotFound); } public static void git_stash_drop(RepositorySafeHandle repo, int index) { - using (ThreadAffinity()) + int res = NativeMethods.git_stash_drop(repo, (UIntPtr)index); + Ensure.BooleanResult(res); + } + + private static StashApplyStatus get_stash_status(int res) + { + if (res == (int)GitErrorCode.Conflict) + { + return StashApplyStatus.Conflicts; + } + + if (res == (int)GitErrorCode.Uncommitted) + { + return StashApplyStatus.UncommittedChanges; + } + + if (res == (int)GitErrorCode.NotFound) { - int res = NativeMethods.git_stash_drop(repo, (UIntPtr) index); - Ensure.BooleanResult(res); + return StashApplyStatus.NotFound; } + + Ensure.ZeroResult(res); + return StashApplyStatus.Applied; + } + + public static StashApplyStatus git_stash_apply( + RepositorySafeHandle repo, + int index, + GitStashApplyOpts opts) + { + return get_stash_status(NativeMethods.git_stash_apply(repo, (UIntPtr)index, opts)); + } + + public static StashApplyStatus git_stash_pop( + RepositorySafeHandle repo, + int index, + GitStashApplyOpts opts) + { + return get_stash_status(NativeMethods.git_stash_pop(repo, (UIntPtr)index, opts)); } #endregion @@ -2672,47 +2860,41 @@ public static void git_stash_drop(RepositorySafeHandle repo, int index) public static FileStatus git_status_file(RepositorySafeHandle repo, FilePath path) { - using (ThreadAffinity()) - { - FileStatus status; - int res = NativeMethods.git_status_file(out status, repo, path); - - switch (res) - { - case (int)GitErrorCode.NotFound: - return FileStatus.Nonexistent; + FileStatus status; + int res = NativeMethods.git_status_file(out status, repo, path); - case (int)GitErrorCode.Ambiguous: - throw new AmbiguousSpecificationException(string.Format(CultureInfo.InvariantCulture, "More than one file matches the pathspec '{0}'. You can either force a literal path evaluation (GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH), or use git_status_foreach().", path)); + switch (res) + { + case (int)GitErrorCode.NotFound: + return FileStatus.Nonexistent; - default: - Ensure.ZeroResult(res); - break; - } + case (int)GitErrorCode.Ambiguous: + throw new AmbiguousSpecificationException("More than one file matches the pathspec '{0}'. " + + "You can either force a literal path evaluation " + + "(GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH), or use git_status_foreach().", + path); - return status; + default: + Ensure.ZeroResult(res); + break; } + + return status; } public static StatusListSafeHandle git_status_list_new(RepositorySafeHandle repo, GitStatusOptions options) { - using (ThreadAffinity()) - { - StatusListSafeHandle handle; - int res = NativeMethods.git_status_list_new(out handle, repo, options); - Ensure.ZeroResult(res); - return handle; - } + StatusListSafeHandle handle; + int res = NativeMethods.git_status_list_new(out handle, repo, options); + Ensure.ZeroResult(res); + return handle; } public static int git_status_list_entrycount(StatusListSafeHandle list) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_status_list_entrycount(list); - Ensure.Int32Result(res); - return res; - } + int res = NativeMethods.git_status_list_entrycount(list); + Ensure.Int32Result(res); + return res; } public static StatusEntrySafeHandle git_status_byindex(StatusListSafeHandle list, long idx) @@ -2735,22 +2917,30 @@ public static void git_status_list_free(IntPtr statusList) /// public static SubmoduleSafeHandle git_submodule_lookup(RepositorySafeHandle repo, FilePath name) { - using (ThreadAffinity()) + SubmoduleSafeHandle reference; + var res = NativeMethods.git_submodule_lookup(out reference, repo, name); + + switch (res) { - SubmoduleSafeHandle reference; - var res = NativeMethods.git_submodule_lookup(out reference, repo, name); + case (int)GitErrorCode.NotFound: + case (int)GitErrorCode.Exists: + case (int)GitErrorCode.OrphanedHead: + return null; - switch (res) - { - case (int)GitErrorCode.NotFound: - case (int)GitErrorCode.Exists: - case (int)GitErrorCode.OrphanedHead: - return null; + default: + Ensure.ZeroResult(res); + return reference; + } + } - default: - Ensure.ZeroResult(res); - return reference; - } + public static string git_submodule_resolve_url(RepositorySafeHandle repo, string url) + { + using (var buf = new GitBuf()) + { + int res = NativeMethods.git_submodule_resolve_url(buf, repo, url); + + Ensure.ZeroResult(res); + return LaxUtf8Marshaler.FromNative(buf.ptr); } } @@ -2761,29 +2951,14 @@ public static ICollection git_submodule_foreach(RepositorySafe public static void git_submodule_add_to_index(SubmoduleSafeHandle submodule, bool write_index) { - using (ThreadAffinity()) - { - var res = NativeMethods.git_submodule_add_to_index(submodule, write_index); - Ensure.ZeroResult(res); - } - } - - public static void git_submodule_save(SubmoduleSafeHandle submodule) - { - using (ThreadAffinity()) - { - var res = NativeMethods.git_submodule_save(submodule); - Ensure.ZeroResult(res); - } + var res = NativeMethods.git_submodule_add_to_index(submodule, write_index); + Ensure.ZeroResult(res); } public static void git_submodule_update(SubmoduleSafeHandle submodule, bool init, ref GitSubmoduleOptions options) { - using (ThreadAffinity()) - { - var res = NativeMethods.git_submodule_update(submodule, init, ref options); - Ensure.ZeroResult(res); - } + var res = NativeMethods.git_submodule_update(submodule, init, ref options); + Ensure.ZeroResult(res); } public static void git_submodule_free(IntPtr submodule) @@ -2833,31 +3008,22 @@ public static SubmoduleRecurse git_submodule_fetch_recurse_submodules(SubmoduleS public static void git_submodule_reload(SubmoduleSafeHandle submodule) { - using (ThreadAffinity()) - { - var res = NativeMethods.git_submodule_reload(submodule, false); - Ensure.ZeroResult(res); - } + var res = NativeMethods.git_submodule_reload(submodule, false); + Ensure.ZeroResult(res); } - public static SubmoduleStatus git_submodule_status(SubmoduleSafeHandle submodule) + public static SubmoduleStatus git_submodule_status(RepositorySafeHandle repo, string name) { - using (ThreadAffinity()) - { - SubmoduleStatus status; - var res = NativeMethods.git_submodule_status(out status, submodule); - Ensure.ZeroResult(res); - return status; - } + SubmoduleStatus status; + var res = NativeMethods.git_submodule_status(out status, repo, name, GitSubmoduleIgnore.Unspecified); + Ensure.ZeroResult(res); + return status; } public static void git_submodule_init(SubmoduleSafeHandle submodule, bool overwrite) { - using (ThreadAffinity()) - { - var res = NativeMethods.git_submodule_init(submodule, overwrite); - Ensure.ZeroResult(res); - } + var res = NativeMethods.git_submodule_init(submodule, overwrite); + Ensure.ZeroResult(res); } #endregion @@ -2871,7 +3037,6 @@ public static ObjectId git_tag_annotation_create( Signature tagger, string message) { - using (ThreadAffinity()) using (var objectPtr = new ObjectSafeWrapper(target.Id, repo)) using (SignatureSafeHandle sigHandle = tagger.BuildHandle()) { @@ -2891,7 +3056,6 @@ public static ObjectId git_tag_create( string message, bool allowOverwrite) { - using (ThreadAffinity()) using (var objectPtr = new ObjectSafeWrapper(target.Id, repo)) using (SignatureSafeHandle sigHandle = tagger.BuildHandle()) { @@ -2905,7 +3069,6 @@ public static ObjectId git_tag_create( public static ObjectId git_tag_create_lightweight(RepositorySafeHandle repo, string name, GitObject target, bool allowOverwrite) { - using (ThreadAffinity()) using (var objectPtr = new ObjectSafeWrapper(target.Id, repo)) { GitOid oid; @@ -2918,30 +3081,24 @@ public static ObjectId git_tag_create_lightweight(RepositorySafeHandle repo, str public static void git_tag_delete(RepositorySafeHandle repo, string name) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_tag_delete(repo, name); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_tag_delete(repo, name); + Ensure.ZeroResult(res); } public static IList git_tag_list(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - var array = new GitStrArrayNative(); + var array = new GitStrArrayNative(); - try - { - int res = NativeMethods.git_tag_list(out array.Array, repo); - Ensure.ZeroResult(res); + try + { + int res = NativeMethods.git_tag_list(out array.Array, repo); + Ensure.ZeroResult(res); - return array.ReadStrings(); - } - finally - { - array.Dispose(); - } + return array.ReadStrings(); + } + finally + { + array.Dispose(); } } @@ -2998,11 +3155,8 @@ public static GitObjectType git_tag_target_type(GitObjectSafeHandle tag) /// public static void git_trace_set(LogLevel level, NativeMethods.git_trace_cb callback) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_trace_set(level, callback); - Ensure.ZeroResult(res); - } + int res = NativeMethods.git_trace_set(level, callback); + Ensure.ZeroResult(res); } #endregion @@ -3011,32 +3165,36 @@ public static void git_trace_set(LogLevel level, NativeMethods.git_trace_cb call public static void git_transport_register(String prefix, IntPtr transport_cb, IntPtr param) { - using (ThreadAffinity()) - { - int res = NativeMethods.git_transport_register(prefix, transport_cb, param); - - if (res == (int)GitErrorCode.Exists) - { - throw new EntryExistsException(String.Format("A custom transport for '{0}' is already registered", prefix)); - } + int res = NativeMethods.git_transport_register(prefix, transport_cb, param); - Ensure.ZeroResult(res); + if (res == (int)GitErrorCode.Exists) + { + throw new EntryExistsException("A custom transport for '{0}' is already registered", + prefix); } + + Ensure.ZeroResult(res); } public static void git_transport_unregister(String prefix) { - using (ThreadAffinity()) + int res = NativeMethods.git_transport_unregister(prefix); + + if (res == (int)GitErrorCode.NotFound) { - int res = NativeMethods.git_transport_unregister(prefix); + throw new NotFoundException("The given transport was not found"); + } - if (res == (int)GitErrorCode.NotFound) - { - throw new NotFoundException("The given transport was not found"); - } + Ensure.ZeroResult(res); + } - Ensure.ZeroResult(res); - } + #endregion + + #region git_transport_smart_ + + public static int git_transport_smart_credentials(out IntPtr cred, IntPtr transport, string user, int methods) + { + return NativeMethods.git_transport_smart_credentials(out cred, transport, user, methods); } #endregion @@ -3055,7 +3213,6 @@ public static TreeEntrySafeHandle git_tree_entry_byindex(GitObjectSafeHandle tre public static TreeEntrySafeHandle_Owned git_tree_entry_bypath(RepositorySafeHandle repo, ObjectId id, FilePath treeentry_path) { - using (ThreadAffinity()) using (var obj = new ObjectSafeWrapper(id, repo)) { TreeEntrySafeHandle_Owned treeEntryPtr; @@ -3103,14 +3260,11 @@ public static int git_tree_entrycount(GitObjectSafeHandle tree) public static TreeBuilderSafeHandle git_treebuilder_new(RepositorySafeHandle repo) { - using (ThreadAffinity()) - { - TreeBuilderSafeHandle builder; - int res = NativeMethods.git_treebuilder_new(out builder, repo, IntPtr.Zero); - Ensure.ZeroResult(res); + TreeBuilderSafeHandle builder; + int res = NativeMethods.git_treebuilder_new(out builder, repo, IntPtr.Zero); + Ensure.ZeroResult(res); - return builder; - } + return builder; } public static void git_treebuilder_free(IntPtr bld) @@ -3120,24 +3274,19 @@ public static void git_treebuilder_free(IntPtr bld) public static void git_treebuilder_insert(TreeBuilderSafeHandle builder, string treeentry_name, TreeEntryDefinition treeEntryDefinition) { - using (ThreadAffinity()) - { - GitOid oid = treeEntryDefinition.TargetId.Oid; - int res = NativeMethods.git_treebuilder_insert(IntPtr.Zero, builder, treeentry_name, ref oid, (uint)treeEntryDefinition.Mode); - Ensure.ZeroResult(res); - } + GitOid oid = treeEntryDefinition.TargetId.Oid; + int res = NativeMethods.git_treebuilder_insert(IntPtr.Zero, builder, treeentry_name, ref oid, + (uint)treeEntryDefinition.Mode); + Ensure.ZeroResult(res); } public static ObjectId git_treebuilder_write(TreeBuilderSafeHandle bld) { - using (ThreadAffinity()) - { - GitOid oid; - int res = NativeMethods.git_treebuilder_write(out oid, bld); - Ensure.ZeroResult(res); + GitOid oid; + int res = NativeMethods.git_treebuilder_write(out oid, bld); + Ensure.ZeroResult(res); - return oid; - } + return oid; } #endregion @@ -3152,6 +3301,60 @@ public static BuiltInFeatures git_libgit2_features() return (BuiltInFeatures)NativeMethods.git_libgit2_features(); } + // C# equivalent of libgit2's git_libgit2_opt_t + private enum LibGitOption + { + GetMWindowSize, // GIT_OPT_GET_MWINDOW_SIZE + SetMWindowSize, // GIT_OPT_SET_MWINDOW_SIZE + GetMWindowMappedLimit, // GIT_OPT_GET_MWINDOW_MAPPED_LIMIT + SetMWindowMappedLimit, // GIT_OPT_SET_MWINDOW_MAPPED_LIMIT + GetSearchPath, // GIT_OPT_GET_SEARCH_PATH + SetSearchPath, // GIT_OPT_SET_SEARCH_PATH + SetCacheObjectLimit, // GIT_OPT_SET_CACHE_OBJECT_LIMIT + SetCacheMaxSize, // GIT_OPT_SET_CACHE_MAX_SIZE + EnableCaching, // GIT_OPT_ENABLE_CACHING + GetCachedMemory, // GIT_OPT_GET_CACHED_MEMORY + GetTemplatePath, // GIT_OPT_GET_TEMPLATE_PATH + SetTemplatePath, // GIT_OPT_SET_TEMPLATE_PATH + SetSslCertLocations, // GIT_OPT_SET_SSL_CERT_LOCATIONS + } + + /// + /// Get the paths under which libgit2 searches for the configuration file of a given level. + /// + /// The level (global/system/XDG) of the config. + /// + /// The paths delimited by 'GIT_PATH_LIST_SEPARATOR'. + /// + public static string git_libgit2_opts_get_search_path(ConfigurationLevel level) + { + string path; + + using (var buf = new GitBuf()) + { + var res = NativeMethods.git_libgit2_opts((int)LibGitOption.GetSearchPath, (uint)level, buf); + Ensure.ZeroResult(res); + + path = LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty; + } + + return path; + } + + /// + /// Set the path(s) under which libgit2 searches for the configuration file of a given level. + /// + /// The level (global/system/XDG) of the config. + /// + /// A string of paths delimited by 'GIT_PATH_LIST_SEPARATOR'. + /// Pass null to reset the search path to the default. + /// + public static void git_libgit2_opts_set_search_path(ConfigurationLevel level, string path) + { + var res = NativeMethods.git_libgit2_opts((int)LibGitOption.SetSearchPath, (uint)level, path); + Ensure.ZeroResult(res); + } + #endregion private static ICollection git_foreach( @@ -3159,23 +3362,20 @@ private static ICollection git_foreach( Func, int> iterator, params GitErrorCode[] ignoredErrorCodes) { - using (ThreadAffinity()) + var result = new List(); + var res = iterator((x, payload) => { - var result = new List(); - var res = iterator((x, payload) => - { - result.Add(resultSelector(x)); - return 0; - }); - - if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) - { - return new TResult[0]; - } + result.Add(resultSelector(x)); + return 0; + }); - Ensure.ZeroResult(res); - return result; + if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) + { + return new TResult[0]; } + + Ensure.ZeroResult(res); + return result; } private static ICollection git_foreach( @@ -3183,23 +3383,20 @@ private static ICollection git_foreach( Func, int> iterator, params GitErrorCode[] ignoredErrorCodes) { - using (ThreadAffinity()) + var result = new List(); + var res = iterator((x, y, payload) => { - var result = new List(); - var res = iterator((x, y, payload) => - { - result.Add(resultSelector(x, y)); - return 0; - }); + result.Add(resultSelector(x, y)); + return 0; + }); - if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) - { - return new TResult[0]; - } - - Ensure.ZeroResult(res); - return result; + if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) + { + return new TResult[0]; } + + Ensure.ZeroResult(res); + return result; } private static ICollection git_foreach( @@ -3207,23 +3404,20 @@ private static ICollection git_foreach( Func, int> iterator, params GitErrorCode[] ignoredErrorCodes) { - using (ThreadAffinity()) + var result = new List(); + var res = iterator((w, x, y, payload) => { - var result = new List(); - var res = iterator((w, x, y, payload) => - { - result.Add(resultSelector(w, x, y)); - return 0; - }); + result.Add(resultSelector(w, x, y)); + return 0; + }); - if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) - { - return new TResult[0]; - } - - Ensure.ZeroResult(res); - return result; + if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) + { + return new TResult[0]; } + + Ensure.ZeroResult(res); + return result; } public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); @@ -3233,23 +3427,20 @@ private static ICollection git_foreach( Func, int> iterator, params GitErrorCode[] ignoredErrorCodes) { - using (ThreadAffinity()) + var result = new List(); + var res = iterator((w, x, y, z, payload) => { - var result = new List(); - var res = iterator((w, x, y, z, payload) => - { - result.Add(resultSelector(w, x, y, z)); - return 0; - }); + result.Add(resultSelector(w, x, y, z)); + return 0; + }); - if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) - { - return new TResult[0]; - } - - Ensure.ZeroResult(res); - return result; + if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) + { + return new TResult[0]; } + + Ensure.ZeroResult(res); + return result; } private delegate int IteratorNew(out THandle iter); @@ -3301,32 +3492,25 @@ Func resultSelector where TIterator : SafeHandleBase where THandle : SafeHandleBase { - using (ThreadAffinity()) + using (var iter = git_iterator_new(newFunc)) { - using (var iter = git_iterator_new(newFunc)) + foreach (var next in git_iterator_next(iter, nextFunc, resultSelector)) { - foreach (var next in git_iterator_next(iter, nextFunc, resultSelector)) - { - yield return next; - } + yield return next; } } } private static bool RepositoryStateChecker(RepositorySafeHandle repo, Func checker) { - using (ThreadAffinity()) - { - int res = checker(repo); - Ensure.BooleanResult(res); + int res = checker(repo); + Ensure.BooleanResult(res); - return (res == 1); - } + return (res == 1); } private static FilePath ConvertPath(Func pathRetriever) { - using (ThreadAffinity()) using (var buf = new GitBuf()) { int result = pathRetriever(buf); @@ -3341,36 +3525,6 @@ private static FilePath ConvertPath(Func pathRetriever) } } - private static Func ThreadAffinity = WithoutThreadAffinity; - - internal static void EnableThreadAffinity() - { - ThreadAffinity = WithThreadAffinity; - } - - private static IDisposable WithoutThreadAffinity() - { - return null; - } - - private static IDisposable WithThreadAffinity() - { - return new DisposableThreadAffinityWrapper(); - } - - private class DisposableThreadAffinityWrapper : IDisposable - { - public DisposableThreadAffinityWrapper() - { - Thread.BeginThreadAffinity(); - } - - public void Dispose() - { - Thread.EndThreadAffinity(); - } - } - private static readonly IDictionary> configurationParser = new Dictionary> { { typeof(int), value => git_config_parse_int32(value) }, @@ -3392,5 +3546,46 @@ internal static int ConvertResultToCancelFlag(bool result) return result ? 0 : (int)GitErrorCode.User; } } + + /// + /// Class to hold extension methods used by the proxy class. + /// + static class ProxyExtensions + { + /// + /// Convert a UIntPtr to a int value. Will throw + /// exception if there is an overflow. + /// + /// + /// + public static int ConvertToInt(this UIntPtr input) + { + ulong ulongValue = (ulong)input; + if (ulongValue > int.MaxValue) + { + throw new LibGit2SharpException("value exceeds size of an int"); + } + + return (int)input; + } + + + /// + /// Convert a UIntPtr to a long value. Will throw + /// exception if there is an overflow. + /// + /// + /// + public static long ConvertToLong(this UIntPtr input) + { + ulong ulongValue = (ulong)input; + if (ulongValue > long.MaxValue) + { + throw new LibGit2SharpException("value exceeds size of long"); + } + + return (long)input; + } + } } // ReSharper restore InconsistentNaming diff --git a/LibGit2Sharp/Core/RawContentStream.cs b/LibGit2Sharp/Core/RawContentStream.cs index d02fe8b81..566eb5851 100644 --- a/LibGit2Sharp/Core/RawContentStream.cs +++ b/LibGit2Sharp/Core/RawContentStream.cs @@ -64,5 +64,5 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); Dispose(handle, linkedResources); } - } + } } diff --git a/LibGit2Sharp/Core/SubmoduleLazyGroup.cs b/LibGit2Sharp/Core/SubmoduleLazyGroup.cs index 0591574d4..10df34a41 100644 --- a/LibGit2Sharp/Core/SubmoduleLazyGroup.cs +++ b/LibGit2Sharp/Core/SubmoduleLazyGroup.cs @@ -15,11 +15,13 @@ public SubmoduleLazyGroup(Repository repo, string name) protected override void EvaluateInternal(Action evaluator) { - repo.Submodules.Lookup(name, handle => - { - evaluator(handle); - return default(object); - }, true); + repo.Submodules.Lookup(name, + handle => + { + evaluator(handle); + return default(object); + }, + true); } } } diff --git a/LibGit2Sharp/Core/TarWriter.cs b/LibGit2Sharp/Core/TarWriter.cs index 9f71db44a..7c479cec9 100644 --- a/LibGit2Sharp/Core/TarWriter.cs +++ b/LibGit2Sharp/Core/TarWriter.cs @@ -75,8 +75,21 @@ public void Write( WriteExtendedHeader(fileNameExtendedHeader, linkExtendedHeader, entrySha, modificationTime); // Note: in case of links, we won't add a content, but the size in the header will still be != 0. It seems strange, but it seem to be what git.git is doing? - WriteHeader(fileNameExtendedHeader.Name, fileNameExtendedHeader.Prefix, modificationTime, (data != null) ? data.Length : 0, mode, - userId, groupId, typeflag, linkExtendedHeader.Link, userName, groupName, deviceMajorNumber, deviceMinorNumber); + WriteHeader(fileNameExtendedHeader.Name, + fileNameExtendedHeader.Prefix, + modificationTime, + (data != null) + ? data.Length + : 0, + mode, + userId, + groupId, + typeflag, + linkExtendedHeader.Link, + userName, + groupName, + deviceMajorNumber, + deviceMinorNumber); // folders have no data, and so do links if (data != null && !isLink) @@ -94,7 +107,9 @@ protected static void WriteContent(long count, Stream data, Stream dest) { int bytesRead = data.Read(buffer, 0, buffer.Length); if (bytesRead < 0) + { throw new IOException("TarWriter unable to read from provided stream"); + } dest.Write(buffer, 0, bytesRead); count -= bytesRead; @@ -103,7 +118,10 @@ protected static void WriteContent(long count, Stream data, Stream dest) { int bytesRead = data.Read(buffer, 0, (int)count); if (bytesRead < 0) + { throw new IOException("TarWriter unable to read from provided stream"); + } + if (bytesRead == 0) { while (count > 0) @@ -113,7 +131,9 @@ protected static void WriteContent(long count, Stream data, Stream dest) } } else + { dest.Write(buffer, 0, bytesRead); + } } } @@ -143,8 +163,19 @@ protected void WriteHeader( string deviceMajorNumber, string deviceMinorNumber) { - var tarHeader = new UsTarHeader(fileName, namePrefix, lastModificationTime, count, mode, - userId, groupId, typeflag, link, userName, groupName, deviceMajorNumber, deviceMinorNumber); + var tarHeader = new UsTarHeader(fileName, + namePrefix, + lastModificationTime, + count, + mode, + userId, + groupId, + typeflag, + link, + userName, + groupName, + deviceMajorNumber, + deviceMinorNumber); var header = tarHeader.GetHeaderValue(); OutStream.Write(header, 0, header.Length); } @@ -168,7 +199,10 @@ private static LinkExtendedHeader ParseLink(bool isLink, Stream data, string ent if (data.Length > 100) { return new LinkExtendedHeader(link, - string.Format(CultureInfo.InvariantCulture, "see %s.paxheader{0}", entrySha), true); + string.Format(CultureInfo.InvariantCulture, + "see %s.paxheader{0}", + entrySha), + true); } return new LinkExtendedHeader(link, link, false); @@ -198,8 +232,20 @@ private void WriteExtendedHeader(FileNameExtendedHeader fileNameExtendedHeader, using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(extHeader))) { - Write(string.Format(CultureInfo.InvariantCulture, "{0}.paxheader", entrySha), stream, modificationTime, "666".OctalToInt32(), - "0", "0", 'x', "root", "root", "0", "0", entrySha, false); + Write(string.Format(CultureInfo.InvariantCulture, + "{0}.paxheader", + entrySha), + stream, modificationTime, + "666".OctalToInt32(), + "0", + "0", + 'x', + "root", + "root", + "0", + "0", + entrySha, + false); } } @@ -208,7 +254,9 @@ private static string BuildKeyValueExtHeader(string key, string value) // "%u %s=%s\n" int len = key.Length + value.Length + 3; for (int i = len; i > 9; i /= 10) + { len++; + } return string.Format(CultureInfo.InvariantCulture, "{0} {1}={2}\n", len, key, value); } @@ -410,8 +458,12 @@ public static FileNameExtendedHeader Parse(string posixPath, string entrySha) return new FileNameExtendedHeader(posixPath, posixPath.Substring(0, position), posixPath.Substring(position, posixPath.Length - position), false); } - return new FileNameExtendedHeader(posixPath, string.Empty, - string.Format(CultureInfo.InvariantCulture, "{0}.data", entrySha), true); + return new FileNameExtendedHeader(posixPath, + string.Empty, + string.Format(CultureInfo.InvariantCulture, + "{0}.data", + entrySha), + true); } return new FileNameExtendedHeader(posixPath, string.Empty, posixPath, false); diff --git a/LibGit2Sharp/Core/Utf8Marshaler.cs b/LibGit2Sharp/Core/Utf8Marshaler.cs index c623fe99f..c56a71c5f 100644 --- a/LibGit2Sharp/Core/Utf8Marshaler.cs +++ b/LibGit2Sharp/Core/Utf8Marshaler.cs @@ -27,8 +27,7 @@ internal class LaxUtf8NoCleanupMarshaler : LaxUtf8Marshaler #region ICustomMarshaler public override void CleanUpNativeData(IntPtr pNativeData) - { - } + { } #endregion } @@ -70,8 +69,9 @@ public static ICustomMarshaler GetInstance(String cookie) public override Object MarshalNativeToManaged(IntPtr pNativeData) { - throw new InvalidOperationException( - string.Format(CultureInfo.InvariantCulture, "{0} cannot be used to retrieve data from libgit2.", GetType().Name)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + "{0} cannot be used to retrieve data from libgit2.", + GetType().Name)); } #endregion @@ -91,9 +91,9 @@ internal class LaxUtf8Marshaler : EncodingMarshaler { private static readonly LaxUtf8Marshaler staticInstance = new LaxUtf8Marshaler(); - private static readonly Encoding encoding = new UTF8Encoding(false, false); + public static readonly Encoding Encoding = new UTF8Encoding(false, false); - public LaxUtf8Marshaler() : base(encoding) + public LaxUtf8Marshaler() : base(Encoding) { } public static ICustomMarshaler GetInstance(String cookie) @@ -105,30 +105,31 @@ public static ICustomMarshaler GetInstance(String cookie) public override IntPtr MarshalManagedToNative(object managedObj) { - throw new InvalidOperationException( - string.Format(CultureInfo.InvariantCulture, "{0} cannot be used to pass data to libgit2.", GetType().Name)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + "{0} cannot be used to pass data to libgit2.", + GetType().Name)); } #endregion public static string FromNative(IntPtr pNativeData) { - return FromNative(encoding, pNativeData); + return FromNative(Encoding, pNativeData); } public static string FromNative(IntPtr pNativeData, int length) { - return FromNative(encoding, pNativeData, length); + return FromNative(Encoding, pNativeData, length); } public static string FromBuffer(byte[] buffer) { - return FromBuffer(encoding, buffer); + return FromBuffer(Encoding, buffer); } public static string FromBuffer(byte[] buffer, int length) { - return FromBuffer(encoding, buffer, length); + return FromBuffer(Encoding, buffer, length); } } } diff --git a/LibGit2Sharp/Core/WriteStream.cs b/LibGit2Sharp/Core/WriteStream.cs new file mode 100644 index 000000000..6899c67d2 --- /dev/null +++ b/LibGit2Sharp/Core/WriteStream.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; + +namespace LibGit2Sharp.Core +{ + class WriteStream : Stream + { + readonly GitWriteStream nextStream; + readonly IntPtr nextPtr; + + public WriteStream(GitWriteStream nextStream, IntPtr nextPtr) + { + this.nextStream = nextStream; + this.nextPtr = nextPtr; + } + + public override bool CanWrite { get { return true; } } + + public override bool CanRead { get { return false; } } + + public override bool CanSeek { get { return false; } } + + public override long Position + { + get { throw new NotImplementedException(); } + set { throw new InvalidOperationException(); } + } + + public override long Length { get { throw new InvalidOperationException(); } } + + public override void Flush() + { } + + public override void SetLength(long value) + { + throw new InvalidOperationException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new InvalidOperationException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new InvalidOperationException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + int res; + unsafe + { + fixed (byte* bufferPtr = &buffer[offset]) + { + res = nextStream.write(nextPtr, (IntPtr)bufferPtr, (UIntPtr)count); + } + } + + Ensure.Int32Result(res); + } + } +} diff --git a/LibGit2Sharp/Credentials.cs b/LibGit2Sharp/Credentials.cs index b5540705e..50b006b74 100644 --- a/LibGit2Sharp/Credentials.cs +++ b/LibGit2Sharp/Credentials.cs @@ -1,5 +1,4 @@ using System; -using LibGit2Sharp.Core; namespace LibGit2Sharp { @@ -12,11 +11,7 @@ public abstract class Credentials /// Callback to acquire a credential object. /// /// The newly created credential object. - /// The resource for which we are demanding a credential. - /// The username that was embedded in a "user@host" - /// A bitmask stating which cred types are OK to return. - /// The payload provided when specifying this callback. /// 0 for success, < 0 to indicate an error, > 0 to indicate no credential was acquired. - protected internal abstract int GitCredentialHandler(out IntPtr cred, IntPtr url, IntPtr usernameFromUrl, GitCredentialType types, IntPtr payload); + protected internal abstract int GitCredentialHandler(out IntPtr cred); } } diff --git a/LibGit2Sharp/CurrentOperation.cs b/LibGit2Sharp/CurrentOperation.cs index 58f7064ae..9050e8235 100644 --- a/LibGit2Sharp/CurrentOperation.cs +++ b/LibGit2Sharp/CurrentOperation.cs @@ -21,39 +21,49 @@ public enum CurrentOperation /// Revert = 2, + /// + /// A sequencer revert is in progress. + /// + RevertSequence = 3, + /// /// A cherry-pick is in progress. /// - CherryPick = 3, + CherryPick = 4, + + /// + /// A sequencer cherry-pick is in progress. + /// + CherryPickSequence = 5, /// /// A bisect is in progress. /// - Bisect = 4, + Bisect = 6, /// /// A rebase is in progress. /// - Rebase = 5, + Rebase = 7, /// /// A rebase --interactive is in progress. /// - RebaseInteractive = 6, + RebaseInteractive = 8, /// /// A rebase --merge is in progress. /// - RebaseMerge = 7, + RebaseMerge = 9, /// /// A mailbox application (am) is in progress. /// - ApplyMailbox = 8, + ApplyMailbox = 10, /// /// A mailbox application (am) or rebase is in progress. /// - ApplyMailboxOrRebase = 9, + ApplyMailboxOrRebase = 11, } } diff --git a/LibGit2Sharp/DefaultCredentials.cs b/LibGit2Sharp/DefaultCredentials.cs index 4354bf8ad..b11b4f540 100644 --- a/LibGit2Sharp/DefaultCredentials.cs +++ b/LibGit2Sharp/DefaultCredentials.cs @@ -13,12 +13,8 @@ public sealed class DefaultCredentials : Credentials /// Callback to acquire a credential object. /// /// The newly created credential object. - /// The resource for which we are demanding a credential. - /// The username that was embedded in a "user@host" - /// A bitmask stating which cred types are OK to return. - /// The payload provided when specifying this callback. /// 0 for success, < 0 to indicate an error, > 0 to indicate no credential was acquired. - protected internal override int GitCredentialHandler(out IntPtr cred, IntPtr url, IntPtr usernameFromUrl, GitCredentialType types, IntPtr payload) + protected internal override int GitCredentialHandler(out IntPtr cred) { return NativeMethods.git_cred_default_new(out cred); } diff --git a/LibGit2Sharp/DescribeOptions.cs b/LibGit2Sharp/DescribeOptions.cs index db8f04655..3ca6f31eb 100644 --- a/LibGit2Sharp/DescribeOptions.cs +++ b/LibGit2Sharp/DescribeOptions.cs @@ -21,6 +21,7 @@ public DescribeOptions() { Strategy = DescribeStrategy.Default; MinimumCommitIdAbbreviatedSize = 7; + OnlyFollowFirstParent = false; } /// @@ -54,5 +55,14 @@ public DescribeOptions() /// /// public bool AlwaysRenderLongFormat { get; set; } + + /// + /// Follow only the first parent commit upon seeing a merge commit. + /// + /// This is useful when you wish to not match tags on branches merged in + /// the history of the target commit. + /// + /// + public bool OnlyFollowFirstParent { get; set; } } } diff --git a/LibGit2Sharp/DetachedHead.cs b/LibGit2Sharp/DetachedHead.cs index 094d9d263..d934db2c4 100644 --- a/LibGit2Sharp/DetachedHead.cs +++ b/LibGit2Sharp/DetachedHead.cs @@ -4,8 +4,7 @@ internal class DetachedHead : Branch { internal DetachedHead(Repository repo, Reference reference) : base(repo, reference, "(no branch)") - { - } + { } protected override string Shorten() { diff --git a/LibGit2Sharp/Diff.cs b/LibGit2Sharp/Diff.cs index 70544f4a8..88c1fdcfc 100644 --- a/LibGit2Sharp/Diff.cs +++ b/LibGit2Sharp/Diff.cs @@ -5,7 +5,6 @@ using System.Text; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; -using Environment = System.Environment; namespace LibGit2Sharp { @@ -50,6 +49,15 @@ private static GitDiffOptions BuildOptions(DiffModifiers diffOptions, FilePath[] options.Flags |= GitDiffOptionFlags.GIT_DIFF_INCLUDE_UNMODIFIED; } + if (compareOptions.Algorithm == DiffAlgorithm.Patience) + { + options.Flags |= GitDiffOptionFlags.GIT_DIFF_PATIENCE; + } + else if (compareOptions.Algorithm == DiffAlgorithm.Minimal) + { + options.Flags |= GitDiffOptionFlags.GIT_DIFF_MINIMAL; + } + if (diffOptions.HasFlag(DiffModifiers.DisablePathspecMatch)) { options.Flags |= GitDiffOptionFlags.GIT_DIFF_DISABLE_PATHSPEC_MATCH; @@ -98,6 +106,31 @@ private static IDictionary new PatchStats(diff) }, }; + + private static T BuildDiffResult(DiffSafeHandle diff) where T : class, IDiffResult + { + Func builder; + + if (!ChangesBuilders.TryGetValue(typeof(T), out builder)) + { + throw new LibGit2SharpException("User-defined types passed to Compare are not supported. Supported values are: {0}", + string.Join(", ", ChangesBuilders.Keys.Select(x => x.Name))); + } + + return (T)builder(diff); + } + + /// + /// Show changes between two s. + /// + /// The you want to compare from. + /// The you want to compare to. + /// A containing the changes between the and the . + public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob) + { + return Compare(oldBlob, newBlob, null); + } + /// /// Show changes between two s. /// @@ -105,7 +138,7 @@ private static IDictionaryThe you want to compare to. /// Additional options to define comparison behavior. /// A containing the changes between the and the . - public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions compareOptions = null) + public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions compareOptions) { using (GitDiffOptions options = BuildOptions(DiffModifiers.None, compareOptions: compareOptions)) { @@ -113,6 +146,29 @@ public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions } } + /// + /// Show changes between two s. + /// + /// The you want to compare from. + /// The you want to compare to. + /// A containing the changes between the and the . + public virtual T Compare(Tree oldTree, Tree newTree) where T : class, IDiffResult + { + return Compare(oldTree, newTree, null, null, null); + } + + /// + /// Show changes between two s. + /// + /// The you want to compare from. + /// The you want to compare to. + /// The list of paths (either files or directories) that should be compared. + /// A containing the changes between the and the . + public virtual T Compare(Tree oldTree, Tree newTree, IEnumerable paths) where T : class, IDiffResult + { + return Compare(oldTree, newTree, paths, null, null); + } + /// /// Show changes between two s. /// @@ -123,20 +179,53 @@ public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// + /// A containing the changes between the and the . + public virtual T Compare(Tree oldTree, Tree newTree, IEnumerable paths, + ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult + { + return Compare(oldTree, newTree, paths, explicitPathsOptions, null); + } + + /// + /// Show changes between two s. + /// + /// The you want to compare from. + /// The you want to compare to. + /// The list of paths (either files or directories) that should be compared. /// Additional options to define patch generation behavior. /// A containing the changes between the and the . - public virtual T Compare(Tree oldTree, Tree newTree, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null, - CompareOptions compareOptions = null) where T : class + public virtual T Compare(Tree oldTree, Tree newTree, IEnumerable paths, CompareOptions compareOptions) where T : class, IDiffResult { - Func builder; + return Compare(oldTree, newTree, paths, null, compareOptions); + } - if (!ChangesBuilders.TryGetValue(typeof (T), out builder)) - { - throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, - "Unexpected type '{0}' passed to Compare. Supported values are either '{1}' or '{2}'.", typeof (T), - typeof (TreeChanges), typeof (Patch))); - } + /// + /// Show changes between two s. + /// + /// The you want to compare from. + /// The you want to compare to. + /// Additional options to define patch generation behavior. + /// A containing the changes between the and the . + public virtual T Compare(Tree oldTree, Tree newTree, CompareOptions compareOptions) where T : class, IDiffResult + { + return Compare(oldTree, newTree, null, null, compareOptions); + } + /// + /// Show changes between two s. + /// + /// The you want to compare from. + /// The you want to compare to. + /// The list of paths (either files or directories) that should be compared. + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + /// Additional options to define patch generation behavior. + /// A containing the changes between the and the . + public virtual T Compare(Tree oldTree, Tree newTree, IEnumerable paths, ExplicitPathsOptions explicitPathsOptions, + CompareOptions compareOptions) where T : class, IDiffResult + { var comparer = TreeToTree(repo); ObjectId oldTreeId = oldTree != null ? oldTree.Id : null; ObjectId newTreeId = newTree != null ? newTree.Id : null; @@ -146,20 +235,53 @@ public virtual T Compare(Tree oldTree, Tree newTree, IEnumerable path { diffOptions |= DiffModifiers.DisablePathspecMatch; - if (explicitPathsOptions.ShouldFailOnUnmatchedPath || - explicitPathsOptions.OnUnmatchedPath != null) + if (explicitPathsOptions.ShouldFailOnUnmatchedPath || explicitPathsOptions.OnUnmatchedPath != null) { diffOptions |= DiffModifiers.IncludeUnmodified; } } - using (DiffSafeHandle diff = BuildDiffList(oldTreeId, newTreeId, comparer, - diffOptions, paths, explicitPathsOptions, compareOptions)) + using (DiffSafeHandle diff = BuildDiffList(oldTreeId, newTreeId, comparer, diffOptions, paths, explicitPathsOptions, compareOptions)) { - return (T)builder(diff); + return BuildDiffResult(diff); } } + /// + /// Show changes between a and the Index, the Working Directory, or both. + /// + /// The level of diff performed can be specified by passing either a + /// or type as the generic parameter. + /// + /// + /// The to compare from. + /// The targets to compare to. + /// Can be either a if you are only interested in the list of files modified, added, ..., or + /// a if you want the actual patch content for the whole diff and for individual files. + /// A containing the changes between the and the selected target. + public virtual T Compare(Tree oldTree, DiffTargets diffTargets) where T : class, IDiffResult + { + return Compare(oldTree, diffTargets, null, null, null); + } + + /// + /// Show changes between a and the Index, the Working Directory, or both. + /// + /// The level of diff performed can be specified by passing either a + /// or type as the generic parameter. + /// + /// + /// The to compare from. + /// The targets to compare to. + /// The list of paths (either files or directories) that should be compared. + /// Can be either a if you are only interested in the list of files modified, added, ..., or + /// a if you want the actual patch content for the whole diff and for individual files. + /// A containing the changes between the and the selected target. + public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable paths) where T : class, IDiffResult + { + return Compare(oldTree, diffTargets, paths, null, null); + } + /// /// Show changes between a and the Index, the Working Directory, or both. /// @@ -174,22 +296,36 @@ public virtual T Compare(Tree oldTree, Tree newTree, IEnumerable path /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// - /// Additional options to define patch generation behavior. /// Can be either a if you are only interested in the list of files modified, added, ..., or /// a if you want the actual patch content for the whole diff and for individual files. /// A containing the changes between the and the selected target. - public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable paths = null, - ExplicitPathsOptions explicitPathsOptions = null, CompareOptions compareOptions = null) where T : class + public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable paths, + ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult { - Func builder; - - if (!ChangesBuilders.TryGetValue(typeof (T), out builder)) - { - throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, - "Unexpected type '{0}' passed to Compare. Supported values are either '{1}' or '{2}'.", typeof (T), - typeof (TreeChanges), typeof (Patch))); - } + return Compare(oldTree, diffTargets, paths, explicitPathsOptions, null); + } + /// + /// Show changes between a and the Index, the Working Directory, or both. + /// + /// The level of diff performed can be specified by passing either a + /// or type as the generic parameter. + /// + /// + /// The to compare from. + /// The targets to compare to. + /// The list of paths (either files or directories) that should be compared. + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + /// Additional options to define patch generation behavior. + /// Can be either a if you are only interested in the list of files modified, added, ..., or + /// a if you want the actual patch content for the whole diff and for individual files. + /// A containing the changes between the and the selected target. + public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable paths, + ExplicitPathsOptions explicitPathsOptions, CompareOptions compareOptions) where T : class, IDiffResult + { var comparer = HandleRetrieverDispatcher[diffTargets](repo); ObjectId oldTreeId = oldTree != null ? oldTree.Id : null; @@ -201,20 +337,87 @@ public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable(diff); } } + /// + /// Show changes between the working directory and the index. + /// + /// The level of diff performed can be specified by passing either a + /// or type as the generic parameter. + /// + /// + /// Can be either a if you are only interested in the list of files modified, added, ..., or + /// a if you want the actual patch content for the whole diff and for individual files. + /// A containing the changes between the working directory and the index. + public virtual T Compare() where T : class, IDiffResult + { + return Compare(DiffModifiers.None); + } + + /// + /// Show changes between the working directory and the index. + /// + /// The level of diff performed can be specified by passing either a + /// or type as the generic parameter. + /// + /// + /// The list of paths (either files or directories) that should be compared. + /// Can be either a if you are only interested in the list of files modified, added, ..., or + /// a if you want the actual patch content for the whole diff and for individual files. + /// A containing the changes between the working directory and the index. + public virtual T Compare(IEnumerable paths) where T : class, IDiffResult + { + return Compare(DiffModifiers.None, paths); + } + + /// + /// Show changes between the working directory and the index. + /// + /// The level of diff performed can be specified by passing either a + /// or type as the generic parameter. + /// + /// + /// The list of paths (either files or directories) that should be compared. + /// If true, include untracked files from the working dir as additions. Otherwise ignore them. + /// Can be either a if you are only interested in the list of files modified, added, ..., or + /// a if you want the actual patch content for the whole diff and for individual files. + /// A containing the changes between the working directory and the index. + public virtual T Compare(IEnumerable paths, bool includeUntracked) where T : class, IDiffResult + { + return Compare(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths); + } + + /// + /// Show changes between the working directory and the index. + /// + /// The level of diff performed can be specified by passing either a + /// or type as the generic parameter. + /// + /// + /// The list of paths (either files or directories) that should be compared. + /// If true, include untracked files from the working dir as additions. Otherwise ignore them. + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + /// Can be either a if you are only interested in the list of files modified, added, ..., or + /// a if you want the actual patch content for the whole diff and for individual files. + /// A containing the changes between the working directory and the index. + public virtual T Compare(IEnumerable paths, bool includeUntracked, ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult + { + return Compare(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions); + } + /// /// Show changes between the working directory and the index. /// @@ -232,41 +435,36 @@ public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerableCan be either a if you are only interested in the list of files modified, added, ..., or /// a if you want the actual patch content for the whole diff and for individual files. /// A containing the changes between the working directory and the index. - public virtual T Compare(IEnumerable paths = null, bool includeUntracked = false, ExplicitPathsOptions explicitPathsOptions = null, - CompareOptions compareOptions = null) where T : class + public virtual T Compare( + IEnumerable paths, + bool includeUntracked, + ExplicitPathsOptions explicitPathsOptions, + CompareOptions compareOptions) where T : class, IDiffResult { return Compare(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions, compareOptions); } - internal virtual T Compare(DiffModifiers diffOptions, IEnumerable paths = null, - ExplicitPathsOptions explicitPathsOptions = null, CompareOptions compareOptions = null) where T : class + internal virtual T Compare( + DiffModifiers diffOptions, + IEnumerable paths = null, + ExplicitPathsOptions explicitPathsOptions = null, + CompareOptions compareOptions = null) where T : class, IDiffResult { - Func builder; - - if (!ChangesBuilders.TryGetValue(typeof (T), out builder)) - { - throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, - "Unexpected type '{0}' passed to Compare. Supported values are either '{1}' or '{2}'.", typeof (T), - typeof (TreeChanges), typeof (Patch))); - } - var comparer = WorkdirToIndex(repo); if (explicitPathsOptions != null) { diffOptions |= DiffModifiers.DisablePathspecMatch; - if (explicitPathsOptions.ShouldFailOnUnmatchedPath || - explicitPathsOptions.OnUnmatchedPath != null) + if (explicitPathsOptions.ShouldFailOnUnmatchedPath || explicitPathsOptions.OnUnmatchedPath != null) { diffOptions |= DiffModifiers.IncludeUnmodified; } } - using (DiffSafeHandle diff = BuildDiffList(null, null, comparer, - diffOptions, paths, explicitPathsOptions, compareOptions)) + using (DiffSafeHandle diff = BuildDiffList(null, null, comparer, diffOptions, paths, explicitPathsOptions, compareOptions)) { - return (T)builder(diff); + return BuildDiffResult(diff); } } @@ -309,8 +507,13 @@ private static TreeComparisonHandleRetriever IndexToTree(Repository repo) return (oh, nh, o) => Proxy.git_diff_tree_to_index(repo.Handle, repo.Index.Handle, oh, o); } - private DiffSafeHandle BuildDiffList(ObjectId oldTreeId, ObjectId newTreeId, TreeComparisonHandleRetriever comparisonHandleRetriever, - DiffModifiers diffOptions, IEnumerable paths, ExplicitPathsOptions explicitPathsOptions, + private DiffSafeHandle BuildDiffList( + ObjectId oldTreeId, + ObjectId newTreeId, + TreeComparisonHandleRetriever comparisonHandleRetriever, + DiffModifiers diffOptions, + IEnumerable paths, + ExplicitPathsOptions explicitPathsOptions, CompareOptions compareOptions) { var matchedPaths = new MatchedPathsAggregator(); @@ -342,8 +545,7 @@ private DiffSafeHandle BuildDiffList(ObjectId oldTreeId, ObjectId newTreeId, Tre private static void DetectRenames(DiffSafeHandle diffList, CompareOptions compareOptions) { var similarityOptions = (compareOptions == null) ? null : compareOptions.Similarity; - if (similarityOptions == null || - similarityOptions.RenameDetectionMode == RenameDetectionMode.Default) + if (similarityOptions == null || similarityOptions.RenameDetectionMode == RenameDetectionMode.Default) { Proxy.git_diff_find_similar(diffList, null); return; @@ -406,9 +608,10 @@ private static void DetectRenames(DiffSafeHandle diffList, CompareOptions compar Proxy.git_diff_find_similar(diffList, opts); } - private static void DispatchUnmatchedPaths(ExplicitPathsOptions explicitPathsOptions, - IEnumerable filePaths, - IEnumerable matchedPaths) + private static void DispatchUnmatchedPaths( + ExplicitPathsOptions explicitPathsOptions, + IEnumerable filePaths, + IEnumerable matchedPaths) { List unmatchedPaths = (filePaths != null ? filePaths.Except(matchedPaths) : Enumerable.Empty()).ToList(); diff --git a/LibGit2Sharp/DiffAlgorithm.cs b/LibGit2Sharp/DiffAlgorithm.cs new file mode 100644 index 000000000..07370e5a9 --- /dev/null +++ b/LibGit2Sharp/DiffAlgorithm.cs @@ -0,0 +1,23 @@ +namespace LibGit2Sharp +{ + /// + /// Algorithm used when performing a Diff. + /// + public enum DiffAlgorithm + { + /// + /// The basic greedy diff algorithm. + /// + Myers = 0, + + /// + /// Use "minimal diff" algorithm when generating patches. + /// + Minimal = 1, + + /// + /// Use "patience diff" algorithm when generating patches. + /// + Patience = 2, + } +} diff --git a/LibGit2Sharp/DirectReference.cs b/LibGit2Sharp/DirectReference.cs index 00de258a6..b9cc304b3 100644 --- a/LibGit2Sharp/DirectReference.cs +++ b/LibGit2Sharp/DirectReference.cs @@ -18,12 +18,21 @@ protected DirectReference() internal DirectReference(string canonicalName, IRepository repo, ObjectId targetId) : base(repo, canonicalName, targetId.Sha) { - targetBuilder = new Lazy(() => repo.Lookup(targetId)); + targetBuilder = new Lazy(() => + { + if (repo == null) + { + throw new InvalidOperationException("Target requires a local repository"); + } + + return repo.Lookup(targetId); + }); } /// /// Gets the target of this /// + /// Throws if Local Repository is not set. public virtual GitObject Target { get { return targetBuilder.Value; } diff --git a/LibGit2Sharp/EmptyCommitException.cs b/LibGit2Sharp/EmptyCommitException.cs index aa54a5f3c..8cd48e49f 100644 --- a/LibGit2Sharp/EmptyCommitException.cs +++ b/LibGit2Sharp/EmptyCommitException.cs @@ -14,8 +14,7 @@ public class EmptyCommitException : LibGit2SharpException /// Initializes a new instance of the class. /// public EmptyCommitException() - { - } + { } /// /// Initializes a new instance of the class with a specified error message. @@ -23,8 +22,16 @@ public EmptyCommitException() /// A message that describes the error. public EmptyCommitException(string message) : base(message) - { - } + { } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A composite format string for use in . + /// An object array that contains zero or more objects to format. + public EmptyCommitException(string format, params object[] args) + : base(format, args) + { } /// /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. @@ -33,8 +40,7 @@ public EmptyCommitException(string message) /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. public EmptyCommitException(string message, Exception innerException) : base(message, innerException) - { - } + { } /// /// Initializes a new instance of the class with a serialized data. @@ -43,7 +49,6 @@ public EmptyCommitException(string message, Exception innerException) /// The that contains contextual information about the source or destination. protected EmptyCommitException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + { } } } diff --git a/LibGit2Sharp/EntryExistsException.cs b/LibGit2Sharp/EntryExistsException.cs index 9dff4d750..2c46e4acd 100644 --- a/LibGit2Sharp/EntryExistsException.cs +++ b/LibGit2Sharp/EntryExistsException.cs @@ -14,8 +14,7 @@ public class EntryExistsException : LibGit2SharpException /// Initializes a new instance of the class. /// public EntryExistsException() - { - } + { } /// /// Initializes a new instance of the class with a specified error message. @@ -23,8 +22,16 @@ public EntryExistsException() /// A message that describes the error. public EntryExistsException(string message) : base(message) - { - } + { } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A composite format string for use in . + /// An object array that contains zero or more objects to format. + public EntryExistsException(string format, params object[] args) + : base(format, args) + { } /// /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. @@ -33,8 +40,7 @@ public EntryExistsException(string message) /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. public EntryExistsException(string message, Exception innerException) : base(message, innerException) - { - } + { } /// /// Initializes a new instance of the class with a serialized data. @@ -43,12 +49,10 @@ public EntryExistsException(string message, Exception innerException) /// The that contains contextual information about the source or destination. protected EntryExistsException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + { } internal EntryExistsException(string message, GitErrorCode code, GitErrorCategory category) : base(message, code, category) - { - } + { } } } diff --git a/LibGit2Sharp/FetchHead.cs b/LibGit2Sharp/FetchHead.cs index 75f12ae01..456abedc2 100644 --- a/LibGit2Sharp/FetchHead.cs +++ b/LibGit2Sharp/FetchHead.cs @@ -15,11 +15,20 @@ internal class FetchHead : ReferenceWrapper protected FetchHead() { } - internal FetchHead(Repository repo, string remoteCanonicalName, - string url, ObjectId targetId, bool forMerge, int index) - : base(repo, new DirectReference( - string.Format(CultureInfo.InvariantCulture, "FETCH_HEAD[{0}]", index), - repo, targetId), r => r.CanonicalName) + internal FetchHead( + Repository repo, + string remoteCanonicalName, + string url, + ObjectId targetId, + bool forMerge, + int index) + : base(repo, + new DirectReference(string.Format(CultureInfo.InvariantCulture, + "FETCH_HEAD[{0}]", + index), + repo, + targetId), + r => r.CanonicalName) { Url = url; ForMerge = forMerge; diff --git a/LibGit2Sharp/FetchOptions.cs b/LibGit2Sharp/FetchOptions.cs index ec42a2d6e..a9d2fb2c7 100644 --- a/LibGit2Sharp/FetchOptions.cs +++ b/LibGit2Sharp/FetchOptions.cs @@ -1,7 +1,4 @@ -using System; -using LibGit2Sharp.Handlers; - -namespace LibGit2Sharp +namespace LibGit2Sharp { /// /// Collection of parameters controlling Fetch behavior. @@ -19,5 +16,14 @@ public sealed class FetchOptions : FetchOptionsBase /// retrieved during this fetch will be retrieved as well). /// public TagFetchMode? TagFetchMode { get; set; } + + /// + /// Specifies the pruning behaviour for the fetch operation + /// + /// If not set, the configuration's setting will take effect. If true, the branches which no longer + /// exist on the remote will be removed from the remote-tracking branches. + /// + /// + public bool? Prune { get; set; } } } diff --git a/LibGit2Sharp/FetchOptionsBase.cs b/LibGit2Sharp/FetchOptionsBase.cs index c2f2aeb44..7b946e9e1 100644 --- a/LibGit2Sharp/FetchOptionsBase.cs +++ b/LibGit2Sharp/FetchOptionsBase.cs @@ -8,8 +8,7 @@ namespace LibGit2Sharp public abstract class FetchOptionsBase { internal FetchOptionsBase() - { - } + { } /// /// Handler for network transfer and indexing progress information. @@ -33,5 +32,21 @@ internal FetchOptionsBase() /// Handler to generate for authentication. /// public CredentialsHandler CredentialsProvider { get; set; } + + /// + /// This hanlder will be called to let the user make a decision on whether to allow + /// the connection to preoceed based on the certificate presented by the server. + /// + public CertificateCheckHandler CertificateCheck { get; set; } + + /// + /// Starting to operate on a new repository. + /// + public RepositoryOperationStarting RepositoryOperationStarting { get; set; } + + /// + /// Completed operating on the current repository. + /// + public RepositoryOperationCompleted RepositoryOperationCompleted { get; set; } } } diff --git a/LibGit2Sharp/FileStatus.cs b/LibGit2Sharp/FileStatus.cs index 68e41b7b5..b07323449 100644 --- a/LibGit2Sharp/FileStatus.cs +++ b/LibGit2Sharp/FileStatus.cs @@ -21,18 +21,36 @@ public enum FileStatus /// /// New file has been added to the Index. It's unknown from the Head. /// + [Obsolete("This enum member will be removed in the next release. Please use NewInIndex instead.")] Added = (1 << 0), /* GIT_STATUS_INDEX_NEW */ + /// + /// New file has been added to the Index. It's unknown from the Head. + /// + NewInIndex = (1 << 0), /* GIT_STATUS_INDEX_NEW */ + /// /// New version of a file has been added to the Index. A previous version exists in the Head. /// + [Obsolete("This enum member will be removed in the next release. Please use ModifiedInIndex instead.")] Staged = (1 << 1), /* GIT_STATUS_INDEX_MODIFIED */ + /// + /// New version of a file has been added to the Index. A previous version exists in the Head. + /// + ModifiedInIndex = (1 << 1), /* GIT_STATUS_INDEX_MODIFIED */ + /// /// The deletion of a file has been promoted from the working directory to the Index. A previous version exists in the Head. /// + [Obsolete("This enum member will be removed in the next release. Please use DeletedFromIndex instead.")] Removed = (1 << 2), /* GIT_STATUS_INDEX_DELETED */ + /// + /// The deletion of a file has been promoted from the working directory to the Index. A previous version exists in the Head. + /// + DeletedFromIndex = (1 << 2), /* GIT_STATUS_INDEX_DELETED */ + /// /// The renaming of a file has been promoted from the working directory to the Index. A previous version exists in the Head. /// @@ -41,32 +59,62 @@ public enum FileStatus /// /// A change in type for a file has been promoted from the working directory to the Index. A previous version exists in the Head. /// + [Obsolete("This enum member will be removed in the next release. Please use TypeChangeInIndex instead.")] StagedTypeChange = (1 << 4), /* GIT_STATUS_INDEX_TYPECHANGE */ + /// + /// A change in type for a file has been promoted from the working directory to the Index. A previous version exists in the Head. + /// + TypeChangeInIndex = (1 << 4), /* GIT_STATUS_INDEX_TYPECHANGE */ + /// /// New file in the working directory, unknown from the Index and the Head. /// + [Obsolete("This enum member will be removed in the next release. Please use NewInWorkdir instead.")] Untracked = (1 << 7), /* GIT_STATUS_WT_NEW */ + /// + /// New file in the working directory, unknown from the Index and the Head. + /// + NewInWorkdir = (1 << 7), /* GIT_STATUS_WT_NEW */ + /// /// The file has been updated in the working directory. A previous version exists in the Index. /// + [Obsolete("This enum member will be removed in the next release. Please use ModifiedInWorkdir instead.")] Modified = (1 << 8), /* GIT_STATUS_WT_MODIFIED */ + /// + /// The file has been updated in the working directory. A previous version exists in the Index. + /// + ModifiedInWorkdir = (1 << 8), /* GIT_STATUS_WT_MODIFIED */ + /// /// The file has been deleted from the working directory. A previous version exists in the Index. /// + [Obsolete("This enum member will be removed in the next release. Please use DeletedFromWorkdir instead.")] Missing = (1 << 9), /* GIT_STATUS_WT_DELETED */ + /// + /// The file has been deleted from the working directory. A previous version exists in the Index. + /// + DeletedFromWorkdir = (1 << 9), /* GIT_STATUS_WT_DELETED */ + /// /// The file type has been changed in the working directory. A previous version exists in the Index. /// + [Obsolete("This enum member will be removed in the next release. Please use TypeChangeInWorkdir instead.")] TypeChanged = (1 << 10), /* GIT_STATUS_WT_TYPECHANGE */ + /// + /// The file type has been changed in the working directory. A previous version exists in the Index. + /// + TypeChangeInWorkdir = (1 << 10), /* GIT_STATUS_WT_TYPECHANGE */ + /// /// The file has been renamed in the working directory. The previous version at the previous name exists in the Index. /// - RenamedInWorkDir = (1 << 11), /* GIT_STATUS_WT_RENAMED */ + RenamedInWorkdir = (1 << 11), /* GIT_STATUS_WT_RENAMED */ /// /// The file is unreadable in the working directory. @@ -74,8 +122,13 @@ public enum FileStatus Unreadable = (1 << 12), /* GIT_STATUS_WT_UNREADABLE */ /// - /// The file is but its name and/or path matches an exclude pattern in a gitignore file. + /// The file is but its name and/or path matches an exclude pattern in a gitignore file. /// Ignored = (1 << 14), /* GIT_STATUS_IGNORED */ + + /// + /// The file is due to a merge. + /// + Conflicted = (1 << 15), /* GIT_STATUS_CONFLICTED */ } } diff --git a/LibGit2Sharp/Filter.cs b/LibGit2Sharp/Filter.cs new file mode 100644 index 000000000..b66889a12 --- /dev/null +++ b/LibGit2Sharp/Filter.cs @@ -0,0 +1,404 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// A filter is a way to execute code against a file as it moves to and from the git + /// repository and into the working directory. + /// + public abstract class Filter : IEquatable + { + private static readonly LambdaEqualityHelper equalityHelper = + new LambdaEqualityHelper(x => x.Name, x => x.Attributes); + // 64K is optimal buffer size per https://technet.microsoft.com/en-us/library/cc938632.aspx + private const int BufferSize = 64 * 1024; + + /// + /// Initializes a new instance of the class. + /// And allocates the filter natively. + /// The unique name with which this filtered is registered with + /// A list of attributes which this filter applies to + /// + protected Filter(string name, IEnumerable attributes) + { + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(attributes, "attributes"); + + this.name = name; + this.attributes = attributes; + var attributesAsString = string.Join(",", this.attributes.Select(attr => attr.FilterDefinition)); + + gitFilter = new GitFilter + { + attributes = EncodingMarshaler.FromManaged(Encoding.UTF8, attributesAsString), + init = InitializeCallback, + stream = StreamCreateCallback, + }; + } + /// + /// Finalizer called by the , deregisters and frees native memory associated with the registered filter in libgit2. + /// + ~Filter() + { + GlobalSettings.DeregisterFilter(this); + +#if LEAKS_IDENTIFYING + int activeStreamCount = activeStreams.Count; + if (activeStreamCount > 0) + { + Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} leaked {1} stream handles at finalization", GetType().Name, activeStreamCount)); + } +#endif + } + + private readonly string name; + private readonly IEnumerable attributes; + private readonly GitFilter gitFilter; + private readonly ConcurrentDictionary activeStreams = new ConcurrentDictionary(); + + /// + /// State bag used to keep necessary reference from being + /// garbage collected during filter processing. + /// + private class StreamState + { + public GitWriteStream thisStream; + public GitWriteStream nextStream; + public IntPtr thisPtr; + public IntPtr nextPtr; + public FilterSource filterSource; + public Stream output; + } + + /// + /// The name that this filter was registered with + /// + public string Name + { + get { return name; } + } + + /// + /// The filter filterForAttributes. + /// + public IEnumerable Attributes + { + get { return attributes; } + } + + /// + /// The marshalled filter + /// + internal GitFilter GitFilter + { + get { return gitFilter; } + } + + /// + /// Complete callback on filter + /// + /// This optional callback will be invoked when the upstream filter is + /// closed. Gives the filter a chance to perform any final actions or + /// necissary clean up. + /// + /// The path of the file being filtered + /// The path of the working directory for the owning repository + /// Output to the downstream filter or output writer + protected virtual void Complete(string path, string root, Stream output) + { } + + /// + /// Initialize callback on filter + /// + /// Specified as `filter.initialize`, this is an optional callback invoked + /// before a filter is first used. It will be called once at most. + /// + /// If non-NULL, the filter's `initialize` callback will be invoked right + /// before the first use of the filter, so you can defer expensive + /// initialization operations (in case the library is being used in a way + /// that doesn't need the filter. + /// + protected virtual void Initialize() + { } + + /// + /// Indicates that a filter is going to be applied for the given file for + /// the given mode. + /// + /// The path of the file being filtered + /// The path of the working directory for the owning repository + /// The filter mode + protected virtual void Create(string path, string root, FilterMode mode) + { } + + /// + /// Clean the input stream and write to the output stream. + /// + /// The path of the file being filtered + /// The path of the working directory for the owning repository + /// Input from the upstream filter or input reader + /// Output to the downstream filter or output writer + protected virtual void Clean(string path, string root, Stream input, Stream output) + { + input.CopyTo(output); + } + + /// + /// Smudge the input stream and write to the output stream. + /// + /// The path of the file being filtered + /// The path of the working directory for the owning repository + /// Input from the upstream filter or input reader + /// Output to the downstream filter or output writer + protected virtual void Smudge(string path, string root, Stream input, Stream output) + { + input.CopyTo(output); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. + public override bool Equals(object obj) + { + return Equals(obj as Filter); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. + public bool Equals(Filter other) + { + return equalityHelper.Equals(this, other); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return equalityHelper.GetHashCode(this); + } + + /// + /// Tests if two are equal. + /// + /// First to compare. + /// Second to compare. + /// True if the two objects are equal; false otherwise. + public static bool operator ==(Filter left, Filter right) + { + return Equals(left, right); + } + + /// + /// Tests if two are different. + /// + /// First to compare. + /// Second to compare. + /// True if the two objects are different; false otherwise. + public static bool operator !=(Filter left, Filter right) + { + return !Equals(left, right); + } + + /// + /// Initialize callback on filter + /// + /// Specified as `filter.initialize`, this is an optional callback invoked + /// before a filter is first used. It will be called once at most. + /// + /// If non-NULL, the filter's `initialize` callback will be invoked right + /// before the first use of the filter, so you can defer expensive + /// initialization operations (in case libgit2 is being used in a way that doesn't need the filter). + /// + int InitializeCallback(IntPtr filterPointer) + { + int result = 0; + try + { + Initialize(); + } + catch (Exception exception) + { + Log.Write(LogLevel.Error, "Filter.InitializeCallback exception"); + Log.Write(LogLevel.Error, exception.ToString()); + Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + result = (int)GitErrorCode.Error; + } + return result; + } + + int StreamCreateCallback(out IntPtr git_writestream_out, GitFilter self, IntPtr payload, IntPtr filterSourcePtr, IntPtr git_writestream_next) + { + int result = 0; + var state = new StreamState(); + + try + { + Ensure.ArgumentNotZeroIntPtr(filterSourcePtr, "filterSourcePtr"); + Ensure.ArgumentNotZeroIntPtr(git_writestream_next, "git_writestream_next"); + + state.thisStream = new GitWriteStream(); + state.thisStream.close = StreamCloseCallback; + state.thisStream.write = StreamWriteCallback; + state.thisStream.free = StreamFreeCallback; + + state.thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf(state.thisStream)); + Marshal.StructureToPtr(state.thisStream, state.thisPtr, false); + + state.nextPtr = git_writestream_next; + state.nextStream = new GitWriteStream(); + Marshal.PtrToStructure(state.nextPtr, state.nextStream); + + state.filterSource = FilterSource.FromNativePtr(filterSourcePtr); + state.output = new WriteStream(state.nextStream, state.nextPtr); + + Create(state.filterSource.Path, state.filterSource.Root, state.filterSource.SourceMode); + + if (!activeStreams.TryAdd(state.thisPtr, state)) + { + // AFAICT this is a theoretical error that could only happen if we manage + // to free the stream pointer but fail to remove the dictionary entry. + throw new InvalidOperationException("Overlapping stream pointers"); + } + } + catch (Exception exception) + { + // unexpected failures means memory clean up required + if (state.thisPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(state.thisPtr); + state.thisPtr = IntPtr.Zero; + } + + Log.Write(LogLevel.Error, "Filter.StreamCreateCallback exception"); + Log.Write(LogLevel.Error, exception.ToString()); + Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + result = (int)GitErrorCode.Error; + } + + git_writestream_out = state.thisPtr; + + return result; + } + + int StreamCloseCallback(IntPtr stream) + { + int result = 0; + StreamState state; + + try + { + Ensure.ArgumentNotZeroIntPtr(stream, "stream"); + + if(!activeStreams.TryGetValue(stream, out state)) + { + throw new ArgumentException("Unknown stream pointer", "stream"); + } + + Ensure.ArgumentIsExpectedIntPtr(stream, state.thisPtr, "stream"); + + using (BufferedStream outputBuffer = new BufferedStream(state.output, BufferSize)) + { + Complete(state.filterSource.Path, state.filterSource.Root, outputBuffer); + } + + result = state.nextStream.close(state.nextPtr); + } + catch (Exception exception) + { + Log.Write(LogLevel.Error, "Filter.StreamCloseCallback exception"); + Log.Write(LogLevel.Error, exception.ToString()); + Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + result = (int)GitErrorCode.Error; + } + + return result; + } + + void StreamFreeCallback(IntPtr stream) + { + StreamState state; + + try + { + Ensure.ArgumentNotZeroIntPtr(stream, "stream"); + + if (!activeStreams.TryRemove(stream, out state)) + { + throw new ArgumentException("Double free or invalid stream pointer", "stream"); + } + + Ensure.ArgumentIsExpectedIntPtr(stream, state.thisPtr, "stream"); + + Marshal.FreeHGlobal(state.thisPtr); + } + catch (Exception exception) + { + Log.Write(LogLevel.Error, "Filter.StreamFreeCallback exception"); + Log.Write(LogLevel.Error, exception.ToString()); + } + } + + unsafe int StreamWriteCallback(IntPtr stream, IntPtr buffer, UIntPtr len) + { + int result = 0; + StreamState state; + + try + { + Ensure.ArgumentNotZeroIntPtr(stream, "stream"); + Ensure.ArgumentNotZeroIntPtr(buffer, "buffer"); + + if (!activeStreams.TryGetValue(stream, out state)) + { + throw new ArgumentException("Invalid or already freed stream pointer", "stream"); + } + + Ensure.ArgumentIsExpectedIntPtr(stream, state.thisPtr, "stream"); + + using (UnmanagedMemoryStream input = new UnmanagedMemoryStream((byte*)buffer.ToPointer(), (long)len)) + using (BufferedStream outputBuffer = new BufferedStream(state.output, BufferSize)) + { + switch (state.filterSource.SourceMode) + { + case FilterMode.Clean: + Clean(state.filterSource.Path, state.filterSource.Root, input, outputBuffer); + break; + + case FilterMode.Smudge: + Smudge(state.filterSource.Path, state.filterSource.Root, input, outputBuffer); + break; + + default: + Proxy.giterr_set_str(GitErrorCategory.Filter, "Unexpected filter mode."); + return (int)GitErrorCode.Ambiguous; + } + } + } + catch (Exception exception) + { + Log.Write(LogLevel.Error, "Filter.StreamWriteCallback exception"); + Log.Write(LogLevel.Error, exception.ToString()); + Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + result = (int)GitErrorCode.Error; + } + + return result; + } + } +} diff --git a/LibGit2Sharp/FilterAttributeEntry.cs b/LibGit2Sharp/FilterAttributeEntry.cs new file mode 100644 index 000000000..117523d3e --- /dev/null +++ b/LibGit2Sharp/FilterAttributeEntry.cs @@ -0,0 +1,53 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// The definition for a given filter found in the .gitattributes file. + /// The filter definition will result as 'filter=filterName' + /// + /// In the .gitattributes file a filter will be matched to a pathspec like so + /// '*.txt filter=filterName' + /// + public class FilterAttributeEntry + { + private const string AttributeFilterDefinition = "filter="; + + private readonly string filterDefinition; + + /// + /// For testing purposes + /// + protected FilterAttributeEntry() { } + + /// + /// The name of the filter found in a .gitattributes file. + /// + /// The name of the filter as found in the .gitattributes file without the "filter=" prefix + /// + /// "filter=" will be prepended to the filterDefinition, therefore the "filter=" portion of the filter + /// name shouldbe omitted on declaration. Inclusion of the "filter=" prefix will cause the FilterDefinition to + /// fail to match the .gitattributes entry and thefore no be invoked correctly. + /// + public FilterAttributeEntry(string filterName) + { + Ensure.ArgumentNotNullOrEmptyString(filterName, "filterName"); + if (filterName.StartsWith("filter=", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException("The filterName parameter should not begin with \"filter=\"", filterName); + } + + filterName = AttributeFilterDefinition + filterName; + this.filterDefinition = filterName; + } + + /// + /// The filter name in the form of 'filter=filterName' + /// + public virtual string FilterDefinition + { + get { return filterDefinition; } + } + } +} diff --git a/LibGit2Sharp/FilterMode.cs b/LibGit2Sharp/FilterMode.cs new file mode 100644 index 000000000..31a9546c1 --- /dev/null +++ b/LibGit2Sharp/FilterMode.cs @@ -0,0 +1,23 @@ +namespace LibGit2Sharp +{ + /// + /// These values control which direction of change is with which which a filter is being applied. + /// + /// + /// These enum values must be identical to the values in Libgit2 filter_mode_t found in filter.h + /// + public enum FilterMode + { + /// + /// Smudge occurs when exporting a file from the Git object database to the working directory. + /// For example, a file would be smudged during a checkout operation. + /// + Smudge = 0, + + /// + /// Clean occurs when importing a file from the working directory to the Git object database. + /// For example, a file would be cleaned when staging a file. + /// + Clean = 1, + } +} diff --git a/LibGit2Sharp/FilterRegistration.cs b/LibGit2Sharp/FilterRegistration.cs new file mode 100644 index 000000000..f6c1e31d0 --- /dev/null +++ b/LibGit2Sharp/FilterRegistration.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// An object representing the registration of a Filter type with libgit2 + /// + public sealed class FilterRegistration + { + /// + /// Maximum priority value a filter can have. A value of 200 will be run last on checkout and first on checkin. + /// + public const int FilterPriorityMax = 200; + /// + /// Minimum priority value a filter can have. A value of 0 will be run first on checkout and last on checkin. + /// + public const int FilterPriorityMin = 0; + + /// + /// + /// + /// + /// + internal FilterRegistration(Filter filter, int priority) + { + System.Diagnostics.Debug.Assert(filter != null); + System.Diagnostics.Debug.Assert(priority >= FilterPriorityMin && priority <= FilterPriorityMax); + + Filter = filter; + Priority = priority; + + // marshal the git_filter strucutre into native memory + FilterPointer = Marshal.AllocHGlobal(Marshal.SizeOf(filter.GitFilter)); + Marshal.StructureToPtr(filter.GitFilter, FilterPointer, false); + + // register the filter with the native libary + Proxy.git_filter_register(filter.Name, FilterPointer, priority); + } + /// + /// Finalizer called by the , deregisters and frees native memory associated with the registered filter in libgit2. + /// + ~FilterRegistration() + { + // deregister the filter + GlobalSettings.DeregisterFilter(this); + // clean up native allocations + Free(); + } + + /// + /// Gets if the registration and underlying filter are valid. + /// + public bool IsValid { get { return !freed; } } + /// + /// The registerd filters + /// + public readonly Filter Filter; + /// + /// The name of the filter in the libgit2 registry + /// + public string Name { get { return Filter.Name; } } + /// + /// The priority of the registered filter + /// + public readonly int Priority; + + private readonly IntPtr FilterPointer; + + private bool freed; + + internal void Free() + { + if (!freed) + { + // unregister the filter with the native libary + Proxy.git_filter_unregister(Filter.Name); + // release native memory + Marshal.FreeHGlobal(FilterPointer); + // remember to not do this twice + freed = true; + } + } + } +} diff --git a/LibGit2Sharp/FilterSource.cs b/LibGit2Sharp/FilterSource.cs new file mode 100644 index 000000000..0843e6221 --- /dev/null +++ b/LibGit2Sharp/FilterSource.cs @@ -0,0 +1,57 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// A filter source - describes the direction of filtering and the file being filtered. + /// + public class FilterSource + { + /// + /// Needed for mocking purposes + /// + protected FilterSource() { } + + internal FilterSource(FilePath path, FilterMode mode, GitFilterSource source) + { + SourceMode = mode; + ObjectId = new ObjectId(source.oid); + Path = path.Native; + Root = Proxy.git_repository_workdir(source.repository).Native; + } + + /// + /// Take an unmanaged pointer and convert it to filter source callback paramater + /// + /// + /// + internal static FilterSource FromNativePtr(IntPtr ptr) + { + var source = ptr.MarshalAs(); + FilePath path = LaxFilePathMarshaler.FromNative(source.path) ?? FilePath.Empty; + FilterMode gitFilterSourceMode = Proxy.git_filter_source_mode(ptr); + return new FilterSource(path, gitFilterSourceMode, source); + } + + /// + /// The filter mode for current file being filtered + /// + public virtual FilterMode SourceMode { get; private set; } + + /// + /// The relative path to the file + /// + public virtual string Path { get; private set; } + + /// + /// The blob id + /// + public virtual ObjectId ObjectId { get; private set; } + + /// + /// The working directory + /// + public virtual string Root { get; private set; } + } +} diff --git a/LibGit2Sharp/FilteringOptions.cs b/LibGit2Sharp/FilteringOptions.cs index f06d5a2a4..22988e62e 100644 --- a/LibGit2Sharp/FilteringOptions.cs +++ b/LibGit2Sharp/FilteringOptions.cs @@ -22,6 +22,6 @@ public FilteringOptions(string hintPath) /// The path to "hint" to the filters will be used to apply /// attributes. /// - public string HintPath { get; private set; } + public string HintPath { get; private set; } } } diff --git a/LibGit2Sharp/FollowFilter.cs b/LibGit2Sharp/FollowFilter.cs new file mode 100644 index 000000000..22681ed18 --- /dev/null +++ b/LibGit2Sharp/FollowFilter.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +namespace LibGit2Sharp +{ + /// + /// Criteria used to order the commits of the repository when querying its history. + /// + /// The commits will be enumerated from the current HEAD of the repository. + /// + /// + public sealed class FollowFilter + { + private static readonly List AllowedSortStrategies = new List + { + CommitSortStrategies.Topological, + CommitSortStrategies.Time, + CommitSortStrategies.Topological | CommitSortStrategies.Time + }; + + private CommitSortStrategies _sortBy; + + /// + /// Initializes a new instance of . + /// + public FollowFilter() + { + SortBy = CommitSortStrategies.Time; + } + + /// + /// The ordering strategy to use. + /// + /// By default, the commits are shown in reverse chronological order. + /// + /// + /// Only 'Topological', 'Time', or 'Topological | Time' are allowed. + /// + /// + public CommitSortStrategies SortBy + { + get { return _sortBy; } + + set + { + if (!AllowedSortStrategies.Contains(value)) + { + throw new ArgumentException("Unsupported sort strategy. Only 'Topological', 'Time', or 'Topological | Time' are allowed.", + "value"); + } + + _sortBy = value; + } + } + } +} diff --git a/LibGit2Sharp/GitLink.cs b/LibGit2Sharp/GitLink.cs index 398ab3217..f03b1d719 100644 --- a/LibGit2Sharp/GitLink.cs +++ b/LibGit2Sharp/GitLink.cs @@ -16,8 +16,7 @@ protected GitLink() internal GitLink(Repository repo, ObjectId id) : base(repo, id) - { - } + { } private string DebuggerDisplay { diff --git a/LibGit2Sharp/GitObject.cs b/LibGit2Sharp/GitObject.cs index 5e11489a2..e2653ca0f 100644 --- a/LibGit2Sharp/GitObject.cs +++ b/LibGit2Sharp/GitObject.cs @@ -15,12 +15,12 @@ public abstract class GitObject : IEquatable, IBelongToARepository { internal static IDictionary TypeToKindMap = new Dictionary - { - { typeof(Commit), ObjectType.Commit }, - { typeof(Tree), ObjectType.Tree }, - { typeof(Blob), ObjectType.Blob }, - { typeof(TagAnnotation), ObjectType.Tag }, - }; + { + { typeof(Commit), ObjectType.Commit }, + { typeof(Tree), ObjectType.Tree }, + { typeof(Blob), ObjectType.Blob }, + { typeof(TagAnnotation), ObjectType.Tag }, + }; private static readonly LambdaEqualityHelper equalityHelper = new LambdaEqualityHelper(x => x.Id); @@ -66,14 +66,20 @@ internal static GitObject BuildFrom(Repository repo, ObjectId id, GitObjectType { case GitObjectType.Commit: return new Commit(repo, id); + case GitObjectType.Tree: return new Tree(repo, id, path); + case GitObjectType.Tag: return new TagAnnotation(repo, id); + case GitObjectType.Blob: return new Blob(repo, id); + default: - throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, "Unexpected type '{0}' for object '{1}'.", type, id)); + throw new LibGit2SharpException("Unexpected type '{0}' for object '{1}'.", + type, + id); } } diff --git a/LibGit2Sharp/GitObjectMetadata.cs b/LibGit2Sharp/GitObjectMetadata.cs index b62e5ebea..348fa4daa 100644 --- a/LibGit2Sharp/GitObjectMetadata.cs +++ b/LibGit2Sharp/GitObjectMetadata.cs @@ -1,5 +1,4 @@ using LibGit2Sharp.Core; -using System; namespace LibGit2Sharp { @@ -11,7 +10,7 @@ public sealed class GitObjectMetadata private readonly GitObjectType type; /// - /// Size of the Object + /// Size of the Object /// public long Size { get; private set; } diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs index b6ea82454..1a52089d7 100644 --- a/LibGit2Sharp/GlobalSettings.cs +++ b/LibGit2Sharp/GlobalSettings.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -9,9 +12,24 @@ namespace LibGit2Sharp public static class GlobalSettings { private static readonly Lazy version = new Lazy(Version.Build); + private static readonly Dictionary registeredFilters; private static LogConfiguration logConfiguration = LogConfiguration.None; + private static string nativeLibraryPath; + private static bool nativeLibraryPathLocked; + + static GlobalSettings() + { + if (Platform.OperatingSystem == OperatingSystemType.Windows) + { + string managedPath = new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath; + nativeLibraryPath = Path.Combine(Path.GetDirectoryName(managedPath), "NativeBinaries"); + } + + registeredFilters = new Dictionary(); + } + /// /// Returns information related to the current LibGit2Sharp /// library. @@ -47,10 +65,9 @@ public static SmartSubtransportRegistration RegisterSmartSubtransport(stri try { - Proxy.git_transport_register( - registration.Scheme, - registration.FunctionPointer, - registration.RegistrationPointer); + Proxy.git_transport_register(registration.Scheme, + registration.FunctionPointer, + registration.RegistrationPointer); } catch (Exception) { @@ -108,5 +125,179 @@ public static LogConfiguration LogConfiguration return logConfiguration; } } + + /// + /// Sets a hint path for searching for native binaries: when + /// specified, native binaries will first be searched in a + /// subdirectory of the given path corresponding to the architecture + /// (eg, "x86" or "amd64") before falling back to the default + /// path ("NativeBinaries\x86" or "NativeBinaries\amd64" next + /// to the application). + /// + /// This must be set before any other calls to the library, + /// and is not available on Unix platforms: see your dynamic + /// library loader's documentation for details. + /// + /// + public static string NativeLibraryPath + { + get + { + if (Platform.OperatingSystem != OperatingSystemType.Windows) + { + throw new LibGit2SharpException("Querying the native hint path is only supported on Windows platforms"); + } + + return nativeLibraryPath; + } + + set + { + if (Platform.OperatingSystem != OperatingSystemType.Windows) + { + throw new LibGit2SharpException("Setting the native hint path is only supported on Windows platforms"); + } + + if (nativeLibraryPathLocked) + { + throw new LibGit2SharpException("You cannot set the native library path after it has been loaded"); + } + + nativeLibraryPath = value; + } + } + + internal static string GetAndLockNativeLibraryPath() + { + nativeLibraryPathLocked = true; + return nativeLibraryPath; + } + + /// + /// Takes a snapshot of the currently registered filters. + /// + /// An array of . + public static IEnumerable GetRegisteredFilters() + { + lock (registeredFilters) + { + FilterRegistration[] array = new FilterRegistration[registeredFilters.Count]; + registeredFilters.Values.CopyTo(array, 0); + return array; + } + } + + /// + /// Register a filter globally with a default priority of 200 allowing the custom filter + /// to imitate a core Git filter driver. It will be run last on checkout and first on checkin. + /// + public static FilterRegistration RegisterFilter(Filter filter) + { + return RegisterFilter(filter, 200); + } + + /// + /// Registers a to be invoked when matches .gitattributes 'filter=name' + /// + /// The filter to be invoked at run time. + /// The priroty of the filter to invoked. + /// A value of 0 () will be run first on checkout and last on checkin. + /// A value of 200 () will be run last on checkout and first on checkin. + /// + /// A object used to manage the lifetime of the registration. + public static FilterRegistration RegisterFilter(Filter filter, int priority) + { + Ensure.ArgumentNotNull(filter, "filter"); + if (priority < FilterRegistration.FilterPriorityMin || priority > FilterRegistration.FilterPriorityMax) + { + throw new ArgumentOutOfRangeException("priority", + priority, + String.Format(System.Globalization.CultureInfo.InvariantCulture, + "Filter priorities must be within the inclusive range of [{0}, {1}].", + FilterRegistration.FilterPriorityMin, + FilterRegistration.FilterPriorityMax)); + } + + FilterRegistration registration = null; + + lock (registeredFilters) + { + // if the filter has already been registered + if (registeredFilters.ContainsKey(filter)) + { + throw new EntryExistsException("The filter has already been registered.", GitErrorCode.Exists, GitErrorCategory.Filter); + } + + // allocate the registration object + registration = new FilterRegistration(filter, priority); + // add the filter and registration object to the global tracking list + registeredFilters.Add(filter, registration); + } + + return registration; + } + + /// + /// Unregisters the associated filter. + /// + /// Registration object with an associated filter. + public static void DeregisterFilter(FilterRegistration registration) + { + Ensure.ArgumentNotNull(registration, "registration"); + + lock (registeredFilters) + { + var filter = registration.Filter; + + // do nothing if the filter isn't registered + if (registeredFilters.ContainsKey(filter)) + { + // remove the register from the global tracking list + registeredFilters.Remove(filter); + // clean up native allocations + registration.Free(); + } + } + } + + internal static void DeregisterFilter(Filter filter) + { + System.Diagnostics.Debug.Assert(filter != null); + + // do nothing if the filter isn't registered + if (registeredFilters.ContainsKey(filter)) + { + var registration = registeredFilters[filter]; + // unregister the filter + DeregisterFilter(registration); + } + } + + /// + /// Get the paths under which libgit2 searches for the configuration file of a given level. + /// + /// The level (global/system/XDG) of the config. + /// The paths that are searched + public static IEnumerable GetConfigSearchPaths(ConfigurationLevel level) + { + return Proxy.git_libgit2_opts_get_search_path(level).Split(Path.PathSeparator); + } + + /// + /// Set the paths under which libgit2 searches for the configuration file of a given level. + /// + /// . + /// + /// The level (global/system/XDG) of the config. + /// + /// The new search paths to set. + /// Pass null to reset to the default. + /// The special string "$PATH" will be substituted with the current search path. + /// + public static void SetConfigSearchPaths(ConfigurationLevel level, params string[] paths) + { + var pathString = (paths == null) ? null : string.Join(Path.PathSeparator.ToString(), paths); + Proxy.git_libgit2_opts_set_search_path(level, pathString); + } } } diff --git a/LibGit2Sharp/Handlers.cs b/LibGit2Sharp/Handlers.cs index 74c746f1a..7e0b572c4 100644 --- a/LibGit2Sharp/Handlers.cs +++ b/LibGit2Sharp/Handlers.cs @@ -1,4 +1,7 @@ -namespace LibGit2Sharp.Handlers +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; + +namespace LibGit2Sharp.Handlers { /// /// Delegate definition to handle Progress callback. @@ -30,6 +33,15 @@ /// Credential types which the server accepts public delegate Credentials CredentialsHandler(string url, string usernameFromUrl, SupportedCredentialTypes types); + /// + /// Delegate definition for the certificate validation + /// + /// The certificate which the server sent + /// The hostname which we tried to connect to + /// Whether libgit2 thinks this certificate is valid + /// True to continue, false to cancel + public delegate bool CertificateCheckHandler(Certificate certificate, bool valid, string host); + /// /// Delegate definition for transfer progress callback. /// @@ -37,6 +49,21 @@ /// True to continue, false to cancel. public delegate bool TransferProgressHandler(TransferProgress progress); + /// + /// Delegate definition to indicate that a repository is about to be operated on. + /// (In the context of a recursive operation). + /// + /// Context on the repository that is being operated on. + /// true to continue, false to cancel. + public delegate bool RepositoryOperationStarting(RepositoryOperationContext context); + + /// + /// Delegate definition to indicate that an operation is done in a repository. + /// (In the context of a recursive operation). + /// + /// Context on the repository that is being operated on. + public delegate void RepositoryOperationCompleted(RepositoryOperationContext context); + /// /// Delegate definition for callback reporting push network progress. /// @@ -55,6 +82,13 @@ /// True to continue, false to cancel. public delegate bool PackBuilderProgressHandler(PackBuilderStage stage, int current, int total); + /// + /// Provides information about what updates will be performed before a push occurs + /// + /// List of updates about to be performed via push + /// True to continue, false to cancel. + public delegate bool PrePushHandler(IEnumerable updates); + /// /// Delegate definition to handle reporting errors when updating references on the remote. /// @@ -96,6 +130,25 @@ /// The refspec which didn't match the default. public delegate void RemoteRenameFailureHandler(string problematicRefspec); + /// + /// Delegate definition for stash application callback. + /// + /// The current step of the stash application. + /// True to continue checkout operation; false to cancel checkout operation. + public delegate bool StashApplyProgressHandler(StashApplyProgress progress); + + /// + /// Delegate to report information on a rebase step that is about to be performed. + /// + /// + public delegate void RebaseStepStartingHandler(BeforeRebaseStepInfo beforeRebaseStep); + + /// + /// Delegate to report information on the rebase step that was just completed. + /// + /// + public delegate void RebaseStepCompletedHandler(AfterRebaseStepInfo afterRebaseStepInfo); + /// /// The stages of pack building. /// diff --git a/LibGit2Sharp/HistoryDivergence.cs b/LibGit2Sharp/HistoryDivergence.cs index 27d84dd19..262c09c15 100644 --- a/LibGit2Sharp/HistoryDivergence.cs +++ b/LibGit2Sharp/HistoryDivergence.cs @@ -19,7 +19,7 @@ protected HistoryDivergence() internal HistoryDivergence(Repository repo, Commit one, Commit another) { - commonAncestor = new Lazy(() => repo.Commits.FindMergeBase(one, another)); + commonAncestor = new Lazy(() => repo.ObjectDatabase.FindMergeBase(one, another)); Tuple div = Proxy.git_graph_ahead_behind(repo.Handle, one, another); One = one; @@ -64,10 +64,7 @@ internal HistoryDivergence(Repository repo, Commit one, Commit another) /// public virtual Commit CommonAncestor { - get - { - return commonAncestor.Value; - } + get { return commonAncestor.Value; } } } diff --git a/LibGit2Sharp/IDiffResult.cs b/LibGit2Sharp/IDiffResult.cs new file mode 100644 index 000000000..d16af711d --- /dev/null +++ b/LibGit2Sharp/IDiffResult.cs @@ -0,0 +1,8 @@ +namespace LibGit2Sharp +{ + /// + /// Marker interface to identify Diff results. + /// + public interface IDiffResult + { } +} diff --git a/LibGit2Sharp/IQueryableCommitLog.cs b/LibGit2Sharp/IQueryableCommitLog.cs index 7642d4122..457ad2fa6 100644 --- a/LibGit2Sharp/IQueryableCommitLog.cs +++ b/LibGit2Sharp/IQueryableCommitLog.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace LibGit2Sharp { @@ -14,12 +15,28 @@ public interface IQueryableCommitLog : ICommitLog /// A list of commits, ready to be enumerated. ICommitLog QueryBy(CommitFilter filter); + /// + /// Returns the list of commits of the repository representing the history of a file beyond renames. + /// + /// The file's path. + /// A list of file history entries, ready to be enumerated. + IEnumerable QueryBy(string path); + + /// + /// Returns the list of commits of the repository representing the history of a file beyond renames. + /// + /// The file's path. + /// The options used to control which commits will be returned. + /// A list of file history entries, ready to be enumerated. + IEnumerable QueryBy(string path, FollowFilter filter); + /// /// Find the best possible merge base given two s. /// /// The first . /// The second . /// The merge base or null if none found. + [Obsolete("This method will be removed in the next release. Please use ObjectDatabase.FindMergeBase() instead.")] Commit FindMergeBase(Commit first, Commit second); /// @@ -28,6 +45,7 @@ public interface IQueryableCommitLog : ICommitLog /// The s for which to find the merge base. /// The strategy to leverage in order to find the merge base. /// The merge base or null if none found. + [Obsolete("This method will be removed in the next release. Please use ObjectDatabase.FindMergeBase() instead.")] Commit FindMergeBase(IEnumerable commits, MergeBaseFindingStrategy strategy); } } diff --git a/LibGit2Sharp/IRepository.cs b/LibGit2Sharp/IRepository.cs index ad3f2d78a..317a0e76b 100644 --- a/LibGit2Sharp/IRepository.cs +++ b/LibGit2Sharp/IRepository.cs @@ -52,7 +52,7 @@ public interface IRepository : IDisposable /// /// Provides access to diffing functionalities to show changes between the working tree and the index or a tree, changes between the index and a tree, changes between two trees, or changes between two files on disk. /// - Diff Diff {get;} + Diff Diff { get; } /// /// Gets the database. @@ -78,9 +78,8 @@ public interface IRepository : IDisposable /// /// The to check out. /// controlling checkout behavior. - /// Identity for use when updating the reflog. /// The that was checked out. - Branch Checkout(Branch branch, CheckoutOptions options, Signature signature); + Branch Checkout(Branch branch, CheckoutOptions options); /// /// Checkout the specified branch, reference or SHA. @@ -91,9 +90,8 @@ public interface IRepository : IDisposable /// /// A revparse spec for the commit or branch to checkout. /// controlling checkout behavior. - /// Identity for use when updating the reflog. /// The that was checked out. - Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature); + Branch Checkout(string committishOrBranchSpec, CheckoutOptions options); /// /// Checkout the specified . @@ -103,9 +101,8 @@ public interface IRepository : IDisposable /// /// The to check out. /// controlling checkout behavior. - /// Identity for use when updating the reflog. /// The that was checked out. - Branch Checkout(Commit commit, CheckoutOptions options, Signature signature); + Branch Checkout(Commit commit, CheckoutOptions options); /// /// Updates specifed paths in the index and working directory with the versions from the specified branch, reference, or SHA. @@ -166,9 +163,16 @@ public interface IRepository : IDisposable /// /// Flavor of reset operation to perform. /// The target commit object. - /// Identity for use when updating the reflog. - /// Message to use when updating the reflog. - void Reset(ResetMode resetMode, Commit commit, Signature signature, string logMessage); + void Reset(ResetMode resetMode, Commit commit); + + /// + /// Sets to the specified commit and optionally resets the and + /// the content of the working tree to match. + /// + /// Flavor of reset operation to perform. + /// The target commit object. + /// Collection of parameters controlling checkout behavior. + void Reset(ResetMode resetMode, Commit commit, CheckoutOptions options); /// /// Replaces entries in the with entries from the specified commit. @@ -179,6 +183,7 @@ public interface IRepository : IDisposable /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// + [Obsolete("This method will be removed in the next release. Please use Index.Replace() instead.")] void Reset(Commit commit, IEnumerable paths, ExplicitPathsOptions explicitPathsOptions); /// @@ -222,6 +227,23 @@ public interface IRepository : IDisposable /// The of the merge. MergeResult Merge(string committish, Signature merger, MergeOptions options); + /// + /// Access to Rebase functionality. + /// + Rebase Rebase { get; } + + /// + /// Merge the reference that was recently fetched. This will merge + /// the branch on the fetched remote that corresponded to the + /// current local branch when we did the fetch. This is the + /// second step in performing a pull operation (after having + /// performed said fetch). + /// + /// The of who is performing the merge. + /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. + /// The of the merge. + MergeResult MergeFetchedRefs(Signature merger, MergeOptions options); + /// /// Cherry picks changes from the commit into the branch pointed at by HEAD. /// @@ -353,14 +375,14 @@ public interface IRepository : IDisposable void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions); /// - /// Retrieves the state of a file in the working directory, comparing it against the staging area and the latest commmit. + /// Retrieves the state of a file in the working directory, comparing it against the staging area and the latest commit. /// /// The relative path within the working directory to the file. /// A representing the state of the parameter. FileStatus RetrieveStatus(string filePath); /// - /// Retrieves the state of all files in the working directory, comparing them against the staging area and the latest commmit. + /// Retrieves the state of all files in the working directory, comparing them against the staging area and the latest commit. /// /// If set, the options that control the status investigation. /// A holding the state of all the files. diff --git a/LibGit2Sharp/Identity.cs b/LibGit2Sharp/Identity.cs new file mode 100644 index 000000000..e3ebd9ff7 --- /dev/null +++ b/LibGit2Sharp/Identity.cs @@ -0,0 +1,70 @@ +using LibGit2Sharp.Core; +using LibGit2Sharp.Core.Handles; + +namespace LibGit2Sharp +{ + /// + /// Represents the identity used when writing reflog entries. + /// + public sealed class Identity + { + private readonly string _name; + private readonly string _email; + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The email. + public Identity(string name, string email) + { + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(email, "email"); + Ensure.ArgumentDoesNotContainZeroByte(name, "name"); + Ensure.ArgumentDoesNotContainZeroByte(email, "email"); + + _name = name; + _email = email; + } + + /// + /// Gets the email. + /// + public string Email + { + get { return _email; } + } + + /// + /// Gets the name. + /// + public string Name + { + get { return _name; } + } + + internal SignatureSafeHandle BuildNowSignatureHandle() + { + return Proxy.git_signature_now(Name, Email); + } + } + + internal static class IdentityHelpers + { + /// + /// Build the handle for the Indentity with the current time, or return a handle + /// to an empty signature. + /// + /// + /// + public static SignatureSafeHandle SafeBuildNowSignatureHandle(this Identity identity) + { + if (identity == null) + { + return new SignatureSafeHandle(); + } + + return identity.BuildNowSignatureHandle(); + } + } +} diff --git a/LibGit2Sharp/Index.cs b/LibGit2Sharp/Index.cs index 3c29db985..f63c91d7c 100644 --- a/LibGit2Sharp/Index.cs +++ b/LibGit2Sharp/Index.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using System.IO; -using System.Linq; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -32,7 +30,7 @@ internal Index(Repository repo) this.repo = repo; handle = Proxy.git_repository_index(repo.Handle); - conflicts = new ConflictCollection(repo); + conflicts = new ConflictCollection(this); repo.RegisterForCleanup(handle); } @@ -43,7 +41,7 @@ internal Index(Repository repo, string indexPath) handle = Proxy.git_index_open(indexPath); Proxy.git_repository_set_index(repo.Handle, handle); - conflicts = new ConflictCollection(repo); + conflicts = new ConflictCollection(this); repo.RegisterForCleanup(handle); } @@ -54,7 +52,7 @@ internal IndexSafeHandle Handle } /// - /// Gets the number of in the index. + /// Gets the number of in the . /// public virtual int Count { @@ -62,7 +60,7 @@ public virtual int Count } /// - /// Determines if the index is free from conflicts. + /// Determines if the is free from conflicts. /// public virtual bool IsFullyMerged { @@ -128,9 +126,9 @@ IEnumerator IEnumerable.GetEnumerator() #endregion /// - /// Replaces entries in the staging area with entries from the specified tree. + /// Replaces entries in the with entries from the specified . /// - /// This overwrites all existing state in the staging area. + /// This overwrites all existing state in the . /// /// /// The to read the entries from. @@ -145,10 +143,10 @@ public virtual void Replace(Tree source) } /// - /// Clears all entries the index. This is semantically equivalent to - /// creating an empty tree object and resetting the index to that tree. + /// Clears all entries the . This is semantically equivalent to + /// creating an empty object and resetting the to that . /// - /// This overwrites all existing state in the staging area. + /// This overwrites all existing state in the . /// /// public virtual void Clear() @@ -163,7 +161,7 @@ private void RemoveFromIndex(string relativePath) } /// - /// Removes a specified entry from the index. + /// Removes a specified entry from the . /// /// The path of the entry to be removed. public virtual void Remove(string indexEntryPath) @@ -179,7 +177,7 @@ public virtual void Remove(string indexEntryPath) } /// - /// Adds a file from the workdir in the . + /// Adds a file from the working directory in the . /// /// If an entry with the same path already exists in the , /// the newly added one will overwrite it. @@ -247,17 +245,17 @@ internal void Replace(TreeChanges changes) continue; case ChangeKind.Deleted: - /* Fall through */ case ChangeKind.Modified: - AddEntryToTheIndex( - treeEntryChanges.OldPath, - treeEntryChanges.OldOid, - treeEntryChanges.OldMode); - + AddEntryToTheIndex(treeEntryChanges.OldPath, + treeEntryChanges.OldOid, + treeEntryChanges.OldMode); continue; default: - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Entry '{0}' bears an unexpected ChangeKind '{1}'", treeEntryChanges.Path, treeEntryChanges.Status)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + "Entry '{0}' bears an unexpected ChangeKind '{1}'", + treeEntryChanges.Path, + treeEntryChanges.Status)); } } @@ -269,10 +267,7 @@ internal void Replace(TreeChanges changes) /// public virtual ConflictCollection Conflicts { - get - { - return conflicts; - } + get { return conflicts; } } private void AddEntryToTheIndex(string path, ObjectId id, Mode mode) @@ -292,9 +287,44 @@ private string DebuggerDisplay { get { - return string.Format(CultureInfo.InvariantCulture, - "Count = {0}", Count); + return string.Format(CultureInfo.InvariantCulture, "Count = {0}", Count); } } + + /// + /// Replaces entries in the with entries from the specified . + /// + /// The target object. + public virtual void Replace(Commit commit) + { + Replace(commit, null, null); + } + + /// + /// Replaces entries in the with entries from the specified . + /// + /// The target object. + /// The list of paths (either files or directories) that should be considered. + public virtual void Replace(Commit commit, IEnumerable paths) + { + Replace(commit, paths, null); + } + + /// + /// Replaces entries in the with entries from the specified . + /// + /// The target object. + /// The list of paths (either files or directories) that should be considered. + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public virtual void Replace(Commit commit, IEnumerable paths, ExplicitPathsOptions explicitPathsOptions) + { + Ensure.ArgumentNotNull(commit, "commit"); + + var changes = repo.Diff.Compare(commit.Tree, DiffTargets.Index, paths, explicitPathsOptions, new CompareOptions { Similarity = SimilarityOptions.None }); + Replace(changes); + } } } diff --git a/LibGit2Sharp/IndexEntry.cs b/LibGit2Sharp/IndexEntry.cs index 7dc6b0150..501d12717 100644 --- a/LibGit2Sharp/IndexEntry.cs +++ b/LibGit2Sharp/IndexEntry.cs @@ -52,13 +52,13 @@ internal static IndexEntry BuildFromPtr(IndexEntrySafeHandle handle) FilePath path = LaxFilePathMarshaler.FromNative(entry.Path); return new IndexEntry - { - Path = path.Native, - Id = entry.Id, - StageLevel = Proxy.git_index_entry_stage(handle), - Mode = (Mode)entry.Mode, - AssumeUnchanged = (GitIndexEntry.GIT_IDXENTRY_VALID & entry.Flags) == GitIndexEntry.GIT_IDXENTRY_VALID - }; + { + Path = path.Native, + Id = entry.Id, + StageLevel = Proxy.git_index_entry_stage(handle), + Mode = (Mode)entry.Mode, + AssumeUnchanged = (GitIndexEntry.GIT_IDXENTRY_VALID & entry.Flags) == GitIndexEntry.GIT_IDXENTRY_VALID + }; } /// @@ -117,7 +117,10 @@ private string DebuggerDisplay get { return string.Format(CultureInfo.InvariantCulture, - "{0} ({1}) => \"{2}\"", Path, StageLevel, Id.ToString(7)); + "{0} ({1}) => \"{2}\"", + Path, + StageLevel, + Id.ToString(7)); } } } diff --git a/LibGit2Sharp/IndexNameEntry.cs b/LibGit2Sharp/IndexNameEntry.cs index ef6fc50d2..5ba24f9c3 100644 --- a/LibGit2Sharp/IndexNameEntry.cs +++ b/LibGit2Sharp/IndexNameEntry.cs @@ -31,12 +31,15 @@ internal static IndexNameEntry BuildFromPtr(IndexNameEntrySafeHandle handle) GitIndexNameEntry entry = handle.MarshalAsGitIndexNameEntry(); - string ancestor = entry.Ancestor != IntPtr.Zero ? - LaxFilePathMarshaler.FromNative(entry.Ancestor).Native : null; - string ours = entry.Ours != IntPtr.Zero ? - LaxFilePathMarshaler.FromNative(entry.Ours).Native : null; - string theirs = entry.Theirs != IntPtr.Zero ? - LaxFilePathMarshaler.FromNative(entry.Theirs).Native : null; + string ancestor = entry.Ancestor != IntPtr.Zero + ? LaxFilePathMarshaler.FromNative(entry.Ancestor).Native + : null; + string ours = entry.Ours != IntPtr.Zero + ? LaxFilePathMarshaler.FromNative(entry.Ours).Native + : null; + string theirs = entry.Theirs != IntPtr.Zero + ? LaxFilePathMarshaler.FromNative(entry.Theirs).Native + : null; return new IndexNameEntry { @@ -117,7 +120,10 @@ private string DebuggerDisplay get { return string.Format(CultureInfo.InvariantCulture, - "{0} {1} {2}", Ancestor, Ours, Theirs); + "{0} {1} {2}", + Ancestor, + Ours, + Theirs); } } } diff --git a/LibGit2Sharp/IndexNameEntryCollection.cs b/LibGit2Sharp/IndexNameEntryCollection.cs index 30dff911b..2896f9124 100644 --- a/LibGit2Sharp/IndexNameEntryCollection.cs +++ b/LibGit2Sharp/IndexNameEntryCollection.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Globalization; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -14,7 +13,7 @@ namespace LibGit2Sharp /// public class IndexNameEntryCollection : IEnumerable { - private readonly Repository repo; + private readonly Index index; /// /// Needed for mocking purposes. @@ -22,16 +21,16 @@ public class IndexNameEntryCollection : IEnumerable protected IndexNameEntryCollection() { } - internal IndexNameEntryCollection(Repository repo) + internal IndexNameEntryCollection(Index index) { - this.repo = repo; + this.index = index; } - private IndexNameEntry this[int index] + private IndexNameEntry this[int idx] { get { - IndexNameEntrySafeHandle entryHandle = Proxy.git_index_name_get_byindex(repo.Index.Handle, (UIntPtr)index); + IndexNameEntrySafeHandle entryHandle = Proxy.git_index_name_get_byindex(index.Handle, (UIntPtr)idx); return IndexNameEntry.BuildFromPtr(entryHandle); } } @@ -42,7 +41,7 @@ private List AllIndexNames() { var list = new List(); - int count = Proxy.git_index_name_entrycount(repo.Index.Handle); + int count = Proxy.git_index_name_entrycount(index.Handle); for (int i = 0; i < count; i++) { diff --git a/LibGit2Sharp/IndexReucEntry.cs b/LibGit2Sharp/IndexReucEntry.cs index ffba71e07..144e5c3c9 100644 --- a/LibGit2Sharp/IndexReucEntry.cs +++ b/LibGit2Sharp/IndexReucEntry.cs @@ -15,9 +15,9 @@ public class IndexReucEntry : IEquatable { private static readonly LambdaEqualityHelper equalityHelper = new LambdaEqualityHelper(x => x.Path, - x => x.AncestorId, x => x.AncestorMode, - x => x.OurId, x => x.OurMode, - x => x.TheirId, x => x.TheirMode); + x => x.AncestorId, x => x.AncestorMode, + x => x.OurId, x => x.OurMode, + x => x.TheirId, x => x.TheirMode); /// /// Needed for mocking purposes. @@ -145,7 +145,11 @@ private string DebuggerDisplay get { return string.Format(CultureInfo.InvariantCulture, - "{0}: {1} {2} {3}", Path, AncestorId, OurId, TheirId); + "{0}: {1} {2} {3}", + Path, + AncestorId, + OurId, + TheirId); } } } diff --git a/LibGit2Sharp/IndexReucEntryCollection.cs b/LibGit2Sharp/IndexReucEntryCollection.cs index 7a81e3b41..8402a285a 100644 --- a/LibGit2Sharp/IndexReucEntryCollection.cs +++ b/LibGit2Sharp/IndexReucEntryCollection.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Globalization; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -14,7 +13,7 @@ namespace LibGit2Sharp /// public class IndexReucEntryCollection : IEnumerable { - private readonly Repository repo; + private readonly Index index; /// /// Needed for mocking purposes. @@ -22,9 +21,9 @@ public class IndexReucEntryCollection : IEnumerable protected IndexReucEntryCollection() { } - internal IndexReucEntryCollection(Repository repo) + internal IndexReucEntryCollection(Index index) { - this.repo = repo; + this.index = index; } /// @@ -36,16 +35,16 @@ public virtual IndexReucEntry this[string path] { Ensure.ArgumentNotNullOrEmptyString(path, "path"); - IndexReucEntrySafeHandle entryHandle = Proxy.git_index_reuc_get_bypath(repo.Index.Handle, path); + IndexReucEntrySafeHandle entryHandle = Proxy.git_index_reuc_get_bypath(index.Handle, path); return IndexReucEntry.BuildFromPtr(entryHandle); } } - private IndexReucEntry this[int index] + private IndexReucEntry this[int idx] { get { - IndexReucEntrySafeHandle entryHandle = Proxy.git_index_reuc_get_byindex(repo.Index.Handle, (UIntPtr)index); + IndexReucEntrySafeHandle entryHandle = Proxy.git_index_reuc_get_byindex(index.Handle, (UIntPtr)idx); return IndexReucEntry.BuildFromPtr(entryHandle); } } @@ -56,7 +55,7 @@ private List AllIndexReucs() { var list = new List(); - int count = Proxy.git_index_reuc_entrycount(repo.Index.Handle); + int count = Proxy.git_index_reuc_entrycount(index.Handle); for (int i = 0; i < count; i++) { diff --git a/LibGit2Sharp/InvalidSpecificationException.cs b/LibGit2Sharp/InvalidSpecificationException.cs index 7c82a4372..64654852c 100644 --- a/LibGit2Sharp/InvalidSpecificationException.cs +++ b/LibGit2Sharp/InvalidSpecificationException.cs @@ -17,8 +17,7 @@ public class InvalidSpecificationException : LibGit2SharpException /// Initializes a new instance of the class. /// public InvalidSpecificationException() - { - } + { } /// /// Initializes a new instance of the class with a specified error message. @@ -26,8 +25,16 @@ public InvalidSpecificationException() /// A message that describes the error. public InvalidSpecificationException(string message) : base(message) - { - } + { } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A composite format string for use in . + /// An object array that contains zero or more objects to format. + public InvalidSpecificationException(string format, params object[] args) + : base(format, args) + { } /// /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. @@ -36,8 +43,7 @@ public InvalidSpecificationException(string message) /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. public InvalidSpecificationException(string message, Exception innerException) : base(message, innerException) - { - } + { } /// /// Initializes a new instance of the class with a serialized data. @@ -46,12 +52,10 @@ public InvalidSpecificationException(string message, Exception innerException) /// The that contains contextual information about the source or destination. protected InvalidSpecificationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + { } internal InvalidSpecificationException(string message, GitErrorCode code, GitErrorCategory category) : base(message, code, category) - { - } + { } } } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index a2e6e616d..72afcc658 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -38,22 +39,28 @@ false bin\Release\LibGit2Sharp.xml + + true + + + libgit2sharp.snk + + + - - @@ -68,6 +75,18 @@ + + + + + + + + + + + + @@ -76,9 +95,20 @@ + + + + + + + + + + + @@ -97,14 +127,27 @@ + + + + + + + + + + + + + @@ -121,8 +164,8 @@ + - @@ -130,8 +173,8 @@ - + @@ -164,7 +207,6 @@ - @@ -189,12 +231,10 @@ - - @@ -218,11 +258,9 @@ - - @@ -233,7 +271,6 @@ - @@ -339,27 +376,46 @@ + + + + + + + + + + + + + + + - + - + + - - $(MSBuildProjectDirectory)\..\Lib\NativeBinaries - - + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + +