diff --git a/.gitignore b/.gitignore
index b1931b11..fd52ad52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,5 +16,4 @@ Reports
test-results
dist
build
-SharedAssemblyInfo.cs
-VersionAssemblyInfo.cs
\ No newline at end of file
+VersionAssemblyInfo.cs
diff --git a/Git/Program.cs b/Git/Program.cs
index a9d29ccb..965f0e1c 100644
--- a/Git/Program.cs
+++ b/Git/Program.cs
@@ -340,7 +340,7 @@ static public void Exit(int exit_code)
Git.DefaultRepository.Close();
#if DEBUG
- Console.WriteLine("\n\nrunning in DEBUG mode, press any key to exit.");
+ Console.WriteLine("\n\nrunning in DEBUG mode, press [ENTER] to exit.");
Console.In.ReadLine();
#endif
Environment.Exit(exit_code);
diff --git a/GitSharp.Core/GitSharp.Core.csproj b/GitSharp.Core/GitSharp.Core.csproj
index d6ff4e9c..b67deda5 100644
--- a/GitSharp.Core/GitSharp.Core.csproj
+++ b/GitSharp.Core/GitSharp.Core.csproj
@@ -51,6 +51,9 @@
+
+ Properties\SharedAssemblyInfo.cs
+
diff --git a/GitSharp.Core/Transport/TransportLocal.cs b/GitSharp.Core/Transport/TransportLocal.cs
index e7153c36..a3c071a7 100644
--- a/GitSharp.Core/Transport/TransportLocal.cs
+++ b/GitSharp.Core/Transport/TransportLocal.cs
@@ -2,6 +2,7 @@
* Copyright (C) 2008, Robin Rosenberg
* Copyright (C) 2008, Shawn O. Pearce
* Copyright (C) 2008, Marek Zawirski
+ * Copyright (C) 2011, Dominique van de Vorle
*
* All rights reserved.
*
@@ -38,17 +39,31 @@
*/
using System;
+using System.Diagnostics;
using System.IO;
+using System.Threading;
+using GitSharp.Core.Exceptions;
using GitSharp.Core.Util;
+using Tamir.Streams;
+
namespace GitSharp.Core.Transport
{
public class TransportLocal : Transport, IPackTransport
{
private const string PWD = ".";
+ protected readonly DirectoryInfo remoteGitDir;
- public TransportLocal(Repository local, URIish uri) : base(local, uri)
+ public TransportLocal(Repository local, URIish uri)
+ : base(local, uri)
{
+ string dir = FS.resolve(new DirectoryInfo(PWD), uri.Path).FullName;
+ if(Directory.Exists(Path.Combine(dir, Constants.DOT_GIT)))
+ {
+ dir = Path.Combine(dir, Constants.DOT_GIT);
+ }
+
+ remoteGitDir = new DirectoryInfo(dir);
}
public static bool canHandle(URIish uri)
@@ -71,17 +86,356 @@ public static bool canHandle(URIish uri)
public override IFetchConnection openFetch()
{
- throw new NotImplementedException();
+ if (OptionUploadPack.Equals("git-upload-pack") || OptionUploadPack.Equals("git upload-pack"))
+ {
+ return new InternalLocalFetchConnection(this);
+ }
+ return new ForkLocalFetchConnection(this);
}
public override IPushConnection openPush()
{
- throw new NotImplementedException();
+ if (OptionReceivePack.Equals("git-receive-pack") || OptionReceivePack.Equals("git receive-pack"))
+ {
+ return new InternalLocalPushConnection(this);
+ }
+ return new ForkLocalPushConnection(this);
}
public override void close()
{
- throw new NotImplementedException();
+ // Resources must be established per-connection.
}
+
+ protected Process Spawn(string cmd)
+ {
+ try
+ {
+ ProcessStartInfo psi = new ProcessStartInfo();
+
+ if (cmd.StartsWith("git-"))
+ {
+ psi.FileName = "git";
+ psi.Arguments = cmd.Substring(4);
+ }
+ else
+ {
+ int gitspace = cmd.IndexOf("git ");
+ if (gitspace >= 0) {
+ psi.FileName = cmd.Substring(0, gitspace + 3);
+ psi.Arguments = cmd.Substring(gitspace + 4);
+ }
+ else
+ {
+ psi.FileName = cmd;
+ }
+ }
+
+ if(psi.Arguments.Equals(String.Empty))
+ {
+ psi.Arguments = PWD;
+ }
+ else
+ {
+ psi.Arguments += " " + PWD;
+ }
+
+ var process = new Process() { StartInfo = psi};
+ process.Start();
+ return process;
+ }
+ catch (IOException ex)
+ {
+ throw new TransportException(Uri, ex.Message, ex);
+ }
+ }
+
+ #region Nested types
+
+ private class InternalLocalFetchConnection : BasePackFetchConnection
+ {
+ private Thread worker;
+ private readonly PipedInputStream in_r;
+ private readonly PipedOutputStream in_w;
+
+ public InternalLocalFetchConnection( TransportLocal transport)
+ : base( transport )
+ {
+
+ Repository dst;
+ try
+ {
+ dst = new Repository(transport.remoteGitDir);
+ }
+ catch (IOException)
+ {
+ throw new TransportException(uri, "Not a Git directory");
+ }
+
+ PipedInputStream out_r;
+ PipedOutputStream out_w;
+ try
+ {
+ in_r = new PipedInputStream();
+ in_w = new PipedOutputStream(in_r);
+
+ out_r = new PipedInputStream();
+ out_w = new PipedOutputStream(out_r);
+ }
+ catch (IOException ex)
+ {
+ dst.Close();
+ throw new TransportException(uri, "Cannot connect pipes", ex);
+ }
+
+ worker = new Thread( () =>
+ {
+ try
+ {
+ UploadPack rp = new UploadPack(dst);
+ rp.Upload(out_r, in_w, null);
+ }
+ catch (IOException ex)
+ {
+ // Client side of the pipes should report the problem.
+ ex.printStackTrace();
+ }
+ catch (Exception ex)
+ {
+ // Clients side will notice we went away, and report.
+ ex.printStackTrace();
+ }
+ finally
+ {
+ try
+ {
+ out_r.close();
+ }
+ catch (IOException)
+ {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ try
+ {
+ in_w.close();
+ } catch (IOException)
+ {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ dst.Close();
+ }
+
+ });
+ worker.Name = "JGit-Upload-Pack";
+ worker.Start();
+
+ init(in_r, out_w);
+ readAdvertisedRefs();
+ }
+
+
+ override public void Close()
+ {
+ base.Close();
+
+ if (worker != null)
+ {
+ try
+ {
+ worker.Join();
+ }
+ catch ( ThreadInterruptedException)
+ {
+ // Stop waiting and return anyway.
+ }
+ finally
+ {
+ worker = null;
+ }
+ }
+ }
+ }
+
+ private class InternalLocalPushConnection : BasePackPushConnection
+ {
+ private Thread worker;
+
+ public InternalLocalPushConnection(TransportLocal transport)
+ : base(transport)
+ {
+ Repository dst;
+ try
+ {
+ dst = new Repository(transport.remoteGitDir);
+ }
+ catch (IOException)
+ {
+ throw new TransportException(uri, "Not a Git directory");
+ }
+
+ PipedInputStream in_r;
+ PipedOutputStream in_w;
+
+ PipedInputStream out_r;
+ PipedOutputStream out_w;
+ try
+ {
+ in_r = new PipedInputStream();
+ in_w = new PipedOutputStream(in_r);
+
+ out_r = new PipedInputStream();
+ out_w = new PipedOutputStream(out_r);
+ }
+ catch (IOException ex)
+ {
+ dst.Close();
+ throw new TransportException(uri, "Cannot connect pipes", ex);
+ }
+
+ worker = new Thread(() =>
+ {
+ try
+ {
+ ReceivePack rp = new ReceivePack(dst);
+ rp.receive(out_r, in_w, null);
+ }
+ catch (IOException)
+ {
+ // Client side of the pipes should report the problem.
+ }
+ catch (Exception)
+ {
+ // Clients side will notice we went away, and report.
+ }
+ finally
+ {
+ try
+ {
+ out_r.close();
+ }
+ catch (IOException)
+ {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ try
+ {
+ in_w.close();
+ }
+ catch (IOException)
+ {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ dst.Close();
+ }
+ });
+ worker.Name = "JGit-Receive-Pack";
+ worker.Start();
+
+ init(in_r, out_w);
+ readAdvertisedRefs();
+ }
+
+ public override void Close()
+ {
+ base.Close();
+
+ if (worker != null)
+ {
+ try
+ {
+ worker.Join();
+ }
+ catch (ThreadInterruptedException)
+ {
+ // Stop waiting and return anyway.
+ }
+ finally
+ {
+ worker = null;
+ }
+ }
+ }
+ }
+
+ private class ForkLocalFetchConnection : BasePackFetchConnection
+ {
+ private Process uploadPack;
+
+ public ForkLocalFetchConnection( TransportLocal transport)
+ : base(transport)
+ {
+ uploadPack = transport.Spawn(transport.OptionUploadPack);
+
+ Stream upIn = new BufferedStream(uploadPack.StandardInput.BaseStream);
+ Stream upOut = new BufferedStream(uploadPack.StandardOutput.BaseStream);
+
+ init(upIn, upOut);
+ readAdvertisedRefs();
+ }
+
+ public override void Close()
+ {
+ base.Close();
+
+ if (uploadPack != null)
+ {
+ try
+ {
+ uploadPack.WaitForExit();
+ }
+ catch (ThreadInterruptedException)
+ {
+ // Stop waiting and return anyway.
+ }
+ finally
+ {
+ uploadPack = null;
+ }
+ }
+ }
+ }
+
+ private class ForkLocalPushConnection : BasePackPushConnection
+ {
+ private Process receivePack;
+
+ public ForkLocalPushConnection( TransportLocal transport)
+ : base(transport)
+ {
+ receivePack = transport.Spawn(transport.OptionReceivePack);
+
+ Stream rpIn = new BufferedStream(receivePack.StandardInput.BaseStream);
+ Stream rpOut = new BufferedStream(receivePack.StandardOutput.BaseStream);
+
+ init(rpIn, rpOut);
+ readAdvertisedRefs();
+ }
+
+ public override void Close()
+ {
+ base.Close();
+
+ if (receivePack != null)
+ {
+ try
+ {
+ receivePack.WaitForExit();
+ }
+ catch (ThreadInterruptedException)
+ {
+ // Stop waiting and return anyway.
+ }
+ finally
+ {
+ receivePack = null;
+ }
+ }
+ }
+ }
+ #endregion
}
}
diff --git a/GitSharp.Core/Util/Extensions.cs b/GitSharp.Core/Util/Extensions.cs
index a65bee3b..128c1773 100644
--- a/GitSharp.Core/Util/Extensions.cs
+++ b/GitSharp.Core/Util/Extensions.cs
@@ -213,11 +213,21 @@ public static string DirectoryName(this FileSystemInfo fileSystemInfo)
return fileSystemInfo.FullName;
}
+ ///
+ /// Checks if a directory exists with the FullName of the FileSystemInfo.
+ ///
+ /// The FileSystemInfo wich needs to be checked.
+ /// True if a directry exists with the FullName of the FileSystemInfo, false otherwise
public static bool IsDirectory(this FileSystemInfo fileSystemInfo)
{
return Directory.Exists(fileSystemInfo.FullName);
}
+ ///
+ /// Checks if a file exists with the FullName of the FileSystemInfo.
+ ///
+ /// The FileSystemInfo wich needs to be checked.
+ /// True if a file exists with the FullName of the FileSystemInfo, false otherwise
public static bool IsFile(this FileSystemInfo fileSystemInfo)
{
return File.Exists(fileSystemInfo.FullName);
diff --git a/GitSharp.Core/Util/IListUtil.cs b/GitSharp.Core/Util/IListUtil.cs
index 16215ee0..56b40112 100644
--- a/GitSharp.Core/Util/IListUtil.cs
+++ b/GitSharp.Core/Util/IListUtil.cs
@@ -1,7 +1,9 @@
using System.Collections.Generic;
+using System.Diagnostics;
namespace GitSharp.Core.Util
{
+ [DebuggerStepThrough]
public static class IListUtil
{
public static bool isEmpty(this ICollection l)
diff --git a/GitSharp.Tests/GitSharp.Core/Util/LocalDiskRepositoryTestCase.cs b/GitSharp.Tests/GitSharp.Core/Util/LocalDiskRepositoryTestCase.cs
index 393ddaca..e3bed004 100644
--- a/GitSharp.Tests/GitSharp.Core/Util/LocalDiskRepositoryTestCase.cs
+++ b/GitSharp.Tests/GitSharp.Core/Util/LocalDiskRepositoryTestCase.cs
@@ -56,6 +56,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Threading;
using GitSharp.Core;
using GitSharp.Core.Util;
using GitSharp.Core.Util.JavaHelper;
@@ -85,7 +86,7 @@ public abstract class LocalDiskRepositoryTestCase {
[SetUp]
public virtual void setUp(){
- recursiveDelete(testName() + " (SetUp)", trash, false, true);
+ recursiveDelete(testName() + " (SetUp)", trash, true);
mockSystemReader = new MockSystemReader();
mockSystemReader.userGitConfig = new FileBasedConfig(new FileInfo(Path.Combine(trash.FullName, "usergitconfig")));
@@ -121,14 +122,14 @@ public virtual void tearDown() {
if (useMMAP)
System.GC.Collect();
- recursiveDelete(testName() + " (TearDown)", trash, false, true);
+ recursiveDelete(testName() + " (TearDown)", trash, true);
}
[TestFixtureTearDown]
public virtual void FixtureTearDown()
{
- recursiveDelete(testName() + " (FixtureTearDown)", trash, false, true);
+ recursiveDelete(testName() + " (FixtureTearDown)", trash, true);
}
/** Increment the {@link #author} and {@link #committer} times. */
@@ -148,21 +149,30 @@ protected void tick() {
* the recursively directory to delete, if present.
*/
protected void recursiveDelete(FileSystemInfo dir) {
- recursiveDelete(testName(), dir, false, true);
+ recursiveDelete(testName(), dir, true);
}
- private static bool recursiveDelete(string testName, FileSystemInfo fs, bool silent, bool failOnError)
+ private static void recursiveDelete(string testName, FileSystemInfo fs, bool failOnError)
{
- Debug.Assert(!(silent && failOnError));
-
if (fs.IsFile())
{
- fs.DeleteFile();
- return silent;
+ if (!fs.DeleteFile())
+ {
+ throw new IOException("Unable to delete file: " + fs.FullName);
+ }
+
+ // Deleting a file only marks it for deletion, following code blocks until
+ // the file is actually deleted. For a more thorough explanation see the
+ // comment below @ the directory.Delete()
+ while (File.Exists(fs.FullName))
+ {
+ Thread.Sleep(0);
+ }
+ return;
}
var dir = new DirectoryInfo(fs.FullName);
- if (!dir.Exists) return silent;
+ if (!Directory.Exists(dir.FullName)) return;
try
{
@@ -170,17 +180,27 @@ private static bool recursiveDelete(string testName, FileSystemInfo fs, bool sil
foreach (FileSystemInfo e in ls)
{
- silent = recursiveDelete(testName, e, silent, failOnError);
+ recursiveDelete(testName, e, failOnError);
}
-
+
dir.Delete();
+
+ // dir.Delete() only marks the directory for deletion (see also: http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/a2fcc569-1835-471f-b731-3fe9c6bcd2d9),
+ // so it creates a race condition between the actual deletion of this directory, and the
+ // deletion of the parent directory. If this directory is not deleted when the parent directory
+ // is tried to be deleted, an IOException ("Directory not empty") will be thrown.
+ // The following code blocks untill the directory is actually deleted.
+ // (btw, dir.Exists keeps returning true on my (Windows 7) machine, even if the Explorer and Command
+ // say otherwise)
+ while (Directory.Exists(dir.FullName))
+ {
+ Thread.Sleep(0);
+ }
}
- catch (IOException e)
+ catch (IOException ex)
{
- ReportDeleteFailure(testName, failOnError, fs, e.Message);
+ ReportDeleteFailure(testName, failOnError, fs, ex.ToString());
}
-
- return silent;
}
private static void ReportDeleteFailure(string name, bool failOnError, FileSystemInfo fsi, string message)
diff --git a/GitSharp.Tests/GitSharp/CloneTests.cs b/GitSharp.Tests/GitSharp/CloneTests.cs
index 2c330f84..53115c15 100644
--- a/GitSharp.Tests/GitSharp/CloneTests.cs
+++ b/GitSharp.Tests/GitSharp/CloneTests.cs
@@ -86,7 +86,6 @@ public void Try_cloning_non_existing_repo_git()
}
[Test]
- [Ignore("TransportLocal is not completely ported yet.")]
public void Checked_cloned_local_dotGit_suffixed_repo()
{
//setup of .git directory
@@ -100,10 +99,8 @@ public void Checked_cloned_local_dotGit_suffixed_repo()
var repositoryPath = new DirectoryInfo(Path.Combine(tempRepository.FullName, Constants.DOT_GIT));
Directory.Move(repositoryPath.FullName + "ted", repositoryPath.FullName);
-
using (var repo = new Repository(repositoryPath.FullName))
{
- Assert.IsTrue(Repository.IsValid(repo.Directory));
Commit headCommit = repo.Head.CurrentCommit;
Assert.AreEqual("f3ca78a01f1baa4eaddcc349c97dcab95a379981", headCommit.Hash);
}
@@ -119,7 +116,6 @@ public void Checked_cloned_local_dotGit_suffixed_repo()
}
[Test]
- [Ignore]
public void Check_cloned_repo_http()
{
string toPath = Path.Combine(trash.FullName, "test");
@@ -127,6 +123,13 @@ public void Check_cloned_repo_http()
using (Repository repo = Git.Clone(fromUrl, toPath))
{
+
+ var status = new RepositoryStatus(repo, new RepositoryStatusOptions { ForceContentCheck = false });
+ Assert.IsFalse(status.AnyDifferences);
+
+ status = new RepositoryStatus(repo, new RepositoryStatusOptions { ForceContentCheck = true });
+ Assert.IsFalse(status.AnyDifferences);
+
Assert.IsTrue(Repository.IsValid(repo.Directory));
//Verify content is in the proper location
var readme = Path.Combine(repo.WorkingDirectory, "master.txt");
diff --git a/GitSharp/Commands/AbstractCommand.cs b/GitSharp/Commands/AbstractCommand.cs
index 2bb33790..55cadb89 100644
--- a/GitSharp/Commands/AbstractCommand.cs
+++ b/GitSharp/Commands/AbstractCommand.cs
@@ -47,7 +47,7 @@ namespace GitSharp.Commands
///
/// Abstract base class of all git commands. It provides basic infrastructure
///
- public abstract class AbstractCommand : IGitCommand
+ public abstract class AbstractCommand : IGitCommand, IDisposable
{
///
/// Abbreviates a ref-name, used in internal output
@@ -212,5 +212,17 @@ public virtual string ActualDirectory
///
public abstract void Execute();
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ if (_repository != null)
+ {
+ _repository.Dispose();
+ }
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/GitSharp/Commands/CloneCommand.cs b/GitSharp/Commands/CloneCommand.cs
index 9bd746d1..57db2c17 100644
--- a/GitSharp/Commands/CloneCommand.cs
+++ b/GitSharp/Commands/CloneCommand.cs
@@ -281,26 +281,25 @@ private FetchResult runFetch()
private static GitSharp.Core.Ref guessHEAD(FetchResult result)
{
- GitSharp.Core.Ref idHEAD = result.GetAdvertisedRef(Constants.HEAD);
- List availableRefs = new List();
- GitSharp.Core.Ref head = null;
+ // Some transports allow us to see where HEAD points to. If that is not so,
+ // we'll have to guess.
+ GitSharp.Core.Ref head = result.GetAdvertisedRef(Constants.HEAD);
+ if (head != null)
+ {
+ return head;
+ }
- foreach (GitSharp.Core.Ref r in result.AdvertisedRefs)
- {
- string n = r.Name;
- if (!n.StartsWith(Constants.R_HEADS))
- continue;
- availableRefs.Add(r);
- if (idHEAD == null || head != null)
- continue;
-
- if (r.ObjectId.Equals(idHEAD.ObjectId))
- head = r;
- }
- availableRefs.Sort(RefComparator.INSTANCE);
- if (idHEAD != null && head == null)
- head = idHEAD;
- return head;
+ var availableHeads = result.AdvertisedRefs.Where(r => r.Name.StartsWith(Constants.R_HEADS));
+
+ // master is our preferred guess, so if it's advertised, return that.
+ GitSharp.Core.Ref guessedHead = result.GetAdvertisedRef(Constants.R_HEADS + Constants.MASTER);
+ if (guessedHead == null && availableHeads.Count() > 0)
+ {
+ // if master is not advertised, return any other head.
+ guessedHead = availableHeads.First();
+ }
+
+ return guessedHead;
}
private void doCheckout(GitSharp.Core.Ref branch)
diff --git a/GitSharp/GitSharp.csproj b/GitSharp/GitSharp.csproj
index bccf7ecb..3274d2dd 100644
--- a/GitSharp/GitSharp.csproj
+++ b/GitSharp/GitSharp.csproj
@@ -51,6 +51,9 @@
+
+ Properties\SharedAssemblyInfo.cs
+
diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs
new file mode 100644
index 00000000..ec834399
--- /dev/null
+++ b/SharedAssemblyInfo.cs
@@ -0,0 +1,13 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyCompany("The Git Development Community")]
+[assembly: AssemblyCopyright("Copyright © 2010 The GitSharp Team")]
+[assembly: ComVisible(false)]
+
+#if DEBUG
+[assembly: AssemblyConfiguration("Debug")]
+#else
+[assembly: AssemblyConfiguration("Release")]
+#endif
\ No newline at end of file