Compare commits

...
77 Commits
Author SHA1 Message Date
David Aguilar e2657c59ad git-difftool: update difftool documentation to include some packaging notes
difftool is now available in cola.git and git.git.
Mention this and recommend usage of the version from git.git.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-19 19:09:46 -08:00
David Aguilar c6b12a1442 difftool: change trap condition from SIGINT to INT
Markus Heidelberg provided this patch against git.git's
contrib/difftool.

In 'man 1p trap' there is written:

   "The condition can be EXIT, 0 (equivalent to EXIT), or a signal
    specified using a symbolic name, without the SIG prefix, [...]"

   "Implementations may permit names with the SIG prefix or ignore case
    in signal names as an extension."

So now we do it the POSIX compliant way instead of using an extension.

Signed-off-by: Markus Heidelberg <markus.heidelberg@web.de>
Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-19 19:07:02 -08:00
David Aguilar fc805e5dd3 git-difftool: incorporate suggestions from the git mailing list
Markus Heidelberg has a number of stylistic improvements to the
difftool documentation and comments.  This also adds usage of the
vim "wincmd" command for positioning the cursor at startup.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-18 17:07:22 -08:00
David Aguilar e7a80fee43 windows: make clone and open work reliably on windows
os.spawnv is a nasty beast in windows-land.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-18 03:10:41 -08:00
David Aguilar 44ed8ab3bf win32/cola: add Python's path to $PATH
This lets us easily launch python scripts.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-18 03:10:41 -08:00
David Aguilar c5b0ecd991 cola: use the new with_stderr flag throughout cola
We now grab stderr only when we really need to do so.
Places that care about stderr but not about the return status
no longer need to grab it into a throw-away variable.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-17 02:50:41 -08:00
David Aguilar 5d437b618b git: add a with_stderr option to control whether or not we get stderr
The git command module will only include stderr when the with_stderr
option is set to True.  This separates with_extended_output and with_stderr
so that they do not have overlapping duties.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-17 02:50:41 -08:00
David Aguilar 9a1f2a7fa6 models: simplify the model worktree status routine
We no longer post-process the status data to remove unmerged
items from the modified files list -- this is done up front
when unmerged entries are first found.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-17 02:44:32 -08:00
David Aguilar 73ecfad626 tests: skip serialization tests when simplejson is not available
Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-16 21:05:13 -08:00
David Aguilar dc7d7b0edf model: simplify the deserializer by using __import__()
We were using the 'imp' module to for loading modules.
Using __import__() is simpler and more robust.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-16 21:04:57 -08:00
David Aguilar 166b38543b git: change with_extended_output to only return status, output
The stderr+stdout handling in the git module was broken because it
could deadlock on older versions of Python.  All of the complication
came from trying to handle both stderr and stdout in the same
function.  We now pass stderr=subprocess.STDOUT to subprocess.Popen
so that things become much simpler.  We only include stderr when
with_extended_output=True, otherwise we ignore it.

This fixes deadlocks on older Pythons and on Mac OSX.
It also seems to speed things up a bit on Linux, which is a plus.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-16 18:13:33 -08:00
David Aguilar d1d3b93da3 tests: simplify the import test by using __import__()
Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-16 18:13:30 -08:00
David Aguilar 369ae9fbc3 inotify: add support for pyinotify 0.8.x
The pyinotify API changed between 0.7.x and 0.8.x.
The cola.inotify module now works with both 0.8.x and as 0.7.x.

We do accomplish this by checking pyinotify.__version__ at runtime
to determine which API to use.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-15 21:51:31 -08:00
David Aguilar 761ede3ce3 git-difftool: simplify the windows exe() handling
Windows doesn't need the full path to git.exe.
We just need to exec "git.exe" instead of "git".

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-15 21:01:12 -08:00
David Aguilar 37a01c1372 win32: update the 'cola' script so that it passes "$@"
We were losing command-line args when run through the 'cola'
windows wrapper script.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-15 20:50:40 -08:00
David Aguilar 273be07870 git-difftool: add portability code for Windows
git-difftool now works on Windows.
The GIT_EXTERNAL_DIFF rewrite broke Windows.
This makes things happy again.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-15 20:50:12 -08:00
David Aguilar 789cdd388f git: fix a deadlock on python2.4
Python 2.4's subprocess.Popen requires that you read from stderr before
reading stdout.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-15 11:34:06 -08:00
David Aguilar 540bb69c65 ui: add a 'Help->About' menu action
The 'About' dialog lists the cola license and version.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-09 02:40:39 -08:00
David Aguilar 35e9fb7e8f Makefile: handle the case where the 'cola' symlink already exists
We should ignore the 'cola' symlink if it already exists.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-09 02:28:14 -08:00
David Aguilar 2459017e15 docs: update the 'git difftool' documentation
'git difftool' is much more powerful (and simpler) now,
so update the documentation accordingly.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-08 22:06:28 -08:00
David Aguilar 8cfbdc740a search: show author and date in the summary line
The default 'git log' format was updated so that the author
and date are part of the summary format.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-07 02:45:42 -08:00
David Aguilar 3c3a08081b git-difftool: simplify the code slightly
Some unnecessary temporary variables and control blocks
were removed.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-07 00:58:41 -08:00
David Aguilar b272078cf6 ui: clean up the options dialog
Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-07 00:58:41 -08:00
David Aguilar 80f22bf71f options: add a cola.tabwidth configuration field
Users can now specify their own tab width in the options dialog.
The setting corresponds to the cola.tabwidth git configuration variable.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-06 01:32:39 -08:00
David Aguilar 30a6c67d05 stash: use --index when applying stashes
git stash apply --index preserves the index so we should use it.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-06 01:09:05 -08:00
David Aguilar ae43deba34 ui: add a stash button to the main ui
Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-05 22:40:22 -08:00
David Aguilar bd0f10bd4e usability: reinstate the rescan button
This brings back the old rescan button.  We hide the button whenever
inotify is available since rescan is automatic on linux.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-05 22:35:37 -08:00
David Aguilar 95dec436a8 doc: update git links to point to github
Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-05 19:31:44 -08:00
David Aguilar aed58632e7 git-difftool: take advantage of GIT_EXTERNAL_DIFF
By using GIT_EXTERNAL_DIFF we can make git-difftool support any
option supported by 'git diff'.  This makes git-difftool much more
useful since it can now handle any type of diff,
e.g. worktree vs. index, worktree vs. commit, commit vs. commit, etc.

'git-difftool-helper' is invoked by git and handles all of the
git configuration and merge tool finding logic.

Signed-off-by: David Aguilar <davvid@gmail.com>
2009-01-02 17:20:56 -08:00
David Aguilar 9cde7c554b git-cola: add 'prefix' to the sys.path
We lost the ability to launch git-cola from its source tree when we
fixed things up for Windows.  It would work if you were sitting in
the cola source tree, but not if your current directory was somewhere
else.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-30 14:40:35 -08:00
David Aguilar 7715361133 .mailmap: add /me to the mailmap
I had a bad user.name configuration at one point in time, so add
a .mailmap entry to fix it.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-29 15:51:58 -08:00
David Aguilar 38be16d8bb win32: vastly simplify the installation procedure for Windows
We now automatically install the bin/cola win32 wrapper script.
This is now the only recommended way to run cola.  Installing
cola on Windows is now no more difficult then unzipping a tarball
and running the 'cola' shell script.

The 'cola' wrapper script is now smarter in that it checks for
both Python2.5 and Python2.6, thus cola can now run with either
version.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-29 15:03:27 -08:00
David Aguilar 43d9101681 win32: make gitk launch properly on win32
Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-29 01:26:09 -08:00
David Aguilar 52a0d1fd07 views: remove the log icon and its toolbar
It doesn't make sense to have a toolbar until we have more buttons
in there.  The 'View Log' functionality was moved to the 'File' menu.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 20:05:16 -08:00
David Aguilar 5735f747f7 ui: re-save all .ui files using Qt 4.4.2
This updates all of the property definitions to the latest
schema.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 19:30:23 -08:00
David Aguilar 44a508004d views: remove exec_ calls so that all dialogs are non-modal
Calling exec_ makes it so that the parent window cannot be
raised above the child window.  This removes all exec()
calls to prevent this.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 19:23:48 -08:00
David Aguilar 5cbaed0f64 search: make the dialog slightly shorter by default
This saves some screen real estate.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 19:05:59 -08:00
David Aguilar 9bf49779f2 views: move cola.syntax into the views namespace
cola.syntax was moved the cola.views.syntax, since it makes more
sense there.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 18:16:48 -08:00
David Aguilar 9448492a74 search: pass the parent view to the search dialog
We now pass the parent view to the search dialog so that it can
properly center its window on the screen.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 18:16:48 -08:00
David Aguilar a542351b8d views: make all dialogs maximizable
Windows requires that we use setWindowFlags on all dialogs
otherwise Qt drops the maximize button.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 18:16:36 -08:00
David Aguilar 62a7ab1be1 Add a .mailmap file
This fixes a commit where I messed up David's name.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 16:19:09 -08:00
David Aguilar be70329b11 setup.py: work around the pyuic4 setSortingEnabled bug
We work around the bug by suppressing all lines that mention
'sortingenabled' in the generated .py files.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 01:29:30 -08:00
David Aguilar 8a1ecb8743 INSTALL: remove references to the old pyuic4 shebang issue
The previous commit works around pyuic4 installations that are
missing shebangs, so we shouldn't mention it in INSTALL anymore.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 00:16:08 -08:00
David Aguilar 8d8922f52b setup.py: guard against pyuic4 that are missing #!/bin/sh
Some installations of pyqt4 have a copy of pyuic4 that's
missing the shebang line.  Guard against this by using
popen() instead.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-26 00:14:04 -08:00
David Aguilar 88ad1302c7 git-difftool: prepend $(git --exec-path) to $PATH
git-sh-setup is no longer in $PATH as of git 1.6.0.
Prepend PATH with git-sh-setup so that git-difftool
can use git-sh-setup.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-25 17:20:46 -08:00
David Aguilar fd3a43bef1 remote: add a rebase checkbox to the pull dialog
The pull dialog now has a 'Rebase' checkbox that tells cola
to pass the --rebase option to pull.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-25 14:05:12 -08:00
David Aguilar 90e23ce2fd INSTALL: fix a typo in the windows installation section
Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-25 05:03:08 -08:00
David Aguilar 250282933a win32: add a win32/ folder for storing all of the win32 hacks
Building cola for windows involves a little bit of voodoo.
We now keep all of the windows-specific scripts and such
in the win32/ folder to help make living in windows-land
a little easier.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-25 04:30:42 -08:00
David Aguilar 27a7e19a1e INSTALL: remove references to the now unused 'file' command
We no longer mention the 'file' command now that
utils.ident_file_type() uses only mimetypes and filenames
when guessing file types.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-24 13:43:16 -08:00
Barry Roberts f18b101db0 Speed up file identification
This is for all platforms and is probably more flexible

Signed-off-by: Barry Roberts <blr@robertsr.us>
2008-12-24 13:40:08 -08:00
David Aguilar 88e9431a62 rebase: list all branches in the branch chooser, not just local ones
The rebase dialog only listed local branches.  It now lists all
branches, including remote ones.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-22 08:29:12 -08:00
David Aguilar 3ed716233b models: call 'git update-index --refresh' in get_workdir_state()
There was an earlier version of git that would report files as
being modified when only their modification times changed.
This causes cola to report false-positives in its modified
files list.  We now guard against this by refreshing the index
in cola's get_workdir_state() method.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-22 08:12:22 -08:00
David Aguilar 09b4384705 Use 'utf-8' instead of 'utf8' in core.encode()
Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-19 10:15:44 -08:00
David Aguilar f31d7bd3b9 compare: fix an edge case when decreasing the number of log results
When changing the number of results in the compare dialog down
to a number less than the index of the currently selected
filename then it would generate a traceback.  We now detect
this condition and behave accordingly.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-14 14:26:25 -08:00
David Aguilar ddb19ffee5 options dialog: fix some annoyances with the font handling
The font handling in the options dialog had some small
annoyances that have now been fixed.  The dialog should now
show instantaneous results and properly react to all user
input.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-14 14:23:50 -08:00
David Aguilar 95bd9b73d7 controllers: fix a typo when restoring the scrollbar values on rescan
Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-12 11:38:35 -08:00
Barry Roberts cffa5f4894 Context menu items for Commits list
This adds checkout, cherry-pick, and create branch
to the commit list context menu.

Signed-off-by: Barry Roberts <blr@robertsr.us>
2008-12-11 19:07:52 -08:00
David Aguilar 117aa32f12 cola: pass --no-color to 'git diff' to avoid receiving ANSI sequences
If someone has a git config such as:

[color]
	ui = on

Then git might ignore whether a TTY is attached when printing diffs.
We now guard against this by passing --no-color to all 'git diff'
invocations.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-11 02:59:22 -08:00
David Aguilar 4c218fe4c1 diff gui: properly restore the scroll bar values on update
We now properly restore the vertical scrollbar values when interactively
staging content for commit.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-10 02:56:12 -08:00
David Aguilar e2efc30125 views: fix obsolete references to the old dockwidgets
The main view needed to be updated to reflect the changes to the
main.ui interface file.  This fixes a bug in the stash view.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-09 22:26:31 -08:00
Barry Roberts aecfd3dbd9 Fix stash typo
Signed-off-by: Barry Roberts <blr@robertsr.us>
2008-12-09 22:18:08 -08:00
David Aguilar 2612b3a3d9 compare: reinstate the Commit -> Compare File... menu action
We recently lost the ability to compare changes to just a single file
across commits.  This has been reinstated.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-09 16:33:26 -08:00
David Aguilar dda2f83805 i18n: fix a few more places where we weren't unicode-safe
The search dialog and the commit browser now properly decode
git's output before handing it off to Qt.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-07 22:02:38 -08:00
David Aguilar c8c4838f48 qobserver: fix a bug where we would respond to redundant notifications
When setting a text field Qt triggers a callback.  We catch this callback
and update our model accordingly.  When the model updates, it triggers
another callback which causes a feedback loop that eventually resolves
with the text field getting set twice.  This is problematic since
it makes entering text problematic.

We now detect this situation and stop the notification loop accordingly.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-04 23:13:36 -08:00
David Aguilar 1e35239366 win32: Updated INSTALL to mention dirname.exe
The INSTALL file now points users to the cola website for file.exe
and dirname.exe.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-04 01:52:41 -08:00
David Aguilar 2e52b5735d models: guard against broken git configs
Users could enter erroneous settings in their .git/config.

For example:

[merge.tool]
	meld

git config --list returns that entry without an "=" sign, which
was crashing cola.  We now ignore these entries.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-04 00:57:31 -08:00
David Aguilar 07bd56e25c create branch: fix gui bugs when entering branch names by hand
We were resetting the user input whenever it changed, so we now
only reset it when it was changed as a result of clicking on a
remote branch.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-04 00:47:12 -08:00
David Aguilar 28f2099bc2 clone: handle massaging .git strings in URLs when guessing the clone dir
This improves the cola behavior when cloning foo/.git and foo.git URLs.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-04 00:13:52 -08:00
David Aguilar a8ce54473c clone: assume that git url = the new destination directory
Previously we used QFileDialog.getSaveFileName() and restricted
users from selecting existing directories.

We now use getExistingDirectory and assume that the destionation
directory should just be the basename of the git url.

We could prompt again, but that would be annoying.  Unfortunately
Qt doesn't have a convenient dialog to allow both existing
directories and new directory names (most of the world is centered
around files, it seems, unlike git which is more dir tree-ish).

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-04 00:02:05 -08:00
David Aguilar 3631deb20c controllers: use the --repo option in menu_clone
Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-03 22:39:54 -08:00
David Aguilar 854e1cb81d opendialog: upddate to use the --repo option
oops, 605a2ba7 changed the option parsing but forgot to update the open dialog
which actually uses it.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-12-03 22:11:22 -08:00
David Aguilar 7a49d47d86 interactive staging: workaround a qt quirk where \r\n is lost in qtextedits
qtextedits unconditionally eat \r\n when returning the selection.
This breaks the interactive staging on files that contain \r\n.
If we're not able to find the selection in the diff then we know
that this has happened so simply replace \n with \r\n and try
again.

This fixes interactive staging on repositories that contain
windows/dos text files.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-11-28 04:43:46 -08:00
David Marti­nez MartÃi dd8cd024e1 search: fix bug that blocks searching
When trying to search something, git-cola complains:
 > AttributeError: Parameter 'commit_list' not available for Model

Attributes for class "Model" must not be null to be detected as a parameter.
Model.get_param_names has the related code to check if it is a parameter
or not.

So, the solution here is initialize model.commit_list with an empty array,
because it is similar to the data used there.

Signed-off-by: David Martínez Martí <deavidsedice@gmail.com>
2008-11-21 00:02:26 -08:00
David Aguilar 6d8bbd16bd models: properly detect merge conflicts with deleted files
When a file is deleted locally and changed on the remote side diff-index
reports the state as 'A' (added) since it's going to add it during the merge.
We now detect this and list it as an unmerged file.

Signed-off-by: David Aguilar <davvid@gmail.com>
2008-11-20 12:36:58 -08:00
David Aguilar 0fe0bd3828 inotify: fix a bug introduced when removing the defaults module
Signed-off-by: David Aguilar <davvid@gmail.com>
2008-11-20 02:56:59 -08:00
David Aguilar 6d04408ecc INSTALL: fix a typo in the pyuic4 section
Signed-off-by: David Aguilar <davvid@gmail.com>
2008-11-20 02:46:00 -08:00
Barry Roberts 04def89eb2 Simple blame
Signed-off-by: Barry Roberts <blr@robertsr.us>
2008-11-20 02:31:30 -08:00
59 changed files with 1450 additions and 1251 deletions
+2
View File
@@ -0,0 +1,2 @@
David Martínez Martí <deavidsedice@gmail.com>
David Aguilar <davvid@gmail.com>
+16 -72
View File
@@ -9,28 +9,28 @@ If you downloaded a tarball add /path/to/cola/bin to your $PATH.
REQUIREMENTS
# apt-get install git-core python python-qt4
Python 2.4 or newer
http://python.org/
apt-get install python
PyQt 4.3 or newer
http://www.riverbankcomputing.co.uk/software/pyqt/intro
apt-get install pyqt4-dev-tools
ADDITIONAL REQUIREMENTS (optional)
# apt-get install python-pyinotify simplejson
pyinotify (enables inotify support on Linux -- highly recommended)
http://pyinotify.sourceforge.net/
apt-get install python-pyinotify
simplejson (enables bookmarks)
http://pypi.python.org/pypi/simplejson
apt-get install python-simplejson
BUILDING FROM SOURCE
# apt-get install pyqt4-dev-tools
Normally you can just do "make install" to install git-cola
in your $HOME directory ($HOME/bin, $HOME/lib, etc).
@@ -46,87 +46,31 @@ modifying the code.
$ make
$ bin/git-cola
If you're developing cola on windows then you can do:
BUILD ISSUES
Some systems have pyuic4 scripts that are missing the shebang line.
If your version of $(wich pyuic4) looks like:
exec python /path/to/pyuic4.py ...
Then simply add this line to the top of the file:
#!/bin/sh
That will fix the build by making pyuic4 a proper shell script.
PYQT4 ISSUES
An older version of pyuic4 generated code that could not be
run by PyQt4. This problem has been fixed in the latest
versions of PyQt4 (4.4.x), but you might encounter it if
you are not running the latest version of PyQt4.
If you try to run git-cola and see error messages
about "setSortingEnabled(..)" then your pyuic4/PyQt4
version is affected. There's a simple workaround, though:
remove the line of code listed in the traceback and
everything should work fine.
PYUIC4 AND WINDOWS
To build cola on windows you'll need to add the following
shell script to a directory in your path and name it 'pyuic4':
#+-------------------------------------------------------------
#!/bin/sh
/c/Python25/python.exe \
/c/Python25/Lib/site-packages/PyQt4/uic/pyuic.py "$@"
#+-------------------------------------------------------------
$ python setup.py build
$ win32/cola
WINDOWS INSTALLATION
1) Install the native Python2.5 and PyQt4 installers (in that order):
Install Git, Python, and PyQt4
http://git.or.cz/gitwiki/WindowsInstall
http://python.org/download/
http://www.riverbankcomputing.co.uk/pyqt/download.php
3) Install msysgit:
http://git.or.cz/gitwiki/WindowsInstall
4) Install the GnuWin32 'file' command:
http://gnuwin32.sourceforge.net/packages/file.htm
Add the installed directory (default: c:\GnuWin32\bin) to your path.
5) Install simplejson
Install simplejson (optional)
http://pypi.python.org/pypi/simplejson
6) Download cola:
Install and run cola from a Git bash:
http://cola.tuxfamily.org/releases/win32/
http://cola.tuxfamily.org/releases/
$ tar xvfz cola-*.tar.gz
$ cola-*/bin/cola
7) Install cola (from a Git Bash):
If you add the cola-*/bin directory to your $PATH then
you can launch cola by simply typing 'cola'.
cd /c/Python25/Lib/site-packages
tar xvfz /path/to/cola-*-win32.tar.gz
mv cola-*-win32 cola
8) Create an executable in your path for bash, cmd.exe, or both:
echo '/c/Python25/python.exe /c/Python25/Lib/site-packages/cola/bin/git-cola' >> /usr/bin/cola
echo 'c:\\Python25\\python.exe c:\\Python25\\Lib\\site-packages\\cola\\bin\\git-cola' > /c/Windows/System32/cola.bat
This assumes the you have Python/PyQt4 installed in C:/Python25.
If this is not the case simply edit the commands accordingly.
voila, cola on Windows.
+1 -1
View File
@@ -7,7 +7,7 @@ all:
install:
$(PYTHON) setup.py install --prefix=$(prefix) --root=$(DESTDIR) --force
cd $(DESTDIR)$(prefix)/bin && rm -f cola && ln -s git-cola cola
cd $(DESTDIR)$(prefix)/bin && ((! test -e cola && ln -s git-cola cola) || true)
doc:
cd share/doc/cola && $(MAKE) all
+2 -2
View File
@@ -1,10 +1,10 @@
git cola: a highly caffeinated git gui
Cola is a sweet, carbonated git gui known for its
cola is a sweet, carbonated git gui known for its
sugary flavour and caffeine-inspired features.
Copyright (C) 2008 David Aguilar and contributors
Copyright (C) 2009 David Aguilar and contributors
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
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
# This should work if used with both the default Python2.5
# from python.org and the default PyQt4 binary package
# from Riverbank. Adjust accordingly.
DIR=$(dirname "$0")
c:/Python25/python.exe "$DIR"/git-cola
+9 -7
View File
@@ -39,13 +39,15 @@ if bin in ('bin', 'Scripts') and prefix != sys.prefix:
sys.prefix = prefix
sys.exec_prefix = prefix
major, minor = sys.version_info[0:2]
local_path = [os.path.join(prefix, 'lib', 'python%s.%s' % (major, minor),
'site-packages'),
os.path.join(prefix, 'Lib', 'site-packages'),
prefix,
os.getcwd(),
]
paths_to_try = [
os.path.join(prefix, 'lib', 'python2.6', 'site-packages'),
os.path.join(prefix, 'lib', 'python2.5', 'site-packages'),
os.path.join(prefix, 'lib', 'python2.4', 'site-packages'),
prefix,
os.getcwd(),
]
local_path = [p for p in paths_to_try if os.path.exists(p)]
sys.path = local_path + sys.path
from cola.main import main
+62 -565
View File
@@ -1,576 +1,73 @@
#!/bin/sh
#!/usr/bin/env perl
# Copyright (c) 2009 David Aguilar
#
# This program uses merge tools to stage and compare commits
#
# Copyright (c) 2008 David Aguilar
#
# Adapted from git-mergetool.sh
# Copyright (c) 2006 Theodore Y. Ts'o
#
# This file is licensed under the GPL v2, or a later version
#
USAGE='
[--tool=tool] [--no-prompt]
[--commit=ref] [--start=ref --end=ref]
[--] [file to view] ...'
SUBDIRECTORY_OK=Yes
OPTIONS_SPEC=
. git-sh-setup
require_work_tree
cd_to_toplevel
# 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, and
# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper.
# Any arguments that are unknown to this script are forwarded to 'git diff'.
keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
PREFIX=.git-difftool."$$"."$(date +%N)".
FILEDIR=
use strict;
use warnings;
use Cwd qw(abs_path);
use File::Basename qw(dirname);
keep_backup () {
# whether to keep the .orig file
test "$keep_backup_mode" = "true"
my $DIR = abs_path(dirname($0));
sub usage
{
print << 'USAGE';
usage: git difftool [--tool=<tool>] [--no-prompt] ["git diff" options]
USAGE
exit 1;
}
parse_arg () {
# parses --foo=BAR
expr "z$1" : 'z-[^=]*=\(.*\)'
sub setup_environment
{
$ENV{PATH} = "$DIR:$ENV{PATH}";
$ENV{GIT_PAGER} = '';
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
}
index_present () {
# does the index contain staged changes?
test -n "$index_mode"
sub exe
{
my $exe = shift;
return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
}
modified_present () {
# are there changes in the requested comparison?
test -n "$modified_mode"
sub generate_command
{
my @command = (exe('git'), 'diff');
my $skip_next = 0;
my $idx = -1;
for my $arg (@ARGV) {
$idx++;
if ($skip_next) {
$skip_next = 0;
next;
}
if ($arg eq '-t' or $arg eq '--tool') {
usage() if $#ARGV <= $idx;
$ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1];
$skip_next = 1;
next;
}
if ($arg =~ /^--tool=/) {
$ENV{GIT_MERGE_TOOL} = substr($arg, 7);
next;
}
if ($arg eq '--no-prompt') {
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
next;
}
if ($arg eq '-h' or $arg eq '--help') {
usage();
}
push @command, $arg;
}
return @command
}
commitish_present () {
# are we comparing against an arbitrary commit?
test -n "$commitish"
}
should_prompt () {
# are we running interactively?
! test -n "$no_prompt"
}
use_rev_range () {
# are we using --start=REF and --end=REF
test -n "$start" && test -n "$end"
}
merge_dir_missing () {
# does dirname($MERGED) exist in the work tree?
test -n "$non_existant_dir"
}
base_present () {
# whether we have 3 things to compare (index, worktree, other)
index_present && modified_present && ! use_rev_range
}
modified_files () {
# returns the appopriate list of differences based on the mode
if use_rev_range; then
git diff --name-only "$start".."$end" -- "$@" 2>/dev/null
elif commitish_present; then
git diff --name-only "$commitish" -- "$@" 2>/dev/null
else
git diff --name-only -- "$@" 2>/dev/null
fi
}
staged_files() {
# returns any staged changes
git diff --name-only --cached "$@" 2>/dev/null
}
cleanup_temp_files () {
# removes temporary files
if test -n "$MERGED"; then
if keep_backup && test "$MERGED" -nt "$BACKUP"; then
test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
rm -f -- "$LOCAL" "$REMOTE" "$BASE"
else
rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
fi
test -n "$MERGEDIR" && rmdir -p "$MERGEDIR"
fi
}
sigint_handler () {
echo
cleanup_temp_files
exit 1
}
merge_file () {
# prepares temporary files and launches the appropriate merge tool
MERGED="$1"
modified_mode=$(modified_files "$MERGED")
index_mode=$(staged_files "$MERGED")
if ! modified_present && use_rev_range; then
echo "$MERGED: no changes between '$start' and '$end'."
exit 1
elif ! modified_present && ! index_present; then
if ! test -f "$MERGED"; then
echo "$MERGED: file not found"
else
echo "$MERGED: file is unchanged."
fi
exit 1
fi
# handle comparing a file that doesn't exist in the current checkout
MERGEDIR=$(dirname "$MERGED")
non_existant_dir=
test -d "$MERGEDIR" || non_existant_dir=true
if merge_dir_missing; then
mkdir -p -- "$MERGEDIR"
else
MERGEDIR=
fi
ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
BACKUP="./$MERGED.BACKUP.$ext"
test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
if use_rev_range; then
# we're comparing two arbitrary commits
BASE="./$MERGED.CURRENT.$ext"
LOCAL="./$MERGED.START.$ext"
REMOTE="./$MERGED.END.$ext"
base=current
local=start
remote=end
touch "$BASE"
touch "$LOCAL"
touch "$REMOTE"
# detects renames.. sweet!
oldname=$(git diff --follow "$start".."$end" -- "$MERGED" |
head -n9 |
grep '^rename from' |
sed -e 's/rename from //')
startname="$MERGED"
test -n "$oldname" && startname="$oldname"
if ! git show "$start":"$startname" > "$LOCAL" 2>/dev/null; then
if should_prompt; then
printf "\nWarning: "
printf "'$startname' does not exist at $start.\n"
fi
else
cp -- "$LOCAL" "$BASE"
fi
if ! git show "$end":"$MERGED" > "$REMOTE" 2>/dev/null; then
if should_prompt; then
printf "\nWarning: "
printf "'$MERGED' does not exist at $end.\n"
fi
else
! test -f "$BASE" && cp -- "$REMOTE" "$BASE"
fi
# $BASE could be used by custom mergetool commands
if test -f "$MERGED"; then
cp -- "$MERGED" "$BASE"
fi
else
# We're either comparing against the index or an
# arbitrary commit.
# The goal is to re-use existing mergetool.$tool.cmd
# configurations so we provide $BASE $LOCAL and $REMOTE
if commitish_present; then
HEAD=OTHER
local=Other
else
HEAD=HEAD
local=Index
fi
base=${commitish-HEAD}
remote=Current
BASE="./$MERGED.$HEAD.$ext"
LOCAL="./$MERGED.INDEX.$ext"
REMOTE="./$MERGED.CURRENT.$ext"
touch "$BASE"
touch "$LOCAL"
touch "$REMOTE"
if ! git show "$base":"$MERGED" > "$BASE" 2>&1; then
printf "\nWarning: "
printf "'$MERGED' does not exist at $base.\n"
else
cp "$BASE" "$LOCAL"
if commitish_present; then
rm -f "$LOCAL"
LOCAL="$BASE"
fi
fi
# If changes are present in the index use them as $LOCAL
# but only if $MERGED exists at $base
if ! commitish_present; then
git checkout-index --prefix="$PREFIX" "$MERGED"
if test -f "$PREFIX""$MERGED"; then
mv -- "$PREFIX""$MERGED" "$LOCAL"
tmpdir=$(dirname "$PREFIX""$MERGED")
test -d "$tmpdir" &&
test "$tmpdir" != "." &&
rmdir -p "$tmpdir"
else
index_mode=
fi
else
index_mode=
fi
if test -f "$MERGED"; then
cp -- "$MERGED" "$REMOTE"
fi
fi
# ensure that we clean up after ourselves
trap sigint_handler SIGINT
if should_prompt; then
printf "\nEditing: '$MERGED'\n"
printf "Hit return to launch '%s': " "$merge_tool"
read ans
fi
case "$merge_tool" in
kdiff3)
basename=$(basename "$MERGED")
if base_present; then
(
"$merge_tool_path" --auto \
--L1 "($base) $basename" \
--L2 "($local) $basename" \
--L3 "($remote) $basename" \
-o "$MERGED" "$BASE" "$LOCAL" "$REMOTE" \
> /dev/null 2>&1
)
else
(
"$merge_tool_path" --auto \
--L1 "($local) $basename" \
--L2 "($remote) $basename" \
-o "$MERGED" "$LOCAL" "$REMOTE" \
> /dev/null 2>&1
)
fi
;;
tkdiff)
if base_present; then
"$merge_tool_path" \
-a "$BASE" \
-o "$MERGED" "$LOCAL" "$REMOTE"
else
"$merge_tool_path" \
-o "$MERGED" "$LOCAL" "$REMOTE"
fi
;;
meld)
"$merge_tool_path" "$LOCAL" "$MERGED"
;;
vimdiff)
if base_present; then
"$merge_tool_path" "$BASE" "$LOCAL" "$MERGED"
else
"$merge_tool_path" "$LOCAL" "$MERGED"
fi
;;
gvimdiff)
if base_present; then
"$merge_tool_path" -f "$BASE" "$LOCAL" "$MERGED"
else
"$merge_tool_path" -f "$LOCAL" "$MERGED"
fi
;;
xxdiff)
if base_present; then
"$merge_tool_path" -X --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$MERGED" \
"$BASE" "$LOCAL" "$REMOTE"
else
"$merge_tool_path" -X --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$MERGED" \
"$LOCAL" "$REMOTE"
fi
;;
opendiff)
if base_present; then
"$merge_tool_path" "$LOCAL" "$REMOTE" \
-ancestor "$BASE" \
-merge "$MERGED" | cat
else
"$merge_tool_path" "$LOCAL" "$REMOTE" \
-merge "$MERGED" | cat
fi
;;
ecmerge)
if base_present; then
"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
--default --mode=merge3 --to="$MERGED"
else
"$merge_tool_path" "$LOCAL" "$REMOTE" \
--default --mode=merge2 --to="$MERGED"
fi
;;
emerge)
if base_present; then
"$merge_tool_path" \
-f emerge-files-with-ancestor-command \
"$LOCAL" "$REMOTE" "$BASE" \
"$(basename "$MERGED")"
else
"$merge_tool_path" -f emerge-files-command \
"$LOCAL" "$REMOTE" "$(basename "$MERGED")"
fi
;;
*)
if test -n "$merge_tool_cmd"; then
if test "$merge_tool_trust_exit_code" = "false"; then
( eval $merge_tool_cmd )
else
( eval $merge_tool_cmd )
fi
fi
;;
esac
cleanup_temp_files
}
while test $# != 0
do
case "$1" in
-t|--tool*)
case "$#,$1" in
*,*=*)
merge_tool=$(parse_arg "$1")
shift
;;
1,*)
usage
;;
*)
shift
merge_tool="$1"
shift
;;
esac
;;
-c|--commit*)
case "$#,$1" in
*,*=*)
commitish=$(parse_arg "$1")
shift
;;
1,*)
usage
;;
*)
shift
commitish="$1"
shift
;;
esac
;;
-s|--start*)
case "$#,$1" in
*,*=*)
start=$(parse_arg "$1")
shift
;;
1,*)
usage
;;
*)
shift
start="$1"
shift
;;
esac
;;
-e|--end*)
case "$#,$1" in
*,*=*)
end=$(parse_arg "$1")
shift
;;
1,*)
usage
;;
*)
shift
end="$1"
shift
;;
esac
;;
--no-prompt)
no_prompt=true
shift
;;
--)
shift
break
;;
-*)
usage
;;
*)
break
;;
esac
done
valid_custom_tool() {
merge_tool_cmd="$(git config mergetool.$1.cmd)"
test -n "$merge_tool_cmd"
}
valid_tool() {
case "$1" in
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
if ! valid_custom_tool "$1"
then
return 1
fi
;;
esac
}
init_merge_tool_path() {
merge_tool_path=$(git config mergetool."$1".path)
if test -z "$merge_tool_path"; then
case "$1" in
emerge)
merge_tool_path=emacs
;;
*)
merge_tool_path="$1"
;;
esac
fi
}
if test -z "$merge_tool"; then
merge_tool=$(git config merge.tool)
if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
unset merge_tool
fi
fi
if test -z "$merge_tool"; then
if test -n "$DISPLAY"; then
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
if test -n "$GNOME_DESKTOP_SESSION_ID"; then
merge_tool_candidates="meld $merge_tool_candidates"
fi
if test "$KDE_FULL_SESSION" = "true"; then
merge_tool_candidates="kdiff3 $merge_tool_candidates"
fi
fi
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates emerge"
fi
if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates vimdiff"
fi
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
echo "merge tool candidates: $merge_tool_candidates"
for i in $merge_tool_candidates
do
init_merge_tool_path $i
if type "$merge_tool_path" > /dev/null 2>&1; then
merge_tool=$i
break
fi
done
if test -z "$merge_tool" ; then
echo "No known merge resolution program available."
exit 1
fi
else
if ! valid_tool "$merge_tool"; then
echo >&2 "Unknown merge tool $merge_tool"
exit 1
fi
init_merge_tool_path "$merge_tool"
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
exit 1
fi
if ! test -z "$merge_tool_cmd"; then
merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
fi
fi
if test $# -eq 0; then
use_index=0
files=$(modified_files)
if test -z "$files"; then
use_index=1
files=$(staged_files)
fi
if test -z "$files"; then
echo "No modified files exist."
exit 1
fi
if test $use_index -eq 0; then
modified_files |
while IFS= read i
do
merge_file "$i" < /dev/tty > /dev/tty
done
elif ! use_rev_range; then
staged_files |
while IFS= read i
do
merge_file "$i" < /dev/tty > /dev/tty
done
else
echo "Nothing to compare."
exit 1
fi
else
while test $# -gt 0
do
merge_file "$1"
shift
done
fi
exit 0
setup_environment();
exec(generate_command());
+243
View File
@@ -0,0 +1,243 @@
#!/bin/sh
# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge,
# vimdiff, gvimdiff, and custom user-configurable tools.
# This script is typically launched by using the 'git difftool'
# convenience command.
#
# Copyright (c) 2009 David Aguilar
# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
should_prompt () {
! test -n "$GIT_DIFFTOOL_NO_PROMPT"
}
# Should we keep the backup .orig file?
keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
keep_backup () {
test "$keep_backup_mode" = "true"
}
# This function manages the backup .orig file.
# A backup $MERGED.orig file is created if changes are detected.
cleanup_temp_files () {
if test -n "$MERGED"; then
if keep_backup && test "$MERGED" -nt "$BACKUP"; then
test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
else
rm -f -- "$BACKUP"
fi
fi
}
# This is called when users Ctrl-C out of git-difftool-helper
sigint_handler () {
cleanup_temp_files
exit 1
}
# This function prepares temporary files and launches the appropriate
# merge tool.
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"
ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
BACKUP="$MERGED.BACKUP.$ext"
# Create and ensure that we clean up $BACKUP
test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
trap sigint_handler INT
# $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"
printf "Hit return to launch '%s': " "$merge_tool"
read ans
fi
# Run the appropriate merge tool command
case "$merge_tool" in
kdiff3)
basename=$(basename "$MERGED")
"$merge_tool_path" --auto \
--L1 "$basename (A)" \
--L2 "$basename (B)" \
-o "$MERGED" "$LOCAL" "$REMOTE" \
> /dev/null 2>&1
;;
tkdiff)
"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
;;
meld)
"$merge_tool_path" "$LOCAL" "$REMOTE"
;;
vimdiff)
"$merge_tool_path" -c "wincmd r" "$LOCAL" "$REMOTE"
;;
gvimdiff)
"$merge_tool_path" -c "wincmd r" -f "$LOCAL" "$REMOTE"
;;
xxdiff)
"$merge_tool_path" \
-X \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$MERGED" \
"$LOCAL" "$REMOTE"
;;
opendiff)
"$merge_tool_path" "$LOCAL" "$REMOTE" \
-merge "$MERGED" | cat
;;
ecmerge)
"$merge_tool_path" "$LOCAL" "$REMOTE" \
--default --mode=merge2 --to="$MERGED"
;;
emerge)
"$merge_tool_path" -f emerge-files-command \
"$LOCAL" "$REMOTE" "$(basename "$MERGED")"
;;
*)
if test -n "$merge_tool_cmd"; then
( eval $merge_tool_cmd )
fi
;;
esac
cleanup_temp_files
}
# Verifies that mergetool.<tool>.cmd exists
valid_custom_tool() {
merge_tool_cmd="$(git config mergetool.$1.cmd)"
test -n "$merge_tool_cmd"
}
# Verifies that the chosen merge tool is properly setup.
# Built-in merge tools are always valid.
valid_tool() {
case "$1" in
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
if ! valid_custom_tool "$1"
then
return 1
fi
;;
esac
}
# Sets up the merge_tool_path variable.
# This handles the mergetool.<tool>.path configuration.
init_merge_tool_path() {
merge_tool_path=$(git config mergetool."$1".path)
if test -z "$merge_tool_path"; then
case "$1" in
emerge)
merge_tool_path=emacs
;;
*)
merge_tool_path="$1"
;;
esac
fi
}
# Allow the GIT_MERGE_TOOL variable to provide a default value
test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
# If not merge tool was specified then use the merge.tool
# configuration variable. If that's invalid then reset merge_tool.
if test -z "$merge_tool"; then
merge_tool=$(git config merge.tool)
if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
unset merge_tool
fi
fi
# Try to guess an appropriate merge tool if no tool has been set.
if test -z "$merge_tool"; then
# We have a $DISPLAY so try some common UNIX merge tools
if test -n "$DISPLAY"; then
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
# If gnome then prefer meld
if test -n "$GNOME_DESKTOP_SESSION_ID"; then
merge_tool_candidates="meld $merge_tool_candidates"
fi
# If KDE then prefer kdiff3
if test "$KDE_FULL_SESSION" = "true"; then
merge_tool_candidates="kdiff3 $merge_tool_candidates"
fi
fi
# $EDITOR is emacs so add emerge as a candidate
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates emerge"
fi
# $EDITOR is vim so add vimdiff as a candidate
if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates vimdiff"
fi
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
echo "merge tool candidates: $merge_tool_candidates"
# Loop over each candidate and stop when a valid merge tool is found.
for i in $merge_tool_candidates
do
init_merge_tool_path $i
if type "$merge_tool_path" > /dev/null 2>&1; then
merge_tool=$i
break
fi
done
if test -z "$merge_tool" ; then
echo "No known merge resolution program available."
exit 1
fi
else
# A merge tool has been set, so verify that it's valid.
if ! valid_tool "$merge_tool"; then
echo >&2 "Unknown merge tool $merge_tool"
exit 1
fi
init_merge_tool_path "$merge_tool"
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
exit 1
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
+94 -38
View File
@@ -42,8 +42,9 @@ from bookmark import save_bookmark
from bookmark import manage_bookmarks
from stash import stash
from compare import compare
from compare import compare_file
from compare import branch_compare
from cola.views import AboutView
class Controller(QObserver):
"""Manages the interaction between models and views."""
@@ -86,11 +87,14 @@ class Controller(QObserver):
# When a model attribute changes, this runs a specific action
self.add_actions(global_cola_fontdiff = self.update_diff_font)
self.add_actions(global_cola_fontui = self.update_ui_font)
self.add_actions(global_cola_tabwidth = self.update_tab_width)
self.add_callbacks(
# Push Buttons
rescan_button = self.rescan,
signoff_button = self.model.add_signoff,
stage_button = self.stage_selected,
stash_button = lambda: stash(self.model, self.view),
commit_button = self.commit,
fetch_button = self.fetch,
push_button = self.push,
@@ -171,14 +175,18 @@ class Controller(QObserver):
lambda: stash(self.model, self.view),
menu_commit_compare =
lambda: compare(self.model, self.view),
menu_commit_compare_file =
lambda: compare_file(self.model, self.view),
menu_stage_modified =
lambda: self.log(self.model.stage_modified()),
menu_stage_untracked =
lambda: self.log(self.model.stage_untracked()),
menu_unstage_all =
lambda: self.log(self.model.unstage_all()),
menu_view_log = self.view_log,
# Help Menu
menu_help_about = self.about,
menu_help_docs =
lambda: self.model.git.web__browse(utils.get_htmldocs()),
)
@@ -191,16 +199,14 @@ class Controller(QObserver):
'itemDoubleClicked(QTreeWidgetItem*, int)',
self.doubleclick_tree)
# Toolbar log button
self.connect(view.toolbar_show_log,
'triggered()', self.show_log)
self.merge_msg_hash = ''
self.load_gui_settings()
self.rescan()
self.init_log_window()
self.refresh_view('global_cola_fontdiff', 'global_cola_fontui')
self.start_inotify_thread()
if self.has_inotify():
self.view.rescan_button.hide()
#####################################################################
# handle when the status tree is clicked
@@ -282,12 +288,14 @@ class Controller(QObserver):
diff = 'no diff'
if idx == self.view.IDX_STAGED:
diff = (self.model.git.diff(cached=True, stat=True,
no_color=True,
with_raw_output=True) + '\n\n' +
self.model.git.diff(cached=True))
elif idx == self.view.IDX_MODIFIED:
diff = (self.model.git.diff(stat=True,
no_color=True,
with_raw_output=True) + '\n\n' +
self.model.git.diff())
self.model.git.diff(no_color=True))
elif idx == self.view.IDX_UNMERGED:
diff = '%s unmerged file(s)' % len(self.model.get_unmerged())
elif idx == self.view.IDX_UNTRACKED:
@@ -359,8 +367,17 @@ class Controller(QObserver):
#####################################################################
# Qt callbacks
def about(self):
view = AboutView(self.view)
style = QtGui.QApplication.instance().styleSheet()
if style:
view.setStyleSheet(style)
view.show()
view.set_version(version.version)
def tr(self, fortr):
return qtutils.tr(fortr)
def goto_grep(self):
line = self.view.selected_line()
filename, lineno, contents = line.split(':', 2)
@@ -374,7 +391,7 @@ class Controller(QObserver):
def gen_search(self, searchtype, browse=False):
def search_handler():
search_commits(self.model, searchtype, browse)
search_commits(self.model, self.view, searchtype, browse)
return search_handler
def grep(self):
@@ -386,8 +403,8 @@ class Controller(QObserver):
self.view.display_text.setText(stuff)
self.view.show_diff()
def show_log(self, *rest):
qtutils.toggle_log_window()
def view_log(self, *rest):
qtutils.show_logger()
def options(self):
update_options(self.model, self.view)
@@ -424,9 +441,7 @@ class Controller(QObserver):
self.model.get_local_branches())
if not branch:
return
status, out, err = self.model.git.checkout(branch,
with_extended_output=True)
self.log(out+err)
self.log(self.model.git.checkout(branch, with_stderr=True))
def browse_commits(self):
self.select_commits_gui('Browse Commits',
@@ -489,13 +504,16 @@ class Controller(QObserver):
else:
self.mode = Controller.MODE_WORKTREE
def view_diff(self, staged=True):
def view_diff(self, staged=True, scrollvalue=None):
idx, selected = self.view.get_selection()
if not selected:
self.reset_mode()
self.view.reset_display()
return
self.view_diff_for_row(idx, staged)
if scrollvalue is not None:
scrollbar = self.view.display_text.verticalScrollBar()
scrollbar.setValue(scrollvalue)
def view_diff_for_row(self, idx, staged):
self.set_mode(staged)
@@ -524,12 +542,12 @@ class Controller(QObserver):
def edit_diff(self, staged=True):
filename = self.get_selected_filename(staged=staged)
if filename:
args = ['git', 'difftool', '--no-prompt',
'-t', self.model.get_mergetool()]
if (self.view.amend_is_checked()
or (staged and
filename not in self.model.partially_staged)):
args.extend(['-c', 'HEAD^'])
args = ['perl', utils.get_libexec('git-difftool'),
'--no-prompt', '-t', self.model.get_mergetool()]
if staged:
args.append('--cached')
if self.view.amend_is_checked():
args.append('HEAD^')
args.extend(['--', filename])
utils.fork(*args)
@@ -566,30 +584,63 @@ class Controller(QObserver):
revs,
output='patches'))
def _quote_repopath(self, repopath):
if os.name in ('nt', 'dos'):
repopath = '"%s"' % repopath
return repopath
def open_repo(self):
"""Spawns a new cola session"""
dirname = qtutils.opendir_dialog(self.view,
'Open Git Repository...',
os.getcwd())
if dirname:
utils.fork(sys.argv[0], dirname)
if not dirname:
return
utils.fork('python', sys.argv[0],
'--repo', self._quote_repopath(dirname))
def clone_repo(self):
"""Clones a git repository"""
url, ok = qtutils.input('Path or URL to clone (Env. $VARS okay)')
url = os.path.expandvars(url)
if not ok or not url:
return
url = os.path.expandvars(url)
msg = 'Enter a directory name for the new repository'
dirname = qtutils.new_dir_dialog(self.view, msg)
while dirname and os.path.exists(dirname):
qtutils.information('Directory Exists',
'Please enter a non-existent path name.')
dirname = qtutils.new_dir_dialog(self.view, msg)
try:
newurl = url.replace('\\', '/')
default = newurl.rsplit('/', 1)[-1]
if default == '.git':
default = os.path.basename(os.path.dirname(newurl))
if default.endswith('.git'):
default = default[:-4]
if url == '.':
default = os.path.basename(os.getcwd())
if not default:
raise
except:
self.log('Oops, could not parse git url: "%s"' % url)
return
msg = 'Select a parent directory for the new clone'
dirname = qtutils.opendir_dialog(self.view, msg, os.getcwd())
if not dirname:
return
self.log(self.model.git.clone(url, dirname))
utils.fork(sys.argv[0], dirname)
count = 1
destdir = os.path.join(dirname, default)
olddestdir = destdir
if os.path.exists(destdir):
qtutils.information(destdir + ' already exists, cola will '
'create a new directory')
while os.path.exists(destdir):
destdir = olddestdir + str(count)
count += 1
self.log(self.model.git.clone(url, destdir))
utils.fork('python', sys.argv[0],
'--repo', self._quote_repopath(destdir))
def has_inotify(self):
return self.inotify_thread and self.inotify_thread.isRunning()
def quit_app(self, *args):
"""Save config settings and cleanup any inotify threads."""
@@ -600,7 +651,7 @@ class Controller(QObserver):
pattern = self.model.get_tmp_file_pattern()
for filename in glob.glob(pattern):
os.unlink(filename)
if self.inotify_thread and self.inotify_thread.isRunning():
if self.has_inotify():
self.inotify_thread.abort = True
self.inotify_thread.wait()
self.view.close()
@@ -618,7 +669,7 @@ class Controller(QObserver):
def rebase(self):
branch = choose_from_combo('Rebase Branch',
self.view,
self.model.get_local_branches())
self.model.get_all_branches())
if not branch:
return
self.log(self.model.git.rebase(branch))
@@ -695,9 +746,8 @@ class Controller(QObserver):
item.setSelected(True)
self.view.status_tree.setCurrentItem(item)
self.view.status_tree.setItemSelected(item, True)
scrollbar.setValue(scrollvalue)
if showdiff:
self.view_diff(False)
self.view_diff(staged=False, scrollvalue=scrollvalue)
else:
self.reset_mode()
self.view.reset_display()
@@ -713,9 +763,8 @@ class Controller(QObserver):
item.setSelected(True)
self.view.status_tree.setCurrentItem(item)
self.view.status_tree.setItemSelected(item, True)
scrollbar.setValue(scrollvalue)
if showdiff:
self.view_diff(True)
self.view_diff(staged=True, scrollvalue=scrollvalue)
else:
self.reset_mode()
self.view.reset_display()
@@ -772,6 +821,7 @@ class Controller(QObserver):
if not branch:
return
zfiles_str = self.model.git.diff(branch, name_only=True,
no_color=True,
z=True).rstrip('\0')
files = zfiles_str.split('\0')
filename = choose_from_list('Select File', self.view, files)
@@ -900,12 +950,12 @@ class Controller(QObserver):
def viz_all(self):
"""Visualizes the entire git history using gitk."""
browser = self.model.get_history_browser()
utils.fork(browser, '--all')
utils.fork('sh', '-c', browser, '--all')
def viz_current(self):
"""Visualizes the current branch's history using gitk."""
browser = self.model.get_history_browser()
utils.fork(browser, self.model.get_currentbranch())
utils.fork('sh', '-c', browser, self.model.get_currentbranch())
def load_gui_settings(self):
try:
@@ -1033,6 +1083,12 @@ class Controller(QObserver):
qfont.fromString(font)
QtGui.qApp.setFont(qfont)
def update_tab_width(self):
tab_width = self.model.get_cola_config('tabwidth')
display_font = self.view.display_text.font()
space_width = QtGui.QFontMetrics(display_font).width(' ')
self.view.display_text.setTabStopWidth(tab_width * space_width)
def init_log_window(self):
branch = self.model.get_currentbranch()
qtutils.log(self.model.get_git_version()
+6 -3
View File
@@ -25,15 +25,18 @@ def manage_bookmarks():
view = BookmarkView(QtGui.qApp.activeWindow())
ctl = BookmarkController(model, view)
view.show()
if view.exec_() == QtGui.QDialog.Accepted:
model.save_all_settings()
class BookmarkController(QObserver):
def init(self, model, view):
self.add_observables( 'bookmarks' )
self.add_callbacks(button_open = self.open,
button_delete = self.delete)
button_delete = self.delete,
button_save = self.save)
self.refresh_view()
def save(self):
self.model.save_all_settings()
self.view.accept()
def open(self):
selection = qtutils.get_selection_list(self.view.bookmarks,
+31 -21
View File
@@ -10,7 +10,13 @@ from cola.views import BranchCompareView
from cola.controllers.repobrowser import select_file_from_repo
from cola.controllers.util import choose_from_list
def compare(model, parent):
def compare_file(model, parent):
filename = select_file_from_repo(model, parent)
if not filename:
return
compare(model, parent, filename)
def compare(model, parent, filename=None):
model = model.clone()
model.create(descriptions_start=[], descriptions_end=[],
revisions_start=[], revisions_end=[],
@@ -18,7 +24,7 @@ def compare(model, parent):
compare_files=[], num_results=100,
show_versions=False)
view = CompareView(parent)
ctl = CompareController(model, view)
ctl = CompareController(model, view, filename)
view.show()
def branch_compare(model, parent):
@@ -158,23 +164,23 @@ class BranchCompareController(QObserver):
def __compare_file(self, filename):
git = self.model.git
args = git.transform_kwargs(no_prompt=True,
tool=self.model.get_mergetool())
if self.use_sandbox:
kwargs = git.transform_kwargs(no_prompt=True,
tool=self.model.get_mergetool(),
commit=self.diff_arg)
args.append(self.diff_arg)
else:
kwargs = git.transform_kwargs(no_prompt=True,
tool=self.model.get_mergetool(),
start=self.start,
end=self.end)
args = (['git', 'difftool'] + kwargs + ['--', filename])
utils.fork(*args)
args.append('%s..%s' % (self.start, self.end))
command = (['perl', utils.get_libexec('git-difftool')] +
args + ['--', filename])
utils.fork(*command)
class CompareController(QObserver):
"""Drives the Commit->Compare Commits dialog.
"""
def init (self, model, view):
def init (self, model, view, filename=None):
self.filename = filename
self.add_observables('descriptions_start', 'descriptions_end',
'revision_start', 'revision_end',
'compare_files', 'num_results',
@@ -232,7 +238,8 @@ class CompareController(QObserver):
self.model.set_notify(True)
show_versions = self.model.get_show_versions()
revs = self.model.update_revision_lists(show_versions=show_versions)
revs = self.model.update_revision_lists(filename=self.filename,
show_versions=show_versions)
if start_selected:
tree_widget = self.view.descriptions_start
self.select_nth_item_from_end(tree_widget, start_delta)
@@ -261,8 +268,11 @@ class CompareController(QObserver):
id_num, selected = qtutils.get_selected_treeitem(tree_widget)
if not selected:
return
revision = self.model.get_param(revisions_param)[id_num]
self.model.set_param(revision_param, revision)
revisionlist = self.model.get_param(revisions_param)
if id_num < len(revisionlist):
revision = self.model.get_param(revisions_param)[id_num]
self.model.set_param(revision_param, revision)
# get the changed files list
start = self.model.get_revision_start()
@@ -302,9 +312,9 @@ class CompareController(QObserver):
git = self.model.git
start = self.model.get_revision_start()
end = self.model.get_revision_end()
kwargs = git.transform_kwargs(no_prompt=True,
tool=self.model.get_mergetool(),
start=start,
end=end)
args = (['git', 'difftool'] + kwargs + ['--', filename])
utils.fork(*args)
args = git.transform_kwargs(no_prompt=True,
tool=self.model.get_mergetool())
args.append('%s..%s' % (start, end))
command = (['perl', utils.get_libexec('git-difftool')] +
args + ['--', filename])
utils.fork(*command)
+8 -6
View File
@@ -10,15 +10,16 @@ from cola import qtutils
from cola.views import CreateBranchView
from cola.qobserver import QObserver
def create_new_branch(model,parent):
def create_new_branch(model,parent,revision=''):
model = model.clone()
view = CreateBranchView(parent)
ctl = CreateBranchController(model, view)
model.set_revision(revision)
view.show()
return view.exec_() == QDialog.Accepted
class CreateBranchController(QObserver):
def init(self, model, view):
self._remoteclicked = False
self.add_observables('revision', 'local_branch')
self.add_callbacks(branch_list = self.item_changed,
create_button = self.create_branch,
@@ -90,10 +91,8 @@ class CreateBranchController(QObserver):
output = self.model.create_branch(branch, revision, track=track)
qtutils.show_output(output)
if chkout:
status, out, err = self.model.git.checkout(branch,
with_extended_output=True)
qtutils.show_output(out+err)
qtutils.show_output(self.model.git.checkout(branch,
with_stderr=True))
self.view.accept()
def item_changed(self, *rest):
@@ -117,7 +116,10 @@ class CreateBranchController(QObserver):
branch = utils.basename(rev)
if branch == 'HEAD':
return
# Signal that we've clicked on a remote branch
self._remoteclicked = True
self.model.set_local_branch(branch)
self._remoteclicked = False
######################################################################
+6 -2
View File
@@ -13,15 +13,17 @@ def remote_action(model, parent, action):
model = model.clone()
model.create(remotename='',
tags_checkbox=False,
rebase_checkbox=False,
ffwd_only_checkbox=True)
view = RemoteView(parent, action)
if action == 'Fetch' or action == 'Pull':
model.set_tags_checkbox(False)
if action == 'Pull':
view.tags_checkbox.hide()
if action != 'Pull':
view.rebase_checkbox.hide()
controller = RemoteController(model, view, action)
view.show()
return view.exec_() == QDialog.Accepted
class RemoteController(QObserver):
def init(self, model, view, action):
@@ -32,6 +34,7 @@ class RemoteController(QObserver):
'remote_branch',
'remote_branches',
'tags_checkbox',
'rebase_checkbox',
'ffwd_only_checkbox')
self.action_method = {
'Fetch': self.gen_remote_callback(self.model.fetch_helper),
@@ -106,12 +109,13 @@ class RemoteController(QObserver):
'remote_branch': self.model.get_remote_branch(),
'ffwd': self.model.get_ffwd_only_checkbox(),
'tags': self.model.get_tags_checkbox(),
'rebase': self.model.get_rebase_checkbox(),
})
def show_results(self, output):
qtutils.show_output(output)
self.view.accept()
qtutils.raise_logger()
qtutils.show_logger()
#+-------------------------------------------------------------
#+ Actions
+29
View File
@@ -4,6 +4,7 @@
import os
from PyQt4.QtGui import QDialog
from PyQt4.QtGui import QMenu
from cola import utils
from cola import qtutils
@@ -42,9 +43,37 @@ class RepoBrowserController(QObserver):
self.add_signals('itemSelectionChanged()', view.commit_list,)
self.add_actions(directory = self.action_directory_changed)
self.add_callbacks(commit_list = self.item_changed)
view.commit_list.contextMenuEvent = self.context_menu_event
# Start at the root of the tree
model.set_directory('')
def context_menu_event(self, event):
menu = QMenu(self.view);
menu.addAction(self.tr('Blame'), self.blame)
menu.exec_(self.view.commit_list.mapToGlobal(event.pos()))
def blame(self):
current = self.view.commit_list.currentRow()
item = self.view.commit_list.item(current)
if item is None or not item.isSelected():
return
directories = self.model.get_directories()
directory_entries = self.model.get_directory_entries()
if current < len(directories):
# ignore directories
return
idx = current - len(directories)
if idx >= len(self.model.get_subtree_sha1s()):
return
objtype, sha1, name = self.model.get_subtree_node(idx)
curdir = self.model.get_directory()
if curdir:
filename = os.path.join(curdir, name)
else:
filename = name
blame = self.model.git.blame(self.model.get_currentbranch(), filename)
self.view.commit_text.setText(blame)
######################################################################
# Actions
def action_directory_changed(self):
+5 -6
View File
@@ -21,7 +21,7 @@ class SearchEngine(object):
pass
def get_rev_args(self):
max = self.model.get_max_results()
return { "max-count": max, "pretty": "oneline" }
return { 'max-count': max, 'pretty': 'format:%H %aN - %s - %ar' }
def get_common_args(self):
return (self.model.get_input(), self.get_rev_args())
def search(self):
@@ -31,7 +31,7 @@ class SearchEngine(object):
def validate(self):
return len(self.model.get_input()) > 1
def get_revisions(self, *args, **kwargs):
revlist = self.model.git.rev_list(*args, **kwargs)
revlist = self.model.git.log(*args, **kwargs)
return self.model.parse_rev_list(revlist)
def get_results(self):
pass
@@ -233,7 +233,7 @@ class SearchController(QObserver):
doraise=True,
quiet=False)
def search_commits(model, mode, browse):
def search_commits(model, parent, mode, browse):
def get_date(timespec):
return '%04d-%02d-%02d' % time.localtime(timespec)[:3]
@@ -242,8 +242,8 @@ def search_commits(model, mode, browse):
max_results=500,
start_date='',
end_date='',
commit_list=None)
view = SearchView(None)
commit_list=[])
view = SearchView(parent)
ctl = SearchController(model, view)
ctl.set_mode(mode)
model.set_start_date(get_date(time.time()-(87640*7)))
@@ -251,4 +251,3 @@ def search_commits(model, mode, browse):
view.show()
if browse:
ctl.browse_callback()
view.exec_()
+4 -7
View File
@@ -70,19 +70,16 @@ class StashController(QObserver):
if not selection:
return
diffstat = self.model.git.stash('show', selection)
diff = self.model.stash('show', '-p', selection)
diff = self.model.git.stash('show', '-p', selection)
self.view.parent_view.display('%s\n\n%s' % (diffstat, diff))
def stash_apply(self):
selection = self.get_selected_stash()
if not selection:
return
(status, stdout, stderr) =\
self.model.git.stash('apply', selection,
with_extended_output=True)
qtutils.log(stdout + stderr,
quiet=False,
doraise=True)
out = self.model.git.stash('apply', '--index', selection,
with_stderr=True)
qtutils.log(out, quiet=False, doraise=True)
self.view.accept()
def stash_drop(self):
+44 -12
View File
@@ -7,6 +7,8 @@ import os
import time
from PyQt4.QtGui import QDialog
from PyQt4.QtGui import QFont
from PyQt4.QtGui import QMenu
from PyQt4 import QtGui
from cola import utils
from cola import qtutils
@@ -17,6 +19,7 @@ from cola.views import CommitView
from cola.views import OptionsView
from cola.views import LogView
from cola.qobserver import QObserver
from createbranch import create_new_branch
def set_diff_font(model, widget):
if model.has_param('global_cola_fontdiff'):
@@ -41,11 +44,13 @@ def choose_from_list(title, parent, items=[], dblclick=None):
#+-------------------------------------------------------------
def select_commits(model, parent, title, revs, summaries):
"""Use the CommitView to select commits from a list."""
original_model = model
model = model.clone()
model.set_revisions(revs)
model.set_summaries(summaries)
view = CommitView(parent, title)
ctl = SelectCommitsController(model, view)
ctl.original_model = original_model
return ctl.select_commits()
class SelectCommitsController(QObserver):
@@ -54,6 +59,7 @@ class SelectCommitsController(QObserver):
self.connect(view.commit_list,
'itemSelectionChanged()',
self.commit_sha1_selected)
view.commit_list.contextMenuEvent = self.context_menu_event
def select_commits(self):
summaries = self.model.get_summaries()
@@ -69,6 +75,36 @@ class SelectCommitsController(QObserver):
list_widget = self.view.commit_list
return qtutils.get_selection_list(list_widget, revs)
def context_menu_event(self, event):
menu = QMenu(self.view);
menu.addAction(self.tr('Checkout'), self.checkout_commit)
menu.addAction(self.tr('Create Branch here'), self.create_branch_at)
menu.addAction(self.tr('Cherry Pick'), self.cherry_pick)
menu.exec_(self.view.commit_list.mapToGlobal(event.pos()))
def checkout_commit(self):
row, selected = qtutils.get_selected_row(self.view.commit_list)
if not selected:
return
sha1 = self.model.get_revision_sha1(row)
out = self.model.git.checkout(sha1, with_stderr=True)
qtutils.show_output(out)
def create_branch_at(self):
row, selected = qtutils.get_selected_row(self.view.commit_list)
if not selected:
return
create_new_branch(self.original_model, self.view,
revision=self.model.get_revision_sha1(row))
def cherry_pick(self):
row, selected = qtutils.get_selected_row(self.view.commit_list)
if not selected:
return
sha1 = self.model.get_revision_sha1(row)
out = self.model.git.cherry_pick(sha1, with_stderr=True)
qtutils.show_output(out)
def commit_sha1_selected(self):
row, selected = qtutils.get_selected_row(self.view.commit_list)
if not selected:
@@ -120,17 +156,18 @@ class OptionsController(QObserver):
'global_merge_tool',
'global_gui_diffcontext',
'global_gui_historybrowser',
'global_cola_fontdiffsize',
'global_cola_fontdiff_size',
'global_cola_fontdiff',
'global_cola_fontuisize',
'global_cola_fontui_size',
'global_cola_fontui',
'global_cola_savewindowsettings',
'global_cola_tabwidth',
)
self.add_actions(global_cola_fontdiffsize = self.update_size)
self.add_actions(global_cola_fontuisize = self.update_size)
self.add_actions(global_cola_fontdiff = self.tell_parent_model)
self.add_actions(global_cola_fontui = self.tell_parent_model)
self.add_callbacks(save_button = self.save_settings)
self.add_callbacks(global_cola_fontdiff_size = self.update_size)
self.add_callbacks(global_cola_fontui_size = self.update_size)
self.connect(self.view, 'rejected()', self.restore_settings)
self.refresh_view()
@@ -139,18 +176,12 @@ class OptionsController(QObserver):
def refresh_view(self):
font = self.model.get_cola_config('fontui')
if font:
size = int(font.split(',')[1])
self.view.global_cola_fontuisize.setValue(size)
self.model.set_global_cola_fontuisize(size)
fontui = QFont()
fontui.fromString(font)
self.view.global_cola_fontui.setCurrentFont(fontui)
font = self.model.get_cola_config('fontdiff')
if font:
size = int(font.split(',')[1])
self.view.global_cola_fontdiffsize.setValue(size)
self.model.set_global_cola_fontdiffsize(size)
fontdiff = QFont()
fontdiff.fromString(font)
self.view.global_cola_fontdiff.setCurrentFont(fontdiff)
@@ -183,9 +214,10 @@ class OptionsController(QObserver):
def tell_parent_model(self,*rest):
params= ('global_cola_fontdiff',
'global_cola_fontui',
'global_cola_fontdiffsize',
'global_cola_fontuisize',
'global_cola_fontdiff_size',
'global_cola_fontui_size',
'global_cola_savewindowsettings',
'global_cola_tabwidth',
)
for param in params:
self.original_model.set_param(
+1 -1
View File
@@ -3,7 +3,7 @@
# Some files are not in UTF-8; some other aren't in any codification.
# Remember that GIT doesn't care about encodings (saves binary data)
_encoding_tests = [
"utf8",
"utf-8",
"iso-8859-15",
"windows1252",
"ascii",
+21 -26
View File
@@ -20,7 +20,7 @@ def dashify(string):
# Enables debugging of GitPython's git commands
GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False)
execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output',
execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', 'with_stderr',
'with_exceptions', 'with_raw_output')
class Git(object):
@@ -47,6 +47,7 @@ class Git(object):
istream=None,
with_keep_cwd=False,
with_extended_output=False,
with_stderr=False,
with_exceptions=False,
with_raw_output=False):
"""
@@ -59,12 +60,17 @@ class Git(object):
``istream``
Standard input filehandle passed to subprocess.Popen.
The following option all default to False.
``with_keep_cwd``
Whether to use the current working directory from os.getcwd().
GitPython uses the cwd set by set_cwd() by default.
``with_extended_output``
Whether to return a (status, stdout, stderr) tuple.
Whether to return a (status, output) tuple.
``with_stderr``
Whether to include stderr in the output.
``with_exceptions``
Whether to raise an exception when git returns a non-zero status.
@@ -82,7 +88,12 @@ class Git(object):
# Allow the user to have the command executed in their working dir.
if with_keep_cwd or not cwd:
cwd = os.getcwd()
cwd = os.getcwd()
# Ignore stderr unless with_extended_output is provided
stderr = None
if with_stderr:
stderr = subprocess.STDOUT
# Start the process
use_shell = sys.platform in ('win32')
@@ -92,12 +103,12 @@ class Git(object):
cwd=cwd,
shell=use_shell,
stdin=istream,
stderr=subprocess.PIPE,
stderr=stderr,
stdout=subprocess.PIPE)
while True:
try:
stdout_value = proc.stdout.read()
stderr_value = proc.stderr.read()
proc.stdout.close()
status = proc.wait()
break
except IOError, e:
@@ -107,40 +118,24 @@ class Git(object):
if e.errno == errno.EINTR:
continue
else:
stdout_value = None
stderr_value = None
stdout_value = ''
status = 42
break
if with_exceptions and status:
raise GitCommandError(command, status, stderr_value)
raise GitCommandError(command, status, stdout_value)
if not stdout_value:
stdout_value = ''
if not stderr_value:
stderr_value = ''
if not with_raw_output:
stdout_value = stdout_value.strip()
stderr_value = stderr_value.strip()
if GIT_PYTHON_TRACE == 'full':
if stderr_value:
print "%s -> %d: '%s' !! '%s'" % (command, status, stdout_value, stderr_value)
elif stdout_value:
print "%s -> %d: '%s'" % (command, status, stdout_value)
else:
print "%s -> %d" % (command, status)
print "%s -> %d: '%s'" % (command, status, stdout_value)
# Allow access to the command's status code
if with_extended_output:
return (status, stdout_value, stderr_value)
return status, stdout_value
else:
if stdout_value and stderr_value:
return stderr_value + '\n' + stdout_value
elif stdout_value:
return stdout_value
else:
return stderr_value
return stdout_value
def transform_kwargs(self, **kwargs):
"""
+29 -9
View File
@@ -11,10 +11,12 @@ from PyQt4.QtCore import QCoreApplication
from PyQt4.QtCore import QThread
from PyQt4.QtCore import QEvent
from PyQt4.QtCore import SIGNAL
import pyinotify
from pyinotify import ProcessEvent
from pyinotify import WatchManager, Notifier, EventsCodes
INOTIFY_EVENT_TYPE = QEvent.User + 0
INOTIFY_EVENT = QEvent.User + 0
class FileSysEvent(ProcessEvent):
"""FileSysEvents are generated by the GitNotifier thread
@@ -44,16 +46,18 @@ class GitNotifier(QThread):
self.git = git # A: git command object
self.path = git.get_work_tree() #: Path to monitor
self.abort = False #: Whether to abort (using during destruction)
self.dirs_seen = {} #: Directories we're watching
self.dirs_seen = set() #: Directories we're watching
#: Events we capture
self.mask = (EventsCodes.IN_CREATE | EventsCodes.IN_DELETE |
EventsCodes.IN_MODIFY | EventsCodes.IN_MOVED_TO)
self.mask = (EventsCodes.ALL_FLAGS['IN_CREATE'] |
EventsCodes.ALL_FLAGS['IN_DELETE'] |
EventsCodes.ALL_FLAGS['IN_MODIFY'] |
EventsCodes.ALL_FLAGS['IN_MOVED_TO'])
def notify(self):
"""Posts a Qt event in response to inotify updates.
"""
if not self.abort:
event_type = QEvent.Type(INOTIFY_EVENT_TYPE)
event_type = QEvent.Type(INOTIFY_EVENT)
event = QEvent(event_type)
QCoreApplication.postEvent(self.receiver, event)
@@ -63,15 +67,27 @@ class GitNotifier(QThread):
directory = os.path.realpath(directory)
if directory not in self.dirs_seen:
self.wm.add_watch(directory, self.mask)
self.dirs_seen[directory] = True
self.dirs_seen.add(directory)
def _is_pyinotify_08x(self):
"""The pyinotify API changed between 0.7.x and 0.8.x.
This allows us to maintain backwards compatibility.
"""
if hasattr(pyinotify, '__version__'):
if pyinotify.__version__[:3] == '0.8':
return True
return False
def run(self):
"""This creates the inotify WatchManager and generates
FileSysEvents."""
# Only capture those events that git cares about
self.wm = WatchManager()
notifier = Notifier(self.wm, FileSysEvent(self))
dirs_seen = {}
if self._is_pyinotify_08x():
notifier = Notifier(self.wm, FileSysEvent(self), timeout=250)
else:
notifier = Notifier(self.wm, FileSysEvent(self))
dirs_seen = set()
added_flag = False
# self.abort signals app termination. The timeout is a tradeoff
# between fast notification response and waiting too long to exit.
@@ -84,6 +100,10 @@ class GitNotifier(QThread):
self.watch_directory(directory)
added_flag = True
notifier.process_events()
if notifier.check_events(timeout=250):
if self._is_pyinotify_08x():
check = notifier.check_events()
else:
check = notifier.check_events(timeout=250)
if check:
notifier.read_events()
notifier.stop()
+13 -5
View File
@@ -79,6 +79,7 @@ def main():
translator.load(qmfile)
app.installTranslator(translator)
style = None
if opts.style:
# This loads the built-in and user-specified stylesheets.
# We allows absolute and relative paths to a stylesheet
@@ -92,11 +93,7 @@ def main():
# Automatically register each subdirectory in the style dir
# as a Qt resource directory.
dirname = os.path.dirname(filename)
resource_paths = []
resource_paths.extend(utils.get_resource_dirs(dirname))
for r in resource_paths:
basename = os.path.basename(r)
QtCore.QDir.setSearchPaths(basename, [r])
setup_resource_dir(dirname)
stylesheet = open(filename, 'r')
style = stylesheet.read()
stylesheet.close()
@@ -104,6 +101,9 @@ def main():
except:
print >> sys.stderr, ("warn: '%s' is not a valid style."
% opts.style)
else:
setup_resource_dir(utils.get_style_dir())
# Initialize the model/view/controller framework
from cola.models import Model
@@ -127,3 +127,11 @@ def main():
view.show()
ctl = Controller(model, view)
sys.exit(app.exec_())
def setup_resource_dir(dirname):
from PyQt4 import QtCore
resource_paths = utils.get_resource_dirs(dirname)
for r in resource_paths:
basename = os.path.basename(r)
QtCore.QDir.setSearchPaths(basename, [r])
+4 -15
View File
@@ -5,6 +5,7 @@ data model.
"""
import os
import sys
import imp
from cStringIO import StringIO
from types import DictType
@@ -357,20 +358,11 @@ class Model(Observable):
modules = items[:-1]
classname = items[-1]
path = None
module = None
for mod in modules:
search = imp.find_module(mod, path)
try:
module = imp.load_module(mod, *search)
if hasattr(module, "__path__"):
path = module.__path__
finally:
if search and search[0]:
search[0].close()
module = __import__('.'.join(modules))
for mod in modules[1:]:
module = getattr(module, mod)
if module:
return getattr(module, classname)
else:
raise Exception("No class found for: %s" % clstr)
@staticmethod
def class_to_str(instance):
@@ -385,9 +377,6 @@ def has_json():
import simplejson
return True
except ImportError:
print "Unable to import simplejson." % action
print "You do not have simplejson installed."
print "try: sudo apt-get install simplejson"
return False
#############################################################################
+54 -40
View File
@@ -199,10 +199,11 @@ class Model(model.Model):
self.__global_defaults = {
'cola_geometry':'',
'cola_fontui': '',
'cola_fontuisize': 12,
'cola_fontui_size': 12,
'cola_fontdiff': '',
'cola_fontdiffsize': 12,
'cola_fontdiff_size': 12,
'cola_savewindowsettings': False,
'cola_tabwidth': 8,
'merge_keepbackup': True,
'merge_tool': os.getenv('MERGETOOL', 'xxdiff'),
'gui_editor': os.getenv('EDITOR', 'gvim'),
@@ -228,10 +229,10 @@ class Model(model.Model):
if font:
setdefault = False
size = int(font.split(',')[1])
self.set_param(param+'size', size)
self.set_param(param+'_size', size)
param = param[len('global_'):]
global_dict[param] = font
global_dict[param+'size'] = size
global_dict[param+'_size'] = size
# Load defaults for all undefined items
local_and_global_defaults = self.__local_and_global_defaults
@@ -303,7 +304,7 @@ class Model(model.Model):
self.__local_and_global_defaults.keys()))
params.extend(map(lambda x: 'global_' + x,
self.__global_defaults.keys()))
return [ p for p in params if not p.endswith('size') ]
return [ p for p in params if not p.endswith('_size') ]
def save_config_param(self, param):
if param not in self.get_config_params():
@@ -525,7 +526,7 @@ class Model(model.Model):
old_font = self.get_param(param)
if not old_font:
old_font = default
size = self.get_param(param+'size')
size = self.get_param(param+'_size')
props = old_font.split(',')
props[1] = str(size)
new_font = ','.join(props)
@@ -536,12 +537,12 @@ class Model(model.Model):
commit = self.git.show(sha1)
first_newline = commit.index('\n')
if commit[first_newline+1:].startswith('Merge:'):
return (commit + '\n\n'
+ self.diff_helper(commit=sha1,
cached=False,
suppress_header=False))
return (decode(commit) + '\n\n' +
decode(self.diff_helper(commit=sha1,
cached=False,
suppress_header=False)))
else:
return commit
return decode(commit)
def get_filename(self, idx, staged=True):
try:
@@ -661,7 +662,11 @@ class Model(model.Model):
config_lines = self.git.config(**kwargs).splitlines()
newdict = {}
for line in config_lines:
k, v = line.split('=', 1)
try:
k, v = line.split('=', 1)
except:
# the user has an invalid entry in their git config
continue
v = decode(v)
k = k.replace('.','_') # git -> model
if v == 'true' or v == 'false':
@@ -689,17 +694,15 @@ class Model(model.Model):
fh.close()
# Run 'git commit'
(status, stdout, stderr) = self.git.commit(F=tmpfile,
v=True,
amend=amend,
with_extended_output=True)
status, out = self.git.commit(F=tmpfile, v=True, amend=amend,
with_extended_output=True,
with_stderr=True)
os.unlink(tmpfile)
return (status, stdout+stderr)
return status, out
def diffindex(self):
return self.git.diff(unified=self.diff_context,
no_color=True,
stat=True,
cached=True)
@@ -727,7 +730,7 @@ class Model(model.Model):
summaries = []
regex = REV_LIST_REGEX
output = self.git.log(pretty='oneline', all=all)
for line in output.splitlines():
for line in map(decode, output.splitlines()):
match = regex.match(line)
if match:
revs.append(match.group(1))
@@ -736,7 +739,7 @@ class Model(model.Model):
def parse_rev_list(self, raw_revs):
revs = []
for line in raw_revs.splitlines():
for line in map(decode, raw_revs.splitlines()):
match = REV_LIST_REGEX.match(line)
if match:
rev_id = match.group(1)
@@ -786,20 +789,21 @@ class Model(model.Model):
deleted = cached and not os.path.exists(encode(filename))
diffoutput = self.git.diff(R=reverse,
no_color=True,
cached=cached,
patch_with_raw=patch_with_raw,
unified=self.diff_context,
with_raw_output=True,
*argv)
diff = diffoutput.splitlines()
diff = diffoutput.split('\n')
for line in map(decode, diff):
if not start and '@@' == line[:2] and '@@' in line[2:]:
start = True
if start or(deleted and del_tag in line):
if start or (deleted and del_tag in line):
output.write(encode(line) + '\n')
else:
if with_diff_header:
headers.append(line)
headers.append(encode(line))
elif not suppress_header:
output.write(encode(line) + '\n')
@@ -844,18 +848,20 @@ class Model(model.Model):
"""RETURNS: A tuple of staged, unstaged untracked, and unmerged
file lists.
"""
self.git.update_index(refresh=True)
self.partially_staged = set()
head = 'HEAD'
if amend:
head = 'HEAD^'
(staged, modified, unmerged, untracked) = ([], [], [], [])
try:
for name in self.git.diff_index(head).splitlines():
rest, name = name.split('\t')
for line in self.git.diff_index(head).splitlines():
rest, name = line.split('\t')
status = rest[-1]
name = eval_path(name)
if status == 'M' or status == 'D':
modified.append(name)
modified.append(eval_path(name))
elif status == 'A':
unmerged.append(eval_path(name))
except:
# handle git init
for name in (self.git.ls_files(modified=True, z=True)
@@ -864,26 +870,31 @@ class Model(model.Model):
modified.append(decode(name))
try:
for name in (self.git.diff_index(head, cached=True)
for line in (self.git.diff_index(head, cached=True)
.splitlines()):
rest, name = name.split('\t')
rest, name = line.split('\t')
status = rest[-1]
name = eval_path(name)
if status == 'M':
staged.append(name)
# is this file partially staged?
diff = self.git.diff('--', name, name_only=True, z=True)
if not diff.strip():
status, out = self.git.diff('--', name,
name_only=True, exit_code=True,
with_extended_output=True)
if status == 0:
modified.remove(name)
else:
self.partially_staged.add(name)
elif status == 'A':
staged.append(name)
if name in unmerged:
unmerged.remove(name)
elif status == 'D':
staged.append(name)
modified.remove(name)
elif status == 'U':
unmerged.append(name)
modified.remove(name)
except:
# handle git init
for name in self.git.ls_files(z=True).strip('\0').split('\0'):
@@ -895,11 +906,6 @@ class Model(model.Model):
if name:
untracked.append(decode(name))
# remove duplicate merged and modified entries
for u in unmerged:
if u in modified:
modified.remove(u)
return (staged, modified, unmerged, untracked)
def reset_helper(self, args):
@@ -924,8 +930,11 @@ class Model(model.Model):
return self.git.config('remote.%s.url' % name, get=True)
def get_remote_args(self, remote,
local_branch='', remote_branch='',
ffwd=True, tags=False):
local_branch='',
remote_branch='',
ffwd=True,
tags=False,
rebase=False):
if ffwd:
branch_arg = '%s:%s' % ( remote_branch, local_branch )
else:
@@ -936,6 +945,8 @@ class Model(model.Model):
kwargs = {
'verbose': True,
'tags': tags,
'rebase': rebase,
'with_stderr': True,
}
return (args, kwargs)
@@ -1038,6 +1049,7 @@ class Model(model.Model):
return self.git.diff(
'HEAD^',
unified=self.diff_context,
no_color=True,
stat=True)
def pad(self, pstr, num=22):
@@ -1086,6 +1098,8 @@ class Model(model.Model):
def get_renamed_files(self, start, end):
files = []
difflines = self.git.diff('%s..%s' % (start, end), M=True).splitlines()
difflines = self.git.diff('%s..%s' % (start, end),
no_color=True,
M=True).splitlines()
return [ eval_path(r[12:].rstrip())
for r in difflines if r.startswith('rename from ') ]
+21 -1
View File
@@ -49,6 +49,8 @@ class QObserver(Observer, QObject):
self.__model_to_view = {}
self.__view_to_model = {}
self.__connected = set()
self.__in_textfield = False
self.__in_callback = False
# Call the subclass's startup routine
self.init(model, view, *args, **kwargs)
@@ -60,6 +62,8 @@ class QObserver(Observer, QObject):
"""Default slot to handle all Qt callbacks.
This method delegates to callbacks from add_signals."""
self.__in_textfield = False
widget = self.sender()
sender = str(widget.objectName())
@@ -67,10 +71,12 @@ class QObserver(Observer, QObject):
model = self.model
model_param = self.__view_to_model[sender]
if isinstance(widget, QTextEdit):
self.__in_textfield = True
value = unicode(widget.toPlainText())
model.set_param(model_param, value,
notify=False)
elif isinstance(widget, QLineEdit):
self.__in_textfield = True
value = unicode(widget.text())
model.set_param(model_param, value)
elif isinstance(widget, QCheckBox):
@@ -79,6 +85,11 @@ class QObserver(Observer, QObject):
model.set_param(model_param, widget.value())
elif isinstance(widget, QFontComboBox):
value = unicode(widget.currentFont().toString())
if model.has_param(model_param+'_size'):
size = model.get_param(model_param+'_size')
props = value.split(',')
props[1] = str(size)
value = ','.join(props)
model.set_param(model_param, value)
elif isinstance(widget, QDateEdit):
fmt = Qt.ISODate
@@ -121,12 +132,15 @@ class QObserver(Observer, QObject):
model.get_param(model_param)[idx])
else:
model.set_param(model_param+'_item', '')
else:
print("SLOT(): Unknown widget:", sender, widget)
self.__in_callback = True
if sender in self.__callbacks:
self.__callbacks[sender](*args)
self.__in_callback = False
self.__in_textfield = False
def connect(self, obj, signal_str, *args):
"""Convenience function so that subclasses do not have
@@ -223,6 +237,12 @@ class QObserver(Observer, QObject):
def subject_changed(self, param, value):
"""Sends a model param to the view(model->view)"""
if self.__in_textfield and not self.__in_callback:
# A slot has changed the model and we're not in
# a user callback. In this case the event is causing
# a feedback loop so skip redundant work and return.
return
if param in self.__model_to_view:
notify = self.model.get_notify()
self.model.set_notify(False)
+4 -11
View File
@@ -30,10 +30,11 @@ def log(output, quiet=True, doraise=False):
LOGGER.show()
if not doraise:
return
raise_logger()
LOGGER.raise_()
def raise_logger():
def show_logger():
"""Raises the log window."""
LOGGER.show()
LOGGER.raise_()
def input(msg, title=None):
@@ -142,7 +143,7 @@ def open_dialog(parent, title, filename=None):
def opendir_dialog(parent, title, directory):
"""Creates an Open Directory dialog and returns the file path."""
flags = QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontResolveSymlinks
flags = QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
return unicode(QFileDialog.getExistingDirectory(parent, parent.tr(title),
directory, flags))
@@ -152,14 +153,6 @@ def save_dialog(parent, title, filename=''):
parent.tr(title),
filename))
def new_dir_dialog(parent, title, filename=''):
"""Creates a New Directory dialog and returns the file path."""
return unicode(QFileDialog.getSaveFileName(parent,
parent.tr(title),
filename,
os.getcwd(),
parent.tr('New Directory ()')))
def get_icon(filename):
"""Given a basename returns a QIcon from the corresponding cola icon."""
return QIcon(utils.get_icon(filename))
+45 -12
View File
@@ -7,6 +7,8 @@ import re
import sys
import platform
import subprocess
import mimetypes
from glob import glob
from cStringIO import StringIO
@@ -21,17 +23,23 @@ ICONSDIR = os.path.join(PREFIX, 'share', 'cola', 'icons')
STYLEDIR = os.path.join(PREFIX, 'share', 'cola', 'styles')
DOCDIR = os.path.join(PREFIX, 'share', 'doc', 'cola')
KNOWN_FILE_TYPES = {
'ascii c': 'c.png',
KNOWN_FILE_MIME_TYPES = {
'text': 'script.png',
'image': 'image.png',
'python': 'script.png',
'ruby': 'script.png',
'shell': 'script.png',
'perl': 'script.png',
'java': 'script.png',
'assembler': 'binary.png',
'binary': 'binary.png',
'byte': 'binary.png',
'image': 'image.png',
'octet': 'binary.png',
}
KNOWN_FILE_EXTENSION = {
'.java': 'script.png',
'.groovy': 'script.png',
'.cpp': 'script.png',
'.c': 'script.png',
'.h': 'script.png',
'.cxx': 'script.png',
}
def run_cmd(*command):
@@ -64,6 +72,17 @@ def get_stylesheet(name):
else:
return None
def get_libexec(*args):
"""Returns the path to the libexec scripts dir.
This currently == 'bin' but it may change in the future.
"""
libexec = os.path.dirname(os.path.abspath(sys.argv[0]))
return os.path.join(libexec, *args)
def get_style_dir():
"""Returns the path to the style dir within the cola install tree."""
return STYLEDIR
def get_htmldocs():
"""Returns the path to the cola html documentation."""
return os.path.join(DOCDIR, 'git-cola.html')
@@ -71,10 +90,16 @@ def get_htmldocs():
def ident_file_type(filename):
"""Returns an icon based on the contents of filename."""
if os.path.exists(filename):
fileinfo = run_cmd('file','-b',filename)
for filetype, iconname in KNOWN_FILE_TYPES.iteritems():
if filetype in fileinfo.lower():
filemimetype = mimetypes.guess_type(filename)
if filemimetype[0] != None:
for filetype, iconname in KNOWN_FILE_MIME_TYPES.iteritems():
if filetype in filemimetype[0].lower():
return iconname
filename = filename.lower()
for fileext, iconname in KNOWN_FILE_EXTENSION.iteritems():
if filename.endswith(fileext):
return iconname
return 'generic.png'
else:
return 'removed.png'
# Fallback for modified files of an unknown type
@@ -103,7 +128,7 @@ def fork(*args):
return os.spawnv(os.P_NOWAIT, filename, args)
except os.error:
pass
raise IOError('cannot find executable: %s' % program)
raise IOError('cannot find executable: %s' % args[0])
else:
argv = map(shell_quote, args)
return os.system(' '.join(argv) + '&')
@@ -340,7 +365,7 @@ class DiffParser(object):
self.__idx = -1
self.__headers = []
for idx, line in enumerate(diff.splitlines()):
for idx, line in enumerate(diff.split('\n')):
match = self.__header_re.match(line)
if match:
self.__headers.append([
@@ -373,6 +398,14 @@ class DiffParser(object):
"""Processes a diff selection and applies changes to the work tree
or index."""
if selection:
# qt destroys \r\n and makes it \n with no way of going back.
# boo! we work around that here
if selection not in self.fwd_diff:
special_selection = selection.replace('\n', '\r\n')
if special_selection in self.fwd_diff:
selection = special_selection
else:
return
start = self.fwd_diff.index(selection)
end = start + len(selection)
self.set_diffs_to_range(start, end)
+26 -17
View File
@@ -6,18 +6,20 @@
import sys
import time
from PyQt4 import QtCore
from PyQt4.QtGui import QDialog
from PyQt4.QtGui import QListWidget
from PyQt4.QtGui import qApp
from PyQt4.QtCore import SIGNAL
from cola.syntax import DiffSyntaxHighlighter
from cola.syntax import LogSyntaxHighlighter
from cola.views.standard import create_standard_view
from cola.views.syntax import DiffSyntaxHighlighter
from cola.views.syntax import LogSyntaxHighlighter
from cola.core import decode
try:
from main import View
from main import CreateStandardView
from cola.gui.about import Ui_about
from cola.gui.bookmark import Ui_bookmark
from cola.gui.branchview import Ui_branchview
from cola.gui.combo import Ui_combo
@@ -36,15 +38,22 @@ except ImportError:
'Try running "make" in the cola source tree.\n')
sys.exit(-1)
# These are views that do not contain any custom methods
OptionsView = CreateStandardView(Ui_options, QDialog)
BranchCompareView = CreateStandardView(Ui_branchview, QDialog)
CreateBranchView = CreateStandardView(Ui_createbranch, QDialog)
BookmarkView = CreateStandardView(Ui_bookmark, QDialog)
StashView = CreateStandardView(Ui_stash, QDialog)
CompareView = CreateStandardView(Ui_compare, QDialog)
class AboutView(Ui_about, QDialog):
def __init__(self, parent):
QDialog.__init__(self, parent)
Ui_about.__init__(self)
self.setupUi(self)
def set_version(self, version):
self.spam.setText(self.spam.text().replace('$VERSION', version))
class LogView(CreateStandardView(Ui_logger, QDialog)):
OptionsView = create_standard_view(Ui_options, QDialog)
BranchCompareView = create_standard_view(Ui_branchview, QDialog)
CreateBranchView = create_standard_view(Ui_createbranch, QDialog)
BookmarkView = create_standard_view(Ui_bookmark, QDialog)
StashView = create_standard_view(Ui_stash, QDialog)
CompareView = create_standard_view(Ui_compare, QDialog)
class LogView(create_standard_view(Ui_logger, QDialog)):
"""A simple dialog to display command logs."""
def init(self, parent=None, output=None):
self.setWindowTitle(self.tr('Git Command Log'))
@@ -91,14 +100,14 @@ class ItemView(object):
else:
return None
class ComboView(CreateStandardView(Ui_combo, QDialog, ItemView), ItemView):
class ComboView(create_standard_view(Ui_combo, QDialog, ItemView), ItemView):
"""A dialog for choosing branches."""
def idx(self):
return self.items_widget.currentIndex()
def value(self):
return str(self.items_widget.currentText())
class ListView(CreateStandardView(Ui_items, QDialog, ItemView), ItemView):
class ListView(create_standard_view(Ui_items, QDialog, ItemView), ItemView):
"""A dialog for an item from a list."""
def idx(self):
return self.items_widget.currentRow()
@@ -108,7 +117,7 @@ class ListView(CreateStandardView(Ui_items, QDialog, ItemView), ItemView):
return None
return str(item.text())
class CommitView(CreateStandardView(Ui_commit, QDialog)):
class CommitView(create_standard_view(Ui_commit, QDialog)):
def init(self, parent=None, title=None):
if title: self.setWindowTitle(title)
# Make the list widget slighty larger
@@ -116,17 +125,17 @@ class CommitView(CreateStandardView(Ui_commit, QDialog)):
self.syntax = DiffSyntaxHighlighter(self.commit_text.document(),
whitespace=False)
class SearchView(CreateStandardView(Ui_search, QDialog)):
class SearchView(create_standard_view(Ui_search, QDialog)):
def init(self, parent=None):
self.input.setFocus()
self.syntax = DiffSyntaxHighlighter(self.commit_text.document(),
whitespace=False)
class MergeView(CreateStandardView(Ui_merge, QDialog)):
class MergeView(create_standard_view(Ui_merge, QDialog)):
def init(self, parent=None):
self.revision.setFocus()
class RemoteView(CreateStandardView(Ui_remote, QDialog)):
class RemoteView(create_standard_view(Ui_remote, QDialog)):
def init(self, parent=None, button_text=''):
if button_text:
self.action_button.setText(button_text)
+5 -36
View File
@@ -6,39 +6,12 @@ from PyQt4.QtCore import SIGNAL
from PyQt4.QtGui import QMainWindow
from cola import qtutils
from cola import syntax
from cola.syntax import DiffSyntaxHighlighter
from cola.views.standard import create_standard_view
from cola.views.syntax import DiffSyntaxHighlighter
from cola.gui.main import Ui_main
def CreateStandardView(uiclass, qtclass, *classes):
"""CreateStandardView returns a class closure of uiclass and qtclass.
This class performs the standard setup common to all view classes."""
class StandardView(uiclass, qtclass):
def __init__(self, parent=None, *args, **kwargs):
qtclass.__init__(self, parent)
uiclass.__init__(self)
self.parent_view = parent
syntax.set_theme_properties(self)
self.setupUi(self)
self.init(parent, *args, **kwargs)
for cls in classes:
cls.init(self, parent, *args, **kwargs)
def init(self, parent, *args, **kwargs):
pass
def get_properties(self):
# user-definable color properties
props = {}
for name in syntax.default_colors:
props[name] = getattr(self, '_'+name)
return props
def reset_syntax(self):
if hasattr(self, 'syntax') and self.syntax:
self.syntax.set_colors(self.get_properties())
self.syntax.reset()
syntax.install_theme_properties(StandardView)
return StandardView
class View(CreateStandardView(Ui_main, QMainWindow)):
class View(create_standard_view(Ui_main, QMainWindow)):
"""The main cola interface."""
IDX_STAGED = 0
IDX_MODIFIED = 1
@@ -60,10 +33,6 @@ class View(CreateStandardView(Ui_main, QMainWindow)):
# Default to creating a new commit(i.e. not an amend commit)
self.new_commit_radio.setChecked(True)
self.toolbar_show_log =\
self.toolbar.addAction(qtutils.get_icon('git.png'),
'Show/Hide Log Window')
self.toolbar_show_log.setEnabled(True)
# Diff/patch syntax highlighter
self.syntax = DiffSyntaxHighlighter(self.display_text.document())
@@ -239,7 +208,7 @@ class View(CreateStandardView(Ui_main, QMainWindow)):
translated = unicode(txt)
self.statusBar().showMessage(translated)
def show_editor(self):
self.editor_dock.raise_()
self.tabwidget.setCurrentIndex(1)
def show_diff(self):
self.tabwidget.setCurrentIndex(0)
def action_cut(self):
@@ -282,7 +251,7 @@ class View(CreateStandardView(Ui_main, QMainWindow)):
return line
def display(self, text):
self.set_display(text)
self.diff_dock.raise_()
self.show_diff()
def show_current_column(self):
cursor = self.commitmsg.textCursor()
colnum = cursor.columnNumber()
+56
View File
@@ -0,0 +1,56 @@
from PyQt4 import QtCore
from cola.views import syntax
def create_standard_view(uiclass, qtclass, *classes):
"""create_standard_view returns a class closure of uiclass and qtclass.
This class performs the standard setup common to all view classes.
The reason we use a closure is because uiclass and qtclass are
both dynamic.
"""
class StandardView(uiclass, qtclass):
def __init__(self, parent=None, *args, **kwargs):
qtclass.__init__(self, parent)
uiclass.__init__(self)
self.__qtclass = qtclass
self.setWindowFlags(QtCore.Qt.Window)
self.parent_view = parent
syntax.set_theme_properties(self)
self.setupUi(self)
self.init(parent, *args, **kwargs)
for cls in classes:
cls.init(self, parent, *args, **kwargs)
def init(self, parent, *args, **kwargs):
pass
def show(self):
"""Automatically centers dialogs relative to their
parent window.
"""
if self.parent_view:
left = self.parent_view.x()
width = self.parent_view.width()
center_x = left + width/2
x = center_x - self.width()/2
y = self.parent_view.y()
self.move(x, y)
# Call the base Qt show()
self.__qtclass.show(self)
def get_properties(self):
# user-definable color properties
props = {}
for name in syntax.default_colors:
props[name] = getattr(self, '_'+name)
return props
def reset_syntax(self):
if hasattr(self, 'syntax') and self.syntax:
self.syntax.set_colors(self.get_properties())
self.syntax.reset()
syntax.install_theme_properties(StandardView)
return StandardView
+48 -5
View File
@@ -16,6 +16,7 @@ def main():
# ensure readable files
old_mask = os.umask(0022)
if sys.argv[1] in ['install', 'build']:
__setup_environment()
__check_python_version()
__check_git_version()
__check_pyqt_version()
@@ -29,7 +30,22 @@ def main():
# restore the old mask
os.umask(old_mask)
def __setup_environment():
if sys.platform != 'win32':
return
path = os.environ['PATH']
win32 = os.path.join(os.path.dirname(__file__), 'win32')
os.environ['PATH'] = win32 + os.pathsep + path
def __run_setup():
scripts = ['bin/git-cola', 'bin/git-difftool', 'bin/git-difftool-helper']
if sys.platform == 'win32':
scripts.append('win32/cola')
scripts.append('win32/dirname')
scripts.append('win32/py2exe-setup.py')
scripts.append('win32/py2exe-setup.cmd')
setup(name = 'cola',
version = version.version,
license = 'GPLv2',
@@ -38,7 +54,7 @@ def __run_setup():
url = 'http://cola.tuxfamily.org/',
description = 'GIT Cola',
long_description = 'A highly caffeinated GIT GUI',
scripts = ['bin/git-cola', 'bin/git-difftool'],
scripts = scripts,
packages = ['cola', 'cola.gui', 'cola.views', 'cola.controllers'],
data_files = [
__app_path('share/cola/qm', '*.qm'),
@@ -95,8 +111,13 @@ def __check_git_version():
def __check_pyqt_version():
"""Check the minimum PYQT version
"""
pyqtver = utils.run_cmd('pyuic4', '--version').split()[-1]
if not __check_min_version(version.pyqt_min_ver, pyqtver):
fail = False
try:
pyqtver = __run_cmd('pyuic4', '--version').split()[-1]
except IndexError:
pyqtver = 'nothing'
fail = True
if fail or not __check_min_version(version.pyqt_min_ver, pyqtver):
print >> sys.stderr, 'PYQT version %s or newer required. Found %s' \
% (version.pyqt_min_ver, pyqtver)
sys.exit(1)
@@ -108,15 +129,29 @@ def __dirty(src, dst):
dststat = os.stat(dst)
return srcstat[stat.ST_MTIME] > dststat[stat.ST_MTIME]
def __workaround_pyuic4(src, dst):
fh = open(src, 'r')
contents = fh.read()
fh.close()
fh = open(dst, 'w')
for line in contents.splitlines():
if 'sortingenabled' in line.lower():
continue
fh.write(line+os.linesep)
fh.close()
os.unlink(src)
def __build_views():
print 'running build_views'
views = os.path.join('cola', 'gui')
sources = glob('ui/*.ui')
for src in sources:
dst = os.path.join(views, os.path.basename(src)[:-3] + '.py')
dsttmp = dst + '.tmp'
if __dirty(src, dst):
print '\tpyuic4 -x %s -o %s' % (src, dst)
utils.run_cmd('pyuic4', '-x', src, '-o', dst)
print '\tpyuic4 -x %s -o %s' % (src, dsttmp)
utils.run_cmd('pyuic4', '-x', src, '-o', dsttmp)
__workaround_pyuic4(dsttmp, dst)
def __build_translations():
print 'running build_translations'
@@ -128,4 +163,12 @@ def __build_translations():
print '\tmsgfmt --qt %s -o %s' % (src, dst)
utils.run_cmd('msgfmt', '--qt', src, '-o', dst)
def __run_cmd(*args):
argstr = utils.shell_quote(*args)
pipe = os.popen(argstr)
contents = pipe.read().strip()
pipe.close()
return contents
main()
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

+3 -3
View File
@@ -85,7 +85,7 @@ SOURCE
A 'git-cola' development repository can be obtained via git:
git clone git://gitorious.org/git-cola/mainline.git cola
git clone git://github.com/davvid/git-cola.git
STYLESHEETS
@@ -105,8 +105,8 @@ LINKS
'git-cola' homepage::
http://cola.tuxfamily.org/
'git-cola' sources on gitorious.org::
http://gitorious.org/projects/git-cola/
'git-cola' sources on github::
http://github.com/davvid/git-cola/tree/master
SEE ALSO
+39 -55
View File
@@ -3,38 +3,36 @@ git-difftool(1)
NAME
----
git-difftool - compare changes using standard git merge tools
git-difftool - compare changes using common merge tools
SYNOPSIS
--------
'git difftool' [--tool=<tool>]
[--commit=<ref> | --start=<ref> --end=<ref>]
[--no-prompt]
[--] [<file>]...
'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
DESCRIPTION
-----------
'git-difftool' is a git command that allows you to compare and edit files
between revisions using standard git merge tools. At its most basic level,
between revisions using common merge tools. At its most basic level,
'git-difftool' does what 'git-mergetool' does but its use is for non-merge
situations, such as when preparing commits or comparing changes against
situations such as when preparing commits or comparing changes against
the index.
'git-difftool' shows differences between the work tree and the index by
default but can also show differences between arbitrary commits by using
the --start and --end options.
'git difftool' is a frontend to 'git diff' and accepts the same
arguments and options.
See linkgit:git-diff[1] for the full list of supported options.
OPTIONS
-------
-t or --tool=<tool>::
-t <tool>::
--tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
+
If a merge resolution program is not specified, 'git-difftool'
will use the configuration variable `merge.tool`. If the
configuration variable `merge.tool` is not set, 'git-difftool'
configuration variable `merge.tool` is not set, 'git difftool'
will pick a suitable default.
+
You can explicitly provide a full path to the tool by setting the
@@ -51,41 +49,17 @@ variable `mergetool.<tool>.cmd`.
When 'git-difftool' is invoked with this tool (either through the
`-t` or `--tool` option or the `merge.tool` configuration variable)
the configured command line will be invoked with the following
variables available: `$BASE` is set to the name of a temporary file
containing the contents of the index; `$LOCAL` set to the name of a
temporary file containing the contents of the file as it appears in
the work tree; `$REMOTE` set to the name of a temporary file
containing the contents of the file to be merged, and `$MERGED` set
to the name of the file to which the merge tool should write the
result of the merge resolution.
+
When `--start=<ref>` and `--end=<ref>` are specified
`$LOCAL` contains the contents of the file from the
starting revision and `$REMOTE` contains the contents
of the file in the ending revision. `$BASE` contains
the contents of the file in the work tree if it exists,
and the starting revision otherwise.
+
If the custom merge tool correctly indicates the success of a
merge resolution with its exit code then the configuration
variable `mergetool.<tool>.trustExitCode` can be set to `true`.
Otherwise, 'git-difftool' will prompt the user to indicate the
success of the resolution after the custom tool has exited.
-c or --commit=<ref>::
Specifies a commit reference for comparing against.
'git-difftool' compares against the index by default.
variables available: `$LOCAL` is set to the name of the temporary
file containing the contents of the diff pre-image and `$REMOTE`
is set to the name of the temporary file containing the contents
of the diff post-image. `$BASE` is provided for compatibility
with custom merge tool commands and has the same value as `$LOCAL`.
--no-prompt::
Do not prompt before and after launching a merge tool.
-s or --start=<ref> and -e or --end=<ref>::
See changes between commits start and end.
This example shows the latest commit: `--start=HEAD^ --end=HEAD`.
Do not prompt before launching a diff tool.
CONFIG VARIABLES
----------------
merge.tool::
The default merge tool to use.
+
@@ -104,28 +78,38 @@ mergetool.<tool>.cmd::
+
See the `--tool=<tool>` option above for more details.
mergetool.<tool>.trustExitCode::
For a custom merge command, specify whether the exit code of
the merge command can be used to determine whether the merge was
successful. If this is not set to true then the merge target file
timestamp is checked and the merge assumed to have been successful
if the file has been updated, otherwise the user is prompted to
indicate the success of the merge.
SEE ALSO
--------
linkgit:git-diff[1]::
Show changes between commits, commit and working tree, etc
linkcola:git-cola[1]::
'git-difftool' is part of the 'git-cola' project.
linkgit:git-mergetool[1]::
'git-difftool' uses many of the same concepts as the core
'git-mergetool' command.
Run merge conflict resolution tools to resolve merge conflicts
linkgit:git-config[1]::
Get and set repository or global options
linkgit:git-config[7]::
Get and set repository or global options.
AUTHOR
------
Written by David Aguilar <davvid@gmail.com>.
Documentation
--------------
Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
GIT
---
Part of the linkgit:git[1] suite
NOTE: Patches to add 'git-difftool' to git.git's contrib area
have been sent to the git mailing list. Packagers are encouraged
to use git.git's contrib/difftool/ scripts instead of the ones
distributed with cola. If git decides to make difftool part of
its core command set then cola will promptly add build rules to
conditionally install difftool based on the user's git version.
Vendored Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env python
import sys
sys.stderr.write('\0' * int(sys.argv[1]))
Vendored Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env python
import sys
sys.stdout.write('\0' * int(sys.argv[1]))
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env python
import sys
if sys.argv[2] == 'stdout':
sys.stdout.write('\0' * int(sys.argv[1]))
sys.stderr.write('\0' * int(sys.argv[1]))
else:
sys.stderr.write('\0' * int(sys.argv[1]))
sys.stdout.write('\0' * int(sys.argv[1]))
+4
View File
@@ -0,0 +1,4 @@
import os
def fixture(*paths):
return os.path.join(os.path.dirname(__file__), 'fixtures', *paths)
+77 -5
View File
@@ -4,22 +4,94 @@ import unittest
from cola import git
import helper
class GitCommandTest(unittest.TestCase):
def setUp(self):
self.git = git.Git()
def testGitVersion(self):
def test_version(self):
version = self.git.version()
self.failUnless(version.startswith('git version'))
def testGitTag(self):
def test_tag(self):
tags = self.git.tag().splitlines()
self.failUnless( 'v1.0.0' in tags )
def testGitShow(self):
id = '1b9742bda5d26a4f250fa64657f66ed20624a084'
contents = self.git.show(id).splitlines()
def test_show(self):
sha = '1b9742bda5d26a4f250fa64657f66ed20624a084'
contents = self.git.show(sha).splitlines()
self.failUnless(contents[0] == '/build')
def test_stdout(self):
status, out = git.Git.execute([helper.fixture('stdout.py'), '65536'],
with_extended_output=True,
with_raw_output=True)
self.assertEqual(status, 0)
self.assertEqual(len(out), 65536)
def test_stderr_empty(self):
"""Test that stderr is ignored by Git.execute() without the with_stderr option."""
status, out = git.Git.execute([helper.fixture('stderr.py'), '65536'],
with_extended_output=True,
with_raw_output=True)
self.assertEqual(status, 0)
self.assertEqual(len(out), 0)
def test_stderr_nonempty_with_stderr(self):
"""Test that with_stderr makes Git.execute() see stderr."""
status, out = git.Git.execute([helper.fixture('stderr.py'), '65536'],
with_extended_output=True,
with_stderr=True,
with_raw_output=True)
self.assertEqual(status, 0)
self.assertEqual(len(out), 65536)
def test_stdout_and_stderr_ignores_stderr(self):
"""Test ignoring stderr when both are provided."""
status, out = git.Git.execute([helper.fixture('stdout_and_stderr.py'),
'65536',
'stdout'], # otherwise, same as below
with_extended_output=True,
with_raw_output=True)
self.assertEqual(status, 0)
self.assertEqual(len(out), 65536)
def test_stdout_and_stderr_ignores_stderr_v2(self):
"""Test ignoring stderr when both are provided. (v2)"""
# stdout_and_stderr.py swaps the order of the stdout, stderr write()
# calls when argv[1] == 'stderr'.
status, out = git.Git.execute([helper.fixture('stdout_and_stderr.py'),
'65536',
'stderr'], # otherwise, same as above
with_extended_output=True,
with_raw_output=True)
self.assertEqual(status, 0)
self.assertEqual(len(out), 65536)
def test_stdout_and_stderr_sees_stderr(self):
"""Test seeing both stderr and stdout when both are available."""
status, out = git.Git.execute([helper.fixture('stdout_and_stderr.py'),
'65536',
'stdout'], # otherwise, same as below
with_extended_output=True,
with_stderr=True,
with_raw_output=True)
self.assertEqual(status, 0)
self.assertEqual(len(out), 65536 * 2)
def test_stdout_and_stderr_sees_stderr_v2(self):
"""Test seeing both stderr and stdout when both are available (v2)."""
# stdout_and_stderr.py swaps the order of the stdout, stderr write()
# calls when argv[1] == 'stderr'.
status, out = git.Git.execute([helper.fixture('stdout_and_stderr.py'),
'65536',
'stderr'], # otherwise, same as above
with_extended_output=True,
with_stderr=True,
with_raw_output=True)
self.assertEqual(status, 0)
self.assertEqual(len(out), 65536 * 2)
if __name__ == '__main__':
unittest.main()
+22 -10
View File
@@ -1,27 +1,31 @@
#!/usr/bin/env python
import os
import sys
import unittest
import testlib
from testlib import InnerModel
from testlib import NestedModel
import cola.model
from cola.model import Model
class TestSaveRestore(unittest.TestCase):
def setUp(self):
testlib.create_test_dir()
self.nested = NestedModel()
path = os.path.join(testlib.get_test_dir(), 'test.data')
# save & reconstitute
self.nested.save(path)
self.clone = Model.instance(path)
self._has_json = cola.model.has_json()
if self._has_json:
testlib.create_test_dir()
self.nested = NestedModel()
path = os.path.join(testlib.get_test_dir(), 'test.data')
# save & reconstitute
self.nested.save(path)
self.clone = Model.instance(path)
def tearDown(self):
testdir = testlib.get_test_dir()
if os.path.exists(testdir):
testlib.remove_test_dir()
if self._has_json:
testdir = testlib.get_test_dir()
if os.path.exists(testdir):
testlib.remove_test_dir()
def testCloneToClass(self):
self.failUnless( str(NestedModel) ==
@@ -55,5 +59,13 @@ class TestSaveRestore(unittest.TestCase):
self.failUnless( str(InnerModel) ==
str(self.clone.innerList[-1]["foo"].__class__))
# All of these tests require simplejson.
# Replace all test methods with a stub lambda function
# if simplejson is not available.
if not cola.model.has_json():
for thing in dir(TestSaveRestore):
if thing.startswith('test'):
setattr(TestSaveRestore, thing, lambda x: True)
if __name__ == '__main__':
unittest.main()
+3 -10
View File
@@ -8,17 +8,10 @@ import testlib
class ImportTest(testlib.TestCase):
pass
def gen_class(module):
def gen_class(themodule):
def import_test(self):
modinfo = None
for idx, path in enumerate(module.split('.')):
if idx == 0:
modinfo = imp.find_module(path)
mod = imp.load_module(module, *modinfo)
else:
modinfo = imp.find_module('cola/'+path, modinfo[0])
mod = imp.load_module(path, *modinfo)
self.failUnless(mod)
mod = __import__(themodule)
self.failUnless(mod)
return import_test
def __create_tests():
+141
View File
@@ -0,0 +1,141 @@
<ui version="4.0" >
<class>about</class>
<widget class="QDialog" name="about" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>280</width>
<height>420</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>280</width>
<height>420</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>280</width>
<height>420</height>
</size>
</property>
<property name="windowTitle" >
<string>about git cola</string>
</property>
<property name="styleSheet" >
<string notr="true" >QWidget { background-color: white; color: #666; }</string>
</property>
<widget class="QLabel" name="ham" >
<property name="geometry" >
<rect>
<x>0</x>
<y>-10</y>
<width>291</width>
<height>121</height>
</rect>
</property>
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap>images:logo-cola.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="spam" >
<property name="geometry" >
<rect>
<x>10</x>
<y>110</y>
<width>261</width>
<height>261</height>
</rect>
</property>
<property name="font" >
<font>
<family>Sans Serif</family>
<pointsize>7</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text" >
<string>git cola: a highly caffeinated git gui v$VERSION
cola is a sweet, carbonated git gui known for its
sugary flavour and caffeine-inspired features.
Copyright (C) 2009 David Aguilar and contributors
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, see http://www.gnu.org/licenses/.
</string>
</property>
<property name="textFormat" >
<enum>Qt::LogText</enum>
</property>
<property name="alignment" >
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
<property name="textInteractionFlags" >
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QPushButton" name="kthx" >
<property name="geometry" >
<rect>
<x>60</x>
<y>380</y>
<width>160</width>
<height>24</height>
</rect>
</property>
<property name="text" >
<string>kthx bye</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>kthx</sender>
<signal>clicked()</signal>
<receiver>about</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>139</x>
<y>391</y>
</hint>
<hint type="destinationlabel" >
<x>140</x>
<y>209</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+3 -19
View File
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>531</width>
<height>513</height>
<width>494</width>
<height>238</height>
</rect>
</property>
<property name="windowTitle" >
@@ -44,7 +44,7 @@
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0" >
<size>
<width>91</width>
<height>20</height>
@@ -72,22 +72,6 @@
</widget>
<resources/>
<connections>
<connection>
<sender>button_save</sender>
<signal>clicked()</signal>
<receiver>bookmark</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>398</x>
<y>490</y>
</hint>
<hint type="destinationlabel" >
<x>265</x>
<y>256</y>
</hint>
</hints>
</connection>
<connection>
<sender>button_cancel</sender>
<signal>clicked()</signal>
+2 -2
View File
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>680</width>
<height>537</height>
<width>658</width>
<height>350</height>
</rect>
</property>
<property name="windowTitle" >
+3 -3
View File
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>780</width>
<height>512</height>
<width>531</width>
<height>467</height>
</rect>
</property>
<layout class="QVBoxLayout" >
@@ -97,7 +97,7 @@
<item>
<widget class="QDialogButtonBox" name="button_box" >
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
+2 -2
View File
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>775</width>
<height>454</height>
<width>649</width>
<height>372</height>
</rect>
</property>
<property name="windowTitle" >
+9 -54
View File
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>621</width>
<height>402</height>
<width>527</width>
<height>304</height>
</rect>
</property>
<property name="windowTitle" >
@@ -16,16 +16,7 @@
<property name="spacing" >
<number>2</number>
</property>
<property name="leftMargin" >
<number>4</number>
</property>
<property name="topMargin" >
<number>4</number>
</property>
<property name="rightMargin" >
<number>4</number>
</property>
<property name="bottomMargin" >
<property name="margin" >
<number>4</number>
</property>
<item>
@@ -51,16 +42,7 @@
<property name="spacing" >
<number>2</number>
</property>
<property name="leftMargin" >
<number>4</number>
</property>
<property name="topMargin" >
<number>4</number>
</property>
<property name="rightMargin" >
<number>4</number>
</property>
<property name="bottomMargin" >
<property name="margin" >
<number>4</number>
</property>
<item>
@@ -111,7 +93,7 @@
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0" >
<size>
<width>201</width>
<height>20</height>
@@ -132,16 +114,7 @@
<property name="spacing" >
<number>2</number>
</property>
<property name="leftMargin" >
<number>4</number>
</property>
<property name="topMargin" >
<number>4</number>
</property>
<property name="rightMargin" >
<number>4</number>
</property>
<property name="bottomMargin" >
<property name="margin" >
<number>4</number>
</property>
<item>
@@ -153,16 +126,7 @@
<property name="spacing" >
<number>2</number>
</property>
<property name="leftMargin" >
<number>4</number>
</property>
<property name="topMargin" >
<number>4</number>
</property>
<property name="rightMargin" >
<number>4</number>
</property>
<property name="bottomMargin" >
<property name="margin" >
<number>4</number>
</property>
<item>
@@ -231,7 +195,7 @@
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0" >
<size>
<width>328</width>
<height>46</height>
@@ -249,16 +213,7 @@
<property name="spacing" >
<number>2</number>
</property>
<property name="leftMargin" >
<number>4</number>
</property>
<property name="topMargin" >
<number>4</number>
</property>
<property name="rightMargin" >
<number>4</number>
</property>
<property name="bottomMargin" >
<property name="margin" >
<number>4</number>
</property>
<item>
+2 -2
View File
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>490</width>
<height>372</height>
<width>523</width>
<height>299</height>
</rect>
</property>
<layout class="QVBoxLayout" >
+7 -16
View File
@@ -5,32 +5,23 @@
<rect>
<x>0</x>
<y>0</y>
<width>830</width>
<height>390</height>
<width>715</width>
<height>306</height>
</rect>
</property>
<property name="windowTitle" >
<string/>
</property>
<layout class="QGridLayout" >
<property name="leftMargin" >
<number>4</number>
</property>
<property name="topMargin" >
<number>4</number>
</property>
<property name="rightMargin" >
<number>4</number>
</property>
<property name="bottomMargin" >
<number>4</number>
</property>
<property name="horizontalSpacing" >
<number>4</number>
</property>
<property name="verticalSpacing" >
<number>0</number>
</property>
<property name="margin" >
<number>4</number>
</property>
<item row="0" column="0" colspan="4" >
<widget class="QTextEdit" name="output_text" >
<property name="font" >
@@ -55,7 +46,7 @@
<bool>false</bool>
</property>
<property name="textInteractionFlags" >
<set>Qt::NoTextInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@@ -105,7 +96,7 @@
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0" >
<size>
<width>217</width>
<height>24</height>
+37 -42
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>621</width>
<height>281</height>
<width>630</width>
<height>295</height>
</rect>
</property>
<property name="dockOptions" >
@@ -52,7 +52,7 @@
</sizepolicy>
</property>
<property name="currentIndex" >
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="diffwidget" >
<attribute name="title" >
@@ -154,6 +154,13 @@
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="rescan_button" >
<property name="text" >
<string>Rescan</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stage_button" >
<property name="sizePolicy" >
@@ -271,6 +278,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stash_button" >
<property name="text" >
<string>Stash</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
@@ -298,14 +312,6 @@
</widget>
</item>
</layout>
<zorder>stage_button</zorder>
<zorder>signoff_button</zorder>
<zorder>commit_button</zorder>
<zorder>fetch_button</zorder>
<zorder>push_button</zorder>
<zorder>pull_button</zorder>
<zorder>column_label</zorder>
<zorder></zorder>
</widget>
</item>
<item>
@@ -424,8 +430,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>621</width>
<height>22</height>
<width>630</width>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="commit_menu" >
@@ -461,6 +467,7 @@
<addaction name="menuPrepare" />
<addaction name="separator" />
<addaction name="menu_commit_compare" />
<addaction name="menu_commit_compare_file" />
<addaction name="separator" />
<addaction name="menu_stash" />
<addaction name="menu_export_patches" />
@@ -540,6 +547,7 @@
<addaction name="menu_clone_repo" />
<addaction name="separator" />
<addaction name="menu_rescan" />
<addaction name="menu_view_log" />
<addaction name="separator" />
<addaction name="menu_manage_bookmarks" />
<addaction name="menu_save_bookmark" />
@@ -562,6 +570,7 @@
<string>Help</string>
</property>
<addaction name="menu_help_docs" />
<addaction name="menu_help_about" />
</widget>
<addaction name="file_menu" />
<addaction name="edit_menu" />
@@ -571,35 +580,6 @@
<addaction name="merge_menu" />
<addaction name="help_menu" />
</widget>
<widget class="QToolBar" name="toolbar" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="windowTitle" >
<string>toolbar</string>
</property>
<property name="movable" >
<bool>true</bool>
</property>
<property name="allowedAreas" >
<set>Qt::BottomToolBarArea|Qt::TopToolBarArea</set>
</property>
<property name="iconSize" >
<size>
<width>24</width>
<height>16</height>
</size>
</property>
<property name="floatable" >
<bool>true</bool>
</property>
<attribute name="toolBarArea" >
<enum>BottomToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak" >
<bool>false</bool>
</attribute>
</widget>
<action name="menu_unstage_selected" >
<property name="text" >
<string>Unstage From Commit</string>
@@ -915,6 +895,21 @@
<string>Get Commit Message Template</string>
</property>
</action>
<action name="menu_commit_compare_file" >
<property name="text" >
<string>Compare File...</string>
</property>
</action>
<action name="menu_view_log" >
<property name="text" >
<string>VIew Log</string>
</property>
</action>
<action name="menu_help_about" >
<property name="text" >
<string>About</string>
</property>
</action>
</widget>
<layoutdefault spacing="2" margin="2" />
<resources/>
+2 -5
View File
@@ -41,10 +41,7 @@
<property name="bottomMargin" >
<number>5</number>
</property>
<property name="horizontalSpacing" >
<number>0</number>
</property>
<property name="verticalSpacing" >
<property name="spacing" >
<number>0</number>
</property>
<item row="0" column="0" >
@@ -102,7 +99,7 @@
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0" >
<size>
<width>231</width>
<height>24</height>
+130 -92
View File
@@ -8,16 +8,31 @@
<rect>
<x>0</x>
<y>0</y>
<width>654</width>
<height>370</height>
<width>469</width>
<height>384</height>
</rect>
</property>
<property name="windowTitle" >
<string>Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<layout class="QGridLayout" name="gridLayout_2" >
<layout class="QGridLayout" name="gridLayout_4" >
<property name="leftMargin" >
<number>8</number>
</property>
<property name="topMargin" >
<number>4</number>
</property>
<property name="rightMargin" >
<number>8</number>
</property>
<property name="bottomMargin" >
<number>4</number>
</property>
<property name="spacing" >
<number>4</number>
</property>
<item row="0" column="0" >
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
@@ -25,16 +40,6 @@
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QSpinBox" name="global_cola_fontuisize" >
<property name="minimum" >
<number>8</number>
</property>
<property name="value" >
<number>12</number>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2" >
<widget class="QFontComboBox" name="global_cola_fontui" >
<property name="currentFont" >
@@ -52,7 +57,7 @@
</widget>
</item>
<item row="1" column="1" >
<widget class="QSpinBox" name="global_cola_fontdiffsize" >
<widget class="QSpinBox" name="global_cola_fontdiff_size" >
<property name="minimum" >
<number>8</number>
</property>
@@ -74,71 +79,111 @@
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label_13" >
<widget class="QLabel" name="label_14" >
<property name="text" >
<string>Editor</string>
<string>Tab Width</string>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QLineEdit" name="global_gui_editor" >
<property name="text" >
<string/>
<widget class="QSpinBox" name="global_cola_tabwidth" >
<property name="wrapping" >
<bool>true</bool>
</property>
<property name="maximum" >
<number>42</number>
</property>
<property name="value" >
<number>8</number>
</property>
</widget>
</item>
<item row="2" column="2" >
<widget class="QLabel" name="label_11" >
<property name="text" >
<string>History browser</string>
</property>
</widget>
</item>
<item row="2" column="3" >
<widget class="QLineEdit" name="global_gui_historybrowser" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_12" >
<property name="text" >
<string>Merge Tool</string>
</property>
</widget>
</item>
<item row="3" column="1" >
<widget class="QLineEdit" name="global_merge_tool" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="3" column="2" >
<widget class="QCheckBox" name="global_cola_savewindowsettings" >
<property name="text" >
<string>Save GUI Settings</string>
</property>
</widget>
</item>
<item row="3" column="3" >
<item row="2" column="3" >
<widget class="QCheckBox" name="global_merge_keepbackup" >
<property name="text" >
<string>Save *.orig Merge Backups</string>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_13" >
<property name="text" >
<string>Editor</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3" >
<widget class="QLineEdit" name="global_gui_editor" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="label_11" >
<property name="text" >
<string>History Browser</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="3" >
<widget class="QLineEdit" name="global_gui_historybrowser" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QLabel" name="label_12" >
<property name="text" >
<string>Merge Tool</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="3" >
<widget class="QLineEdit" name="global_merge_tool" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QSpinBox" name="global_cola_fontui_size" >
<property name="minimum" >
<number>8</number>
</property>
<property name="value" >
<number>12</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8" >
<item row="1" column="0" >
<layout class="QHBoxLayout" name="horizontalLayout" >
<property name="spacing" >
<number>4</number>
</property>
<item>
<widget class="QGroupBox" name="local_groupbox" >
<property name="title" >
<string>Local Repository</string>
</property>
<layout class="QGridLayout" name="gridLayout" >
<layout class="QGridLayout" name="gridLayout_2" >
<item row="0" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>User Name</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2" >
<widget class="QLineEdit" name="local_user_name" />
</item>
@@ -153,27 +198,13 @@
<widget class="QLineEdit" name="local_user_email" />
</item>
<item row="2" column="0" colspan="2" >
<widget class="QCheckBox" name="local_merge_summary" >
<property name="text" >
<string>Summarize Merge Commits</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3" >
<widget class="QCheckBox" name="local_merge_diffstat" >
<property name="text" >
<string>Show Diffstat After Merge</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<string>Merge Verbosity</string>
</property>
</widget>
</item>
<item row="4" column="2" >
<item row="2" column="2" >
<widget class="QSpinBox" name="local_merge_verbosity" >
<property name="minimum" >
<number>0</number>
@@ -186,14 +217,14 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2" >
<item row="3" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>Number of Diff Context Lines</string>
</property>
</widget>
</item>
<item row="5" column="2" >
<item row="3" column="2" >
<widget class="QSpinBox" name="local_gui_diffcontext" >
<property name="minimum" >
<number>2</number>
@@ -203,10 +234,17 @@
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_4" >
<item row="4" column="0" colspan="2" >
<widget class="QCheckBox" name="local_merge_summary" >
<property name="text" >
<string>User Name</string>
<string>Summarize Merge Commits</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2" >
<widget class="QCheckBox" name="local_merge_diffstat" >
<property name="text" >
<string>Show Diffstat After Merge</string>
</property>
</widget>
</item>
@@ -218,7 +256,7 @@
<property name="title" >
<string>Global (All Repositories)</string>
</property>
<layout class="QGridLayout" >
<layout class="QGridLayout" name="gridLayout_3" >
<item row="0" column="0" >
<widget class="QLabel" name="label_7" >
<property name="text" >
@@ -240,27 +278,13 @@
<widget class="QLineEdit" name="global_user_email" />
</item>
<item row="2" column="0" colspan="2" >
<widget class="QCheckBox" name="global_merge_summary" >
<property name="text" >
<string>Summarize Merge Commits</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3" >
<widget class="QCheckBox" name="global_merge_diffstat" >
<property name="text" >
<string>Show Diffstat After Merge</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2" >
<widget class="QLabel" name="label_10" >
<property name="text" >
<string>Merge Verbosity</string>
</property>
</widget>
</item>
<item row="4" column="2" >
<item row="2" column="2" >
<widget class="QSpinBox" name="global_merge_verbosity" >
<property name="minimum" >
<number>0</number>
@@ -273,14 +297,14 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2" >
<item row="3" column="0" colspan="2" >
<widget class="QLabel" name="label_9" >
<property name="text" >
<string>Number of Diff Context Lines</string>
</property>
</widget>
</item>
<item row="5" column="2" >
<item row="3" column="2" >
<widget class="QSpinBox" name="global_gui_diffcontext" >
<property name="minimum" >
<number>2</number>
@@ -290,12 +314,26 @@
</property>
</widget>
</item>
<item row="4" column="0" colspan="2" >
<widget class="QCheckBox" name="global_merge_summary" >
<property name="text" >
<string>Summarize Merge Commits</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2" >
<widget class="QCheckBox" name="global_merge_diffstat" >
<property name="text" >
<string>Show Diffstat After Merge</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<item row="2" column="0" >
<layout class="QHBoxLayout" name="horizontalLayout_9" >
<item>
<spacer name="horizontalSpacer" >
+8 -1
View File
@@ -83,12 +83,19 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rebase_checkbox" >
<property name="text" >
<string>Rebase</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
+4 -4
View File
@@ -6,7 +6,7 @@
<x>0</x>
<y>0</y>
<width>600</width>
<height>550</height>
<height>400</height>
</rect>
</property>
<property name="windowTitle" >
@@ -97,7 +97,7 @@
<item row="2" column="3" >
<widget class="QDateEdit" name="start_date" >
<property name="currentSection" >
<enum>QDateTimeEdit::DaySection</enum>
<enum>QDateTimeEdit::YearSection</enum>
</property>
<property name="displayFormat" >
<string>yyyy-MM-dd</string>
@@ -110,7 +110,7 @@
<item row="2" column="4" >
<widget class="QDateEdit" name="end_date" >
<property name="currentSection" >
<enum>QDateTimeEdit::DaySection</enum>
<enum>QDateTimeEdit::YearSection</enum>
</property>
<property name="displayFormat" >
<string>yyyy-MM-dd</string>
@@ -216,7 +216,7 @@
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0" >
<size>
<width>111</width>
<height>20</height>
Executable
+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
# This should work with either Python2.5 or Python2.6.
dirname() {
dir=$(echo "$1" | perl -pe 's,(.*)/[^/]+,\1,')
test "$dir" = "$1" && dir=.
echo "$dir"
}
mydir="$(dirname $0)"
parentdir="$(dirname $mydir)"
for python in /c/Python26 /c/Python25; do
if test -d "$python"; then
PATH="$PATH":$python
exec $python/python.exe "$parentdir"/bin/git-cola "$@"
fi
done
exit 1
Executable
+22
View File
@@ -0,0 +1,22 @@
#!/bin/sh
""":"
exec python $0 ${1+"$@"}; exit
"""
# msysgit does not include dirname.exe, so this simple python
# script should do the trick.
import sys
if len(sys.argv) > 1:
item = sys.argv[1]
if '\\' in item:
joinchar = '\\' # allow win32..
else:
joinchar = '/' # but assume unix
item = item.rstrip(joinchar)
while joinchar+joinchar in item:
item = item.replace(joinchar+joinchar, joinchar)
items = item.split(joinchar)
dirname = joinchar.join(items[:-1])
print dirname or '.'
sys.exit(0)
else:
sys.exit(1)
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
for python in /c/Python26 /c/Python25; do
if test -d "$python"; then
exec $python/python.exe \
$python/Lib/site-packages/PyQt4/uic/pyuic.py "$@"
fi
done
exit 1