Commits

Christian Specht committed af3874b Merge

Merge

  • Participants
  • Parent commits 6af6fd3, fd13267

Comments (0)

Files changed (9)

 833e0b686f22dc515e304f817360269099d4ddd0 1.0
+c3fcecf20d8b4013b12fffb3fe3515ce2b6438e8 1.1
 ## How does it work?
 
 Bitbucket Backup uses the [Bitbucket API](https://api.bitbucket.org/) to get a list of all your repositories.  
-Then, it uses [Mercurial](http://mercurial.selenic.com/) (which needs to be installed on your machine) to clone every repository into your local backup folder (or just pull the newest changes if it already **is** in your local backup folder).  
-It also checks for each repository, whether it has a wiki ([which is a Mercurial repository itself](http://confluence.atlassian.com/display/BITBUCKET/Using+your+Bitbucket+Wiki)). If yes, that will be automatically cloned/pulled as well.
+Then, it uses [Mercurial](http://mercurial.selenic.com/) and/or [Git](http://git-scm.com/) (which need to be installed on your machine if you have at least one repository of the given type) to clone every repository into your local backup folder (or just pull the newest changes if it already **is** in your local backup folder).  
+It also checks for each repository, whether it has a wiki ([which is a repository itself](http://confluence.atlassian.com/display/BITBUCKET/Using+your+Bitbucket+Wiki)). If yes, that will be automatically cloned/pulled as well.
+
+**DISCLAIMER: Git support is still very unstable.**  
+For now, it works on my machine with Git 1.7.7 (but not on another machine with Git v1.6.5.1, for example). It definitely needs improvement.
 
 ## Setup
 
 Bitbucket Backup loads all important values (your Bitbucket username and password, and the backup folder on your local machine) from a config file.  
-Before you run Bitbucket Backup the first time, you need to edit **BitbucketBackup.exe.config** and provide your data.
+Before you run Bitbucket Backup the first time, you need to edit **BitbucketBackup.exe.config** and provide your data.  
+
+Please note that Bitbucket Backup assumes that you have the Mercurial and Git executables in your **%PATH%** variable.  
+(depending on the version, Git may come with a **git.exe** AND a **git.cmd** - it doesn't matter which one is in the **%PATH%**, Bitbucket Backup will find both)
 
 ## How to build
 

File src/BitbucketBackup/BitbucketBackup.csproj

     <Compile Include="BitbucketRequest.cs" />
     <Compile Include="ClientException.cs" />
     <Compile Include="Config.cs" />
+    <Compile Include="GitRepository.cs" />
+    <Compile Include="GitWrapper.cs" />
     <Compile Include="MercurialRepository.cs" />
     <Compile Include="RepositoryBase.cs" />
     <Compile Include="Program.cs" />

File src/BitbucketBackup/GitRepository.cs

+using System.IO;
+
+namespace BitbucketBackup
+{
+    /// <summary>
+    /// Creates and pulls from Git repositories.
+    /// </summary>
+    internal class GitRepository : RepositoryBase
+    {
+        private GitWrapper git;
+
+        public GitRepository(string remoteUri, string folder) : base(remoteUri, folder) { }
+
+        protected override void Init()
+        {
+            this.git = new GitWrapper(folder);
+
+            if (!File.Exists(Path.Combine(this.folder, "HEAD")))
+            {
+                this.git.Execute("init --bare");
+            }
+        }
+
+        public override string PullingMessage
+        {
+            get { return Resources.PullingGit; }
+        }
+
+        public override void Pull()
+        {
+            this.git.Execute("fetch " + this.remoteuri + " refs/heads/*:refs/heads/*");
+        }
+    }
+}

File src/BitbucketBackup/GitWrapper.cs

+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace BitbucketBackup
+{
+    /// <summary>
+    /// Simple wrapper for Git (assumes that git.exe or git.cmd is in your %PATH%)
+    /// </summary>
+    internal class GitWrapper
+    {
+        private string folder;
+        private string executable;
+
+        /// <summary>
+        /// Creates a new GitWrapper instance.
+        /// </summary>
+        /// <param name="folder">The folder for the local repository.</param>
+        public GitWrapper(string folder)
+        {
+            this.folder = folder;
+            this.executable = this.FindExecutable();
+        }
+
+        /// <summary>
+        /// Executes Git with the given command
+        /// </summary>
+        /// <param name="gitCommand">The command to execute, e.g. "init"</param>
+        public void Execute(string gitCommand)
+        {
+            var info = new ProcessStartInfo();
+            info.FileName = this.executable;
+            info.Arguments = gitCommand;
+            info.CreateNoWindow = true;
+            info.WorkingDirectory = this.folder;
+            info.UseShellExecute = false;
+
+            var git = new Process();
+            git.StartInfo = info;
+            git.Start();
+            git.WaitForExit();
+            git.Close();      
+        }
+
+        /// <summary>
+        /// Finds out the path to git.exe
+        /// </summary>
+        /// <returns>The complete path to git.exe</returns>
+        private string FindExecutable()
+        {
+            string pathVariable = Environment.GetEnvironmentVariable("path");
+
+            foreach (var value in pathVariable.Split(';'))
+            {
+                if (File.Exists(Path.Combine(value, "git.exe")))
+                {
+                    return Path.Combine(value, "git.exe");
+                }
+
+                if (File.Exists(Path.Combine(value, "git.cmd")))
+                {
+                    // Calling git.cmd with Process.Start doesn't work, so we always need to use git.exe.
+                    // git.cmd is in a folder called "cmd", and git.exe is in a folder called "bin", which is on the same level as "cmd".
+                    var exePath = Path.Combine(Directory.GetParent(value).ToString(), "bin");
+
+                    if (File.Exists(Path.Combine(exePath, "git.exe")))
+                    {
+                        return Path.Combine(exePath, "git.exe");
+                    }
+                }
+            }
+
+            throw new FileNotFoundException("No Git executable found!");
+        }
+    }
+}

File src/BitbucketBackup/Program.cs

 
                 var baseUri = new Uri("https://bitbucket.org/" + config.UserName + "/");
 
-                foreach (var repo in repos)
+                foreach (var repo in repos.OrderBy(r => r.RepoName))
                 {
-                    if (repo.Scm == "hg")
+                    var repoUri = new Uri(baseUri, repo.RepoName);
+                    string repoPath = Path.Combine(config.BackupFolder, repo.RepoName);
+
+                    var updater = new RepositoryUpdater(repo.Scm, repoUri, repoPath, config);
+                    updater.Update();
+
+                    if (repo.HasWiki)
                     {
-                        var repoUri = new Uri(baseUri, repo.RepoName);
-                        string repoPath = Path.Combine(config.BackupFolder, repo.RepoName);
+                        var wikiUri = new Uri(baseUri, repo.RepoName + "/wiki");
+                        string wikiPath = Path.Combine(config.BackupFolder, repo.RepoName + "-wiki");
 
-                        var updater = new RepositoryUpdater(repo.Scm, repoUri, repoPath, config);
-                        updater.Update();
-
-                        if (repo.HasWiki)
-                        {
-                            var wikiUri = new Uri(baseUri, repo.RepoName + "/wiki");
-                            string wikiPath = Path.Combine(config.BackupFolder, repo.RepoName + "-wiki");
-
-                            var wikiUpdater = new RepositoryUpdater(repo.Scm, wikiUri, wikiPath, config);
-                            wikiUpdater.Update();
-                        }
+                        var wikiUpdater = new RepositoryUpdater(repo.Scm, wikiUri, wikiPath, config);
+                        wikiUpdater.Update();
                     }
                 }
 

File src/BitbucketBackup/RepositoryFactory.cs

             {
                 case "hg":
                     return new MercurialRepository(remoteUri, localFolder);
+                case "git":
+                    return new GitRepository(remoteUri, localFolder);
                 default:
                     return null;
             }

File src/BitbucketBackup/Resources.Designer.cs

         }
         
         /// <summary>
-        ///   Sucht eine lokalisierte Zeichenfolge, die hg pull: {0} ähnelt.
+        ///   Sucht eine lokalisierte Zeichenfolge, die git pull: {0} ähnelt.
+        /// </summary>
+        internal static string PullingGit {
+            get {
+                return ResourceManager.GetString("PullingGit", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Sucht eine lokalisierte Zeichenfolge, die  hg pull: {0} ähnelt.
         /// </summary>
         internal static string PullingMercurial {
             get {

File src/BitbucketBackup/Resources.resx

     <value>Bitbucket API didn't return a response: {0}</value>
   </data>
   <data name="PullingMercurial" xml:space="preserve">
-    <value>hg pull: {0}</value>
+    <value> hg pull: {0}</value>
   </data>
   <data name="ClientExceptionHeadline" xml:space="preserve">
     <value>Backup failed!</value>
     <value>Authentication failed.
 Please check if the password is valid!</value>
   </data>
+  <data name="PullingGit" xml:space="preserve">
+    <value>git pull: {0}</value>
+  </data>
 </root>