Merge remote-tracking branch 'hbons/master' into gettext-cs
Conflicts: .gitignore .gitmodules SparkleLib/Git/SparkleFetcherGit.cs SparkleLib/Hg/SparkleRepoHg.cs SparkleLib/SparkleConfig.cs SparkleLib/SparkleFetcherBase.cs SparkleLib/SparkleListenerIrc.cs SparkleShare/SparkleBubblesController.cs SparkleShare/SparkleControllerBase.cs SparkleShare/SparkleStatusIcon.cs
This commit is contained in:
commit
b98c4ee5fb
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -27,6 +27,8 @@ aclocal.m4
|
|||
autom4te.cache/
|
||||
bin/
|
||||
obj/
|
||||
/bin/
|
||||
SparkleShare/Mac/bin
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
basedirs = build help SmartIrc4net SparkleLib data po
|
||||
basedirs = build help SparkleLib data po
|
||||
|
||||
SUBDIRS = $(basedirs) $(GUISUBDIRS)
|
||||
DIST_SUBDIRS = $(basedirs) SparkleShare
|
||||
|
|
22
NEWS
22
NEWS
|
@ -1,3 +1,25 @@
|
|||
0.6.0 for Linux and Mac (Sun Dec 25 2011):
|
||||
|
||||
Hylke:
|
||||
- Several fixes for annoying bugs and crashes
|
||||
- Fix freeze on quit on Mac
|
||||
- Show project and history size in the event log
|
||||
|
||||
|
||||
0.4.2 for Linux and Mac (Fri Dec 2 2011):
|
||||
Hylke: Fix crash trying to add a project
|
||||
|
||||
|
||||
0.4.1 for Linux and Mac (Tue Nov 29 2011):
|
||||
Hylke: Just some small tweaks and fixes:
|
||||
|
||||
- Bundle git with the Mac application
|
||||
- Warn about potential global gitignore files
|
||||
- Remove SmartIrc4Net
|
||||
- Build system fixes
|
||||
- Code cleanups
|
||||
|
||||
|
||||
0.4.0 for Linux and Mac (Sun Nov 12 2011):
|
||||
Hylke: It has been a while since the last release. Since so many
|
||||
things changed, and it being (softly) incompatible with 0.2, I decided
|
||||
|
|
145
README
145
README
|
@ -1,145 +0,0 @@
|
|||
# SparkleShare
|
||||
|
||||
SparkleShare is a collaboration and sharing tool that is designed to keep
|
||||
things simple and to stay out of your way. It allows you to instantly sync
|
||||
with any Git repository you have access to.
|
||||
|
||||
SparkleShare currently works on Linux and Mac. A Windows port and mobile
|
||||
device support are planned for the future.
|
||||
|
||||
|
||||
# License
|
||||
|
||||
SparkleShare is free software and licensed under the GNU GPLv3 or later. You
|
||||
are welcome to change and redistribute it under certain conditions. For more
|
||||
information see the LICENSE file or visit http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
|
||||
# Run on Linux
|
||||
|
||||
Requirements:
|
||||
|
||||
- git >= 1.7.0
|
||||
- gtk-sharp2
|
||||
- gvfs
|
||||
- intltool
|
||||
- libnotify
|
||||
- mono-core >= 2.8
|
||||
- notify-sharp
|
||||
- nautilus-python
|
||||
- openssh
|
||||
- pygtk
|
||||
- webkitgtk
|
||||
- webkit-sharp
|
||||
|
||||
Run the service, either click the SparkleShare launcher or:
|
||||
|
||||
$ sparkleshare start
|
||||
|
||||
You can stop the service via the graphical interface or by typing:
|
||||
|
||||
$ sparkleshare stop
|
||||
|
||||
For help:
|
||||
|
||||
$ sparkleshare --help
|
||||
|
||||
Note:
|
||||
|
||||
SparkleShare creates its own RSA keypair in ~/config/sparkleshare/ and uses
|
||||
that for authentication. Please mind this if you're planning to set up your
|
||||
own server by hand.
|
||||
|
||||
|
||||
# Build on Linux
|
||||
|
||||
Installing the build dependencies on Debian or Ubuntu:
|
||||
|
||||
$ sudo apt-get install gtk-sharp2 mono-runtime mono-devel monodevelop \
|
||||
libndesk-dbus1.0-cil-dev nant libnotify-cil-dev libgtk2.0-cil-dev mono-gmcs \
|
||||
libwebkit-cil-dev intltool libtool python-nautilus libndesk-dbus-glib1.0-cil-dev
|
||||
|
||||
For Ubuntu libappindicator support, run the following before building:
|
||||
|
||||
$ sudo apt-get install libappindicator0.1-cil-dev
|
||||
|
||||
On Fedora:
|
||||
|
||||
$ sudo yum install gtk-sharp2-devel mono-core mono-devel monodevelop \
|
||||
ndesk-dbus-devel ndesk-dbus-glib-devel nautilus-python-devel nant \
|
||||
notify-sharp-devel webkit-sharp-devel webkitgtk-devel libtool intltool \
|
||||
gnome-doc-utils
|
||||
|
||||
You can build and install SparkleShare like this:
|
||||
|
||||
$ ./configure --prefix=/usr (or ./autogen.sh if you build from the repository)
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
||||
Note:
|
||||
|
||||
Use '--prefix=/usr' if you want the Nautilus extension to work.
|
||||
|
||||
|
||||
# Run on Mac
|
||||
|
||||
Just double-click the SparkleShare bundle.
|
||||
|
||||
|
||||
# Build on Mac
|
||||
|
||||
Install Xcode, the Mono Framework, MonoDevelop and the MonoMac plugin (you can find it in MonoDevelop => Add-in Manager).
|
||||
|
||||
You may need to adjust some environment variables to let the build environment tools find mono:
|
||||
|
||||
$ export PATH=/Library/Frameworks/Mono.framework/Versions/Current/bin:$PATH
|
||||
$ export PKG_CONFIG=/Library/Frameworks/Mono.framework/Versions/Current/bin/pkg-config
|
||||
$ export PKG_CONFIG_PATH=/Library/Frameworks/Mono.framework/Versions/Current/lib/pkgconfig
|
||||
|
||||
Install git, automake, and intltool using MacPorts:
|
||||
|
||||
$ sudo port install git-core automake intltool
|
||||
|
||||
Start the first part of the build:
|
||||
|
||||
$ ./autogen.sh --enable-gtkui=no
|
||||
$ make
|
||||
|
||||
Now that you have compiled the libraries, open 'SparkleShare/Mac/SparkleShare.sln' in
|
||||
MonoDevelop and start the build.
|
||||
|
||||
To create the SparkleShare.app, make sure the project is focused and select Project from the menu bar
|
||||
and click "Create Mac Installer...". Make sure to select "Don't link assemblies".
|
||||
|
||||
Save the SparkleShare.app somewhere. Paste the contents of
|
||||
the following file in SparkleShare.app/Contents/MonoBundle/config:
|
||||
https://raw.github.com/gist/1aeffa61bac73fc08eca/0c0f09ef9e36864c35f34fd5e8bf4f99886be193/gistfile1.txt
|
||||
|
||||
Copy /Library/Frameworks/Mono.framework/Versions/Current/lib/libintl.dylib
|
||||
to SparkleShare.app/Contents/Resources
|
||||
|
||||
Now you should have a working bundle that you can run.
|
||||
|
||||
|
||||
# Info
|
||||
|
||||
Official website:
|
||||
http://www.sparkleshare.org/
|
||||
|
||||
Source code:
|
||||
http://github.com/SparkleShare/
|
||||
|
||||
IRC Channel:
|
||||
#sparkleshare on irc.gnome.org
|
||||
|
||||
Wiki:
|
||||
http://github.com/hbons/SparkleShare/wiki/
|
||||
|
||||
Report issues:
|
||||
http://github.com/hbons/SparkleShare/issues/
|
||||
|
||||
Translation project:
|
||||
http://www.transifex.net/projects/p/sparkleshare/
|
||||
|
||||
|
||||
Now have fun and create cool things together! :)
|
162
README.md
Normal file
162
README.md
Normal file
|
@ -0,0 +1,162 @@
|
|||
# SparkleShare
|
||||
|
||||
SparkleShare is a collaboration and sharing tool that is designed to keep
|
||||
things simple and to stay out of your way. It allows you to instantly sync
|
||||
with any Git repository you have access to.
|
||||
|
||||
SparkleShare currently works on Linux and Mac. A Windows port and mobile
|
||||
device support are planned for the future.
|
||||
|
||||
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/thing/21770/SparkleShare-Sharing-work-made-easy)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
SparkleShare is free software and licensed under the GNU GPLv3 or later. You
|
||||
are welcome to change and redistribute it under certain conditions. For more
|
||||
information see the LICENSE file or visit http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
|
||||
## Run on Linux
|
||||
|
||||
Requirements:
|
||||
|
||||
- git >= 1.7.0
|
||||
- gtk-sharp2
|
||||
- gvfs
|
||||
- intltool
|
||||
- libnotify
|
||||
- mono-core >= 2.8
|
||||
- notify-sharp
|
||||
- nautilus-python
|
||||
- openssh
|
||||
- pygtk
|
||||
- webkitgtk
|
||||
- webkit-sharp
|
||||
|
||||
Run the service, either click the SparkleShare launcher or:
|
||||
|
||||
```bash
|
||||
$ sparkleshare start
|
||||
```
|
||||
|
||||
You can stop the service via the graphical interface or by typing:
|
||||
|
||||
```bash
|
||||
$ sparkleshare stop
|
||||
```
|
||||
|
||||
For help:
|
||||
|
||||
```bash
|
||||
$ sparkleshare --help
|
||||
```
|
||||
|
||||
**Note:**
|
||||
|
||||
SparkleShare creates its own RSA keypair in `~/config/sparkleshare/` and uses
|
||||
that for authentication. Please mind this if you're planning to set up your
|
||||
own server by hand.
|
||||
|
||||
|
||||
## Build on Linux
|
||||
|
||||
### Install build dependencies
|
||||
|
||||
#### Debian or Ubuntu (apt):
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install gtk-sharp2 mono-runtime mono-devel monodevelop \
|
||||
libndesk-dbus1.0-cil-dev nant libnotify-cil-dev libgtk2.0-cil-dev mono-mcs mono-gmcs \
|
||||
libwebkit-cil-dev intltool libtool python-nautilus libndesk-dbus-glib1.0-cil-dev
|
||||
```
|
||||
|
||||
#### Fedora (yum):
|
||||
|
||||
```bash
|
||||
$ sudo yum install gtk-sharp2-devel mono-core mono-devel monodevelop \
|
||||
ndesk-dbus-devel ndesk-dbus-glib-devel nautilus-python-devel nant \
|
||||
notify-sharp-devel webkit-sharp-devel webkitgtk-devel libtool intltool \
|
||||
gnome-doc-utils
|
||||
```
|
||||
|
||||
For Ubuntu `libappindicator` support, install the following package:
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install libappindicator0.1-cil-dev
|
||||
```
|
||||
|
||||
You can then build and install SparkleShare like this:
|
||||
|
||||
```bash
|
||||
$ ./configure --prefix=/usr (or ./autogen.sh if you build from the repository)
|
||||
$ make
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
**Note:** Use `--prefix=/usr` if you want the Nautilus extension to work.
|
||||
|
||||
|
||||
## Run on Mac
|
||||
|
||||
Just double-click the SparkleShare bundle.
|
||||
|
||||
|
||||
## Build on Mac
|
||||
|
||||
Install <tt>Xcode</tt>, the <tt>Mono</tt> Framework, <tt>MonoDevelop</tt> and the <tt>MonoMac</tt> plugin
|
||||
(you can find it in <tt>MonoDevelop</tt> => <tt>Add-in Manager</tt>).
|
||||
|
||||
You may need to adjust some environment variables to let the build environment tools find mono:
|
||||
|
||||
```bash
|
||||
$ export PATH=/Library/Frameworks/Mono.framework/Versions/Current/bin:$PATH
|
||||
$ export PKG_CONFIG=/Library/Frameworks/Mono.framework/Versions/Current/bin/pkg-config
|
||||
$ export PKG_CONFIG_PATH=/Library/Frameworks/Mono.framework/Versions/Current/lib/pkgconfig
|
||||
```
|
||||
|
||||
Install <tt>git</tt>, <tt>automake</tt>, and <tt>intltool</tt> using <tt>MacPorts</tt>:
|
||||
|
||||
```bash
|
||||
$ sudo port install git-core automake intltool
|
||||
```
|
||||
|
||||
Start the first part of the build:
|
||||
|
||||
```bash
|
||||
$ ./autogen.sh --enable-gtkui=no
|
||||
$ make
|
||||
```
|
||||
|
||||
Now that you have compiled the libraries, open `SparkleShare/Mac/SparkleShare.sln` in
|
||||
MonoDevelop and start the build.
|
||||
|
||||
To create the <tt>SparkleShare.app</tt>, make sure the project is focused and select Project from the menu bar
|
||||
and click <tt>"Create Mac Installer..."</tt>. Make sure to select <tt>"Don't link assemblies"</tt>.
|
||||
|
||||
Save the <tt>SparkleShare.app</tt> somewhere. Paste the contents of
|
||||
the following file in `SparkleShare.app/Contents/MonoBundle/config`:
|
||||
|
||||
```
|
||||
https://raw.github.com/gist/1aeffa61bac73fc08eca/0c0f09ef9e36864c35f34fd5e8bf4f99886be193/gistfile1.txt
|
||||
```
|
||||
|
||||
Copy `/Library/Frameworks/Mono.framework/Versions/Current/lib/libintl.dylib` to `SparkleShare.app/Contents/Resources`
|
||||
|
||||
|
||||
Now you should have a working bundle that you can run.
|
||||
|
||||
|
||||
## Info
|
||||
|
||||
|||
|
||||
|-----------------------------------:|:--------------------------|
|
||||
| **Official website**: | http://www.sparkleshare.org/ |
|
||||
| **Source code**: | http://github.com/SparkleShare/ |
|
||||
| **IRC Channel**: | #sparkleshare on irc.gnome.org |
|
||||
| **Wiki**: | http://github.com/hbons/SparkleShare/wiki/ |
|
||||
| **Report issues**: | http://github.com/hbons/SparkleShare/issues/ |
|
||||
| **Translation project**: | http://www.transifex.net/projects/p/sparkleshare/ |
|
||||
|
||||
|
||||
Now have fun and create cool things together! :)
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 036e2233aea9bd3b4a30f8c5136daff0187eb5ec
|
|
@ -94,6 +94,9 @@ namespace SparkleLib {
|
|||
double percentage = 1.0;
|
||||
Regex progress_regex = new Regex (@"([0-9]+)%", RegexOptions.Compiled);
|
||||
|
||||
DateTime last_change = DateTime.Now;
|
||||
TimeSpan change_interval = new TimeSpan (0, 0, 0, 1);
|
||||
|
||||
while (!this.git.StandardError.EndOfStream) {
|
||||
string line = this.git.StandardError.ReadLine ();
|
||||
Match match = progress_regex.Match (line);
|
||||
|
@ -107,7 +110,7 @@ namespace SparkleLib {
|
|||
// the "Receiving objects" stage which we count as the last 80%
|
||||
if (line.Contains ("|"))
|
||||
// "Receiving objects" stage
|
||||
number = (number / 100 * 75 + 20);
|
||||
number = (number / 100 * 80 + 20);
|
||||
else
|
||||
// "Compressing objects" stage
|
||||
number = (number / 100 * 20);
|
||||
|
@ -116,19 +119,20 @@ namespace SparkleLib {
|
|||
if (number >= percentage) {
|
||||
percentage = number;
|
||||
|
||||
// FIXME: for some reason it doesn't go above 95%
|
||||
if (DateTime.Compare (last_change, DateTime.Now.Subtract (change_interval)) < 0) {
|
||||
base.OnProgressChanged (percentage);
|
||||
last_change = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
System.Threading.Thread.Sleep (100);
|
||||
}
|
||||
|
||||
this.git.WaitForExit ();
|
||||
|
||||
SparkleHelpers.DebugInfo ("Git", "Exit code " + this.git.ExitCode.ToString ());
|
||||
|
||||
|
||||
if (this.git.ExitCode != 0) {
|
||||
return false;
|
||||
|
||||
} else {
|
||||
InstallConfiguration ();
|
||||
InstallExcludeRules ();
|
||||
|
@ -137,9 +141,33 @@ namespace SparkleLib {
|
|||
}
|
||||
|
||||
|
||||
public override string [] Warnings {
|
||||
get {
|
||||
SparkleGit git = new SparkleGit (SparkleConfig.DefaultConfig.TmpPath,
|
||||
"config --global core.excludesfile");
|
||||
|
||||
git.Start ();
|
||||
|
||||
// Reading the standard output HAS to go before
|
||||
// WaitForExit, or it will hang forever on output > 4096 bytes
|
||||
string output = git.StandardOutput.ReadToEnd ().Trim ();
|
||||
git.WaitForExit ();
|
||||
|
||||
if (string.IsNullOrEmpty (output)) {
|
||||
return null;
|
||||
|
||||
} else {
|
||||
return new string [] {
|
||||
string.Format ("You seem to have configured a system ‘gitignore’ file. " +
|
||||
"This may interfere with SparkleShare.\n({0})", output)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop ()
|
||||
{
|
||||
if (this.git != null) {
|
||||
if (this.git != null && !this.git.HasExited) {
|
||||
this.git.Kill ();
|
||||
this.git.Dispose ();
|
||||
}
|
||||
|
@ -179,22 +207,11 @@ namespace SparkleLib {
|
|||
// Add a .gitignore file to the repo
|
||||
private void InstallExcludeRules ()
|
||||
{
|
||||
string exclude_rules_file_path = SparkleHelpers.CombineMore (
|
||||
this.target_folder, ".git", "info", "exclude");
|
||||
DirectoryInfo info = Directory.CreateDirectory (SparkleHelpers.CombineMore (
|
||||
this.target_folder, ".git", "info"));
|
||||
|
||||
string directory = Path.GetDirectoryName(exclude_rules_file_path);
|
||||
|
||||
if (directory == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
TextWriter writer = new StreamWriter (exclude_rules_file_path);
|
||||
string exlude_rules_file_path = Path.Combine (info.FullName, "exclude");
|
||||
TextWriter writer = new StreamWriter (exlude_rules_file_path);
|
||||
|
||||
// gedit and emacs
|
||||
writer.WriteLine ("*~");
|
||||
|
@ -257,6 +274,7 @@ namespace SparkleLib {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public class SparkleGit : Process {
|
||||
|
||||
public SparkleGit (string path, string args) : base ()
|
||||
|
|
|
@ -26,15 +26,8 @@ namespace SparkleLib {
|
|||
|
||||
public class SparkleRepoGit : SparkleRepoBase {
|
||||
|
||||
private string exlude_rules_file_path;
|
||||
private string ExclusionBlock = "#Temporary Exclusions";
|
||||
|
||||
public SparkleRepoGit (string path, SparkleBackend backend) :
|
||||
base (path, backend) {
|
||||
// Set exclude file path
|
||||
exlude_rules_file_path = SparkleHelpers.CombineMore (
|
||||
LocalPath, ".git", "info", "exclude");
|
||||
}
|
||||
base (path, backend) { }
|
||||
|
||||
|
||||
private string identifier = null;
|
||||
|
@ -66,6 +59,34 @@ namespace SparkleLib {
|
|||
}
|
||||
|
||||
|
||||
public override List<string> ExcludePaths {
|
||||
get {
|
||||
List<string> rules = new List<string> ();
|
||||
rules.Add (Path.DirectorySeparatorChar + ".git");
|
||||
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override double Size {
|
||||
get {
|
||||
return CalculateSize (
|
||||
new DirectoryInfo (LocalPath)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override double HistorySize {
|
||||
get {
|
||||
return CalculateSize (
|
||||
new DirectoryInfo (Path.Combine (LocalPath, ".git"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string [] UnsyncedFilePaths {
|
||||
get {
|
||||
List<string> file_paths = new List<string> ();
|
||||
|
@ -144,16 +165,73 @@ namespace SparkleLib {
|
|||
|
||||
public override bool SyncUp ()
|
||||
{
|
||||
if (AnyDifferences) {
|
||||
Add ();
|
||||
|
||||
string message = FormatCommitMessage ();
|
||||
Commit (message);
|
||||
}
|
||||
|
||||
SparkleGit git = new SparkleGit (LocalPath, "push origin master");
|
||||
|
||||
SparkleGit git = new SparkleGit (LocalPath,
|
||||
"push --progress " + // Redirects progress stats to standarderror
|
||||
"origin master");
|
||||
|
||||
git.StartInfo.RedirectStandardError = true;
|
||||
git.Start ();
|
||||
git.StandardOutput.ReadToEnd ();
|
||||
|
||||
double percentage = 1.0;
|
||||
Regex progress_regex = new Regex (@"([0-9]+)%", RegexOptions.Compiled);
|
||||
|
||||
DateTime last_change = DateTime.Now;
|
||||
TimeSpan change_interval = new TimeSpan (0, 0, 0, 1);
|
||||
|
||||
while (!git.StandardError.EndOfStream) {
|
||||
string line = git.StandardError.ReadLine ();
|
||||
Match match = progress_regex.Match (line);
|
||||
string speed = "";
|
||||
double number = 0.0;
|
||||
|
||||
if (match.Success) {
|
||||
number = double.Parse (match.Groups [1].Value);
|
||||
|
||||
// The pushing progress consists of two stages: the "Compressing
|
||||
// objects" stage which we count as 20% of the total progress, and
|
||||
// the "Writing objects" stage which we count as the last 80%
|
||||
if (line.StartsWith ("Compressing")) {
|
||||
// "Compressing objects" stage
|
||||
number = (number / 100 * 20);
|
||||
|
||||
} else {
|
||||
// "Writing objects" stage
|
||||
number = (number / 100 * 80 + 20);
|
||||
|
||||
if (line.Contains ("|")) {
|
||||
speed = line.Substring (line.IndexOf ("|") + 1).Trim ();
|
||||
speed = speed.Replace (", done.", "").Trim ();
|
||||
speed = speed.Replace ("i", "");
|
||||
speed = speed.Replace ("KB/s", "ᴋʙ/s");
|
||||
speed = speed.Replace ("MB/s", "ᴍʙ/s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (number >= percentage) {
|
||||
percentage = number;
|
||||
|
||||
if (percentage == 100.0)
|
||||
percentage = 99.0;
|
||||
|
||||
if (DateTime.Compare (last_change, DateTime.Now.Subtract (change_interval)) < 0) {
|
||||
base.OnSyncProgressChanged (percentage, speed);
|
||||
last_change = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
git.WaitForExit ();
|
||||
|
||||
|
||||
if (git.ExitCode == 0)
|
||||
return true;
|
||||
else
|
||||
|
@ -163,10 +241,63 @@ namespace SparkleLib {
|
|||
|
||||
public override bool SyncDown ()
|
||||
{
|
||||
SparkleGit git = new SparkleGit (LocalPath, "fetch -v");
|
||||
SparkleGit git = new SparkleGit (LocalPath, "fetch --progress");
|
||||
|
||||
git.StartInfo.RedirectStandardError = true;
|
||||
git.Start ();
|
||||
|
||||
double percentage = 1.0;
|
||||
Regex progress_regex = new Regex (@"([0-9]+)%", RegexOptions.Compiled);
|
||||
|
||||
DateTime last_change = DateTime.Now;
|
||||
TimeSpan change_interval = new TimeSpan (0, 0, 0, 1);
|
||||
|
||||
while (!git.StandardError.EndOfStream) {
|
||||
string line = git.StandardError.ReadLine ();
|
||||
Match match = progress_regex.Match (line);
|
||||
string speed = "";
|
||||
double number = 0.0;
|
||||
|
||||
if (match.Success) {
|
||||
number = double.Parse (match.Groups [1].Value);
|
||||
|
||||
// The fetching progress consists of two stages: the "Compressing
|
||||
// objects" stage which we count as 20% of the total progress, and
|
||||
// the "Receiving objects" stage which we count as the last 80%
|
||||
if (line.StartsWith ("Compressing")) {
|
||||
// "Compressing objects" stage
|
||||
number = (number / 100 * 20);
|
||||
|
||||
} else {
|
||||
// "Writing objects" stage
|
||||
number = (number / 100 * 80 + 20);
|
||||
|
||||
if (line.Contains ("|")) {
|
||||
speed = line.Substring (line.IndexOf ("|") + 1).Trim ();
|
||||
speed = speed.Replace (", done.", "").Trim ();
|
||||
speed = speed.Replace ("i", "");
|
||||
speed = speed.Replace ("KB/s", "ᴋʙ/s");
|
||||
speed = speed.Replace ("MB/s", "ᴍʙ/s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (number >= percentage) {
|
||||
percentage = number;
|
||||
|
||||
if (percentage == 100.0)
|
||||
percentage = 99.0;
|
||||
|
||||
if (DateTime.Compare (last_change, DateTime.Now.Subtract (change_interval)) < 0) {
|
||||
base.OnSyncProgressChanged (percentage, speed);
|
||||
last_change = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
git.WaitForExit ();
|
||||
|
||||
|
||||
if (git.ExitCode == 0) {
|
||||
Rebase ();
|
||||
return true;
|
||||
|
@ -216,6 +347,7 @@ namespace SparkleLib {
|
|||
if (value) {
|
||||
if (!File.Exists (unsynced_file_path))
|
||||
File.Create (unsynced_file_path).Close ();
|
||||
|
||||
} else {
|
||||
File.Delete (unsynced_file_path);
|
||||
}
|
||||
|
@ -233,177 +365,16 @@ namespace SparkleLib {
|
|||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes staged");
|
||||
}
|
||||
|
||||
// Add a new file to be ignored
|
||||
public override bool AddExclusionRule (FileSystemEventArgs args) {
|
||||
|
||||
string RelativePath = SparkleHelpers.DiffPaths(args.FullPath, LocalPath);
|
||||
|
||||
List<String> exclusions;
|
||||
try {
|
||||
exclusions = ReadExclusionRules();
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for the local exclusions section
|
||||
bool added = false;
|
||||
for(int i = 0; i < exclusions.Count; i++) {
|
||||
string entry = exclusions[i];
|
||||
if(entry.Equals(ExclusionBlock)) {
|
||||
// add a new exclusion rule containing a file path
|
||||
exclusions.Insert(i + 1, RelativePath);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For compability to existing repos:
|
||||
* Add a "#Temporary Exclusions"-Block to the
|
||||
* ignore file in order to recognize this
|
||||
* exclude rules later on
|
||||
*/
|
||||
if(!added) {
|
||||
exclusions.Add(ExclusionBlock);
|
||||
exclusions.Add(RelativePath);
|
||||
}
|
||||
|
||||
// Write exceptions list back to file
|
||||
return WriteExclusionRules(exclusions);
|
||||
}
|
||||
|
||||
// Check whether a specific rule exists in the exclusion file
|
||||
public override bool ExclusionRuleExists(FileSystemEventArgs args) {
|
||||
string RelativePath = SparkleHelpers.DiffPaths(args.FullPath, LocalPath);
|
||||
|
||||
List<String> exclusions;
|
||||
try {
|
||||
// Read rules from temporary block only
|
||||
exclusions = ReadExclusionRules(true);
|
||||
|
||||
foreach(string entry in exclusions) {
|
||||
if(entry.Equals(RelativePath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
SparkleHelpers.DebugInfo("Error", "Cannot determine whether an exclusion rule for " +
|
||||
args.FullPath + " already exists or not.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove file from exclusion list when they are readable again
|
||||
public override bool RemoveExclusionRule(FileSystemEventArgs args) {
|
||||
string RelativePath = SparkleHelpers.DiffPaths(args.FullPath, LocalPath);
|
||||
|
||||
List<String> exclusions;
|
||||
try {
|
||||
exclusions = ReadExclusionRules();
|
||||
|
||||
/*
|
||||
* Removing a rule should only apply to rules in the "Temporary Exclusion"-block.
|
||||
* Therefore we first read until reaching the block and then remove the rule.
|
||||
*
|
||||
* We cannot use ReadExclusionRules(true) here since we write all lines back
|
||||
* to the file. This would result in a crippled exclusion file.
|
||||
*/
|
||||
bool BlockReached = false;
|
||||
foreach(string entry in exclusions) {
|
||||
if(entry.Equals(ExclusionBlock)) {
|
||||
BlockReached = true;
|
||||
}
|
||||
|
||||
// Remove this rule
|
||||
if(BlockReached && entry.Equals(RelativePath)) {
|
||||
exclusions.Remove(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return WriteExclusionRules(exclusions);
|
||||
} catch {
|
||||
SparkleHelpers.DebugInfo("Error", "Unable to remove exclusion rule for entry " + RelativePath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the exclusion rules file into a string list
|
||||
private List<String> ReadExclusionRules() {
|
||||
|
||||
List<String> exclusions = new List<String>();
|
||||
TextReader reader = new StreamReader (exlude_rules_file_path);;
|
||||
|
||||
try {
|
||||
while(reader.Peek() > -1) {
|
||||
exclusions.Add(reader.ReadLine().TrimEnd());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
SparkleHelpers.DebugInfo("Error", "Reading from exclusion file failed: " + e.Message);
|
||||
return new List<String>();
|
||||
}
|
||||
finally {
|
||||
if(reader != null) {
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return exclusions;
|
||||
}
|
||||
|
||||
// Reads rules only from temporary exclusion block
|
||||
private List<String> ReadExclusionRules(bool TempOnly) {
|
||||
if(TempOnly) {
|
||||
bool ForceRead = false;
|
||||
List<String> exclusions = new List<String>();
|
||||
foreach(string entry in ReadExclusionRules()) {
|
||||
if(ForceRead || entry.Equals(ExclusionBlock)) {
|
||||
exclusions.Add(entry);
|
||||
ForceRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
return exclusions;
|
||||
}
|
||||
|
||||
return ReadExclusionRules();
|
||||
}
|
||||
|
||||
// Writes the exclusion rules file with a given string list
|
||||
private bool WriteExclusionRules(List<String> lines) {
|
||||
|
||||
TextWriter writer = new StreamWriter (exlude_rules_file_path);
|
||||
|
||||
try {
|
||||
foreach(string line in lines) {
|
||||
writer.WriteLine(line.TrimEnd());
|
||||
}
|
||||
} catch(IOException e) {
|
||||
SparkleHelpers.DebugInfo("Error", "Writing into exclusion file failed: " + e.Message);
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
if(writer != null) {
|
||||
writer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes unneeded objects
|
||||
private void CollectGarbage ()
|
||||
/* private void CollectGarbage ()
|
||||
{
|
||||
SparkleGit git = new SparkleGit (LocalPath, "gc");
|
||||
git.Start ();
|
||||
git.WaitForExit ();
|
||||
|
||||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Garbage collected.");
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
// Commits the made changes
|
||||
|
@ -486,11 +457,6 @@ namespace SparkleLib {
|
|||
string output = git_status.StandardOutput.ReadToEnd ().TrimEnd ();
|
||||
git_status.WaitForExit ();
|
||||
|
||||
if (String.IsNullOrEmpty (output)) {
|
||||
// no conflict any more.
|
||||
return;
|
||||
}
|
||||
|
||||
string [] lines = output.Split ("\n".ToCharArray ());
|
||||
|
||||
foreach (string line in lines) {
|
||||
|
@ -571,13 +537,10 @@ namespace SparkleLib {
|
|||
|
||||
List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> ();
|
||||
|
||||
SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso");
|
||||
if ((SparkleBackend.Platform == PlatformID.Unix ||
|
||||
SparkleBackend.Platform == PlatformID.MacOSX)) {
|
||||
// this causes an IOException on windows
|
||||
// Console.InputEncoding = System.Text.Encoding.Unicode;
|
||||
Console.OutputEncoding = System.Text.Encoding.Unicode;
|
||||
}
|
||||
|
||||
SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso");
|
||||
git_log.Start ();
|
||||
|
||||
// Reading the standard output HAS to go before
|
||||
|
@ -709,9 +672,13 @@ namespace SparkleLib {
|
|||
FillEmptyDirectories (child_path);
|
||||
}
|
||||
|
||||
if (Directory.GetFiles (path).Length == 0 && !path.Equals (LocalPath))
|
||||
if (Directory.GetFiles (path).Length == 0 &&
|
||||
Directory.GetDirectories (path).Length == 0 &&
|
||||
!path.Equals (LocalPath)) {
|
||||
|
||||
File.Create (Path.Combine (path, ".empty")).Close ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Creates a pretty commit message based on what has changed
|
||||
|
@ -752,6 +719,10 @@ namespace SparkleLib {
|
|||
|
||||
foreach (string added in Added) {
|
||||
file_name = added.Trim ("\"".ToCharArray ());
|
||||
|
||||
if (file_name.EndsWith (".empty"))
|
||||
file_name = file_name.Substring (0, file_name.Length - 6);
|
||||
|
||||
message += "+ ‘" + file_name + "’" + n;
|
||||
|
||||
count++;
|
||||
|
@ -761,6 +732,10 @@ namespace SparkleLib {
|
|||
|
||||
foreach (string modified in Modified) {
|
||||
file_name = modified.Trim ("\"".ToCharArray ());
|
||||
|
||||
if (file_name.EndsWith (".empty"))
|
||||
file_name = file_name.Substring (0, file_name.Length - 6);
|
||||
|
||||
message += "/ ‘" + file_name + "’" + n;
|
||||
|
||||
count++;
|
||||
|
@ -770,6 +745,10 @@ namespace SparkleLib {
|
|||
|
||||
foreach (string removed in Removed) {
|
||||
file_name = removed.Trim ("\"".ToCharArray ());
|
||||
|
||||
if (file_name.EndsWith (".empty"))
|
||||
file_name = file_name.Substring (0, file_name.Length - 6);
|
||||
|
||||
message += "- ‘" + file_name + "’" + n;
|
||||
|
||||
count++;
|
||||
|
@ -796,5 +775,38 @@ namespace SparkleLib {
|
|||
base.CreateInitialChangeSet ();
|
||||
SyncUp ();
|
||||
}
|
||||
|
||||
|
||||
// Recursively gets a folder's size in bytes
|
||||
public override double CalculateSize (DirectoryInfo parent)
|
||||
{
|
||||
if (!Directory.Exists (parent.ToString ()))
|
||||
return 0;
|
||||
|
||||
double size = 0;
|
||||
|
||||
// Ignore the temporary 'rebase-apply' and '.tmp' directories. This prevents potential
|
||||
// crashes when files are being queried whilst the files have already been deleted.
|
||||
if (parent.Name.Equals ("rebase-apply") ||
|
||||
parent.Name.Equals (".tmp"))
|
||||
return 0;
|
||||
|
||||
try {
|
||||
foreach (FileInfo file in parent.GetFiles()) {
|
||||
if (!file.Exists)
|
||||
return 0;
|
||||
|
||||
size += file.Length;
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo directory in parent.GetDirectories ())
|
||||
size += CalculateSize (directory);
|
||||
|
||||
} catch (Exception) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
// SparkleShare, a collaboration and sharing tool.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
|
||||
namespace SparkleLib {
|
||||
|
||||
// Sets up a fetcher that can get remote folders
|
||||
public class SparkleFetcherHg : SparkleFetcherBase {
|
||||
|
||||
public SparkleFetcherHg (string server, string remote_folder, string target_folder) :
|
||||
base (server, remote_folder, target_folder) { }
|
||||
|
||||
|
||||
public override bool Fetch ()
|
||||
{
|
||||
SparkleHg hg = new SparkleHg (SparklePaths.SparkleTmpPath,
|
||||
"clone \"" + base.remote_url + "\" " + "\"" + base.target_folder + "\"");
|
||||
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
SparkleHelpers.DebugInfo ("Hg", "Exit code " + hg.ExitCode.ToString ());
|
||||
|
||||
if (hg.ExitCode != 0) {
|
||||
return false;
|
||||
} else {
|
||||
InstallConfiguration ();
|
||||
InstallExcludeRules ();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Install the user's name and email and some config into
|
||||
// the newly cloned repository
|
||||
private void InstallConfiguration ()
|
||||
{
|
||||
string global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config.xml");
|
||||
|
||||
if (!File.Exists (global_config_file_path))
|
||||
return;
|
||||
|
||||
string repo_config_file_path = SparkleHelpers.CombineMore (base.target_folder, ".hg", "hgrc");
|
||||
string config = String.Join (Environment.NewLine, File.ReadAllLines (repo_config_file_path));
|
||||
|
||||
// Add user info
|
||||
string n = Environment.NewLine;
|
||||
XmlDocument xml = new XmlDocument();
|
||||
xml.Load (global_config_file_path);
|
||||
|
||||
XmlNode node_name = xml.SelectSingleNode ("//user/name/text()");
|
||||
XmlNode node_email = xml.SelectSingleNode ("//user/email/text()");
|
||||
|
||||
// TODO this ignore duplicate names (FolderName (2))
|
||||
string ignore_file_path = base.target_folder.Replace (SparklePaths.SparkleTmpPath,
|
||||
SparklePaths.SparklePath);
|
||||
|
||||
ignore_file_path = SparkleHelpers.CombineMore (ignore_file_path, ".hg", "hgignore");
|
||||
|
||||
config += n +
|
||||
"[ui]" + n +
|
||||
"username = " + node_name.Value + " <" + node_email.Value + ">" + n +
|
||||
"ignore = " + ignore_file_path + n;
|
||||
|
||||
// Write the config to the file
|
||||
TextWriter writer = new StreamWriter (repo_config_file_path);
|
||||
writer.WriteLine (config);
|
||||
writer.Close ();
|
||||
|
||||
string style_file_path = SparkleHelpers.CombineMore (base.target_folder, ".hg", "log.style");
|
||||
|
||||
string style = "changeset = \"{file_mods}{file_adds}{file_dels}\"" + n +
|
||||
"file_add = \"A {file_add}\\n\"" + n +
|
||||
"file_mod = \"M {file_mod}\\n\"" + n +
|
||||
"file_del = \"D {file_del}\\n\"" + n;
|
||||
|
||||
writer = new StreamWriter (style_file_path);
|
||||
writer.WriteLine (style);
|
||||
writer.Close ();
|
||||
|
||||
SparkleHelpers.DebugInfo ("Config", "Added configuration to '" + repo_config_file_path + "'");
|
||||
}
|
||||
|
||||
|
||||
// Add a .gitignore file to the repo
|
||||
private void InstallExcludeRules ()
|
||||
{
|
||||
string exlude_rules_file_path = SparkleHelpers.CombineMore (
|
||||
this.target_folder, ".hg", "hgignore");
|
||||
|
||||
TextWriter writer = new StreamWriter (exlude_rules_file_path);
|
||||
|
||||
writer.WriteLine ("syntax: glob");
|
||||
|
||||
// gedit and emacs
|
||||
writer.WriteLine ("*~");
|
||||
|
||||
// vi(m)
|
||||
writer.WriteLine (".*.sw[a-z]");
|
||||
writer.WriteLine ("*.un~");
|
||||
writer.WriteLine ("*.swp");
|
||||
writer.WriteLine ("*.swo");
|
||||
|
||||
// KDE
|
||||
writer.WriteLine (".directory");
|
||||
|
||||
// Mac OSX
|
||||
writer.WriteLine (".DS_Store");
|
||||
writer.WriteLine ("Icon?");
|
||||
writer.WriteLine ("._*");
|
||||
writer.WriteLine (".Spotlight-V100");
|
||||
writer.WriteLine (".Trashes");
|
||||
|
||||
// Mac OSX
|
||||
writer.WriteLine ("*(Autosaved).graffle");
|
||||
|
||||
// Windows
|
||||
writer.WriteLine ("Thumbs.db");
|
||||
writer.WriteLine ("Desktop.ini");
|
||||
|
||||
// CVS
|
||||
writer.WriteLine ("*/CVS/*");
|
||||
writer.WriteLine (".cvsignore");
|
||||
writer.WriteLine ("*/.cvsignore");
|
||||
|
||||
// Subversion
|
||||
writer.WriteLine ("/.svn/*");
|
||||
writer.WriteLine ("*/.svn/*");
|
||||
|
||||
writer.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SparkleHg : Process {
|
||||
|
||||
public SparkleHg (string path, string args) : base ()
|
||||
{
|
||||
EnableRaisingEvents = true;
|
||||
StartInfo.FileName = "/opt/local/bin/hg";
|
||||
StartInfo.Arguments = args;
|
||||
StartInfo.RedirectStandardOutput = true;
|
||||
StartInfo.UseShellExecute = false;
|
||||
StartInfo.WorkingDirectory = path;
|
||||
}
|
||||
|
||||
|
||||
new public void Start ()
|
||||
{
|
||||
SparkleHelpers.DebugInfo ("Cmd", StartInfo.FileName + " " + StartInfo.Arguments);
|
||||
base.Start ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,487 +0,0 @@
|
|||
// SparkleShare, a collaboration and sharing tool.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SparkleLib {
|
||||
|
||||
public class SparkleRepoHg : SparkleRepoBase {
|
||||
|
||||
private string exlude_rules_file_path;
|
||||
private string ExclusionBlock = "#Temporary Exclusions";
|
||||
|
||||
public SparkleRepoHg (string path, SparkleBackend backend) :
|
||||
base (path, backend) {
|
||||
// Set exclude file path
|
||||
exlude_rules_file_path = SparkleHelpers.CombineMore (
|
||||
LocalPath, ".hg", "hgignore");
|
||||
}
|
||||
|
||||
|
||||
public override string Identifier {
|
||||
get {
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "log -r : --limit 1 --template \"{node}\"");
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
return hg.StandardOutput.ReadToEnd ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string CurrentRevision {
|
||||
get {
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "log --limit 1 --template \"{node}\"");
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
string hash = hg.StandardOutput.ReadToEnd ().Trim ();
|
||||
if (hash.Length > 0)
|
||||
return hash;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CheckForRemoteChanges ()
|
||||
{
|
||||
return true; // Mercurial doesn't have a way to check for the remote hash
|
||||
}
|
||||
|
||||
|
||||
public override bool SyncUp ()
|
||||
{
|
||||
Add ();
|
||||
|
||||
string message = FormatCommitMessage ();
|
||||
Commit (message);
|
||||
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "push");
|
||||
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
if (hg.ExitCode == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool SyncDown ()
|
||||
{
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "pull");
|
||||
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
if (hg.ExitCode == 0) {
|
||||
Merge ();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool AnyDifferences {
|
||||
get {
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "status");
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
string output = hg.StandardOutput.ReadToEnd ().TrimEnd ();
|
||||
string [] lines = output.Split ("\n".ToCharArray ());
|
||||
|
||||
foreach (string line in lines) {
|
||||
if (line.Length > 1 && !line [1].Equals (" "))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool HasUnsyncedChanges {
|
||||
get {
|
||||
string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
|
||||
".hg", "has_unsynced_changes");
|
||||
|
||||
return File.Exists (unsynced_file_path);
|
||||
}
|
||||
|
||||
set {
|
||||
string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
|
||||
".hg", "has_unsynced_changes");
|
||||
|
||||
if (value) {
|
||||
if (!File.Exists (unsynced_file_path))
|
||||
File.Create (unsynced_file_path).Close ();
|
||||
} else {
|
||||
File.Delete (unsynced_file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Stages the made changes
|
||||
private void Add ()
|
||||
{
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "addremove --quiet");
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
SparkleHelpers.DebugInfo ("Hg", "[" + Name + "] Changes staged");
|
||||
}
|
||||
|
||||
// Add a new file to be ignored
|
||||
public override bool AddExclusionRule (FileSystemEventArgs args) {
|
||||
|
||||
string RelativePath = SparkleHelpers.DiffPaths(args.FullPath, LocalPath);
|
||||
|
||||
List<String> exclusions;
|
||||
try {
|
||||
exclusions = ReadExclusionRules();
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for the local exclusions section
|
||||
bool added = false;
|
||||
for(int i = 0; i < exclusions.Count; i++) {
|
||||
string entry = exclusions[i];
|
||||
if(entry.Equals(ExclusionBlock)) {
|
||||
// add a new exclusion rule containing a file path
|
||||
exclusions.Insert(i + 1, RelativePath);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For compability to existing repos:
|
||||
* Add a "#Temporary Exclusions"-Block to the
|
||||
* ignore file in order to recognize this
|
||||
* exclude rules later on
|
||||
*/
|
||||
if(!added) {
|
||||
exclusions.Add(ExclusionBlock);
|
||||
exclusions.Add(RelativePath);
|
||||
}
|
||||
|
||||
// Write exceptions list back to file
|
||||
return WriteExclusionRules(exclusions);
|
||||
}
|
||||
|
||||
// Check whether a specific rule exists in the exclusion file
|
||||
public override bool ExclusionRuleExists(FileSystemEventArgs args) {
|
||||
string RelativePath = SparkleHelpers.DiffPaths(args.FullPath, LocalPath);
|
||||
|
||||
List<String> exclusions;
|
||||
try {
|
||||
// Read rules from temporary block only
|
||||
exclusions = ReadExclusionRules(true);
|
||||
|
||||
foreach(string entry in exclusions) {
|
||||
if(entry.Equals(RelativePath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
SparkleHelpers.DebugInfo("Error", "Cannot determine whether an exclusion rule for " +
|
||||
args.FullPath + " already exists or not.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove file from exclusion list when they are readable again
|
||||
public override bool RemoveExclusionRule(FileSystemEventArgs args) {
|
||||
string RelativePath = SparkleHelpers.DiffPaths(args.FullPath, LocalPath);
|
||||
|
||||
List<String> exclusions;
|
||||
try {
|
||||
exclusions = ReadExclusionRules();
|
||||
|
||||
/*
|
||||
* Removing a rule should only apply to rules in the "Temporary Exclusion"-block.
|
||||
* Therefore we first read until reaching the block and then remove the rule.
|
||||
*
|
||||
* We cannot use ReadExclusionRules(true) here since we write all lines back
|
||||
* to the file. This would result in a crippled exclusion file.
|
||||
*/
|
||||
bool BlockReached = false;
|
||||
foreach(string entry in exclusions) {
|
||||
if(entry.Equals(ExclusionBlock)) {
|
||||
BlockReached = true;
|
||||
}
|
||||
|
||||
// Remove this rule
|
||||
if(BlockReached && entry.Equals(RelativePath)) {
|
||||
exclusions.Remove(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return WriteExclusionRules(exclusions);
|
||||
} catch {
|
||||
SparkleHelpers.DebugInfo("Error", "Unable to remove exclusion rule for entry " + RelativePath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the exclusion rules file into a string list
|
||||
private List<String> ReadExclusionRules() {
|
||||
|
||||
List<String> exclusions = new List<String>();
|
||||
TextReader reader = new StreamReader (exlude_rules_file_path);;
|
||||
|
||||
try {
|
||||
while(reader.Peek() > -1) {
|
||||
exclusions.Add(reader.ReadLine().TrimEnd());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
SparkleHelpers.DebugInfo("Error", "Reading from exclusion file failed: " + e.Message);
|
||||
return new List<String>();
|
||||
}
|
||||
finally {
|
||||
if(reader != null) {
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return exclusions;
|
||||
}
|
||||
|
||||
// Reads rules only from temporary exclusion block
|
||||
private List<String> ReadExclusionRules(bool TempOnly) {
|
||||
if(TempOnly) {
|
||||
bool ForceRead = false;
|
||||
List<String> exclusions = new List<String>();
|
||||
foreach(string entry in ReadExclusionRules()) {
|
||||
if(ForceRead || entry.Equals(ExclusionBlock)) {
|
||||
exclusions.Add(entry);
|
||||
ForceRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
return exclusions;
|
||||
}
|
||||
|
||||
return ReadExclusionRules();
|
||||
}
|
||||
|
||||
// Writes the exclusion rules file with a given string list
|
||||
private bool WriteExclusionRules(List<String> lines) {
|
||||
|
||||
TextWriter writer = new StreamWriter (exlude_rules_file_path);
|
||||
|
||||
try {
|
||||
foreach(string line in lines) {
|
||||
writer.WriteLine(line.TrimEnd());
|
||||
}
|
||||
} catch(IOException e) {
|
||||
SparkleHelpers.DebugInfo("Error", "Writing into exclusion file failed: " + e.Message);
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
if(writer != null) {
|
||||
writer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Commits the made changes
|
||||
private void Commit (string message)
|
||||
{
|
||||
if (!AnyDifferences)
|
||||
return;
|
||||
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "commit -m '" + message + "'");
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message);
|
||||
}
|
||||
|
||||
|
||||
// Merges the fetched changes
|
||||
private void Merge ()
|
||||
{
|
||||
DisableWatching ();
|
||||
|
||||
if (AnyDifferences) {
|
||||
Add ();
|
||||
|
||||
string commit_message = FormatCommitMessage ();
|
||||
Commit (commit_message);
|
||||
}
|
||||
|
||||
SparkleHg hg = new SparkleHg (LocalPath, "update");
|
||||
|
||||
hg.Start ();
|
||||
hg.WaitForExit ();
|
||||
|
||||
EnableWatching ();
|
||||
}
|
||||
|
||||
|
||||
// Returns a list of the latest change sets
|
||||
public override List<SparkleChangeSet> GetChangeSets (int count)
|
||||
{
|
||||
if (count < 1)
|
||||
count = 30;
|
||||
|
||||
List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> ();
|
||||
|
||||
SparkleHg hg_log = new SparkleHg (LocalPath, "log --limit " + count + " --style changelog --verbose --stat");
|
||||
Console.OutputEncoding = System.Text.Encoding.Unicode;
|
||||
hg_log.Start ();
|
||||
|
||||
// Reading the standard output HAS to go before
|
||||
// WaitForExit, or it will hang forever on output > 4096 bytes
|
||||
string output = hg_log.StandardOutput.ReadToEnd ();
|
||||
hg_log.WaitForExit ();
|
||||
|
||||
string [] lines = output.Split ("\n".ToCharArray ());
|
||||
List <string> entries = new List <string> ();
|
||||
|
||||
int j = 0;
|
||||
string entry = "", last_entry = "";
|
||||
foreach (string line in lines) {
|
||||
if (line.StartsWith ("2") && line.EndsWith (")") && j > 0) {
|
||||
entries.Add (entry);
|
||||
entry = "";
|
||||
}
|
||||
|
||||
entry += line + "\n";
|
||||
j++;
|
||||
|
||||
last_entry = entry;
|
||||
}
|
||||
|
||||
entries.Add (last_entry);
|
||||
|
||||
Regex regex = new Regex (@"([0-9]{4})-([0-9]{2})-([0-9]{2}).*([0-9]{2}):([0-9]{2}).*.([0-9]{4})" +
|
||||
"(.+)<(.+)>.*.([a-z0-9]{12})", RegexOptions.Compiled);
|
||||
|
||||
foreach (string log_entry in entries) {
|
||||
|
||||
bool is_merge_commit = false;
|
||||
|
||||
Match match = regex.Match (log_entry);
|
||||
|
||||
if (!match.Success)
|
||||
continue;
|
||||
|
||||
SparkleChangeSet change_set = new SparkleChangeSet () {
|
||||
Revision = match.Groups [9].Value,
|
||||
IsMagical = is_merge_commit
|
||||
};
|
||||
|
||||
change_set.User.Name = match.Groups [7].Value.Trim ();
|
||||
change_set.User.Email = match.Groups [8].Value;
|
||||
|
||||
|
||||
change_set.Timestamp = new DateTime (int.Parse (match.Groups [1].Value),
|
||||
int.Parse (match.Groups [2].Value), int.Parse (match.Groups [3].Value),
|
||||
int.Parse (match.Groups [4].Value), int.Parse (match.Groups [5].Value), 0);
|
||||
|
||||
string [] entry_lines = log_entry.Split ("\n".ToCharArray ());
|
||||
|
||||
foreach (string entry_line in entry_lines) {
|
||||
if (!entry_line.StartsWith ("\t* "))
|
||||
continue;
|
||||
|
||||
if (entry_line.EndsWith ("new file.")) {
|
||||
string files = entry_line.Substring (3, entry_line.Length - 13);
|
||||
string [] added_files = files.Split (",".ToCharArray ());
|
||||
|
||||
foreach (string added_file in added_files) {
|
||||
string file = added_file.TrimEnd (": ".ToCharArray ());
|
||||
change_set.Added.Add (file);
|
||||
}
|
||||
|
||||
} else if (entry_line.EndsWith ("deleted file.")) {
|
||||
string files = entry_line.Substring (3, entry_line.Length - 17);
|
||||
string [] deleted_files = files.Split (",".ToCharArray ());
|
||||
|
||||
foreach (string deleted_file in deleted_files) {
|
||||
string file = deleted_file.TrimEnd (": ".ToCharArray ());
|
||||
change_set.Deleted.Add (file);
|
||||
}
|
||||
|
||||
} else if (!"".Equals (entry_line.Trim ())){
|
||||
string files = entry_line.Substring (3);
|
||||
files = files.TrimEnd (":".ToCharArray());
|
||||
string [] edited_files = files.Split (",".ToCharArray ());
|
||||
|
||||
foreach (string edited_file in edited_files) {
|
||||
if (!change_set.Added.Contains (edited_file) &&
|
||||
!change_set.Deleted.Contains (edited_file)) {
|
||||
|
||||
change_set.Edited.Add (edited_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
change_sets.Add (change_set);
|
||||
}
|
||||
|
||||
return change_sets;
|
||||
}
|
||||
|
||||
|
||||
// Creates a pretty commit message based on what has changed
|
||||
private string FormatCommitMessage () // TODO
|
||||
{
|
||||
return "SparkleShare Hg";
|
||||
}
|
||||
|
||||
|
||||
public override void CreateInitialChangeSet ()
|
||||
{
|
||||
base.CreateInitialChangeSet ();
|
||||
Add ();
|
||||
|
||||
string message = FormatCommitMessage ();
|
||||
Commit (message);
|
||||
}
|
||||
|
||||
|
||||
public override bool UsesNotificationCenter
|
||||
{
|
||||
get {
|
||||
string file_path = SparkleHelpers.CombineMore (LocalPath, ".hg", "disable_notification_center");
|
||||
return !File.Exists (file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,6 @@
|
|||
ASSEMBLY = SparkleLib
|
||||
TARGET = library
|
||||
|
||||
if HAVE_SMARTIRC4NET
|
||||
LINK = $(REF_SPARKLELIB) $(LINK_SMARTIRC4NET_SYSTEM)
|
||||
else
|
||||
LINK = $(REF_SPARKLELIB) $(LINK_SMARTIRC4NET_LOCAL)
|
||||
endif
|
||||
|
||||
SOURCES = \
|
||||
Defines.cs \
|
||||
Git/SparkleFetcherGit.cs \
|
||||
|
@ -17,17 +11,11 @@ SOURCES = \
|
|||
SparkleFetcherBase.cs \
|
||||
SparkleHelpers.cs \
|
||||
SparkleListenerBase.cs \
|
||||
SparkleListenerIrc.cs \
|
||||
SparkleListenerTcp.cs \
|
||||
SparkleOptions.cs \
|
||||
SparkleRepoBase.cs \
|
||||
SparkleWatcher.cs
|
||||
|
||||
|
||||
SMARTIRC4NET_FILES_EXPANDED = $(foreach file, $(SMARTIRC4NET_FILES), $(top_builddir)/$(file))
|
||||
|
||||
EXTRA_BUNDLE = $(SMARTIRC4NET_FILES_EXPANDED)
|
||||
|
||||
install-data-hook:
|
||||
for ASM in $(EXTRA_BUNDLE); do \
|
||||
$(INSTALL) -m 0755 $$ASM $(DESTDIR)$(moduledir); \
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace SparkleLib {
|
|||
public class SparkleFolder {
|
||||
|
||||
public string Name;
|
||||
// TODO: Uri
|
||||
public Uri RemoteAddress;
|
||||
|
||||
public string FullPath {
|
||||
get {
|
||||
|
|
|
@ -19,10 +19,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
#if __MonoCS__
|
||||
using Mono.Unix;
|
||||
#endif
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace SparkleLib {
|
||||
|
||||
|
@ -33,12 +30,14 @@ namespace SparkleLib {
|
|||
"sparkleshare");
|
||||
|
||||
public static SparkleConfig DefaultConfig = new SparkleConfig (ConfigPath, "config.xml");
|
||||
|
||||
|
||||
public string FullPath;
|
||||
|
||||
public string HomePath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
|
||||
public string TmpPath;
|
||||
public string HomePath {
|
||||
get {
|
||||
return Environment.GetFolderPath (Environment.SpecialFolder.Personal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string FoldersPath {
|
||||
get {
|
||||
|
@ -49,11 +48,16 @@ namespace SparkleLib {
|
|||
}
|
||||
}
|
||||
|
||||
public string TmpPath {
|
||||
get {
|
||||
return Path.Combine (FoldersPath, ".tmp");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public SparkleConfig (string config_path, string config_file_name)
|
||||
{
|
||||
FullPath = System.IO.Path.Combine (config_path, config_file_name);
|
||||
TmpPath = Path.Combine (FoldersPath, ".tmp");
|
||||
|
||||
if (!Directory.Exists (config_path)) {
|
||||
Directory.CreateDirectory (config_path);
|
||||
|
@ -99,13 +103,13 @@ namespace SparkleLib {
|
|||
|
||||
if (SparkleBackend.Platform == PlatformID.Unix ||
|
||||
SparkleBackend.Platform == PlatformID.MacOSX) {
|
||||
#if __MonoCS__
|
||||
user_name = new UnixUserInfo (UnixEnvironment.UserName).RealName;
|
||||
|
||||
user_name = Environment.UserName;
|
||||
if (string.IsNullOrEmpty (user_name))
|
||||
user_name = UnixEnvironment.UserName;
|
||||
user_name = "";
|
||||
else
|
||||
user_name = user_name.TrimEnd (",".ToCharArray ());
|
||||
#endif
|
||||
|
||||
} else {
|
||||
user_name = Environment.UserName;
|
||||
}
|
||||
|
@ -148,10 +152,59 @@ namespace SparkleLib {
|
|||
email_node.InnerText = user.Email;
|
||||
|
||||
this.Save ();
|
||||
|
||||
ConfigureSSH ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ConfigureSSH ()
|
||||
{
|
||||
if (User.Email.Equals ("Unknown"))
|
||||
return;
|
||||
|
||||
string path = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
|
||||
|
||||
if (!(SparkleBackend.Platform == PlatformID.Unix ||
|
||||
SparkleBackend.Platform == PlatformID.MacOSX)) {
|
||||
|
||||
path = Environment.ExpandEnvironmentVariables ("%HOMEDRIVE%%HOMEPATH%");
|
||||
}
|
||||
|
||||
string ssh_config_path = Path.Combine (path, ".ssh");
|
||||
string ssh_config_file_path = SparkleHelpers.CombineMore (path, ".ssh", "config");
|
||||
string ssh_config = "IdentityFile " +
|
||||
Path.Combine (SparkleConfig.ConfigPath, "sparkleshare." + User.Email + ".key");
|
||||
|
||||
if (!Directory.Exists (ssh_config_path))
|
||||
Directory.CreateDirectory (ssh_config_path);
|
||||
|
||||
if (File.Exists (ssh_config_file_path)) {
|
||||
string current_config = File.ReadAllText (ssh_config_file_path);
|
||||
if (current_config.Contains (ssh_config))
|
||||
return;
|
||||
|
||||
if (current_config.EndsWith ("\n\n"))
|
||||
ssh_config = "# SparkleShare's key\n" + ssh_config;
|
||||
else if (current_config.EndsWith ("\n"))
|
||||
ssh_config = "\n# SparkleShare's key\n" + ssh_config;
|
||||
else
|
||||
ssh_config = "\n\n# SparkleShare's key\n" + ssh_config;
|
||||
|
||||
TextWriter writer = File.AppendText (ssh_config_file_path);
|
||||
writer.Write (ssh_config + "\n");
|
||||
writer.Close ();
|
||||
|
||||
} else {
|
||||
File.WriteAllText (ssh_config_file_path, ssh_config);
|
||||
}
|
||||
|
||||
Chmod644 (ssh_config_file_path);
|
||||
|
||||
SparkleHelpers.DebugInfo ("Config", "Added key to " + ssh_config_file_path);
|
||||
}
|
||||
|
||||
|
||||
public List<string> Folders {
|
||||
get {
|
||||
List<string> folders = new List<string> ();
|
||||
|
@ -348,6 +401,16 @@ namespace SparkleLib {
|
|||
this.Save (FullPath);
|
||||
SparkleHelpers.DebugInfo ("Config", "Updated \"" + FullPath + "\"");
|
||||
}
|
||||
|
||||
|
||||
private void Chmod644 (string file_path)
|
||||
{
|
||||
// Hack to be able to set the permissions on a file
|
||||
// that OpenSSH still likes without resorting to Mono.Unix
|
||||
FileInfo file_info = new FileInfo (file_path);
|
||||
file_info.Attributes = FileAttributes.ReadOnly;
|
||||
file_info.Attributes = FileAttributes.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,20 +18,17 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
#if __MonoCS__
|
||||
using Mono.Unix;
|
||||
#endif
|
||||
|
||||
namespace SparkleLib {
|
||||
|
||||
// Sets up a fetcher that can get remote folders
|
||||
public abstract class SparkleFetcherBase {
|
||||
|
||||
public delegate void StartedEventHandler ();
|
||||
public delegate void FinishedEventHandler ();
|
||||
public delegate void FinishedEventHandler (string [] warnings);
|
||||
public delegate void FailedEventHandler ();
|
||||
public delegate void ProgressChangedEventHandler (double percentage);
|
||||
|
||||
|
@ -54,7 +51,7 @@ namespace SparkleLib {
|
|||
|
||||
|
||||
public abstract bool Fetch ();
|
||||
|
||||
public abstract string [] Warnings { get; }
|
||||
|
||||
// Clones the remote repository
|
||||
public void Start ()
|
||||
|
@ -85,7 +82,7 @@ namespace SparkleLib {
|
|||
EnableHostKeyCheckingForHost (host);
|
||||
|
||||
if (Finished != null)
|
||||
Finished ();
|
||||
Finished (Warnings);
|
||||
|
||||
} else {
|
||||
SparkleHelpers.DebugInfo ("Fetcher", "Failed");
|
||||
|
@ -159,13 +156,8 @@ namespace SparkleLib {
|
|||
File.WriteAllText (ssh_config_file_path, ssh_config);
|
||||
}
|
||||
|
||||
#if __MonoCS__
|
||||
UnixFileSystemInfo file_info = new UnixFileInfo (ssh_config_file_path);
|
||||
file_info.FileAccessPermissions = (FileAccessPermissions.UserRead |
|
||||
FileAccessPermissions.UserWrite);
|
||||
#endif
|
||||
|
||||
SparkleHelpers.DebugInfo ("Fetcher", "Disabled host key checking " + host);
|
||||
Chmod644 (ssh_config_file_path);
|
||||
SparkleHelpers.DebugInfo ("Fetcher", "Disabled host key checking for " + host);
|
||||
}
|
||||
|
||||
|
||||
|
@ -173,8 +165,8 @@ namespace SparkleLib {
|
|||
{
|
||||
string path = SparkleConfig.DefaultConfig.HomePath;
|
||||
|
||||
if (!(SparkleBackend.Platform == PlatformID.Unix ||
|
||||
SparkleBackend.Platform == PlatformID.MacOSX)) {
|
||||
if (SparkleBackend.Platform != PlatformID.Unix &&
|
||||
SparkleBackend.Platform != PlatformID.MacOSX) {
|
||||
|
||||
path = Environment.ExpandEnvironmentVariables ("%HOMEDRIVE%%HOMEPATH%");
|
||||
}
|
||||
|
@ -211,12 +203,7 @@ namespace SparkleLib {
|
|||
|
||||
} else {
|
||||
File.WriteAllText (ssh_config_file_path, new_ssh_config.Trim ());
|
||||
|
||||
#if __MonoCS__
|
||||
UnixFileSystemInfo file_info = new UnixFileInfo (ssh_config_file_path);
|
||||
file_info.FileAccessPermissions = (FileAccessPermissions.UserRead |
|
||||
FileAccessPermissions.UserWrite);
|
||||
#endif
|
||||
Chmod644 (ssh_config_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,5 +221,15 @@ namespace SparkleLib {
|
|||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void Chmod644 (string file_path)
|
||||
{
|
||||
// Hack to be able to set the permissions on a file
|
||||
// that OpenSSH still likes without resorting to Mono.Unix
|
||||
FileInfo file_info = new FileInfo (file_path);
|
||||
file_info.Attributes = FileAttributes.ReadOnly;
|
||||
file_info.Attributes = FileAttributes.Normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,10 +31,6 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Mono.Posix" />
|
||||
<Reference Include="Meebey.SmartIrc4net, Version=0.4.5.0, Culture=neutral, PublicKeyToken=7868485fbf407e0f">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\bin\Meebey.SmartIrc4net.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="SparkleRepoBase.cs" />
|
||||
|
|
|
@ -75,9 +75,6 @@ namespace SparkleLib {
|
|||
case "tcp":
|
||||
listeners.Add (new SparkleListenerTcp (announce_uri, folder_identifier));
|
||||
break;
|
||||
case "irc":
|
||||
listeners.Add (new SparkleListenerIrc (announce_uri, folder_identifier));
|
||||
break;
|
||||
default:
|
||||
listeners.Add (new SparkleListenerTcp (announce_uri, folder_identifier));
|
||||
break;
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
// SparkleShare, a collaboration and sharing tool.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
using Meebey.SmartIrc4net;
|
||||
|
||||
namespace SparkleLib {
|
||||
|
||||
public class SparkleListenerIrc : SparkleListenerBase {
|
||||
|
||||
private Thread thread;
|
||||
private IrcClient client;
|
||||
private string nick;
|
||||
private string announcements_password;
|
||||
private bool allow_passwordless_join;
|
||||
|
||||
|
||||
public SparkleListenerIrc (Uri server, string folder_identifier) :
|
||||
base (server, folder_identifier)
|
||||
{
|
||||
// Try to get a uniqueish nickname
|
||||
this.nick = SHA1 (DateTime.Now.ToString ("ffffff") + "sparkles");
|
||||
|
||||
// Most irc servers don't allow nicknames starting
|
||||
// with a number, so prefix an alphabetic character
|
||||
this.nick = "s" + this.nick.Substring (0, 7);
|
||||
|
||||
// Optional password to make the channel access more safe
|
||||
this.announcements_password =
|
||||
SparkleConfig.DefaultConfig.GetConfigOption ("announcements_password");
|
||||
|
||||
// Option to allow access to channel when no password is defined
|
||||
try {
|
||||
string option = SparkleConfig.DefaultConfig.GetConfigOption ("allow_passwordless_join");
|
||||
this.allow_passwordless_join = (option == null || Convert.ToBoolean (option));
|
||||
} catch (Exception) {
|
||||
this.allow_passwordless_join = true;
|
||||
}
|
||||
|
||||
base.channels.Add ("#" + folder_identifier);
|
||||
|
||||
this.client = new IrcClient () {
|
||||
PingTimeout = 180,
|
||||
PingInterval = 60
|
||||
};
|
||||
|
||||
string proxy = Environment.GetEnvironmentVariable ("http_proxy");
|
||||
Uri proxy_uri = null;
|
||||
if (!String.IsNullOrEmpty (proxy) &&
|
||||
Uri.TryCreate (proxy, UriKind.Absolute, out proxy_uri)) {
|
||||
|
||||
#if __MonoCS__
|
||||
if (proxy_uri.Scheme == "http") {
|
||||
this.client.ProxyType = ProxyType.Http;
|
||||
this.client.ProxyHost = proxy_uri.Host;
|
||||
this.client.ProxyPort = proxy_uri.Port;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
this.client.OnConnected += delegate {
|
||||
base.is_connecting = false;
|
||||
OnConnected ();
|
||||
};
|
||||
|
||||
this.client.OnDisconnected += delegate {
|
||||
base.is_connecting = false;
|
||||
OnDisconnected ();
|
||||
};
|
||||
|
||||
this.client.OnError += delegate {
|
||||
base.is_connecting = false;
|
||||
OnDisconnected ();
|
||||
};
|
||||
|
||||
this.client.OnChannelMessage += delegate (object o, IrcEventArgs args) {
|
||||
string message = args.Data.Message.Trim ();
|
||||
string folder_id = args.Data.Channel.Substring (1); // remove the starting hash
|
||||
OnAnnouncement (new SparkleAnnouncement (folder_id, message));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public override bool IsConnected {
|
||||
get {
|
||||
return this.client.IsConnected;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Starts a new thread and listens to the channel
|
||||
public override void Connect ()
|
||||
{
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Connecting to " + Server);
|
||||
|
||||
base.is_connecting = true;
|
||||
|
||||
this.thread = new Thread (
|
||||
new ThreadStart (delegate {
|
||||
try {
|
||||
// Connect, login, and join the channel
|
||||
int port = base.server.Port;
|
||||
|
||||
if (port < 0)
|
||||
port = 6667;
|
||||
|
||||
this.client.Connect (base.server.Host, port);
|
||||
this.client.Login (this.nick, this.nick, 8, this.nick);
|
||||
|
||||
foreach (string channel in base.channels) {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Joining channel " + channel);
|
||||
|
||||
if (!string.IsNullOrEmpty (this.announcements_password)) {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Password set to access the channel");
|
||||
this.client.RfcJoin (channel, this.announcements_password);
|
||||
this.client.RfcMode (channel, "+k " + this.announcements_password);
|
||||
|
||||
} else {
|
||||
if (this.allow_passwordless_join) {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Accessing unprotected channel, change the setting to not access");
|
||||
this.client.RfcJoin (channel);
|
||||
|
||||
} else {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Unprotected channel, change the setting to access");
|
||||
base.is_connecting = false;
|
||||
OnDisconnected ();
|
||||
throw new ConnectionException ("Unprotected channel, change the setting to access");
|
||||
}
|
||||
}
|
||||
|
||||
this.client.RfcMode (channel, "+s");
|
||||
}
|
||||
|
||||
// List to the channel, this blocks the thread
|
||||
this.client.Listen ();
|
||||
|
||||
// Disconnect when we time out
|
||||
this.client.Disconnect ();
|
||||
|
||||
} catch (ConnectionException e) {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Could not connect to " + Server + ": " + e.Message);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.thread.Start ();
|
||||
}
|
||||
|
||||
|
||||
public override void AlsoListenTo (string folder_identifier)
|
||||
{
|
||||
string channel = "#" + folder_identifier;
|
||||
if (!base.channels.Contains (channel)) {
|
||||
base.channels.Add (channel);
|
||||
|
||||
if (IsConnected) {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Joining channel " + channel);
|
||||
if (this.announcements_password != null) {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Password set to access the channel");
|
||||
this.client.RfcJoin (channel, this.announcements_password);
|
||||
this.client.RfcMode (channel, "+k " + this.announcements_password);
|
||||
} else {
|
||||
if (allow_passwordless_join) {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Accessing a dangerous channel change the setting to not access");
|
||||
this.client.RfcJoin (channel);
|
||||
} else {
|
||||
SparkleHelpers.DebugInfo ("ListenerIrc", "Dangerous channel, change the setting to access");
|
||||
}
|
||||
}
|
||||
this.client.RfcMode (channel, "+s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Announce (SparkleAnnouncement announcement)
|
||||
{
|
||||
string channel = "#" + announcement.FolderIdentifier;
|
||||
this.client.SendMessage (SendType.Message, channel, announcement.Message);
|
||||
|
||||
// Also announce to ourselves for debugging purposes
|
||||
// base.OnAnnouncement (announcement);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose ()
|
||||
{
|
||||
this.thread.Abort ();
|
||||
this.thread.Join ();
|
||||
base.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
// Creates a SHA-1 hash of input
|
||||
private string SHA1 (string s)
|
||||
{
|
||||
SHA1 sha1 = new SHA1CryptoServiceProvider ();
|
||||
Byte[] bytes = ASCIIEncoding.Default.GetBytes (s);
|
||||
Byte[] encoded_bytes = sha1.ComputeHash (bytes);
|
||||
return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", "");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
// SparkleShare, a collaboration and sharing tool.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SparkleLib {
|
||||
|
||||
public static class SparklePaths {
|
||||
|
||||
public static string HomePath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
|
||||
public static string SparklePath = Path.Combine (HomePath ,"SparkleShare");
|
||||
public static string SparkleTmpPath = Path.Combine (SparklePath, ".tmp");
|
||||
public static string SparkleConfigPath = Path.Combine (Environment.GetFolderPath (
|
||||
Environment.SpecialFolder.ApplicationData), "sparkleshare");
|
||||
public static string SparkleLocalIconPath = Path.Combine (SparkleConfigPath, "icons");
|
||||
|
||||
public static string SparkleInstallPath = Path.Combine (Defines.PREFIX, "sparkleshare");
|
||||
public static string SparkleIconPath = SparkleHelpers.CombineMore (Defines.DATAROOTDIR, "sparkleshare", "icons");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,8 @@ namespace SparkleLib {
|
|||
private bool has_changed = false;
|
||||
private Object change_lock = new Object ();
|
||||
private Object watch_lock = new Object ();
|
||||
private double progress_percentage = 0.0;
|
||||
private string progress_speed = "";
|
||||
|
||||
protected SparkleListenerBase listener;
|
||||
protected SyncStatus status;
|
||||
|
@ -64,19 +66,23 @@ namespace SparkleLib {
|
|||
public abstract string CurrentRevision { get; }
|
||||
public abstract bool SyncUp ();
|
||||
public abstract bool SyncDown ();
|
||||
public abstract double CalculateSize (DirectoryInfo parent);
|
||||
public abstract bool HasUnsyncedChanges { get; set; }
|
||||
public abstract List<string> ExcludePaths { get; }
|
||||
|
||||
public abstract bool AddExclusionRule (FileSystemEventArgs args);
|
||||
public abstract bool RemoveExclusionRule (FileSystemEventArgs args);
|
||||
public abstract bool ExclusionRuleExists (FileSystemEventArgs args);
|
||||
public abstract double Size { get; }
|
||||
public abstract double HistorySize { get; }
|
||||
|
||||
public delegate void SyncStatusChangedEventHandler (SyncStatus new_status);
|
||||
public event SyncStatusChangedEventHandler SyncStatusChanged;
|
||||
|
||||
public delegate void SyncProgressChangedEventHandler (double percentage, string speed);
|
||||
public event SyncProgressChangedEventHandler SyncProgressChanged;
|
||||
|
||||
public delegate void NewChangeSetEventHandler (SparkleChangeSet change_set);
|
||||
public event NewChangeSetEventHandler NewChangeSet;
|
||||
|
||||
public delegate void NewNoteEventHandler (string user_name, string user_email);
|
||||
public delegate void NewNoteEventHandler (SparkleUser user);
|
||||
public event NewNoteEventHandler NewNote;
|
||||
|
||||
public delegate void ConflictResolvedEventHandler ();
|
||||
|
@ -108,8 +114,6 @@ namespace SparkleLib {
|
|||
};
|
||||
|
||||
this.remote_timer.Elapsed += delegate {
|
||||
string identifier = Identifier;
|
||||
|
||||
bool time_to_poll = (DateTime.Compare (this.last_poll,
|
||||
DateTime.Now.Subtract (this.poll_interval)) < 0);
|
||||
|
||||
|
@ -122,7 +126,7 @@ namespace SparkleLib {
|
|||
|
||||
// In the unlikely case that we haven't synced up our
|
||||
// changes or the server was down, sync up again
|
||||
if (HasUnsyncedChanges)
|
||||
if (HasUnsyncedChanges && !IsSyncing && this.server_online)
|
||||
SyncUpBase ();
|
||||
};
|
||||
|
||||
|
@ -157,6 +161,20 @@ namespace SparkleLib {
|
|||
}
|
||||
|
||||
|
||||
public double ProgressPercentage {
|
||||
get {
|
||||
return this.progress_percentage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string ProgressSpeed {
|
||||
get {
|
||||
return this.progress_speed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual string [] UnsyncedFilePaths {
|
||||
get {
|
||||
return new string [0];
|
||||
|
@ -186,7 +204,7 @@ namespace SparkleLib {
|
|||
}
|
||||
|
||||
|
||||
public virtual bool CheckForRemoteChanges () // HasRemoteChanges { get; } ?
|
||||
public virtual bool CheckForRemoteChanges () // TODO: HasRemoteChanges { get; }
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -223,11 +241,8 @@ namespace SparkleLib {
|
|||
public void Dispose ()
|
||||
{
|
||||
this.remote_timer.Dispose ();
|
||||
this.remote_timer = null;
|
||||
this.local_timer.Dispose ();
|
||||
this.local_timer = null;
|
||||
this.listener.Dispose ();
|
||||
this.listener = null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -301,7 +316,7 @@ namespace SparkleLib {
|
|||
}
|
||||
|
||||
|
||||
private bool IsSyncing {
|
||||
public bool IsSyncing {
|
||||
get {
|
||||
return (Status == SyncStatus.SyncUp ||
|
||||
Status == SyncStatus.SyncDown ||
|
||||
|
@ -318,7 +333,7 @@ namespace SparkleLib {
|
|||
this.sizebuffer.RemoveAt (0);
|
||||
|
||||
DirectoryInfo dir_info = new DirectoryInfo (LocalPath);
|
||||
this.sizebuffer.Add (CalculateFolderSize (dir_info));
|
||||
this.sizebuffer.Add (CalculateSize (dir_info));
|
||||
|
||||
if (this.sizebuffer.Count >= 4 &&
|
||||
this.sizebuffer [0].Equals (this.sizebuffer [1]) &&
|
||||
|
@ -347,40 +362,11 @@ namespace SparkleLib {
|
|||
if (!this.watcher.EnableRaisingEvents)
|
||||
return;
|
||||
|
||||
if (args.FullPath.Contains (Path.DirectorySeparatorChar + ".") &&
|
||||
!args.FullPath.Contains (Path.DirectorySeparatorChar + ".notes"))
|
||||
string relative_path = args.FullPath.Replace (LocalPath, "");
|
||||
|
||||
foreach (string exclude_path in ExcludePaths) {
|
||||
if (relative_path.Contains (exclude_path))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check whether the file which triggered the action
|
||||
* is readable so that git can actually commit it
|
||||
*/
|
||||
try {
|
||||
if(File.Exists(args.FullPath)) {
|
||||
FileStream file = File.OpenRead(args.FullPath);
|
||||
file.Close();
|
||||
}
|
||||
} catch {
|
||||
if(!ExclusionRuleExists(args)) {
|
||||
SparkleHelpers.DebugInfo("Warning", "File " + args.FullPath + " is not readable. Adding to ignore list.");
|
||||
AddExclusionRule(args);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove rule if file is readable but there is still
|
||||
* an exclusion rule set
|
||||
*/
|
||||
if(ExclusionRuleExists(args)) {
|
||||
SparkleHelpers.DebugInfo("Info", "Removing exclusion rule for " + args.Name);
|
||||
RemoveExclusionRule(args);
|
||||
|
||||
// If a file was former unreadable but has now been (re)moved, skip the process.
|
||||
if(!File.Exists(args.FullPath)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WatcherChangeTypes wct = args.ChangeType;
|
||||
|
@ -402,7 +388,6 @@ namespace SparkleLib {
|
|||
SparkleHelpers.DebugInfo ("Event", "[" + Name + "] " + wct.ToString () + " '" + args.Name + "'");
|
||||
SparkleHelpers.DebugInfo ("Event", "[" + Name + "] Changes found, checking if settled.");
|
||||
|
||||
if (this.remote_timer != null)
|
||||
this.remote_timer.Stop ();
|
||||
|
||||
lock (this.change_lock) {
|
||||
|
@ -452,7 +437,6 @@ namespace SparkleLib {
|
|||
{
|
||||
try {
|
||||
DisableWatching ();
|
||||
if (this.remote_timer != null)
|
||||
this.remote_timer.Stop ();
|
||||
|
||||
SparkleHelpers.DebugInfo ("SyncUp", "[" + Name + "] Initiated");
|
||||
|
@ -468,7 +452,6 @@ namespace SparkleLib {
|
|||
if (SyncStatusChanged != null)
|
||||
SyncStatusChanged (SyncStatus.Idle);
|
||||
|
||||
if (this.listener != null)
|
||||
this.listener.AnnounceBase (new SparkleAnnouncement (Identifier, CurrentRevision));
|
||||
|
||||
} else {
|
||||
|
@ -476,26 +459,30 @@ namespace SparkleLib {
|
|||
|
||||
HasUnsyncedChanges = true;
|
||||
SyncDownBase ();
|
||||
DisableWatching ();
|
||||
|
||||
if (SyncUp ()) {
|
||||
if (this.server_online && SyncUp ()) {
|
||||
HasUnsyncedChanges = false;
|
||||
|
||||
if (SyncStatusChanged != null)
|
||||
SyncStatusChanged (SyncStatus.Idle);
|
||||
|
||||
if (this.listener != null)
|
||||
this.listener.AnnounceBase (new SparkleAnnouncement (Identifier, CurrentRevision));
|
||||
|
||||
} else {
|
||||
this.server_online = false;
|
||||
|
||||
if (SyncStatusChanged != null)
|
||||
SyncStatusChanged (SyncStatus.Error);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (this.remote_timer != null)
|
||||
this.remote_timer.Start ();
|
||||
EnableWatching ();
|
||||
|
||||
this.progress_percentage = 0.0;
|
||||
this.progress_speed = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -503,7 +490,6 @@ namespace SparkleLib {
|
|||
private void SyncDownBase ()
|
||||
{
|
||||
SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Initiated");
|
||||
if (this.remote_timer != null)
|
||||
this.remote_timer.Stop ();
|
||||
DisableWatching ();
|
||||
|
||||
|
@ -529,7 +515,7 @@ namespace SparkleLib {
|
|||
foreach (string added in change_set.Added) {
|
||||
if (added.Contains (".notes")) {
|
||||
if (NewNote != null)
|
||||
NewNote (change_set.User.Name, change_set.User.Email);
|
||||
NewNote (change_set.User);
|
||||
|
||||
note_added = true;
|
||||
break;
|
||||
|
@ -560,17 +546,18 @@ namespace SparkleLib {
|
|||
if (SyncStatusChanged != null)
|
||||
SyncStatusChanged (SyncStatus.Idle);
|
||||
|
||||
if (this.remote_timer != null)
|
||||
this.remote_timer.Start ();
|
||||
EnableWatching ();
|
||||
|
||||
this.progress_percentage = 0.0;
|
||||
this.progress_speed = "";
|
||||
}
|
||||
|
||||
|
||||
public void DisableWatching ()
|
||||
{
|
||||
lock (watch_lock) {
|
||||
lock (this.watch_lock) {
|
||||
this.watcher.EnableRaisingEvents = false;
|
||||
if (this.local_timer != null)
|
||||
this.local_timer.Stop ();
|
||||
}
|
||||
}
|
||||
|
@ -578,9 +565,8 @@ namespace SparkleLib {
|
|||
|
||||
public void EnableWatching ()
|
||||
{
|
||||
lock (watch_lock) {
|
||||
lock (this.watch_lock) {
|
||||
this.watcher.EnableRaisingEvents = true;
|
||||
if (this.local_timer != null)
|
||||
this.local_timer.Start ();
|
||||
}
|
||||
}
|
||||
|
@ -635,30 +621,15 @@ namespace SparkleLib {
|
|||
}
|
||||
|
||||
|
||||
// Recursively gets a folder's size in bytes
|
||||
private double CalculateFolderSize (DirectoryInfo parent)
|
||||
protected void OnSyncProgressChanged (double progress_percentage, string progress_speed)
|
||||
{
|
||||
if (!System.IO.Directory.Exists (parent.ToString ()))
|
||||
return 0;
|
||||
// Console.WriteLine ("OnProgressChanged: " + progress_percentage + " " + progress_speed);
|
||||
|
||||
double size = 0;
|
||||
this.progress_percentage = progress_percentage;
|
||||
this.progress_speed = progress_speed;
|
||||
|
||||
// Ignore the temporary 'rebase-apply' directory. This prevents potential
|
||||
// crashes when files are being queried whilst the files have already been deleted.
|
||||
if (parent.Name.Equals ("rebase-apply"))
|
||||
return 0;
|
||||
|
||||
foreach (FileInfo file in parent.GetFiles ()) {
|
||||
if (!file.Exists)
|
||||
return 0;
|
||||
|
||||
size += file.Length;
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo directory in parent.GetDirectories())
|
||||
size += CalculateFolderSize (directory);
|
||||
|
||||
return size;
|
||||
if (SyncProgressChanged != null)
|
||||
SyncProgressChanged (progress_percentage, progress_speed);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace SparkleLib {
|
|||
if (ChangeEvent != null)
|
||||
ChangeEvent (args);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
<string>org.sparkleshare.sparkleshare</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>SparkleShare</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.6</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
|
|
|
@ -6,7 +6,6 @@ EXTRA_DIST = \
|
|||
MainMenu.xib \
|
||||
MainMenu.xib.designer.cs \
|
||||
SparkleAbout.cs \
|
||||
SparkleAlert.cs \
|
||||
SparkleBadger.cs \
|
||||
SparkleBubbles.cs \
|
||||
SparkleController.cs \
|
||||
|
|
|
@ -57,6 +57,8 @@ namespace SparkleShare {
|
|||
OrderFrontRegardless ();
|
||||
MakeKeyAndOrderFront (this);
|
||||
|
||||
Program.UI.UpdateDockIconVisibility ();
|
||||
|
||||
Controller.NewVersionEvent += delegate (string new_version) {
|
||||
InvokeOnMainThread (delegate {
|
||||
UpdatesTextField.StringValue = "A newer version (" + new_version + ") is available!";
|
||||
|
@ -161,6 +163,8 @@ namespace SparkleShare {
|
|||
public override bool WindowShouldClose (NSObject sender)
|
||||
{
|
||||
(sender as SparkleAbout).OrderOut (this);
|
||||
Program.UI.UpdateDockIconVisibility ();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
// SparkleShare, a collaboration and sharing tool.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
using MonoMac.Foundation;
|
||||
using MonoMac.AppKit;
|
||||
using MonoMac.ObjCRuntime;
|
||||
using MonoMac.WebKit;
|
||||
|
||||
namespace SparkleShare {
|
||||
|
||||
public class SparkleAlert : NSAlert {
|
||||
|
||||
public SparkleAlert () : base ()
|
||||
{
|
||||
MessageText = "SparkleShare couldn't find Git on your system. Do you want to download it?";
|
||||
InformativeText = "Git is required to run SparkleShare.";
|
||||
|
||||
Icon = NSImage.ImageNamed ("sparkleshare.icns");
|
||||
|
||||
AddButton ("Download");
|
||||
AddButton ("Cancel");
|
||||
|
||||
Buttons [0].Activated += delegate {
|
||||
NSUrl url = new NSUrl ("http://code.google.com/p/git-osx-installer/downloads/list");
|
||||
NSWorkspace.SharedWorkspace.OpenUrl (url);
|
||||
Environment.Exit (0);
|
||||
};
|
||||
|
||||
Buttons [1].Activated += delegate {
|
||||
Environment.Exit (-1);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,6 +41,21 @@ namespace SparkleShare {
|
|||
|
||||
public SparkleController () : base ()
|
||||
{
|
||||
string content_path =
|
||||
Directory.GetParent (System.AppDomain.CurrentDomain.BaseDirectory)
|
||||
.ToString ();
|
||||
|
||||
string app_path = Directory.GetParent (content_path).ToString ();
|
||||
string growl_path = Path.Combine (app_path, "Frameworks", "Growl.framework", "Growl");
|
||||
|
||||
// Needed for Growl
|
||||
Dlfcn.dlopen (growl_path, 0);
|
||||
NSApplication.Init ();
|
||||
|
||||
// Let's use the bundled git first
|
||||
SparkleBackend.DefaultBackend.Path =
|
||||
Path.Combine (NSBundle.MainBundle.ResourcePath,
|
||||
"git", "bin", "git");
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,11 +204,10 @@ namespace SparkleShare {
|
|||
}
|
||||
|
||||
|
||||
new public void Quit ()
|
||||
public override void OpenFile (string url)
|
||||
{
|
||||
this.watcher.Dispose ();
|
||||
NSApplication.SharedApplication.Terminate (new NSObject ());
|
||||
base.Quit ();
|
||||
url = url.Replace ("%20", " ");
|
||||
NSWorkspace.SharedWorkspace.OpenFile (url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
using MonoMac.Foundation;
|
||||
|
@ -36,14 +35,17 @@ namespace SparkleShare {
|
|||
PolicyDelegate = new SparkleWebPolicyDelegate ()
|
||||
};
|
||||
|
||||
private NSBox Separator = new NSBox (new RectangleF (0, 579, 480, 1)) {
|
||||
private NSBox separator = new NSBox (new RectangleF (0, 579, 480, 1)) {
|
||||
BorderColor = NSColor.LightGray,
|
||||
BoxType = NSBoxType.NSBoxCustom
|
||||
};
|
||||
|
||||
private NSPopUpButton popup_button;
|
||||
private NSProgressIndicator progress_indicator;
|
||||
|
||||
private NSTextField size_label;
|
||||
private NSTextField size_label_value;
|
||||
private NSTextField history_label;
|
||||
private NSTextField history_label_value;
|
||||
|
||||
public SparkleEventLog (IntPtr handle) : base (handle) { }
|
||||
|
||||
|
@ -65,7 +67,54 @@ namespace SparkleShare {
|
|||
HasShadow = true;
|
||||
BackingType = NSBackingStore.Buffered;
|
||||
|
||||
ContentView.AddSubview (Separator);
|
||||
|
||||
this.size_label = new NSTextField () {
|
||||
Alignment = NSTextAlignment.Right,
|
||||
BackgroundColor = NSColor.WindowBackground,
|
||||
Bordered = false,
|
||||
Editable = false,
|
||||
Frame = new RectangleF (0, 588, 60, 20),
|
||||
StringValue = "Size:",
|
||||
Font = SparkleUI.BoldFont
|
||||
};
|
||||
|
||||
this.size_label_value = new NSTextField () {
|
||||
Alignment = NSTextAlignment.Left,
|
||||
BackgroundColor = NSColor.WindowBackground,
|
||||
Bordered = false,
|
||||
Editable = false,
|
||||
Frame = new RectangleF (60, 588, 75, 20),
|
||||
StringValue = Controller.Size,
|
||||
Font = SparkleUI.Font
|
||||
};
|
||||
|
||||
|
||||
this.history_label = new NSTextField () {
|
||||
Alignment = NSTextAlignment.Right,
|
||||
BackgroundColor = NSColor.WindowBackground,
|
||||
Bordered = false,
|
||||
Editable = false,
|
||||
Frame = new RectangleF (130, 588, 60, 20),
|
||||
StringValue = "History:",
|
||||
Font = SparkleUI.BoldFont
|
||||
};
|
||||
|
||||
this.history_label_value = new NSTextField () {
|
||||
Alignment = NSTextAlignment.Left,
|
||||
BackgroundColor = NSColor.WindowBackground,
|
||||
Bordered = false,
|
||||
Editable = false,
|
||||
Frame = new RectangleF (190, 588, 75, 20),
|
||||
StringValue = Controller.HistorySize,
|
||||
Font = SparkleUI.Font
|
||||
};
|
||||
|
||||
|
||||
ContentView.AddSubview (this.size_label);
|
||||
ContentView.AddSubview (this.size_label_value);
|
||||
ContentView.AddSubview (this.history_label);
|
||||
ContentView.AddSubview (this.history_label_value);
|
||||
ContentView.AddSubview (this.separator);
|
||||
|
||||
|
||||
this.progress_indicator = new NSProgressIndicator () {
|
||||
|
@ -81,6 +130,7 @@ namespace SparkleShare {
|
|||
UpdateChooser (null);
|
||||
OrderFrontRegardless ();
|
||||
|
||||
Program.UI.UpdateDockIconVisibility ();
|
||||
|
||||
// Hook up the controller events
|
||||
Controller.UpdateChooserEvent += delegate (string [] folders) {
|
||||
|
@ -103,6 +153,13 @@ namespace SparkleShare {
|
|||
ContentView.AddSubview (this.progress_indicator);
|
||||
});
|
||||
};
|
||||
|
||||
Controller.UpdateSizeInfoEvent += delegate (string size, string history_size) {
|
||||
InvokeOnMainThread (delegate {
|
||||
this.size_label_value.StringValue = size;
|
||||
this.history_label_value.StringValue = history_size;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,7 +180,7 @@ namespace SparkleShare {
|
|||
this.popup_button.Font = NSFontManager.SharedFontManager.FontWithFamily
|
||||
("Lucida Grande", NSFontTraitMask.Condensed, 0, NSFont.SmallSystemFontSize);
|
||||
|
||||
this.popup_button.AddItem ("All Folders");
|
||||
this.popup_button.AddItem ("All Projects");
|
||||
this.popup_button.Menu.AddItem (NSMenuItem.SeparatorItem);
|
||||
this.popup_button.AddItems (folders);
|
||||
|
||||
|
@ -184,6 +241,8 @@ namespace SparkleShare {
|
|||
public override bool WindowShouldClose (NSObject sender)
|
||||
{
|
||||
(sender as SparkleEventLog).OrderOut (this);
|
||||
Program.UI.UpdateDockIconVisibility ();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -194,30 +253,7 @@ namespace SparkleShare {
|
|||
public override void DecidePolicyForNavigation (WebView web_view, NSDictionary action_info,
|
||||
NSUrlRequest request, WebFrame frame, NSObject decision_token)
|
||||
{
|
||||
string url = request.Url.ToString ();
|
||||
|
||||
if (url.StartsWith (Path.VolumeSeparatorChar.ToString ())) {
|
||||
string file_path = request.Url.ToString ();
|
||||
file_path = file_path.Replace ("%20", " ");
|
||||
|
||||
NSWorkspace.SharedWorkspace.OpenFile (file_path);
|
||||
|
||||
} else {
|
||||
Regex regex = new Regex (@"(.+)~(.+)~(.+)");
|
||||
Match match = regex.Match (url);
|
||||
|
||||
if (match.Success) {
|
||||
string folder_name = match.Groups [1].Value;
|
||||
string revision = match.Groups [2].Value;
|
||||
string note = match.Groups [3].Value;
|
||||
|
||||
Thread thread = new Thread (new ThreadStart (delegate {
|
||||
Program.Controller.AddNoteToFolder (folder_name, revision, note);
|
||||
}));
|
||||
|
||||
thread.Start ();
|
||||
}
|
||||
}
|
||||
SparkleEventLogController.LinkClicked (request.Url.ToString ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,9 @@ namespace SparkleShare {
|
|||
public delegate void ChangedEventHandler (string path);
|
||||
public event ChangedEventHandler Changed;
|
||||
|
||||
private DirectoryInfo last_changed;
|
||||
private FileSystemInfo last_changed;
|
||||
private Thread thread;
|
||||
private int poll_count = 0;
|
||||
|
||||
|
||||
public SparkleMacWatcher (string path)
|
||||
|
@ -50,7 +51,8 @@ namespace SparkleShare {
|
|||
Changed (relative_path);
|
||||
}
|
||||
|
||||
Thread.Sleep (2500);
|
||||
Thread.Sleep (7500);
|
||||
this.poll_count++;
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -73,7 +75,20 @@ namespace SparkleShare {
|
|||
}
|
||||
}
|
||||
|
||||
} catch (Exception) { }
|
||||
if (this.poll_count >= 8) {
|
||||
foreach (FileInfo info in parent.GetFiles ()) {
|
||||
if (!info.FullName.Contains ("/.")) {
|
||||
if (DateTime.Compare (info.LastWriteTime, this.last_changed.LastWriteTime) > 0)
|
||||
this.last_changed = info;
|
||||
}
|
||||
}
|
||||
|
||||
this.poll_count = 0;
|
||||
}
|
||||
|
||||
} catch (Exception) {
|
||||
// Don't care...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,11 +16,9 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Timers;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Mono.Unix;
|
||||
using MonoMac.Foundation;
|
||||
|
@ -54,7 +52,9 @@ namespace SparkleShare {
|
|||
private NSTextField PathLabel;
|
||||
private NSTextField PathHelpLabel;
|
||||
private NSTextField AddProjectTextField;
|
||||
private Timer timer;
|
||||
private NSTextField WarningTextField;
|
||||
private NSImage WarningImage;
|
||||
private NSImageView WarningImageView;
|
||||
private NSTableView TableView;
|
||||
private NSScrollView ScrollView;
|
||||
private NSTableColumn IconColumn;
|
||||
|
@ -64,7 +64,7 @@ namespace SparkleShare {
|
|||
|
||||
public SparkleSetup () : base ()
|
||||
{
|
||||
Controller.ChangePageEvent += delegate (PageType type) {
|
||||
Controller.ChangePageEvent += delegate (PageType type, string [] warnings) {
|
||||
InvokeOnMainThread (delegate {
|
||||
Reset ();
|
||||
|
||||
|
@ -72,8 +72,8 @@ namespace SparkleShare {
|
|||
case PageType.Setup: {
|
||||
|
||||
Header = "Welcome to SparkleShare!";
|
||||
Description = "Before we can create a SparkleShare folder on this " +
|
||||
"computer, we need some information from you.";
|
||||
Description = "We'll need some info to mark your changes in the event log. " +
|
||||
"Don't worry, this stays between you and your peers.";
|
||||
|
||||
|
||||
FullNameLabel = new NSTextField () {
|
||||
|
@ -88,7 +88,8 @@ namespace SparkleShare {
|
|||
|
||||
FullNameTextField = new NSTextField () {
|
||||
Frame = new RectangleF (330, Frame.Height - 238, 196, 22),
|
||||
StringValue = Controller.GuessedUserName
|
||||
StringValue = Controller.GuessedUserName,
|
||||
Delegate = new SparkleTextFieldDelegate ()
|
||||
};
|
||||
|
||||
EmailLabel = new NSTextField () {
|
||||
|
@ -103,41 +104,44 @@ namespace SparkleShare {
|
|||
|
||||
EmailTextField = new NSTextField () {
|
||||
Frame = new RectangleF (330, Frame.Height - 268, 196, 22),
|
||||
StringValue = Controller.GuessedUserEmail
|
||||
StringValue = Controller.GuessedUserEmail,
|
||||
Delegate = new SparkleTextFieldDelegate ()
|
||||
};
|
||||
|
||||
// TODO: Ugly hack, do properly with events
|
||||
timer = new Timer () {
|
||||
Interval = 50
|
||||
|
||||
(FullNameTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
|
||||
Controller.CheckSetupPage (
|
||||
FullNameTextField.StringValue,
|
||||
EmailTextField.StringValue
|
||||
);
|
||||
};
|
||||
|
||||
(EmailTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
|
||||
Controller.CheckSetupPage (
|
||||
FullNameTextField.StringValue,
|
||||
EmailTextField.StringValue
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
ContinueButton = new NSButton () {
|
||||
Title = "Continue",
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
ContinueButton.Activated += delegate {
|
||||
timer.Stop ();
|
||||
timer = null;
|
||||
|
||||
string full_name = FullNameTextField.StringValue.Trim ();
|
||||
string email = EmailTextField.StringValue.Trim ();
|
||||
|
||||
Controller.SetupPageCompleted (full_name, email);
|
||||
};
|
||||
|
||||
timer.Elapsed += delegate {
|
||||
Controller.UpdateSetupContinueButtonEvent += delegate (bool button_enabled) {
|
||||
InvokeOnMainThread (delegate {
|
||||
bool name_is_valid = !FullNameTextField.StringValue.Trim ().Equals ("");
|
||||
|
||||
bool email_is_valid = Program.Controller.IsValidEmail (
|
||||
EmailTextField.StringValue.Trim ());
|
||||
|
||||
ContinueButton.Enabled = (name_is_valid && email_is_valid);
|
||||
ContinueButton.Enabled = button_enabled;
|
||||
});
|
||||
};
|
||||
|
||||
timer.Start ();
|
||||
|
||||
ContentView.AddSubview (FullNameLabel);
|
||||
ContentView.AddSubview (FullNameTextField);
|
||||
|
@ -146,6 +150,11 @@ namespace SparkleShare {
|
|||
|
||||
Buttons.Add (ContinueButton);
|
||||
|
||||
Controller.CheckSetupPage (
|
||||
FullNameTextField.StringValue,
|
||||
EmailTextField.StringValue
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -168,7 +177,8 @@ namespace SparkleShare {
|
|||
Frame = new RectangleF (190, Frame.Height - 336, 196, 22),
|
||||
Font = SparkleUI.Font,
|
||||
StringValue = Controller.PreviousAddress,
|
||||
Enabled = (Controller.SelectedPlugin.Address == null)
|
||||
Enabled = (Controller.SelectedPlugin.Address == null),
|
||||
Delegate = new SparkleTextFieldDelegate ()
|
||||
};
|
||||
|
||||
|
||||
|
@ -186,7 +196,7 @@ namespace SparkleShare {
|
|||
Frame = new RectangleF (190 + 196 + 16, Frame.Height - 336, 196, 22),
|
||||
StringValue = Controller.PreviousPath,
|
||||
Enabled = (Controller.SelectedPlugin.Path == null),
|
||||
Bezeled = true
|
||||
Delegate = new SparkleTextFieldDelegate ()
|
||||
};
|
||||
|
||||
|
||||
|
@ -210,7 +220,8 @@ namespace SparkleShare {
|
|||
Frame = new RectangleF (0, 0, 0, 0),
|
||||
RowHeight = 30,
|
||||
IntercellSpacing = new SizeF (0, 12),
|
||||
HeaderView = null
|
||||
HeaderView = null,
|
||||
Delegate = new SparkleTableViewDelegate ()
|
||||
};
|
||||
|
||||
ScrollView = new NSScrollView () {
|
||||
|
@ -270,32 +281,42 @@ namespace SparkleShare {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
TableView.SelectRow (Controller.SelectedPluginIndex, false);
|
||||
|
||||
|
||||
timer = new Timer () {
|
||||
Interval = 50
|
||||
(AddressTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
|
||||
Controller.CheckAddPage (
|
||||
AddressTextField.StringValue,
|
||||
PathTextField.StringValue,
|
||||
TableView.SelectedRow
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: Use an event
|
||||
timer.Elapsed += delegate {
|
||||
if (TableView.SelectedRow != Controller.SelectedPluginIndex)
|
||||
(PathTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
|
||||
Controller.CheckAddPage (
|
||||
AddressTextField.StringValue,
|
||||
PathTextField.StringValue,
|
||||
TableView.SelectedRow
|
||||
);
|
||||
};
|
||||
|
||||
(TableView.Delegate as SparkleTableViewDelegate).SelectionChanged += delegate {
|
||||
Controller.SelectedPluginChanged (TableView.SelectedRow);
|
||||
|
||||
InvokeOnMainThread (delegate {
|
||||
// TODO: Move checking logic to controller
|
||||
if (!string.IsNullOrWhiteSpace (AddressTextField.StringValue) &&
|
||||
!string.IsNullOrWhiteSpace (PathTextField.StringValue)) {
|
||||
|
||||
SyncButton.Enabled = true;
|
||||
|
||||
} else {
|
||||
SyncButton.Enabled = false;
|
||||
}
|
||||
});
|
||||
Controller.CheckAddPage (
|
||||
AddressTextField.StringValue,
|
||||
PathTextField.StringValue,
|
||||
TableView.SelectedRow
|
||||
);
|
||||
};
|
||||
|
||||
timer.Start ();
|
||||
|
||||
Controller.UpdateAddProjectButtonEvent += delegate (bool button_enabled) {
|
||||
InvokeOnMainThread (delegate {
|
||||
SyncButton.Enabled = button_enabled;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
ContentView.AddSubview (ScrollView);
|
||||
|
@ -311,9 +332,6 @@ namespace SparkleShare {
|
|||
};
|
||||
|
||||
SyncButton.Activated += delegate {
|
||||
timer.Stop ();
|
||||
timer = null;
|
||||
|
||||
Controller.AddPageCompleted (
|
||||
AddressTextField.StringValue,
|
||||
PathTextField.StringValue
|
||||
|
@ -334,6 +352,13 @@ namespace SparkleShare {
|
|||
|
||||
Buttons.Add (CancelButton);
|
||||
|
||||
Controller.CheckAddPage (
|
||||
AddressTextField.StringValue,
|
||||
PathTextField.StringValue,
|
||||
TableView.SelectedRow
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -442,6 +467,28 @@ namespace SparkleShare {
|
|||
"‘" + Controller.SyncingFolder + "’ in " +
|
||||
"your SparkleShare folder.";
|
||||
|
||||
if (warnings != null) {
|
||||
WarningImage = NSImage.ImageNamed ("NSCaution");
|
||||
WarningImage.Size = new SizeF (24, 24);
|
||||
|
||||
WarningImageView = new NSImageView () {
|
||||
Image = WarningImage,
|
||||
Frame = new RectangleF (190, Frame.Height - 175, 24, 24)
|
||||
};
|
||||
|
||||
WarningTextField = new NSTextField () {
|
||||
Frame = new RectangleF (230, Frame.Height - 245, 325, 100),
|
||||
StringValue = warnings [0],
|
||||
BackgroundColor = NSColor.WindowBackground,
|
||||
Bordered = false,
|
||||
Editable = false,
|
||||
Font = SparkleUI.Font
|
||||
};
|
||||
|
||||
ContentView.AddSubview (WarningImageView);
|
||||
ContentView.AddSubview (WarningTextField);
|
||||
}
|
||||
|
||||
FinishButton = new NSButton () {
|
||||
Title = "Finish"
|
||||
};
|
||||
|
@ -497,7 +544,7 @@ namespace SparkleShare {
|
|||
};
|
||||
|
||||
string slide_image_path = Path.Combine (NSBundle.MainBundle.ResourcePath,
|
||||
"Pixmaps", "tutorial-slide-1.png");
|
||||
"Pixmaps", "tutorial-slide-1-mac.png");
|
||||
|
||||
SlideImage = new NSImage (slide_image_path) {
|
||||
Size = new SizeF (350, 200)
|
||||
|
@ -529,7 +576,7 @@ namespace SparkleShare {
|
|||
};
|
||||
|
||||
string slide_image_path = Path.Combine (NSBundle.MainBundle.ResourcePath,
|
||||
"Pixmaps", "tutorial-slide-2.png");
|
||||
"Pixmaps", "tutorial-slide-2-mac.png");
|
||||
|
||||
SlideImage = new NSImage (slide_image_path) {
|
||||
Size = new SizeF (350, 200)
|
||||
|
@ -560,7 +607,7 @@ namespace SparkleShare {
|
|||
};
|
||||
|
||||
string slide_image_path = Path.Combine (NSBundle.MainBundle.ResourcePath,
|
||||
"Pixmaps", "tutorial-slide-3.png");
|
||||
"Pixmaps", "tutorial-slide-3-mac.png");
|
||||
|
||||
SlideImage = new NSImage (slide_image_path) {
|
||||
Size = new SizeF (350, 200)
|
||||
|
@ -673,4 +720,39 @@ namespace SparkleShare {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SparkleTextFieldDelegate : NSTextFieldDelegate {
|
||||
|
||||
public event StringValueChangedHandler StringValueChanged;
|
||||
public delegate void StringValueChangedHandler ();
|
||||
|
||||
|
||||
public override void Changed (NSNotification notification)
|
||||
{
|
||||
if (StringValueChanged!= null)
|
||||
StringValueChanged ();
|
||||
}
|
||||
|
||||
|
||||
public override string [] GetCompletions (NSControl control, NSTextView text_view,
|
||||
string [] a, MonoMac.Foundation.NSRange range, int b)
|
||||
{
|
||||
return new string [0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SparkleTableViewDelegate : NSTableViewDelegate {
|
||||
|
||||
public event SelectionChangedHandler SelectionChanged;
|
||||
public delegate void SelectionChangedHandler ();
|
||||
|
||||
|
||||
public override void SelectionDidChange (NSNotification notification)
|
||||
{
|
||||
if (SelectionChanged != null)
|
||||
SelectionChanged ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ namespace SparkleShare {
|
|||
MakeKeyAndOrderFront (this);
|
||||
|
||||
OrderFrontRegardless ();
|
||||
Program.UI.UpdateDockIconVisibility ();
|
||||
}
|
||||
|
||||
|
||||
|
@ -143,8 +144,10 @@ namespace SparkleShare {
|
|||
|
||||
public override void PerformClose (NSObject sender)
|
||||
{
|
||||
OrderOut (this);
|
||||
base.OrderOut (this);
|
||||
NSApplication.SharedApplication.RemoveWindowsItem (this);
|
||||
Program.UI.UpdateDockIconVisibility ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<RootNamespace>SparkleShare</RootNamespace>
|
||||
<AssemblyName>SparkleShare</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<ReleaseVersion>0.2.5</ReleaseVersion>
|
||||
<ReleaseVersion>0.6.0</ReleaseVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<ConsolePause>false</ConsolePause>
|
||||
<CustomCommands>
|
||||
<CustomCommands>
|
||||
<Command type="AfterBuild" command="mkdir -p ${TargetDir}/${SolutionName}.app/Contents/Frameworks; cp -r Growl.framework ${TargetDir}/${SolutionName}.app/Contents/Frameworks" externalConsole="true" />
|
||||
<Command type="AfterBuild" command="mkdir -p ${TargetDir}/${SolutionName}.app/Contents/Frameworks; cp -r Growl.framework ${TargetDir}/${SolutionName}.app/Contents/Frameworks;mkdir -p ${TargetDir}/${SolutionName}.app/Contents/Resources; cp -r git ${TargetDir}/${SolutionName}.app/Contents/Resources" externalConsole="true" />
|
||||
</CustomCommands>
|
||||
</CustomCommands>
|
||||
</PropertyGroup>
|
||||
|
@ -58,15 +58,11 @@
|
|||
<Reference Include="Mono.Posix">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Meebey.SmartIrc4net, Version=0.4.5.0, Culture=neutral, PublicKeyToken=7868485fbf407e0f">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\bin\Meebey.SmartIrc4net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SparkleLib, Version=0.2.5.0, Culture=neutral, PublicKeyToken=null">
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="SparkleLib, Version=0.6.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\bin\SparkleLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AppDelegate.cs">
|
||||
|
@ -83,8 +79,6 @@
|
|||
<Compile Include="..\Program.cs">
|
||||
<Link>Program.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="SparkleAbout.cs" />
|
||||
<Compile Include="SparkleAlert.cs" />
|
||||
<Compile Include="SparkleMacWatcher.cs" />
|
||||
<Compile Include="SparkleEventLog.cs" />
|
||||
<Compile Include="SparkleBadger.cs" />
|
||||
|
@ -113,6 +107,13 @@
|
|||
<Compile Include="..\SparklePlugin.cs">
|
||||
<Link>SparklePlugin.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\SparkleOptions.cs">
|
||||
<Link>SparkleOptions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="SparkleAbout.cs" />
|
||||
<Compile Include="..\SparkleInvite.cs">
|
||||
<Link>SparkleInvite.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="MainMenu.xib" />
|
||||
|
@ -277,6 +278,15 @@
|
|||
<Content Include="..\..\data\tutorial-slide-1.png">
|
||||
<Link>Pixmaps\tutorial-slide-1.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\data\tutorial-slide-1-mac.png">
|
||||
<Link>Pixmaps\tutorial-slide-1-mac.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\data\tutorial-slide-2-mac.png">
|
||||
<Link>Pixmaps\tutorial-slide-2-mac.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\data\tutorial-slide-3-mac.png">
|
||||
<Link>Pixmaps\tutorial-slide-3-mac.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\data\tutorial-slide-2.png">
|
||||
<Link>Pixmaps\tutorial-slide-2.png</Link>
|
||||
</Content>
|
||||
|
|
2
SparkleShare/Mac/SparkleShare.sln
Executable file → Normal file
2
SparkleShare/Mac/SparkleShare.sln
Executable file → Normal file
|
@ -16,6 +16,6 @@ Global
|
|||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
StartupItem = SparkleShare.csproj
|
||||
version = 0.2.5
|
||||
version = 0.6.0
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace SparkleShare {
|
|||
private NSMenuItem AboutMenuItem;
|
||||
private NSMenuItem NotificationsMenuItem;
|
||||
private NSMenuItem RecentEventsMenuItem;
|
||||
private NSMenuItem QuitMenuItem;
|
||||
private NSImage [] AnimationFrames;
|
||||
private NSImage [] AnimationFramesActive;
|
||||
private NSImage ErrorImage;
|
||||
|
@ -110,7 +111,10 @@ namespace SparkleShare {
|
|||
|
||||
case IconState.Syncing:
|
||||
|
||||
StateText = _("Syncing…");
|
||||
StateText = _("Syncing… " +
|
||||
Controller.ProgressPercentage + "% " +
|
||||
Controller.ProgressSpeed);
|
||||
|
||||
StateMenuItem.Title = StateText;
|
||||
|
||||
if (!Animation.Enabled)
|
||||
|
@ -120,6 +124,8 @@ namespace SparkleShare {
|
|||
|
||||
case IconState.Error:
|
||||
|
||||
Animation.Stop ();
|
||||
|
||||
StateText = _("Not everything is synced");
|
||||
StateMenuItem.Title = StateText;
|
||||
CreateMenu ();
|
||||
|
@ -240,7 +246,7 @@ namespace SparkleShare {
|
|||
|
||||
RecentEventsMenuItem = new NSMenuItem () {
|
||||
Title = "Open Recent Events",
|
||||
Enabled = true
|
||||
Enabled = (Controller.Folders.Length > 0)
|
||||
};
|
||||
|
||||
if (Controller.Folders.Length > 0) {
|
||||
|
@ -299,8 +305,20 @@ namespace SparkleShare {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
Menu.AddItem (AboutMenuItem);
|
||||
Menu.AddItem (NSMenuItem.SeparatorItem);
|
||||
|
||||
|
||||
QuitMenuItem = new NSMenuItem () {
|
||||
Title = "Quit",
|
||||
Enabled = true
|
||||
};
|
||||
|
||||
QuitMenuItem.Activated += delegate {
|
||||
Program.Controller.Quit ();
|
||||
};
|
||||
|
||||
Menu.AddItem (QuitMenuItem);
|
||||
|
||||
StatusItem.Menu = Menu;
|
||||
StatusItem.Menu.Update ();
|
||||
|
|
|
@ -37,22 +37,11 @@ namespace SparkleShare {
|
|||
public static SparkleBubbles Bubbles;
|
||||
public static SparkleAbout About;
|
||||
public static NSFont Font;
|
||||
|
||||
private NSAlert alert;
|
||||
public static NSFont BoldFont;
|
||||
|
||||
|
||||
public SparkleUI ()
|
||||
{
|
||||
string content_path = Directory.GetParent (
|
||||
System.AppDomain.CurrentDomain.BaseDirectory).ToString ();
|
||||
|
||||
string app_path = Directory.GetParent (content_path).ToString ();
|
||||
string growl_path = Path.Combine (app_path, "Frameworks", "Growl.framework", "Growl");
|
||||
|
||||
// Needed for Growl
|
||||
Dlfcn.dlopen (growl_path, 0);
|
||||
NSApplication.Init ();
|
||||
|
||||
// Use translations
|
||||
Catalog.Init ("sparkleshare",
|
||||
Path.Combine (NSBundle.MainBundle.ResourcePath, "Translations"));
|
||||
|
@ -66,17 +55,14 @@ namespace SparkleShare {
|
|||
NSApplication.SharedApplication.ApplicationIconImage
|
||||
= NSImage.ImageNamed ("sparkleshare.icns");
|
||||
|
||||
if (!Program.Controller.BackendIsPresent) {
|
||||
this.alert = new SparkleAlert ();
|
||||
this.alert.RunModal ();
|
||||
return;
|
||||
}
|
||||
|
||||
SetFolderIcon ();
|
||||
|
||||
Font = NSFontManager.SharedFontManager.FontWithFamily
|
||||
("Lucida Grande", NSFontTraitMask.Condensed, 0, 13);
|
||||
|
||||
BoldFont = NSFontManager.SharedFontManager.FontWithFamily
|
||||
("Lucida Grande", NSFontTraitMask.Bold, 0, 13);
|
||||
|
||||
StatusIcon = new SparkleStatusIcon ();
|
||||
Bubbles = new SparkleBubbles ();
|
||||
|
||||
|
@ -106,6 +92,29 @@ namespace SparkleShare {
|
|||
}
|
||||
|
||||
|
||||
public void UpdateDockIconVisibility ()
|
||||
{
|
||||
if (true) { // TODO: check for open windows
|
||||
|
||||
ShowDockIcon ();
|
||||
|
||||
} else {
|
||||
HideDockIcon ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void HideDockIcon () {
|
||||
// Currently not supported, here for completeness sake (see Apple's docs)
|
||||
// NSApplication.SharedApplication.ActivationPolicy = NSApplicationActivationPolicy.None;
|
||||
}
|
||||
|
||||
|
||||
private void ShowDockIcon () {
|
||||
NSApplication.SharedApplication.ActivationPolicy = NSApplicationActivationPolicy.Regular;
|
||||
}
|
||||
|
||||
|
||||
[Export("registrationDictionaryForGrowl")]
|
||||
NSDictionary RegistrationDictionaryForGrowl ()
|
||||
{
|
||||
|
@ -122,6 +131,7 @@ namespace SparkleShare {
|
|||
NSApplication.SharedApplication.DockTile.BadgeLabel = null;
|
||||
}
|
||||
|
||||
|
||||
public override void WillTerminate (NSNotification notification)
|
||||
{
|
||||
Program.Controller.Quit ();
|
||||
|
|
370
SparkleShare/Mac/git/LICENSE
Normal file
370
SparkleShare/Mac/git/LICENSE
Normal file
|
@ -0,0 +1,370 @@
|
|||
From https://github.com/gitster/git:
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
GIT - the stupid content tracker
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
"git" can mean anything, depending on your mood.
|
||||
|
||||
- random three-letter combination that is pronounceable, and not
|
||||
actually used by any common UNIX command. The fact that it is a
|
||||
mispronunciation of "get" may or may not be relevant.
|
||||
- stupid. contemptible and despicable. simple. Take your pick from the
|
||||
dictionary of slang.
|
||||
- "global information tracker": you're in a good mood, and it actually
|
||||
works for you. Angels sing, and a light suddenly fills the room.
|
||||
- "goddamn idiotic truckload of sh*t": when it breaks
|
||||
|
||||
Git is a fast, scalable, distributed revision control system with an
|
||||
unusually rich command set that provides both high-level operations
|
||||
and full access to internals.
|
||||
|
||||
Git is an Open Source project covered by the GNU General Public License.
|
||||
It was originally written by Linus Torvalds with help of a group of
|
||||
hackers around the net. It is currently maintained by Junio C Hamano.
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 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.
|
||||
|
||||
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 Lesser 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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.
|
||||
|
||||
<signature of Ty Coon>, 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 Lesser General
|
||||
Public License instead of this License.
|
||||
|
BIN
SparkleShare/Mac/git/bin/git
Executable file
BIN
SparkleShare/Mac/git/bin/git
Executable file
Binary file not shown.
3696
SparkleShare/Mac/git/bin/git-cvsserver
Executable file
3696
SparkleShare/Mac/git/bin/git-cvsserver
Executable file
File diff suppressed because it is too large
Load diff
1
SparkleShare/Mac/git/bin/git-receive-pack
Symbolic link
1
SparkleShare/Mac/git/bin/git-receive-pack
Symbolic link
|
@ -0,0 +1 @@
|
|||
git
|
BIN
SparkleShare/Mac/git/bin/git-shell
Executable file
BIN
SparkleShare/Mac/git/bin/git-shell
Executable file
Binary file not shown.
1
SparkleShare/Mac/git/bin/git-upload-archive
Symbolic link
1
SparkleShare/Mac/git/bin/git-upload-archive
Symbolic link
|
@ -0,0 +1 @@
|
|||
git
|
BIN
SparkleShare/Mac/git/bin/git-upload-pack
Executable file
BIN
SparkleShare/Mac/git/bin/git-upload-pack
Executable file
Binary file not shown.
11702
SparkleShare/Mac/git/bin/gitk
Executable file
11702
SparkleShare/Mac/git/bin/gitk
Executable file
File diff suppressed because it is too large
Load diff
1
SparkleShare/Mac/git/libexec/git-core/git
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-add
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-add
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1610
SparkleShare/Mac/git/libexec/git-core/git-add--interactive
Executable file
1610
SparkleShare/Mac/git/libexec/git-core/git-add--interactive
Executable file
File diff suppressed because it is too large
Load diff
841
SparkleShare/Mac/git/libexec/git-core/git-am
Executable file
841
SparkleShare/Mac/git/libexec/git-core/git-am
Executable file
|
@ -0,0 +1,841 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005, 2006 Junio C Hamano
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_SPEC="\
|
||||
git am [options] [(<mbox>|<Maildir>)...]
|
||||
git am [options] (--resolved | --skip | --abort)
|
||||
--
|
||||
i,interactive run interactively
|
||||
b,binary* (historical option -- no-op)
|
||||
3,3way allow fall back on 3way merging if needed
|
||||
q,quiet be quiet
|
||||
s,signoff add a Signed-off-by line to the commit message
|
||||
u,utf8 recode into utf8 (default)
|
||||
k,keep pass -k flag to git-mailinfo
|
||||
keep-cr pass --keep-cr flag to git-mailsplit for mbox format
|
||||
no-keep-cr do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
|
||||
c,scissors strip everything before a scissors line
|
||||
whitespace= pass it through git-apply
|
||||
ignore-space-change pass it through git-apply
|
||||
ignore-whitespace pass it through git-apply
|
||||
directory= pass it through git-apply
|
||||
C= pass it through git-apply
|
||||
p= pass it through git-apply
|
||||
patch-format= format the patch(es) are in
|
||||
reject pass it through git-apply
|
||||
resolvemsg= override error message when patch failure occurs
|
||||
continue continue applying patches after resolving a conflict
|
||||
r,resolved synonyms for --continue
|
||||
skip skip the current patch
|
||||
abort restore the original branch and abort the patching operation.
|
||||
committer-date-is-author-date lie about committer date
|
||||
ignore-date use current timestamp for author date
|
||||
rerere-autoupdate update the index with reused conflict resolution if possible
|
||||
rebasing* (internal use for git-rebase)"
|
||||
|
||||
. git-sh-setup
|
||||
prefix=$(git rev-parse --show-prefix)
|
||||
set_reflog_action am
|
||||
require_work_tree
|
||||
cd_to_toplevel
|
||||
|
||||
git var GIT_COMMITTER_IDENT >/dev/null ||
|
||||
die "You need to set your committer info first"
|
||||
|
||||
if git rev-parse --verify -q HEAD >/dev/null
|
||||
then
|
||||
HAS_HEAD=yes
|
||||
else
|
||||
HAS_HEAD=
|
||||
fi
|
||||
|
||||
cmdline="git am"
|
||||
if test '' != "$interactive"
|
||||
then
|
||||
cmdline="$cmdline -i"
|
||||
fi
|
||||
if test '' != "$threeway"
|
||||
then
|
||||
cmdline="$cmdline -3"
|
||||
fi
|
||||
|
||||
sq () {
|
||||
git rev-parse --sq-quote "$@"
|
||||
}
|
||||
|
||||
stop_here () {
|
||||
echo "$1" >"$dotest/next"
|
||||
git rev-parse --verify -q HEAD >"$dotest/abort-safety"
|
||||
exit 1
|
||||
}
|
||||
|
||||
safe_to_abort () {
|
||||
if test -f "$dotest/dirtyindex"
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! test -s "$dotest/abort-safety"
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
abort_safety=$(cat "$dotest/abort-safety")
|
||||
if test "z$(git rev-parse --verify -q HEAD)" = "z$abort_safety"
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
echo >&2 "You seem to have moved HEAD since the last 'am' failure."
|
||||
echo >&2 "Not rewinding to ORIG_HEAD"
|
||||
return 1
|
||||
}
|
||||
|
||||
stop_here_user_resolve () {
|
||||
if [ -n "$resolvemsg" ]; then
|
||||
printf '%s\n' "$resolvemsg"
|
||||
stop_here $1
|
||||
fi
|
||||
echo "When you have resolved this problem run \"$cmdline --resolved\"."
|
||||
echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
|
||||
echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
|
||||
|
||||
stop_here $1
|
||||
}
|
||||
|
||||
go_next () {
|
||||
rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
|
||||
"$dotest/patch" "$dotest/info"
|
||||
echo "$next" >"$dotest/next"
|
||||
this=$next
|
||||
}
|
||||
|
||||
cannot_fallback () {
|
||||
echo "$1"
|
||||
echo "Cannot fall back to three-way merge."
|
||||
exit 1
|
||||
}
|
||||
|
||||
fall_back_3way () {
|
||||
O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
|
||||
|
||||
rm -fr "$dotest"/patch-merge-*
|
||||
mkdir "$dotest/patch-merge-tmp-dir"
|
||||
|
||||
# First see if the patch records the index info that we can use.
|
||||
git apply --build-fake-ancestor "$dotest/patch-merge-tmp-index" \
|
||||
"$dotest/patch" &&
|
||||
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
|
||||
git write-tree >"$dotest/patch-merge-base+" ||
|
||||
cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
|
||||
|
||||
say Using index info to reconstruct a base tree...
|
||||
if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
|
||||
git apply --cached <"$dotest/patch"
|
||||
then
|
||||
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
|
||||
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
|
||||
else
|
||||
cannot_fallback "Did you hand edit your patch?
|
||||
It does not apply to blobs recorded in its index."
|
||||
fi
|
||||
|
||||
test -f "$dotest/patch-merge-index" &&
|
||||
his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git write-tree) &&
|
||||
orig_tree=$(cat "$dotest/patch-merge-base") &&
|
||||
rm -fr "$dotest"/patch-merge-* || exit 1
|
||||
|
||||
say Falling back to patching base and 3-way merge...
|
||||
|
||||
# This is not so wrong. Depending on which base we picked,
|
||||
# orig_tree may be wildly different from ours, but his_tree
|
||||
# has the same set of wildly different changes in parts the
|
||||
# patch did not touch, so recursive ends up canceling them,
|
||||
# saying that we reverted all those changes.
|
||||
|
||||
eval GITHEAD_$his_tree='"$FIRSTLINE"'
|
||||
export GITHEAD_$his_tree
|
||||
if test -n "$GIT_QUIET"
|
||||
then
|
||||
GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
|
||||
fi
|
||||
git-merge-recursive $orig_tree -- HEAD $his_tree || {
|
||||
git rerere $allow_rerere_autoupdate
|
||||
echo Failed to merge in the changes.
|
||||
exit 1
|
||||
}
|
||||
unset GITHEAD_$his_tree
|
||||
}
|
||||
|
||||
clean_abort () {
|
||||
test $# = 0 || echo >&2 "$@"
|
||||
rm -fr "$dotest"
|
||||
exit 1
|
||||
}
|
||||
|
||||
patch_format=
|
||||
|
||||
check_patch_format () {
|
||||
# early return if patch_format was set from the command line
|
||||
if test -n "$patch_format"
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# we default to mbox format if input is from stdin and for
|
||||
# directories
|
||||
if test $# = 0 || test "x$1" = "x-" || test -d "$1"
|
||||
then
|
||||
patch_format=mbox
|
||||
return 0
|
||||
fi
|
||||
|
||||
# otherwise, check the first few lines of the first patch to try
|
||||
# to detect its format
|
||||
{
|
||||
read l1
|
||||
read l2
|
||||
read l3
|
||||
case "$l1" in
|
||||
"From "* | "From: "*)
|
||||
patch_format=mbox
|
||||
;;
|
||||
'# This series applies on GIT commit'*)
|
||||
patch_format=stgit-series
|
||||
;;
|
||||
"# HG changeset patch")
|
||||
patch_format=hg
|
||||
;;
|
||||
*)
|
||||
# if the second line is empty and the third is
|
||||
# a From, Author or Date entry, this is very
|
||||
# likely an StGIT patch
|
||||
case "$l2,$l3" in
|
||||
,"From: "* | ,"Author: "* | ,"Date: "*)
|
||||
patch_format=stgit
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
if test -z "$patch_format" &&
|
||||
test -n "$l1" &&
|
||||
test -n "$l2" &&
|
||||
test -n "$l3"
|
||||
then
|
||||
# This begins with three non-empty lines. Is this a
|
||||
# piece of e-mail a-la RFC2822? Grab all the headers,
|
||||
# discarding the indented remainder of folded lines,
|
||||
# and see if it looks like that they all begin with the
|
||||
# header field names...
|
||||
tr -d '\015' <"$1" |
|
||||
sed -n -e '/^$/q' -e '/^[ ]/d' -e p |
|
||||
sane_egrep -v '^[!-9;-~]+:' >/dev/null ||
|
||||
patch_format=mbox
|
||||
fi
|
||||
} < "$1" || clean_abort
|
||||
}
|
||||
|
||||
split_patches () {
|
||||
case "$patch_format" in
|
||||
mbox)
|
||||
if test -n "$rebasing" || test t = "$keepcr"
|
||||
then
|
||||
keep_cr=--keep-cr
|
||||
else
|
||||
keep_cr=
|
||||
fi
|
||||
git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
|
||||
clean_abort
|
||||
;;
|
||||
stgit-series)
|
||||
if test $# -ne 1
|
||||
then
|
||||
clean_abort "Only one StGIT patch series can be applied at once"
|
||||
fi
|
||||
series_dir=`dirname "$1"`
|
||||
series_file="$1"
|
||||
shift
|
||||
{
|
||||
set x
|
||||
while read filename
|
||||
do
|
||||
set "$@" "$series_dir/$filename"
|
||||
done
|
||||
# remove the safety x
|
||||
shift
|
||||
# remove the arg coming from the first-line comment
|
||||
shift
|
||||
} < "$series_file" || clean_abort
|
||||
# set the patch format appropriately
|
||||
patch_format=stgit
|
||||
# now handle the actual StGIT patches
|
||||
split_patches "$@"
|
||||
;;
|
||||
stgit)
|
||||
this=0
|
||||
for stgit in "$@"
|
||||
do
|
||||
this=`expr "$this" + 1`
|
||||
msgnum=`printf "%0${prec}d" $this`
|
||||
# Perl version of StGIT parse_patch. The first nonemptyline
|
||||
# not starting with Author, From or Date is the
|
||||
# subject, and the body starts with the next nonempty
|
||||
# line not starting with Author, From or Date
|
||||
perl -ne 'BEGIN { $subject = 0 }
|
||||
if ($subject > 1) { print ; }
|
||||
elsif (/^\s+$/) { next ; }
|
||||
elsif (/^Author:/) { print s/Author/From/ ; }
|
||||
elsif (/^(From|Date)/) { print ; }
|
||||
elsif ($subject) {
|
||||
$subject = 2 ;
|
||||
print "\n" ;
|
||||
print ;
|
||||
} else {
|
||||
print "Subject: ", $_ ;
|
||||
$subject = 1;
|
||||
}
|
||||
' < "$stgit" > "$dotest/$msgnum" || clean_abort
|
||||
done
|
||||
echo "$this" > "$dotest/last"
|
||||
this=
|
||||
msgnum=
|
||||
;;
|
||||
*)
|
||||
if test -n "$parse_patch" ; then
|
||||
clean_abort "Patch format $patch_format is not supported."
|
||||
else
|
||||
clean_abort "Patch format detection failed."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
prec=4
|
||||
dotest="$GIT_DIR/rebase-apply"
|
||||
sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort=
|
||||
resolvemsg= resume= scissors= no_inbody_headers=
|
||||
git_apply_opt=
|
||||
committer_date_is_author_date=
|
||||
ignore_date=
|
||||
allow_rerere_autoupdate=
|
||||
|
||||
if test "$(git config --bool --get am.keepcr)" = true
|
||||
then
|
||||
keepcr=t
|
||||
fi
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-i|--interactive)
|
||||
interactive=t ;;
|
||||
-b|--binary)
|
||||
: ;;
|
||||
-3|--3way)
|
||||
threeway=t ;;
|
||||
-s|--signoff)
|
||||
sign=t ;;
|
||||
-u|--utf8)
|
||||
utf8=t ;; # this is now default
|
||||
--no-utf8)
|
||||
utf8= ;;
|
||||
-k|--keep)
|
||||
keep=t ;;
|
||||
-c|--scissors)
|
||||
scissors=t ;;
|
||||
--no-scissors)
|
||||
scissors=f ;;
|
||||
-r|--resolved|--continue)
|
||||
resolved=t ;;
|
||||
--skip)
|
||||
skip=t ;;
|
||||
--abort)
|
||||
abort=t ;;
|
||||
--rebasing)
|
||||
rebasing=t threeway=t keep=t scissors=f no_inbody_headers=t ;;
|
||||
-d|--dotest)
|
||||
die "-d option is no longer supported. Do not use."
|
||||
;;
|
||||
--resolvemsg)
|
||||
shift; resolvemsg=$1 ;;
|
||||
--whitespace|--directory)
|
||||
git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
|
||||
-C|-p)
|
||||
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
|
||||
--patch-format)
|
||||
shift ; patch_format="$1" ;;
|
||||
--reject|--ignore-whitespace|--ignore-space-change)
|
||||
git_apply_opt="$git_apply_opt $1" ;;
|
||||
--committer-date-is-author-date)
|
||||
committer_date_is_author_date=t ;;
|
||||
--ignore-date)
|
||||
ignore_date=t ;;
|
||||
--rerere-autoupdate|--no-rerere-autoupdate)
|
||||
allow_rerere_autoupdate="$1" ;;
|
||||
-q|--quiet)
|
||||
GIT_QUIET=t ;;
|
||||
--keep-cr)
|
||||
keepcr=t ;;
|
||||
--no-keep-cr)
|
||||
keepcr=f ;;
|
||||
--)
|
||||
shift; break ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# If the dotest directory exists, but we have finished applying all the
|
||||
# patches in them, clear it out.
|
||||
if test -d "$dotest" &&
|
||||
last=$(cat "$dotest/last") &&
|
||||
next=$(cat "$dotest/next") &&
|
||||
test $# != 0 &&
|
||||
test "$next" -gt "$last"
|
||||
then
|
||||
rm -fr "$dotest"
|
||||
fi
|
||||
|
||||
if test -d "$dotest"
|
||||
then
|
||||
case "$#,$skip$resolved$abort" in
|
||||
0,*t*)
|
||||
# Explicit resume command and we do not have file, so
|
||||
# we are happy.
|
||||
: ;;
|
||||
0,)
|
||||
# No file input but without resume parameters; catch
|
||||
# user error to feed us a patch from standard input
|
||||
# when there is already $dotest. This is somewhat
|
||||
# unreliable -- stdin could be /dev/null for example
|
||||
# and the caller did not intend to feed us a patch but
|
||||
# wanted to continue unattended.
|
||||
test -t 0
|
||||
;;
|
||||
*)
|
||||
false
|
||||
;;
|
||||
esac ||
|
||||
die "previous rebase directory $dotest still exists but mbox given."
|
||||
resume=yes
|
||||
|
||||
case "$skip,$abort" in
|
||||
t,t)
|
||||
die "Please make up your mind. --skip or --abort?"
|
||||
;;
|
||||
t,)
|
||||
git rerere clear
|
||||
git read-tree --reset -u HEAD HEAD
|
||||
orig_head=$(cat "$GIT_DIR/ORIG_HEAD")
|
||||
git reset HEAD
|
||||
git update-ref ORIG_HEAD $orig_head
|
||||
;;
|
||||
,t)
|
||||
if test -f "$dotest/rebasing"
|
||||
then
|
||||
exec git rebase --abort
|
||||
fi
|
||||
git rerere clear
|
||||
if safe_to_abort
|
||||
then
|
||||
git read-tree --reset -u HEAD ORIG_HEAD
|
||||
git reset ORIG_HEAD
|
||||
fi
|
||||
rm -fr "$dotest"
|
||||
exit ;;
|
||||
esac
|
||||
rm -f "$dotest/dirtyindex"
|
||||
else
|
||||
# Make sure we are not given --skip, --resolved, nor --abort
|
||||
test "$skip$resolved$abort" = "" ||
|
||||
die "Resolve operation not in progress, we are not resuming."
|
||||
|
||||
# Start afresh.
|
||||
mkdir -p "$dotest" || exit
|
||||
|
||||
if test -n "$prefix" && test $# != 0
|
||||
then
|
||||
first=t
|
||||
for arg
|
||||
do
|
||||
test -n "$first" && {
|
||||
set x
|
||||
first=
|
||||
}
|
||||
if is_absolute_path "$arg"
|
||||
then
|
||||
set "$@" "$arg"
|
||||
else
|
||||
set "$@" "$prefix$arg"
|
||||
fi
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
check_patch_format "$@"
|
||||
|
||||
split_patches "$@"
|
||||
|
||||
# -i can and must be given when resuming; everything
|
||||
# else is kept
|
||||
echo " $git_apply_opt" >"$dotest/apply-opt"
|
||||
echo "$threeway" >"$dotest/threeway"
|
||||
echo "$sign" >"$dotest/sign"
|
||||
echo "$utf8" >"$dotest/utf8"
|
||||
echo "$keep" >"$dotest/keep"
|
||||
echo "$keepcr" >"$dotest/keepcr"
|
||||
echo "$scissors" >"$dotest/scissors"
|
||||
echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
|
||||
echo "$GIT_QUIET" >"$dotest/quiet"
|
||||
echo 1 >"$dotest/next"
|
||||
if test -n "$rebasing"
|
||||
then
|
||||
: >"$dotest/rebasing"
|
||||
else
|
||||
: >"$dotest/applying"
|
||||
if test -n "$HAS_HEAD"
|
||||
then
|
||||
git update-ref ORIG_HEAD HEAD
|
||||
else
|
||||
git update-ref -d ORIG_HEAD >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$resolved" in
|
||||
'')
|
||||
case "$HAS_HEAD" in
|
||||
'')
|
||||
files=$(git ls-files) ;;
|
||||
?*)
|
||||
files=$(git diff-index --cached --name-only HEAD --) ;;
|
||||
esac || exit
|
||||
if test "$files"
|
||||
then
|
||||
test -n "$HAS_HEAD" && : >"$dotest/dirtyindex"
|
||||
die "Dirty index: cannot apply patches (dirty: $files)"
|
||||
fi
|
||||
esac
|
||||
|
||||
if test "$(cat "$dotest/utf8")" = t
|
||||
then
|
||||
utf8=-u
|
||||
else
|
||||
utf8=-n
|
||||
fi
|
||||
if test "$(cat "$dotest/keep")" = t
|
||||
then
|
||||
keep=-k
|
||||
fi
|
||||
case "$(cat "$dotest/keepcr")" in
|
||||
t)
|
||||
keepcr=--keep-cr ;;
|
||||
f)
|
||||
keepcr=--no-keep-cr ;;
|
||||
esac
|
||||
case "$(cat "$dotest/scissors")" in
|
||||
t)
|
||||
scissors=--scissors ;;
|
||||
f)
|
||||
scissors=--no-scissors ;;
|
||||
esac
|
||||
if test "$(cat "$dotest/no_inbody_headers")" = t
|
||||
then
|
||||
no_inbody_headers=--no-inbody-headers
|
||||
else
|
||||
no_inbody_headers=
|
||||
fi
|
||||
if test "$(cat "$dotest/quiet")" = t
|
||||
then
|
||||
GIT_QUIET=t
|
||||
fi
|
||||
if test "$(cat "$dotest/threeway")" = t
|
||||
then
|
||||
threeway=t
|
||||
fi
|
||||
git_apply_opt=$(cat "$dotest/apply-opt")
|
||||
if test "$(cat "$dotest/sign")" = t
|
||||
then
|
||||
SIGNOFF=`git var GIT_COMMITTER_IDENT | sed -e '
|
||||
s/>.*/>/
|
||||
s/^/Signed-off-by: /'
|
||||
`
|
||||
else
|
||||
SIGNOFF=
|
||||
fi
|
||||
|
||||
last=`cat "$dotest/last"`
|
||||
this=`cat "$dotest/next"`
|
||||
if test "$skip" = t
|
||||
then
|
||||
this=`expr "$this" + 1`
|
||||
resume=
|
||||
fi
|
||||
|
||||
while test "$this" -le "$last"
|
||||
do
|
||||
msgnum=`printf "%0${prec}d" $this`
|
||||
next=`expr "$this" + 1`
|
||||
test -f "$dotest/$msgnum" || {
|
||||
resume=
|
||||
go_next
|
||||
continue
|
||||
}
|
||||
|
||||
# If we are not resuming, parse and extract the patch information
|
||||
# into separate files:
|
||||
# - info records the authorship and title
|
||||
# - msg is the rest of commit log message
|
||||
# - patch is the patch body.
|
||||
#
|
||||
# When we are resuming, these files are either already prepared
|
||||
# by the user, or the user can tell us to do so by --resolved flag.
|
||||
case "$resume" in
|
||||
'')
|
||||
git mailinfo $keep $no_inbody_headers $scissors $utf8 "$dotest/msg" "$dotest/patch" \
|
||||
<"$dotest/$msgnum" >"$dotest/info" ||
|
||||
stop_here $this
|
||||
|
||||
# skip pine's internal folder data
|
||||
sane_grep '^Author: Mail System Internal Data$' \
|
||||
<"$dotest"/info >/dev/null &&
|
||||
go_next && continue
|
||||
|
||||
test -s "$dotest/patch" || {
|
||||
echo "Patch is empty. Was it split wrong?"
|
||||
echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
|
||||
echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
|
||||
stop_here $this
|
||||
}
|
||||
rm -f "$dotest/original-commit" "$dotest/author-script"
|
||||
if test -f "$dotest/rebasing" &&
|
||||
commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
|
||||
-e q "$dotest/$msgnum") &&
|
||||
test "$(git cat-file -t "$commit")" = commit
|
||||
then
|
||||
git cat-file commit "$commit" |
|
||||
sed -e '1,/^$/d' >"$dotest/msg-clean"
|
||||
echo "$commit" > "$dotest/original-commit"
|
||||
get_author_ident_from_commit "$commit" > "$dotest/author-script"
|
||||
else
|
||||
{
|
||||
sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
|
||||
echo
|
||||
cat "$dotest/msg"
|
||||
} |
|
||||
git stripspace > "$dotest/msg-clean"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -f "$dotest/author-script"
|
||||
then
|
||||
eval $(cat "$dotest/author-script")
|
||||
else
|
||||
GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
|
||||
GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
|
||||
GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
|
||||
fi
|
||||
|
||||
if test -z "$GIT_AUTHOR_EMAIL"
|
||||
then
|
||||
echo "Patch does not have a valid e-mail address."
|
||||
stop_here $this
|
||||
fi
|
||||
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
|
||||
case "$resume" in
|
||||
'')
|
||||
if test '' != "$SIGNOFF"
|
||||
then
|
||||
LAST_SIGNED_OFF_BY=`
|
||||
sed -ne '/^Signed-off-by: /p' \
|
||||
"$dotest/msg-clean" |
|
||||
sed -ne '$p'
|
||||
`
|
||||
ADD_SIGNOFF=`
|
||||
test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
|
||||
test '' = "$LAST_SIGNED_OFF_BY" && echo
|
||||
echo "$SIGNOFF"
|
||||
}`
|
||||
else
|
||||
ADD_SIGNOFF=
|
||||
fi
|
||||
{
|
||||
if test -s "$dotest/msg-clean"
|
||||
then
|
||||
cat "$dotest/msg-clean"
|
||||
fi
|
||||
if test '' != "$ADD_SIGNOFF"
|
||||
then
|
||||
echo "$ADD_SIGNOFF"
|
||||
fi
|
||||
} >"$dotest/final-commit"
|
||||
;;
|
||||
*)
|
||||
case "$resolved$interactive" in
|
||||
tt)
|
||||
# This is used only for interactive view option.
|
||||
git diff-index -p --cached HEAD -- >"$dotest/patch"
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
|
||||
resume=
|
||||
if test "$interactive" = t
|
||||
then
|
||||
test -t 0 ||
|
||||
die "cannot be interactive without stdin connected to a terminal."
|
||||
action=again
|
||||
while test "$action" = again
|
||||
do
|
||||
echo "Commit Body is:"
|
||||
echo "--------------------------"
|
||||
cat "$dotest/final-commit"
|
||||
echo "--------------------------"
|
||||
printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
|
||||
read reply
|
||||
case "$reply" in
|
||||
[yY]*) action=yes ;;
|
||||
[aA]*) action=yes interactive= ;;
|
||||
[nN]*) action=skip ;;
|
||||
[eE]*) git_editor "$dotest/final-commit"
|
||||
action=again ;;
|
||||
[vV]*) action=again
|
||||
git_pager "$dotest/patch" ;;
|
||||
*) action=again ;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
action=yes
|
||||
fi
|
||||
|
||||
if test -f "$dotest/final-commit"
|
||||
then
|
||||
FIRSTLINE=$(sed 1q "$dotest/final-commit")
|
||||
else
|
||||
FIRSTLINE=""
|
||||
fi
|
||||
|
||||
if test $action = skip
|
||||
then
|
||||
go_next
|
||||
continue
|
||||
fi
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/applypatch-msg
|
||||
then
|
||||
"$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
|
||||
stop_here $this
|
||||
fi
|
||||
|
||||
say "Applying: $FIRSTLINE"
|
||||
|
||||
case "$resolved" in
|
||||
'')
|
||||
# When we are allowed to fall back to 3-way later, don't give
|
||||
# false errors during the initial attempt.
|
||||
squelch=
|
||||
if test "$threeway" = t
|
||||
then
|
||||
squelch='>/dev/null 2>&1 '
|
||||
fi
|
||||
eval "git apply $squelch$git_apply_opt"' --index "$dotest/patch"'
|
||||
apply_status=$?
|
||||
;;
|
||||
t)
|
||||
# Resolved means the user did all the hard work, and
|
||||
# we do not have to do any patch application. Just
|
||||
# trust what the user has in the index file and the
|
||||
# working tree.
|
||||
resolved=
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
echo "No changes - did you forget to use 'git add'?"
|
||||
echo "If there is nothing left to stage, chances are that something else"
|
||||
echo "already introduced the same changes; you might want to skip this patch."
|
||||
stop_here_user_resolve $this
|
||||
}
|
||||
unmerged=$(git ls-files -u)
|
||||
if test -n "$unmerged"
|
||||
then
|
||||
echo "You still have unmerged paths in your index"
|
||||
echo "did you forget to use 'git add'?"
|
||||
stop_here_user_resolve $this
|
||||
fi
|
||||
apply_status=0
|
||||
git rerere
|
||||
;;
|
||||
esac
|
||||
|
||||
if test $apply_status != 0 && test "$threeway" = t
|
||||
then
|
||||
if (fall_back_3way)
|
||||
then
|
||||
# Applying the patch to an earlier tree and merging the
|
||||
# result may have produced the same tree as ours.
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
say No changes -- Patch already applied.
|
||||
go_next
|
||||
continue
|
||||
}
|
||||
# clear apply_status -- we have successfully merged.
|
||||
apply_status=0
|
||||
fi
|
||||
fi
|
||||
if test $apply_status != 0
|
||||
then
|
||||
printf 'Patch failed at %s %s\n' "$msgnum" "$FIRSTLINE"
|
||||
stop_here_user_resolve $this
|
||||
fi
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/pre-applypatch
|
||||
then
|
||||
"$GIT_DIR"/hooks/pre-applypatch || stop_here $this
|
||||
fi
|
||||
|
||||
tree=$(git write-tree) &&
|
||||
commit=$(
|
||||
if test -n "$ignore_date"
|
||||
then
|
||||
GIT_AUTHOR_DATE=
|
||||
fi
|
||||
parent=$(git rev-parse --verify -q HEAD) ||
|
||||
say >&2 "applying to an empty history"
|
||||
|
||||
if test -n "$committer_date_is_author_date"
|
||||
then
|
||||
GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
||||
export GIT_COMMITTER_DATE
|
||||
fi &&
|
||||
git commit-tree $tree ${parent:+-p} $parent <"$dotest/final-commit"
|
||||
) &&
|
||||
git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
|
||||
stop_here $this
|
||||
|
||||
if test -f "$dotest/original-commit"; then
|
||||
echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
|
||||
fi
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/post-applypatch
|
||||
then
|
||||
"$GIT_DIR"/hooks/post-applypatch
|
||||
fi
|
||||
|
||||
go_next
|
||||
done
|
||||
|
||||
if test -s "$dotest"/rewritten; then
|
||||
git notes copy --for-rewrite=rebase < "$dotest"/rewritten
|
||||
if test -x "$GIT_DIR"/hooks/post-rewrite; then
|
||||
"$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -fr "$dotest"
|
||||
git gc --auto
|
1
SparkleShare/Mac/git/libexec/git-core/git-annotate
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-annotate
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-apply
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-apply
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1135
SparkleShare/Mac/git/libexec/git-core/git-archimport
Executable file
1135
SparkleShare/Mac/git/libexec/git-core/git-archimport
Executable file
File diff suppressed because it is too large
Load diff
1
SparkleShare/Mac/git/libexec/git-core/git-archive
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-archive
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
457
SparkleShare/Mac/git/libexec/git-core/git-bisect
Executable file
457
SparkleShare/Mac/git/libexec/git-core/git-bisect
Executable file
|
@ -0,0 +1,457 @@
|
|||
#!/bin/sh
|
||||
|
||||
USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
|
||||
LONG_USAGE='git bisect help
|
||||
print this long help message.
|
||||
git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
|
||||
reset bisect state and start bisection.
|
||||
git bisect bad [<rev>]
|
||||
mark <rev> a known-bad revision.
|
||||
git bisect good [<rev>...]
|
||||
mark <rev>... known-good revisions.
|
||||
git bisect skip [(<rev>|<range>)...]
|
||||
mark <rev>... untestable revisions.
|
||||
git bisect next
|
||||
find next bisection to test and check it out.
|
||||
git bisect reset [<commit>]
|
||||
finish bisection search and go back to commit.
|
||||
git bisect visualize
|
||||
show bisect status in gitk.
|
||||
git bisect replay <logfile>
|
||||
replay bisection log.
|
||||
git bisect log
|
||||
show bisect log.
|
||||
git bisect run <cmd>...
|
||||
use <cmd>... to automatically bisect.
|
||||
|
||||
Please use "git help bisect" to get the full man page.'
|
||||
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
|
||||
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
|
||||
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
|
||||
|
||||
bisect_autostart() {
|
||||
test -s "$GIT_DIR/BISECT_START" || {
|
||||
echo >&2 'You need to start by "git bisect start"'
|
||||
if test -t 0
|
||||
then
|
||||
echo >&2 -n 'Do you want me to do it for you [Y/n]? '
|
||||
read yesno
|
||||
case "$yesno" in
|
||||
[Nn]*)
|
||||
exit ;;
|
||||
esac
|
||||
bisect_start
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
bisect_start() {
|
||||
#
|
||||
# Verify HEAD.
|
||||
#
|
||||
head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
|
||||
head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
|
||||
die "Bad HEAD - I need a HEAD"
|
||||
|
||||
#
|
||||
# Check if we are bisecting.
|
||||
#
|
||||
start_head=''
|
||||
if test -s "$GIT_DIR/BISECT_START"
|
||||
then
|
||||
# Reset to the rev from where we started.
|
||||
start_head=$(cat "$GIT_DIR/BISECT_START")
|
||||
git checkout "$start_head" -- || exit
|
||||
else
|
||||
# Get rev from where we start.
|
||||
case "$head" in
|
||||
refs/heads/*|$_x40)
|
||||
# This error message should only be triggered by
|
||||
# cogito usage, and cogito users should understand
|
||||
# it relates to cg-seek.
|
||||
[ -s "$GIT_DIR/head-name" ] &&
|
||||
die "won't bisect on seeked tree"
|
||||
start_head="${head#refs/heads/}"
|
||||
;;
|
||||
*)
|
||||
die "Bad HEAD - strange symbolic ref"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
#
|
||||
# Get rid of any old bisect state.
|
||||
#
|
||||
bisect_clean_state || exit
|
||||
|
||||
#
|
||||
# Check for one bad and then some good revisions.
|
||||
#
|
||||
has_double_dash=0
|
||||
for arg; do
|
||||
case "$arg" in --) has_double_dash=1; break ;; esac
|
||||
done
|
||||
orig_args=$(git rev-parse --sq-quote "$@")
|
||||
bad_seen=0
|
||||
eval=''
|
||||
while [ $# -gt 0 ]; do
|
||||
arg="$1"
|
||||
case "$arg" in
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
rev=$(git rev-parse -q --verify "$arg^{commit}") || {
|
||||
test $has_double_dash -eq 1 &&
|
||||
die "'$arg' does not appear to be a valid revision"
|
||||
break
|
||||
}
|
||||
case $bad_seen in
|
||||
0) state='bad' ; bad_seen=1 ;;
|
||||
*) state='good' ;;
|
||||
esac
|
||||
eval="$eval bisect_write '$state' '$rev' 'nolog'; "
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
#
|
||||
# Change state.
|
||||
# In case of mistaken revs or checkout error, or signals received,
|
||||
# "bisect_auto_next" below may exit or misbehave.
|
||||
# We have to trap this to be able to clean up using
|
||||
# "bisect_clean_state".
|
||||
#
|
||||
trap 'bisect_clean_state' 0
|
||||
trap 'exit 255' 1 2 3 15
|
||||
|
||||
#
|
||||
# Write new start state.
|
||||
#
|
||||
echo "$start_head" >"$GIT_DIR/BISECT_START" &&
|
||||
git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
|
||||
eval "$eval" &&
|
||||
echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
|
||||
#
|
||||
# Check if we can proceed to the next bisect state.
|
||||
#
|
||||
bisect_auto_next
|
||||
|
||||
trap '-' 0
|
||||
}
|
||||
|
||||
bisect_write() {
|
||||
state="$1"
|
||||
rev="$2"
|
||||
nolog="$3"
|
||||
case "$state" in
|
||||
bad) tag="$state" ;;
|
||||
good|skip) tag="$state"-"$rev" ;;
|
||||
*) die "Bad bisect_write argument: $state" ;;
|
||||
esac
|
||||
git update-ref "refs/bisect/$tag" "$rev" || exit
|
||||
echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
|
||||
test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
is_expected_rev() {
|
||||
test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
|
||||
test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
|
||||
}
|
||||
|
||||
check_expected_revs() {
|
||||
for _rev in "$@"; do
|
||||
if ! is_expected_rev "$_rev"; then
|
||||
rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
|
||||
rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
bisect_skip() {
|
||||
all=''
|
||||
for arg in "$@"
|
||||
do
|
||||
case "$arg" in
|
||||
*..*)
|
||||
revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
|
||||
*)
|
||||
revs=$(git rev-parse --sq-quote "$arg") ;;
|
||||
esac
|
||||
all="$all $revs"
|
||||
done
|
||||
eval bisect_state 'skip' $all
|
||||
}
|
||||
|
||||
bisect_state() {
|
||||
bisect_autostart
|
||||
state=$1
|
||||
case "$#,$state" in
|
||||
0,*)
|
||||
die "Please call 'bisect_state' with at least one argument." ;;
|
||||
1,bad|1,good|1,skip)
|
||||
rev=$(git rev-parse --verify HEAD) ||
|
||||
die "Bad rev input: HEAD"
|
||||
bisect_write "$state" "$rev"
|
||||
check_expected_revs "$rev" ;;
|
||||
2,bad|*,good|*,skip)
|
||||
shift
|
||||
eval=''
|
||||
for rev in "$@"
|
||||
do
|
||||
sha=$(git rev-parse --verify "$rev^{commit}") ||
|
||||
die "Bad rev input: $rev"
|
||||
eval="$eval bisect_write '$state' '$sha'; "
|
||||
done
|
||||
eval "$eval"
|
||||
check_expected_revs "$@" ;;
|
||||
*,bad)
|
||||
die "'git bisect bad' can take only one argument." ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_next_check() {
|
||||
missing_good= missing_bad=
|
||||
git show-ref -q --verify refs/bisect/bad || missing_bad=t
|
||||
test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
|
||||
|
||||
case "$missing_good,$missing_bad,$1" in
|
||||
,,*)
|
||||
: have both good and bad - ok
|
||||
;;
|
||||
*,)
|
||||
# do not have both but not asked to fail - just report.
|
||||
false
|
||||
;;
|
||||
t,,good)
|
||||
# have bad but not good. we could bisect although
|
||||
# this is less optimum.
|
||||
echo >&2 'Warning: bisecting only with a bad commit.'
|
||||
if test -t 0
|
||||
then
|
||||
printf >&2 'Are you sure [Y/n]? '
|
||||
read yesno
|
||||
case "$yesno" in [Nn]*) exit 1 ;; esac
|
||||
fi
|
||||
: bisect without good...
|
||||
;;
|
||||
*)
|
||||
THEN=''
|
||||
test -s "$GIT_DIR/BISECT_START" || {
|
||||
echo >&2 'You need to start by "git bisect start".'
|
||||
THEN='then '
|
||||
}
|
||||
echo >&2 'You '$THEN'need to give me at least one good' \
|
||||
'and one bad revisions.'
|
||||
echo >&2 '(You can use "git bisect bad" and' \
|
||||
'"git bisect good" for that.)'
|
||||
exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
bisect_auto_next() {
|
||||
bisect_next_check && bisect_next || :
|
||||
}
|
||||
|
||||
bisect_next() {
|
||||
case "$#" in 0) ;; *) usage ;; esac
|
||||
bisect_autostart
|
||||
bisect_next_check good
|
||||
|
||||
# Perform all bisection computation, display and checkout
|
||||
git bisect--helper --next-all
|
||||
res=$?
|
||||
|
||||
# Check if we should exit because bisection is finished
|
||||
test $res -eq 10 && exit 0
|
||||
|
||||
# Check for an error in the bisection process
|
||||
test $res -ne 0 && exit $res
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
bisect_visualize() {
|
||||
bisect_next_check fail
|
||||
|
||||
if test $# = 0
|
||||
then
|
||||
case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
|
||||
'') set git log ;;
|
||||
set*) set gitk ;;
|
||||
esac
|
||||
else
|
||||
case "$1" in
|
||||
git*|tig) ;;
|
||||
-*) set git log "$@" ;;
|
||||
*) set git "$@" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
|
||||
}
|
||||
|
||||
bisect_reset() {
|
||||
test -s "$GIT_DIR/BISECT_START" || {
|
||||
echo "We are not bisecting."
|
||||
return
|
||||
}
|
||||
case "$#" in
|
||||
0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
|
||||
1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null ||
|
||||
die "'$1' is not a valid commit"
|
||||
branch="$1" ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
if git checkout "$branch" -- ; then
|
||||
bisect_clean_state
|
||||
else
|
||||
die "Could not check out original HEAD '$branch'." \
|
||||
"Try 'git bisect reset <commit>'."
|
||||
fi
|
||||
}
|
||||
|
||||
bisect_clean_state() {
|
||||
# There may be some refs packed during bisection.
|
||||
git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
|
||||
while read ref hash
|
||||
do
|
||||
git update-ref -d $ref $hash || exit
|
||||
done
|
||||
rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
|
||||
rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
|
||||
rm -f "$GIT_DIR/BISECT_LOG" &&
|
||||
rm -f "$GIT_DIR/BISECT_NAMES" &&
|
||||
rm -f "$GIT_DIR/BISECT_RUN" &&
|
||||
# Cleanup head-name if it got left by an old version of git-bisect
|
||||
rm -f "$GIT_DIR/head-name" &&
|
||||
|
||||
rm -f "$GIT_DIR/BISECT_START"
|
||||
}
|
||||
|
||||
bisect_replay () {
|
||||
test "$#" -eq 1 || die "No logfile given"
|
||||
test -r "$1" || die "cannot read $1 for replaying"
|
||||
bisect_reset
|
||||
while read git bisect command rev
|
||||
do
|
||||
test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
|
||||
if test "$git" = "git-bisect"; then
|
||||
rev="$command"
|
||||
command="$bisect"
|
||||
fi
|
||||
case "$command" in
|
||||
start)
|
||||
cmd="bisect_start $rev"
|
||||
eval "$cmd" ;;
|
||||
good|bad|skip)
|
||||
bisect_write "$command" "$rev" ;;
|
||||
*)
|
||||
die "?? what are you talking about?" ;;
|
||||
esac
|
||||
done <"$1"
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_run () {
|
||||
bisect_next_check fail
|
||||
|
||||
while true
|
||||
do
|
||||
echo "running $@"
|
||||
"$@"
|
||||
res=$?
|
||||
|
||||
# Check for really bad run error.
|
||||
if [ $res -lt 0 -o $res -ge 128 ]; then
|
||||
echo >&2 "bisect run failed:"
|
||||
echo >&2 "exit code $res from '$@' is < 0 or >= 128"
|
||||
exit $res
|
||||
fi
|
||||
|
||||
# Find current state depending on run success or failure.
|
||||
# A special exit code of 125 means cannot test.
|
||||
if [ $res -eq 125 ]; then
|
||||
state='skip'
|
||||
elif [ $res -gt 0 ]; then
|
||||
state='bad'
|
||||
else
|
||||
state='good'
|
||||
fi
|
||||
|
||||
# We have to use a subshell because "bisect_state" can exit.
|
||||
( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
|
||||
res=$?
|
||||
|
||||
cat "$GIT_DIR/BISECT_RUN"
|
||||
|
||||
if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
|
||||
> /dev/null; then
|
||||
echo >&2 "bisect run cannot continue any more"
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if [ $res -ne 0 ]; then
|
||||
echo >&2 "bisect run failed:"
|
||||
echo >&2 "'bisect_state $state' exited with error code $res"
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
|
||||
echo "bisect run success"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
bisect_log () {
|
||||
test -s "$GIT_DIR/BISECT_LOG" || die "We are not bisecting."
|
||||
cat "$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
case "$#" in
|
||||
0)
|
||||
usage ;;
|
||||
*)
|
||||
cmd="$1"
|
||||
shift
|
||||
case "$cmd" in
|
||||
help)
|
||||
git bisect -h ;;
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
bad|good)
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
skip)
|
||||
bisect_skip "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
visualize|view)
|
||||
bisect_visualize "$@" ;;
|
||||
reset)
|
||||
bisect_reset "$@" ;;
|
||||
replay)
|
||||
bisect_replay "$@" ;;
|
||||
log)
|
||||
bisect_log ;;
|
||||
run)
|
||||
bisect_run "$@" ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
esac
|
1
SparkleShare/Mac/git/libexec/git-core/git-bisect--helper
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-bisect--helper
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-blame
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-blame
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-branch
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-branch
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-bundle
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-bundle
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-cat-file
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-cat-file
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-check-attr
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-check-attr
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-check-ref-format
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-check-ref-format
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-checkout
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-checkout
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-checkout-index
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-checkout-index
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-cherry
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-cherry
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-cherry-pick
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-cherry-pick
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
8
SparkleShare/Mac/git/libexec/git-core/git-citool
Executable file
8
SparkleShare/Mac/git/libexec/git-core/git-citool
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
if test "z$*" = zversion ||
|
||||
test "z$*" = z--version
|
||||
then
|
||||
echo 'git-gui version 0.13.0.8.g8f85'
|
||||
else
|
||||
exec '/usr/local/git/share/git-gui/lib/Git Gui.app/Contents/MacOS/Wish' "$0" "$@"
|
||||
fi
|
1
SparkleShare/Mac/git/libexec/git-core/git-clean
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-clean
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-clone
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-clone
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-commit
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-commit
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-commit-tree
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-commit-tree
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-config
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-config
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-count-objects
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-count-objects
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
456
SparkleShare/Mac/git/libexec/git-core/git-cvsexportcommit
Executable file
456
SparkleShare/Mac/git/libexec/git-core/git-cvsexportcommit
Executable file
|
@ -0,0 +1,456 @@
|
|||
#!/usr/bin/perl
|
||||
use lib (split(/:/, $ENV{GITPERLLIB} || "/usr/local/git/lib/perl5/site_perl"));
|
||||
|
||||
use 5.008;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Std;
|
||||
use File::Temp qw(tempdir);
|
||||
use Data::Dumper;
|
||||
use File::Basename qw(basename dirname);
|
||||
use File::Spec;
|
||||
use Git;
|
||||
|
||||
our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w, $opt_W, $opt_k);
|
||||
|
||||
getopts('uhPpvcfkam:d:w:W');
|
||||
|
||||
$opt_h && usage();
|
||||
|
||||
die "Need at least one commit identifier!" unless @ARGV;
|
||||
|
||||
# Get git-config settings
|
||||
my $repo = Git->repository();
|
||||
$opt_w = $repo->config('cvsexportcommit.cvsdir') unless defined $opt_w;
|
||||
|
||||
if ($opt_w || $opt_W) {
|
||||
# Remember where GIT_DIR is before changing to CVS checkout
|
||||
unless ($ENV{GIT_DIR}) {
|
||||
# No GIT_DIR set. Figure it out for ourselves
|
||||
my $gd =`git-rev-parse --git-dir`;
|
||||
chomp($gd);
|
||||
$ENV{GIT_DIR} = $gd;
|
||||
}
|
||||
# Make sure GIT_DIR is absolute
|
||||
$ENV{GIT_DIR} = File::Spec->rel2abs($ENV{GIT_DIR});
|
||||
}
|
||||
|
||||
if ($opt_w) {
|
||||
if (! -d $opt_w."/CVS" ) {
|
||||
die "$opt_w is not a CVS checkout";
|
||||
}
|
||||
chdir $opt_w or die "Cannot change to CVS checkout at $opt_w";
|
||||
}
|
||||
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
|
||||
die "GIT_DIR is not defined or is unreadable";
|
||||
}
|
||||
|
||||
|
||||
my @cvs;
|
||||
if ($opt_d) {
|
||||
@cvs = ('cvs', '-d', $opt_d);
|
||||
} else {
|
||||
@cvs = ('cvs');
|
||||
}
|
||||
|
||||
# resolve target commit
|
||||
my $commit;
|
||||
$commit = pop @ARGV;
|
||||
$commit = safe_pipe_capture('git-rev-parse', '--verify', "$commit^0");
|
||||
chomp $commit;
|
||||
if ($?) {
|
||||
die "The commit reference $commit did not resolve!";
|
||||
}
|
||||
|
||||
# resolve what parent we want
|
||||
my $parent;
|
||||
if (@ARGV) {
|
||||
$parent = pop @ARGV;
|
||||
$parent = safe_pipe_capture('git-rev-parse', '--verify', "$parent^0");
|
||||
chomp $parent;
|
||||
if ($?) {
|
||||
die "The parent reference did not resolve!";
|
||||
}
|
||||
}
|
||||
|
||||
# find parents from the commit itself
|
||||
my @commit = safe_pipe_capture('git-cat-file', 'commit', $commit);
|
||||
my @parents;
|
||||
my $committer;
|
||||
my $author;
|
||||
my $stage = 'headers'; # headers, msg
|
||||
my $title;
|
||||
my $msg = '';
|
||||
|
||||
foreach my $line (@commit) {
|
||||
chomp $line;
|
||||
if ($stage eq 'headers' && $line eq '') {
|
||||
$stage = 'msg';
|
||||
next;
|
||||
}
|
||||
|
||||
if ($stage eq 'headers') {
|
||||
if ($line =~ m/^parent (\w{40})$/) { # found a parent
|
||||
push @parents, $1;
|
||||
} elsif ($line =~ m/^author (.+) \d+ [-+]\d+$/) {
|
||||
$author = $1;
|
||||
} elsif ($line =~ m/^committer (.+) \d+ [-+]\d+$/) {
|
||||
$committer = $1;
|
||||
}
|
||||
} else {
|
||||
$msg .= $line . "\n";
|
||||
unless ($title) {
|
||||
$title = $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $noparent = "0000000000000000000000000000000000000000";
|
||||
if ($parent) {
|
||||
my $found;
|
||||
# double check that it's a valid parent
|
||||
foreach my $p (@parents) {
|
||||
if ($p eq $parent) {
|
||||
$found = 1;
|
||||
last;
|
||||
}; # found it
|
||||
}
|
||||
die "Did not find $parent in the parents for this commit!" if !$found and !$opt_P;
|
||||
} else { # we don't have a parent from the cmdline...
|
||||
if (@parents == 1) { # it's safe to get it from the commit
|
||||
$parent = $parents[0];
|
||||
} elsif (@parents == 0) { # there is no parent
|
||||
$parent = $noparent;
|
||||
} else { # cannot choose automatically from multiple parents
|
||||
die "This commit has more than one parent -- please name the parent you want to use explicitly";
|
||||
}
|
||||
}
|
||||
|
||||
my $go_back_to = 0;
|
||||
|
||||
if ($opt_W) {
|
||||
$opt_v && print "Resetting to $parent\n";
|
||||
$go_back_to = `git symbolic-ref HEAD 2> /dev/null ||
|
||||
git rev-parse HEAD` || die "Could not determine current branch";
|
||||
system("git checkout -q $parent^0") && die "Could not check out $parent^0";
|
||||
}
|
||||
|
||||
$opt_v && print "Applying to CVS commit $commit from parent $parent\n";
|
||||
|
||||
# grab the commit message
|
||||
open(MSG, ">.msg") or die "Cannot open .msg for writing";
|
||||
if ($opt_m) {
|
||||
print MSG $opt_m;
|
||||
}
|
||||
print MSG $msg;
|
||||
if ($opt_a) {
|
||||
print MSG "\n\nAuthor: $author\n";
|
||||
if ($author ne $committer) {
|
||||
print MSG "Committer: $committer\n";
|
||||
}
|
||||
}
|
||||
close MSG;
|
||||
|
||||
if ($parent eq $noparent) {
|
||||
`git-diff-tree --binary -p --root $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
|
||||
} else {
|
||||
`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
|
||||
}
|
||||
|
||||
## apply non-binary changes
|
||||
|
||||
# In pedantic mode require all lines of context to match. In normal
|
||||
# mode, be compatible with diff/patch: assume 3 lines of context and
|
||||
# require at least one line match, i.e. ignore at most 2 lines of
|
||||
# context, like diff/patch do by default.
|
||||
my $context = $opt_p ? '' : '-C1';
|
||||
|
||||
print "Checking if patch will apply\n";
|
||||
|
||||
my @stat;
|
||||
open APPLY, "GIT_DIR= git-apply $context --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
|
||||
@stat=<APPLY>;
|
||||
close APPLY || die "Cannot patch";
|
||||
my (@bfiles,@files,@afiles,@dfiles);
|
||||
chomp @stat;
|
||||
foreach (@stat) {
|
||||
push (@bfiles,$1) if m/^-\t-\t(.*)$/;
|
||||
push (@files, $1) if m/^-\t-\t(.*)$/;
|
||||
push (@files, $1) if m/^\d+\t\d+\t(.*)$/;
|
||||
push (@afiles,$1) if m/^ create mode [0-7]+ (.*)$/;
|
||||
push (@dfiles,$1) if m/^ delete mode [0-7]+ (.*)$/;
|
||||
}
|
||||
map { s/^"(.*)"$/$1/g } @bfiles,@files;
|
||||
map { s/\\([0-7]{3})/sprintf('%c',oct $1)/eg } @bfiles,@files;
|
||||
|
||||
# check that the files are clean and up to date according to cvs
|
||||
my $dirty;
|
||||
my @dirs;
|
||||
foreach my $p (@afiles) {
|
||||
my $path = dirname $p;
|
||||
while (!-d $path and ! grep { $_ eq $path } @dirs) {
|
||||
unshift @dirs, $path;
|
||||
$path = dirname $path;
|
||||
}
|
||||
}
|
||||
|
||||
# ... check dirs,
|
||||
foreach my $d (@dirs) {
|
||||
if (-e $d) {
|
||||
$dirty = 1;
|
||||
warn "$d exists and is not a directory!\n";
|
||||
}
|
||||
}
|
||||
|
||||
# ... query status of all files that we have a directory for and parse output of 'cvs status' to %cvsstat.
|
||||
my @canstatusfiles;
|
||||
foreach my $f (@files) {
|
||||
my $path = dirname $f;
|
||||
next if (grep { $_ eq $path } @dirs);
|
||||
push @canstatusfiles, $f;
|
||||
}
|
||||
|
||||
my %cvsstat;
|
||||
if (@canstatusfiles) {
|
||||
if ($opt_u) {
|
||||
my @updated = xargs_safe_pipe_capture([@cvs, 'update'], @canstatusfiles);
|
||||
print @updated;
|
||||
}
|
||||
# "cvs status" reorders the parameters, notably when there are multiple
|
||||
# arguments with the same basename. So be precise here.
|
||||
|
||||
my %added = map { $_ => 1 } @afiles;
|
||||
my %todo = map { $_ => 1 } @canstatusfiles;
|
||||
|
||||
while (%todo) {
|
||||
my @canstatusfiles2 = ();
|
||||
my %fullname = ();
|
||||
foreach my $name (keys %todo) {
|
||||
my $basename = basename($name);
|
||||
|
||||
# CVS reports files that don't exist in the current revision as
|
||||
# "no file $basename" in its "status" output, so we should
|
||||
# anticipate that. Totally unknown files will have a status
|
||||
# "Unknown". However, if they exist in the Attic, their status
|
||||
# will be "Up-to-date" (this means they were added once but have
|
||||
# been removed).
|
||||
$basename = "no file $basename" if $added{$basename};
|
||||
|
||||
$basename =~ s/^\s+//;
|
||||
$basename =~ s/\s+$//;
|
||||
|
||||
if (!exists($fullname{$basename})) {
|
||||
$fullname{$basename} = $name;
|
||||
push (@canstatusfiles2, $name);
|
||||
delete($todo{$name});
|
||||
}
|
||||
}
|
||||
my @cvsoutput;
|
||||
@cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles2);
|
||||
foreach my $l (@cvsoutput) {
|
||||
chomp $l;
|
||||
next unless
|
||||
my ($file, $status) = $l =~ /^File:\s+(.*\S)\s+Status: (.*)$/;
|
||||
|
||||
my $fullname = $fullname{$file};
|
||||
print STDERR "Huh? Status '$status' reported for unexpected file '$file'\n"
|
||||
unless defined $fullname;
|
||||
|
||||
# This response means the file does not exist except in
|
||||
# CVS's attic, so set the status accordingly
|
||||
$status = "In-attic"
|
||||
if $file =~ /^no file /
|
||||
&& $status eq 'Up-to-date';
|
||||
|
||||
$cvsstat{$fullname{$file}} = $status
|
||||
if defined $fullname{$file};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ... Validate that new files have the correct status
|
||||
foreach my $f (@afiles) {
|
||||
next unless defined(my $stat = $cvsstat{$f});
|
||||
|
||||
# This means the file has never been seen before
|
||||
next if $stat eq 'Unknown';
|
||||
|
||||
# This means the file has been seen before but was removed
|
||||
next if $stat eq 'In-attic';
|
||||
|
||||
$dirty = 1;
|
||||
warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
|
||||
warn "Status was: $cvsstat{$f}\n";
|
||||
}
|
||||
|
||||
# ... validate known files.
|
||||
foreach my $f (@files) {
|
||||
next if grep { $_ eq $f } @afiles;
|
||||
# TODO:we need to handle removed in cvs
|
||||
unless (defined ($cvsstat{$f}) and $cvsstat{$f} eq "Up-to-date") {
|
||||
$dirty = 1;
|
||||
warn "File $f not up to date but has status '$cvsstat{$f}' in your CVS checkout!\n";
|
||||
}
|
||||
|
||||
# Depending on how your GIT tree got imported from CVS you may
|
||||
# have a conflict between expanded keywords in your CVS tree and
|
||||
# unexpanded keywords in the patch about to be applied.
|
||||
if ($opt_k) {
|
||||
my $orig_file ="$f.orig";
|
||||
rename $f, $orig_file;
|
||||
open(FILTER_IN, "<$orig_file") or die "Cannot open $orig_file\n";
|
||||
open(FILTER_OUT, ">$f") or die "Cannot open $f\n";
|
||||
while (<FILTER_IN>)
|
||||
{
|
||||
my $line = $_;
|
||||
$line =~ s/\$([A-Z][a-z]+):[^\$]+\$/\$$1\$/g;
|
||||
print FILTER_OUT $line;
|
||||
}
|
||||
close FILTER_IN;
|
||||
close FILTER_OUT;
|
||||
}
|
||||
}
|
||||
|
||||
if ($dirty) {
|
||||
if ($opt_f) { warn "The tree is not clean -- forced merge\n";
|
||||
$dirty = 0;
|
||||
} else {
|
||||
die "Exiting: your CVS tree is not clean for this merge.";
|
||||
}
|
||||
}
|
||||
|
||||
print "Applying\n";
|
||||
if ($opt_W) {
|
||||
system("git checkout -q $commit^0") && die "cannot patch";
|
||||
} else {
|
||||
`GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
|
||||
}
|
||||
|
||||
print "Patch applied successfully. Adding new files and directories to CVS\n";
|
||||
my $dirtypatch = 0;
|
||||
|
||||
#
|
||||
# We have to add the directories in order otherwise we will have
|
||||
# problems when we try and add the sub-directory of a directory we
|
||||
# have not added yet.
|
||||
#
|
||||
# Luckily this is easy to deal with by sorting the directories and
|
||||
# dealing with the shortest ones first.
|
||||
#
|
||||
@dirs = sort { length $a <=> length $b} @dirs;
|
||||
|
||||
foreach my $d (@dirs) {
|
||||
if (system(@cvs,'add',$d)) {
|
||||
$dirtypatch = 1;
|
||||
warn "Failed to cvs add directory $d -- you may need to do it manually";
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $f (@afiles) {
|
||||
if (grep { $_ eq $f } @bfiles) {
|
||||
system(@cvs, 'add','-kb',$f);
|
||||
} else {
|
||||
system(@cvs, 'add', $f);
|
||||
}
|
||||
if ($?) {
|
||||
$dirtypatch = 1;
|
||||
warn "Failed to cvs add $f -- you may need to do it manually";
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $f (@dfiles) {
|
||||
system(@cvs, 'rm', '-f', $f);
|
||||
if ($?) {
|
||||
$dirtypatch = 1;
|
||||
warn "Failed to cvs rm -f $f -- you may need to do it manually";
|
||||
}
|
||||
}
|
||||
|
||||
print "Commit to CVS\n";
|
||||
print "Patch title (first comment line): $title\n";
|
||||
my @commitfiles = map { unless (m/\s/) { '\''.$_.'\''; } else { $_; }; } (@files);
|
||||
my $cmd = join(' ', @cvs)." commit -F .msg @commitfiles";
|
||||
|
||||
if ($dirtypatch) {
|
||||
print "NOTE: One or more hunks failed to apply cleanly.\n";
|
||||
print "You'll need to apply the patch in .cvsexportcommit.diff manually\n";
|
||||
print "using a patch program. After applying the patch and resolving the\n";
|
||||
print "problems you may commit using:";
|
||||
print "\n cd \"$opt_w\"" if $opt_w;
|
||||
print "\n $cmd\n";
|
||||
print "\n git checkout $go_back_to\n" if $go_back_to;
|
||||
print "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($opt_c) {
|
||||
print "Autocommit\n $cmd\n";
|
||||
print xargs_safe_pipe_capture([@cvs, 'commit', '-F', '.msg'], @files);
|
||||
if ($?) {
|
||||
die "Exiting: The commit did not succeed";
|
||||
}
|
||||
print "Committed successfully to CVS\n";
|
||||
# clean up
|
||||
unlink(".msg");
|
||||
} else {
|
||||
print "Ready for you to commit, just run:\n\n $cmd\n";
|
||||
}
|
||||
|
||||
# clean up
|
||||
unlink(".cvsexportcommit.diff");
|
||||
|
||||
if ($opt_W) {
|
||||
system("git checkout $go_back_to") && die "cannot move back to $go_back_to";
|
||||
if (!($go_back_to =~ /^[0-9a-fA-F]{40}$/)) {
|
||||
system("git symbolic-ref HEAD $go_back_to") &&
|
||||
die "cannot move back to $go_back_to";
|
||||
}
|
||||
}
|
||||
|
||||
# CVS version 1.11.x and 1.12.x sleeps the wrong way to ensure the timestamp
|
||||
# used by CVS and the one set by subsequence file modifications are different.
|
||||
# If they are not different CVS will not detect changes.
|
||||
sleep(1);
|
||||
|
||||
sub usage {
|
||||
print STDERR <<END;
|
||||
Usage: GIT_DIR=/path/to/.git git cvsexportcommit [-h] [-p] [-v] [-c] [-f] [-u] [-k] [-w cvsworkdir] [-m msgprefix] [ parent ] commit
|
||||
END
|
||||
exit(1);
|
||||
}
|
||||
|
||||
# An alternative to `command` that allows input to be passed as an array
|
||||
# to work around shell problems with weird characters in arguments
|
||||
# if the exec returns non-zero we die
|
||||
sub safe_pipe_capture {
|
||||
my @output;
|
||||
if (my $pid = open my $child, '-|') {
|
||||
@output = (<$child>);
|
||||
close $child or die join(' ',@_).": $! $?";
|
||||
} else {
|
||||
exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
|
||||
}
|
||||
return wantarray ? @output : join('',@output);
|
||||
}
|
||||
|
||||
sub xargs_safe_pipe_capture {
|
||||
my $MAX_ARG_LENGTH = 65536;
|
||||
my $cmd = shift;
|
||||
my @output;
|
||||
my $output;
|
||||
while(@_) {
|
||||
my @args;
|
||||
my $length = 0;
|
||||
while(@_ && $length < $MAX_ARG_LENGTH) {
|
||||
push @args, shift;
|
||||
$length += length($args[$#args]);
|
||||
}
|
||||
if (wantarray) {
|
||||
push @output, safe_pipe_capture(@$cmd, @args);
|
||||
}
|
||||
else {
|
||||
$output .= safe_pipe_capture(@$cmd, @args);
|
||||
}
|
||||
}
|
||||
return wantarray ? @output : $output;
|
||||
}
|
1097
SparkleShare/Mac/git/libexec/git-core/git-cvsimport
Executable file
1097
SparkleShare/Mac/git/libexec/git-core/git-cvsimport
Executable file
File diff suppressed because it is too large
Load diff
3696
SparkleShare/Mac/git/libexec/git-core/git-cvsserver
Executable file
3696
SparkleShare/Mac/git/libexec/git-core/git-cvsserver
Executable file
File diff suppressed because it is too large
Load diff
BIN
SparkleShare/Mac/git/libexec/git-core/git-daemon
Executable file
BIN
SparkleShare/Mac/git/libexec/git-core/git-daemon
Executable file
Binary file not shown.
1
SparkleShare/Mac/git/libexec/git-core/git-describe
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-describe
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-diff
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-diff
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-diff-files
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-diff-files
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-diff-index
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-diff-index
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-diff-tree
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-diff-tree
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
122
SparkleShare/Mac/git/libexec/git-core/git-difftool
Executable file
122
SparkleShare/Mac/git/libexec/git-core/git-difftool
Executable file
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/perl
|
||||
use lib (split(/:/, $ENV{GITPERLLIB} || "/usr/local/git/lib/perl5/site_perl"));
|
||||
# Copyright (c) 2009, 2010 David Aguilar
|
||||
#
|
||||
# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
|
||||
# git-difftool--helper script.
|
||||
#
|
||||
# This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
|
||||
# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL
|
||||
# are exported for use by git-difftool--helper.
|
||||
#
|
||||
# Any arguments that are unknown to this script are forwarded to 'git diff'.
|
||||
|
||||
use 5.008;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Cwd qw(abs_path);
|
||||
use File::Basename qw(dirname);
|
||||
|
||||
require Git;
|
||||
|
||||
my $DIR = abs_path(dirname($0));
|
||||
|
||||
|
||||
sub usage
|
||||
{
|
||||
print << 'USAGE';
|
||||
usage: git difftool [-t|--tool=<tool>] [-x|--extcmd=<cmd>]
|
||||
[-y|--no-prompt] [-g|--gui]
|
||||
['git diff' options]
|
||||
USAGE
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub setup_environment
|
||||
{
|
||||
$ENV{PATH} = "$DIR:$ENV{PATH}";
|
||||
$ENV{GIT_PAGER} = '';
|
||||
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
|
||||
}
|
||||
|
||||
sub exe
|
||||
{
|
||||
my $exe = shift;
|
||||
if ($^O eq 'MSWin32' || $^O eq 'msys') {
|
||||
return "$exe.exe";
|
||||
}
|
||||
return $exe;
|
||||
}
|
||||
|
||||
sub generate_command
|
||||
{
|
||||
my @command = (exe('git'), 'diff');
|
||||
my $skip_next = 0;
|
||||
my $idx = -1;
|
||||
my $prompt = '';
|
||||
for my $arg (@ARGV) {
|
||||
$idx++;
|
||||
if ($skip_next) {
|
||||
$skip_next = 0;
|
||||
next;
|
||||
}
|
||||
if ($arg eq '-t' || $arg eq '--tool') {
|
||||
usage() if $#ARGV <= $idx;
|
||||
$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
|
||||
$skip_next = 1;
|
||||
next;
|
||||
}
|
||||
if ($arg =~ /^--tool=/) {
|
||||
$ENV{GIT_DIFF_TOOL} = substr($arg, 7);
|
||||
next;
|
||||
}
|
||||
if ($arg eq '-x' || $arg eq '--extcmd') {
|
||||
usage() if $#ARGV <= $idx;
|
||||
$ENV{GIT_DIFFTOOL_EXTCMD} = $ARGV[$idx + 1];
|
||||
$skip_next = 1;
|
||||
next;
|
||||
}
|
||||
if ($arg =~ /^--extcmd=/) {
|
||||
$ENV{GIT_DIFFTOOL_EXTCMD} = substr($arg, 9);
|
||||
next;
|
||||
}
|
||||
if ($arg eq '-g' || $arg eq '--gui') {
|
||||
eval {
|
||||
my $tool = Git::command_oneline('config',
|
||||
'diff.guitool');
|
||||
if (length($tool)) {
|
||||
$ENV{GIT_DIFF_TOOL} = $tool;
|
||||
}
|
||||
};
|
||||
next;
|
||||
}
|
||||
if ($arg eq '-y' || $arg eq '--no-prompt') {
|
||||
$prompt = 'no';
|
||||
next;
|
||||
}
|
||||
if ($arg eq '--prompt') {
|
||||
$prompt = 'yes';
|
||||
next;
|
||||
}
|
||||
if ($arg eq '-h' || $arg eq '--help') {
|
||||
usage();
|
||||
}
|
||||
push @command, $arg;
|
||||
}
|
||||
if ($prompt eq 'yes') {
|
||||
$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
|
||||
} elsif ($prompt eq 'no') {
|
||||
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
|
||||
}
|
||||
return @command
|
||||
}
|
||||
|
||||
setup_environment();
|
||||
|
||||
# ActiveState Perl for Win32 does not implement POSIX semantics of
|
||||
# exec* system call. It just spawns the given executable and finishes
|
||||
# the starting program, exiting with code 0.
|
||||
# system will at least catch the errors returned by git diff,
|
||||
# allowing the caller of git difftool better handling of failures.
|
||||
my $rc = system(generate_command());
|
||||
exit($rc | ($rc >> 8));
|
72
SparkleShare/Mac/git/libexec/git-core/git-difftool--helper
Executable file
72
SparkleShare/Mac/git/libexec/git-core/git-difftool--helper
Executable file
|
@ -0,0 +1,72 @@
|
|||
#!/bin/sh
|
||||
# git-difftool--helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
|
||||
# This script is typically launched by using the 'git difftool'
|
||||
# convenience command.
|
||||
#
|
||||
# Copyright (c) 2009, 2010 David Aguilar
|
||||
|
||||
TOOL_MODE=diff
|
||||
. git-mergetool--lib
|
||||
|
||||
# difftool.prompt controls the default prompt/no-prompt behavior
|
||||
# and is overridden with $GIT_DIFFTOOL*_PROMPT.
|
||||
should_prompt () {
|
||||
prompt_merge=$(git config --bool mergetool.prompt || echo true)
|
||||
prompt=$(git config --bool difftool.prompt || echo $prompt_merge)
|
||||
if test "$prompt" = true; then
|
||||
test -z "$GIT_DIFFTOOL_NO_PROMPT"
|
||||
else
|
||||
test -n "$GIT_DIFFTOOL_PROMPT"
|
||||
fi
|
||||
}
|
||||
|
||||
# Indicates that --extcmd=... was specified
|
||||
use_ext_cmd () {
|
||||
test -n "$GIT_DIFFTOOL_EXTCMD"
|
||||
}
|
||||
|
||||
launch_merge_tool () {
|
||||
# Merged is the filename as it appears in the work tree
|
||||
# Local is the contents of a/filename
|
||||
# Remote is the contents of b/filename
|
||||
# Custom merge tool commands might use $BASE so we provide it
|
||||
MERGED="$1"
|
||||
LOCAL="$2"
|
||||
REMOTE="$3"
|
||||
BASE="$1"
|
||||
|
||||
# $LOCAL and $REMOTE are temporary files so prompt
|
||||
# the user with the real $MERGED name before launching $merge_tool.
|
||||
if should_prompt; then
|
||||
printf "\nViewing: '$MERGED'\n"
|
||||
if use_ext_cmd; then
|
||||
printf "Hit return to launch '%s': " \
|
||||
"$GIT_DIFFTOOL_EXTCMD"
|
||||
else
|
||||
printf "Hit return to launch '%s': " "$merge_tool"
|
||||
fi
|
||||
read ans
|
||||
fi
|
||||
|
||||
if use_ext_cmd; then
|
||||
export BASE
|
||||
eval $GIT_DIFFTOOL_EXTCMD '"$LOCAL"' '"$REMOTE"'
|
||||
else
|
||||
run_merge_tool "$merge_tool"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! use_ext_cmd; then
|
||||
if test -n "$GIT_DIFF_TOOL"; then
|
||||
merge_tool="$GIT_DIFF_TOOL"
|
||||
else
|
||||
merge_tool="$(get_merge_tool)" || exit
|
||||
fi
|
||||
fi
|
||||
|
||||
# Launch the merge tool on each path provided by 'git diff'
|
||||
while test $# -gt 6
|
||||
do
|
||||
launch_merge_tool "$1" "$2" "$5"
|
||||
shift 7
|
||||
done
|
1
SparkleShare/Mac/git/libexec/git-core/git-fast-export
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-fast-export
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
BIN
SparkleShare/Mac/git/libexec/git-core/git-fast-import
Executable file
BIN
SparkleShare/Mac/git/libexec/git-core/git-fast-import
Executable file
Binary file not shown.
1
SparkleShare/Mac/git/libexec/git-core/git-fetch
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-fetch
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-fetch-pack
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-fetch-pack
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
516
SparkleShare/Mac/git/libexec/git-core/git-filter-branch
Executable file
516
SparkleShare/Mac/git/libexec/git-core/git-filter-branch
Executable file
|
@ -0,0 +1,516 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Rewrite revision history
|
||||
# Copyright (c) Petr Baudis, 2006
|
||||
# Minimal changes to "port" it to core-git (c) Johannes Schindelin, 2007
|
||||
#
|
||||
# Lets you rewrite the revision history of the current branch, creating
|
||||
# a new branch. You can specify a number of filters to modify the commits,
|
||||
# files and trees.
|
||||
|
||||
# The following functions will also be available in the commit filter:
|
||||
|
||||
functions=$(cat << \EOF
|
||||
warn () {
|
||||
echo "$*" >&2
|
||||
}
|
||||
|
||||
map()
|
||||
{
|
||||
# if it was not rewritten, take the original
|
||||
if test -r "$workdir/../map/$1"
|
||||
then
|
||||
cat "$workdir/../map/$1"
|
||||
else
|
||||
echo "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# if you run 'skip_commit "$@"' in a commit filter, it will print
|
||||
# the (mapped) parents, effectively skipping the commit.
|
||||
|
||||
skip_commit()
|
||||
{
|
||||
shift;
|
||||
while [ -n "$1" ];
|
||||
do
|
||||
shift;
|
||||
map "$1";
|
||||
shift;
|
||||
done;
|
||||
}
|
||||
|
||||
# if you run 'git_commit_non_empty_tree "$@"' in a commit filter,
|
||||
# it will skip commits that leave the tree untouched, commit the other.
|
||||
git_commit_non_empty_tree()
|
||||
{
|
||||
if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
|
||||
map "$3"
|
||||
else
|
||||
git commit-tree "$@"
|
||||
fi
|
||||
}
|
||||
# override die(): this version puts in an extra line break, so that
|
||||
# the progress is still visible
|
||||
|
||||
die()
|
||||
{
|
||||
echo >&2
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
eval "$functions"
|
||||
|
||||
# When piped a commit, output a script to set the ident of either
|
||||
# "author" or "committer
|
||||
|
||||
set_ident () {
|
||||
lid="$(echo "$1" | tr "[A-Z]" "[a-z]")"
|
||||
uid="$(echo "$1" | tr "[a-z]" "[A-Z]")"
|
||||
pick_id_script='
|
||||
/^'$lid' /{
|
||||
s/'\''/'\''\\'\'\''/g
|
||||
h
|
||||
s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p
|
||||
|
||||
g
|
||||
s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
|
||||
|
||||
g
|
||||
s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
|
||||
|
||||
q
|
||||
}
|
||||
'
|
||||
|
||||
LANG=C LC_ALL=C sed -ne "$pick_id_script"
|
||||
# Ensure non-empty id name.
|
||||
echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
|
||||
}
|
||||
|
||||
USAGE="[--env-filter <command>] [--tree-filter <command>]
|
||||
[--index-filter <command>] [--parent-filter <command>]
|
||||
[--msg-filter <command>] [--commit-filter <command>]
|
||||
[--tag-name-filter <command>] [--subdirectory-filter <directory>]
|
||||
[--original <namespace>] [-d <directory>] [-f | --force]
|
||||
[<rev-list options>...]"
|
||||
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
|
||||
if [ "$(is_bare_repository)" = false ]; then
|
||||
git diff-files --ignore-submodules --quiet &&
|
||||
git diff-index --cached --quiet HEAD -- ||
|
||||
die "Cannot rewrite branch(es) with a dirty working directory."
|
||||
fi
|
||||
|
||||
tempdir=.git-rewrite
|
||||
filter_env=
|
||||
filter_tree=
|
||||
filter_index=
|
||||
filter_parent=
|
||||
filter_msg=cat
|
||||
filter_commit=
|
||||
filter_tag_name=
|
||||
filter_subdir=
|
||||
orig_namespace=refs/original/
|
||||
force=
|
||||
prune_empty=
|
||||
remap_to_ancestor=
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
--force|-f)
|
||||
shift
|
||||
force=t
|
||||
continue
|
||||
;;
|
||||
--remap-to-ancestor)
|
||||
# deprecated ($remap_to_ancestor is set now automatically)
|
||||
shift
|
||||
remap_to_ancestor=t
|
||||
continue
|
||||
;;
|
||||
--prune-empty)
|
||||
shift
|
||||
prune_empty=t
|
||||
continue
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break;
|
||||
esac
|
||||
|
||||
# all switches take one argument
|
||||
ARG="$1"
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
OPTARG="$1"
|
||||
shift
|
||||
|
||||
case "$ARG" in
|
||||
-d)
|
||||
tempdir="$OPTARG"
|
||||
;;
|
||||
--env-filter)
|
||||
filter_env="$OPTARG"
|
||||
;;
|
||||
--tree-filter)
|
||||
filter_tree="$OPTARG"
|
||||
;;
|
||||
--index-filter)
|
||||
filter_index="$OPTARG"
|
||||
;;
|
||||
--parent-filter)
|
||||
filter_parent="$OPTARG"
|
||||
;;
|
||||
--msg-filter)
|
||||
filter_msg="$OPTARG"
|
||||
;;
|
||||
--commit-filter)
|
||||
filter_commit="$functions; $OPTARG"
|
||||
;;
|
||||
--tag-name-filter)
|
||||
filter_tag_name="$OPTARG"
|
||||
;;
|
||||
--subdirectory-filter)
|
||||
filter_subdir="$OPTARG"
|
||||
remap_to_ancestor=t
|
||||
;;
|
||||
--original)
|
||||
orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$prune_empty,$filter_commit" in
|
||||
,)
|
||||
filter_commit='git commit-tree "$@"';;
|
||||
t,)
|
||||
filter_commit="$functions;"' git_commit_non_empty_tree "$@"';;
|
||||
,*)
|
||||
;;
|
||||
*)
|
||||
die "Cannot set --prune-empty and --commit-filter at the same time"
|
||||
esac
|
||||
|
||||
case "$force" in
|
||||
t)
|
||||
rm -rf "$tempdir"
|
||||
;;
|
||||
'')
|
||||
test -d "$tempdir" &&
|
||||
die "$tempdir already exists, please remove it"
|
||||
esac
|
||||
mkdir -p "$tempdir/t" &&
|
||||
tempdir="$(cd "$tempdir"; pwd)" &&
|
||||
cd "$tempdir/t" &&
|
||||
workdir="$(pwd)" ||
|
||||
die ""
|
||||
|
||||
# Remove tempdir on exit
|
||||
trap 'cd ../..; rm -rf "$tempdir"' 0
|
||||
|
||||
ORIG_GIT_DIR="$GIT_DIR"
|
||||
ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
|
||||
ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
|
||||
GIT_WORK_TREE=.
|
||||
export GIT_DIR GIT_WORK_TREE
|
||||
|
||||
# Make sure refs/original is empty
|
||||
git for-each-ref > "$tempdir"/backup-refs || exit
|
||||
while read sha1 type name
|
||||
do
|
||||
case "$force,$name" in
|
||||
,$orig_namespace*)
|
||||
die "Cannot create a new backup.
|
||||
A previous backup already exists in $orig_namespace
|
||||
Force overwriting the backup with -f"
|
||||
;;
|
||||
t,$orig_namespace*)
|
||||
git update-ref -d "$name" $sha1
|
||||
;;
|
||||
esac
|
||||
done < "$tempdir"/backup-refs
|
||||
|
||||
# The refs should be updated if their heads were rewritten
|
||||
git rev-parse --no-flags --revs-only --symbolic-full-name \
|
||||
--default HEAD "$@" > "$tempdir"/raw-heads || exit
|
||||
sed -e '/^^/d' "$tempdir"/raw-heads >"$tempdir"/heads
|
||||
|
||||
test -s "$tempdir"/heads ||
|
||||
die "Which ref do you want to rewrite?"
|
||||
|
||||
GIT_INDEX_FILE="$(pwd)/../index"
|
||||
export GIT_INDEX_FILE
|
||||
|
||||
# map old->new commit ids for rewriting parents
|
||||
mkdir ../map || die "Could not create map/ directory"
|
||||
|
||||
# we need "--" only if there are no path arguments in $@
|
||||
nonrevs=$(git rev-parse --no-revs "$@") || exit
|
||||
if test -z "$nonrevs"
|
||||
then
|
||||
dashdash=--
|
||||
else
|
||||
dashdash=
|
||||
remap_to_ancestor=t
|
||||
fi
|
||||
|
||||
rev_args=$(git rev-parse --revs-only "$@")
|
||||
|
||||
case "$filter_subdir" in
|
||||
"")
|
||||
eval set -- "$(git rev-parse --sq --no-revs "$@")"
|
||||
;;
|
||||
*)
|
||||
eval set -- "$(git rev-parse --sq --no-revs "$@" $dashdash \
|
||||
"$filter_subdir")"
|
||||
;;
|
||||
esac
|
||||
|
||||
git rev-list --reverse --topo-order --default HEAD \
|
||||
--parents --simplify-merges $rev_args "$@" > ../revs ||
|
||||
die "Could not get the commits"
|
||||
commits=$(wc -l <../revs | tr -d " ")
|
||||
|
||||
test $commits -eq 0 && die "Found nothing to rewrite"
|
||||
|
||||
# Rewrite the commits
|
||||
|
||||
git_filter_branch__commit_count=0
|
||||
while read commit parents; do
|
||||
git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
|
||||
printf "\rRewrite $commit ($git_filter_branch__commit_count/$commits)"
|
||||
|
||||
case "$filter_subdir" in
|
||||
"")
|
||||
git read-tree -i -m $commit
|
||||
;;
|
||||
*)
|
||||
# The commit may not have the subdirectory at all
|
||||
err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
|
||||
if ! git rev-parse -q --verify $commit:"$filter_subdir"
|
||||
then
|
||||
rm -f "$GIT_INDEX_FILE"
|
||||
else
|
||||
echo >&2 "$err"
|
||||
false
|
||||
fi
|
||||
}
|
||||
esac || die "Could not initialize the index"
|
||||
|
||||
GIT_COMMIT=$commit
|
||||
export GIT_COMMIT
|
||||
git cat-file commit "$commit" >../commit ||
|
||||
die "Cannot read commit $commit"
|
||||
|
||||
eval "$(set_ident AUTHOR <../commit)" ||
|
||||
die "setting author failed for commit $commit"
|
||||
eval "$(set_ident COMMITTER <../commit)" ||
|
||||
die "setting committer failed for commit $commit"
|
||||
eval "$filter_env" < /dev/null ||
|
||||
die "env filter failed: $filter_env"
|
||||
|
||||
if [ "$filter_tree" ]; then
|
||||
git checkout-index -f -u -a ||
|
||||
die "Could not checkout the index"
|
||||
# files that $commit removed are now still in the working tree;
|
||||
# remove them, else they would be added again
|
||||
git clean -d -q -f -x
|
||||
eval "$filter_tree" < /dev/null ||
|
||||
die "tree filter failed: $filter_tree"
|
||||
|
||||
(
|
||||
git diff-index -r --name-only --ignore-submodules $commit &&
|
||||
git ls-files --others
|
||||
) > "$tempdir"/tree-state || exit
|
||||
git update-index --add --replace --remove --stdin \
|
||||
< "$tempdir"/tree-state || exit
|
||||
fi
|
||||
|
||||
eval "$filter_index" < /dev/null ||
|
||||
die "index filter failed: $filter_index"
|
||||
|
||||
parentstr=
|
||||
for parent in $parents; do
|
||||
for reparent in $(map "$parent"); do
|
||||
parentstr="$parentstr -p $reparent"
|
||||
done
|
||||
done
|
||||
if [ "$filter_parent" ]; then
|
||||
parentstr="$(echo "$parentstr" | eval "$filter_parent")" ||
|
||||
die "parent filter failed: $filter_parent"
|
||||
fi
|
||||
|
||||
sed -e '1,/^$/d' <../commit | \
|
||||
eval "$filter_msg" > ../message ||
|
||||
die "msg filter failed: $filter_msg"
|
||||
/bin/sh -c "$filter_commit" "git commit-tree" \
|
||||
$(git write-tree) $parentstr < ../message > ../map/$commit ||
|
||||
die "could not write rewritten commit"
|
||||
done <../revs
|
||||
|
||||
# If we are filtering for paths, as in the case of a subdirectory
|
||||
# filter, it is possible that a specified head is not in the set of
|
||||
# rewritten commits, because it was pruned by the revision walker.
|
||||
# Ancestor remapping fixes this by mapping these heads to the unique
|
||||
# nearest ancestor that survived the pruning.
|
||||
|
||||
if test "$remap_to_ancestor" = t
|
||||
then
|
||||
while read ref
|
||||
do
|
||||
sha1=$(git rev-parse "$ref"^0)
|
||||
test -f "$workdir"/../map/$sha1 && continue
|
||||
ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@")
|
||||
test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
|
||||
done < "$tempdir"/heads
|
||||
fi
|
||||
|
||||
# Finally update the refs
|
||||
|
||||
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
|
||||
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
|
||||
echo
|
||||
while read ref
|
||||
do
|
||||
# avoid rewriting a ref twice
|
||||
test -f "$orig_namespace$ref" && continue
|
||||
|
||||
sha1=$(git rev-parse "$ref"^0)
|
||||
rewritten=$(map $sha1)
|
||||
|
||||
test $sha1 = "$rewritten" &&
|
||||
warn "WARNING: Ref '$ref' is unchanged" &&
|
||||
continue
|
||||
|
||||
case "$rewritten" in
|
||||
'')
|
||||
echo "Ref '$ref' was deleted"
|
||||
git update-ref -m "filter-branch: delete" -d "$ref" $sha1 ||
|
||||
die "Could not delete $ref"
|
||||
;;
|
||||
$_x40)
|
||||
echo "Ref '$ref' was rewritten"
|
||||
if ! git update-ref -m "filter-branch: rewrite" \
|
||||
"$ref" $rewritten $sha1 2>/dev/null; then
|
||||
if test $(git cat-file -t "$ref") = tag; then
|
||||
if test -z "$filter_tag_name"; then
|
||||
warn "WARNING: You said to rewrite tagged commits, but not the corresponding tag."
|
||||
warn "WARNING: Perhaps use '--tag-name-filter cat' to rewrite the tag."
|
||||
fi
|
||||
else
|
||||
die "Could not rewrite $ref"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# NEEDSWORK: possibly add -Werror, making this an error
|
||||
warn "WARNING: '$ref' was rewritten into multiple commits:"
|
||||
warn "$rewritten"
|
||||
warn "WARNING: Ref '$ref' points to the first one now."
|
||||
rewritten=$(echo "$rewritten" | head -n 1)
|
||||
git update-ref -m "filter-branch: rewrite to first" \
|
||||
"$ref" $rewritten $sha1 ||
|
||||
die "Could not rewrite $ref"
|
||||
;;
|
||||
esac
|
||||
git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 ||
|
||||
exit
|
||||
done < "$tempdir"/heads
|
||||
|
||||
# TODO: This should possibly go, with the semantics that all positive given
|
||||
# refs are updated, and their original heads stored in refs/original/
|
||||
# Filter tags
|
||||
|
||||
if [ "$filter_tag_name" ]; then
|
||||
git for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags |
|
||||
while read sha1 type ref; do
|
||||
ref="${ref#refs/tags/}"
|
||||
# XXX: Rewrite tagged trees as well?
|
||||
if [ "$type" != "commit" -a "$type" != "tag" ]; then
|
||||
continue;
|
||||
fi
|
||||
|
||||
if [ "$type" = "tag" ]; then
|
||||
# Dereference to a commit
|
||||
sha1t="$sha1"
|
||||
sha1="$(git rev-parse -q "$sha1"^{commit})" || continue
|
||||
fi
|
||||
|
||||
[ -f "../map/$sha1" ] || continue
|
||||
new_sha1="$(cat "../map/$sha1")"
|
||||
GIT_COMMIT="$sha1"
|
||||
export GIT_COMMIT
|
||||
new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
|
||||
die "tag name filter failed: $filter_tag_name"
|
||||
|
||||
echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
|
||||
|
||||
if [ "$type" = "tag" ]; then
|
||||
new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
|
||||
"$new_sha1" "$new_ref"
|
||||
git cat-file tag "$ref" |
|
||||
sed -n \
|
||||
-e '1,/^$/{
|
||||
/^object /d
|
||||
/^type /d
|
||||
/^tag /d
|
||||
}' \
|
||||
-e '/^-----BEGIN PGP SIGNATURE-----/q' \
|
||||
-e 'p' ) |
|
||||
git mktag) ||
|
||||
die "Could not create new tag object for $ref"
|
||||
if git cat-file tag "$ref" | \
|
||||
sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
|
||||
then
|
||||
warn "gpg signature stripped from tag object $sha1t"
|
||||
fi
|
||||
fi
|
||||
|
||||
git update-ref "refs/tags/$new_ref" "$new_sha1" ||
|
||||
die "Could not write tag $new_ref"
|
||||
done
|
||||
fi
|
||||
|
||||
cd ../..
|
||||
rm -rf "$tempdir"
|
||||
|
||||
trap - 0
|
||||
|
||||
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
|
||||
test -z "$ORIG_GIT_DIR" || {
|
||||
GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
|
||||
}
|
||||
test -z "$ORIG_GIT_WORK_TREE" || {
|
||||
GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
|
||||
export GIT_WORK_TREE
|
||||
}
|
||||
test -z "$ORIG_GIT_INDEX_FILE" || {
|
||||
GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
|
||||
export GIT_INDEX_FILE
|
||||
}
|
||||
|
||||
if [ "$(is_bare_repository)" = false ]; then
|
||||
git read-tree -u -m HEAD || exit
|
||||
fi
|
||||
|
||||
exit 0
|
1
SparkleShare/Mac/git/libexec/git-core/git-fmt-merge-msg
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-fmt-merge-msg
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-for-each-ref
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-for-each-ref
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-format-patch
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-format-patch
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-fsck
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-fsck
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-fsck-objects
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-fsck-objects
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-gc
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-gc
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-get-tar-commit-id
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-get-tar-commit-id
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-grep
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-grep
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
8
SparkleShare/Mac/git/libexec/git-core/git-gui
Executable file
8
SparkleShare/Mac/git/libexec/git-core/git-gui
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
if test "z$*" = zversion ||
|
||||
test "z$*" = z--version
|
||||
then
|
||||
echo 'git-gui version 0.13.0.8.g8f85'
|
||||
else
|
||||
exec '/usr/local/git/share/git-gui/lib/Git Gui.app/Contents/MacOS/Wish' "$0" "$@"
|
||||
fi
|
66
SparkleShare/Mac/git/libexec/git-core/git-gui--askpass
Executable file
66
SparkleShare/Mac/git/libexec/git-core/git-gui--askpass
Executable file
|
@ -0,0 +1,66 @@
|
|||
#!/bin/sh
|
||||
# Tcl ignores the next line -*- tcl -*- \
|
||||
exec wish "$0" -- "$@"
|
||||
|
||||
# This is a trivial implementation of an SSH_ASKPASS handler.
|
||||
# Git-gui uses this script if none are already configured.
|
||||
|
||||
package require Tk
|
||||
|
||||
set answer {}
|
||||
set yesno 0
|
||||
set rc 255
|
||||
|
||||
if {$argc < 1} {
|
||||
set prompt "Enter your OpenSSH passphrase:"
|
||||
} else {
|
||||
set prompt [join $argv " "]
|
||||
if {[regexp -nocase {\(yes\/no\)\?\s*$} $prompt]} {
|
||||
set yesno 1
|
||||
}
|
||||
}
|
||||
|
||||
message .m -text $prompt -justify center -aspect 4000
|
||||
pack .m -side top -fill x -padx 20 -pady 20 -expand 1
|
||||
|
||||
entry .e -textvariable answer -width 50
|
||||
pack .e -side top -fill x -padx 10 -pady 10
|
||||
|
||||
if {!$yesno} {
|
||||
.e configure -show "*"
|
||||
}
|
||||
|
||||
frame .b
|
||||
button .b.ok -text OK -command finish
|
||||
button .b.cancel -text Cancel -command cancel
|
||||
|
||||
pack .b.ok -side left -expand 1
|
||||
pack .b.cancel -side right -expand 1
|
||||
pack .b -side bottom -fill x -padx 10 -pady 10
|
||||
|
||||
bind . <Visibility> {focus -force .e}
|
||||
bind . <Key-Return> [list .b.ok invoke]
|
||||
bind . <Key-Escape> [list .b.cancel invoke]
|
||||
bind . <Destroy> {set rc $rc}
|
||||
|
||||
proc cancel {} {
|
||||
set ::rc 255
|
||||
}
|
||||
|
||||
proc finish {} {
|
||||
if {$::yesno} {
|
||||
if {$::answer ne "yes" && $::answer ne "no"} {
|
||||
tk_messageBox -icon error -title "Error" -type ok \
|
||||
-message "Only 'yes' or 'no' input allowed."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
puts $::answer
|
||||
set ::rc 0
|
||||
}
|
||||
|
||||
wm title . "OpenSSH"
|
||||
tk::PlaceWindow .
|
||||
vwait rc
|
||||
exit $rc
|
1
SparkleShare/Mac/git/libexec/git-core/git-hash-object
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-hash-object
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
1
SparkleShare/Mac/git/libexec/git-core/git-help
Symbolic link
1
SparkleShare/Mac/git/libexec/git-core/git-help
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../bin/git
|
BIN
SparkleShare/Mac/git/libexec/git-core/git-http-backend
Executable file
BIN
SparkleShare/Mac/git/libexec/git-core/git-http-backend
Executable file
Binary file not shown.
BIN
SparkleShare/Mac/git/libexec/git-core/git-http-fetch
Executable file
BIN
SparkleShare/Mac/git/libexec/git-core/git-http-fetch
Executable file
Binary file not shown.
BIN
SparkleShare/Mac/git/libexec/git-core/git-http-push
Executable file
BIN
SparkleShare/Mac/git/libexec/git-core/git-http-push
Executable file
Binary file not shown.
BIN
SparkleShare/Mac/git/libexec/git-core/git-imap-send
Executable file
BIN
SparkleShare/Mac/git/libexec/git-core/git-imap-send
Executable file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue