Merge commit 'origin/master'

* commit 'origin/master':
  CommitController: Don't turn of off automatic rearranging
  PBGitRepository: Clean up the readFromURL: method
  PBGitRepository: Fix opening of large directories due to bug in NSFileWrapper.
  PBGitIndexController: Renamed "Revert" to "Discard"
  CommitController: Reject merges
  HistoryController: Add "Open Files" menu item
  WebHistoryView: Also use the tree context menu
  HistoryController: Use a programatically created context menu
  WebHistoryController: Refactor menu search to be recursive
  HistoryController: Add some marks
  PBGitHistory: Add "Show in Finder" to files
  Display context menu in the history tree to show related commits
  History fileview: select current item on rightclick
  PBGitConfig: Add missing sentinel
  Remove use of deprecated stringWithCString
  HistoryView: only add parents if parents array exists
  keyboardNavigation: Fix keys 'c' and 'v' from webView
  Site: add link to Twitter

Conflicts:
	PBGitHistoryView.xib
	PBGitIndexController.m
This commit is contained in:
Pieter de Bie
2009-07-07 11:58:35 +01:00
14 changed files with 205 additions and 82 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ static NSString* gitPath = nil;
// Try to find the path of the Git binary
char* path = getenv("GIT_PATH");
if (path && [self acceptBinary:[NSString stringWithCString:path]])
if (path && [self acceptBinary:[NSString stringWithUTF8String:path]])
return;
// No explicit path. Try it with "which"
+5 -1
View File
@@ -201,7 +201,6 @@
- (void) readOtherFiles:(NSNotification *)notification;
{
[unstagedFilesController setAutomaticallyRearrangesObjects:NO];
NSArray *lines = [self linesFromNotification:notification];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:[lines count]];
// We fake this files status as good as possible.
@@ -324,6 +323,11 @@
- (IBAction) commit:(id) sender
{
if ([[NSFileManager defaultManager] fileExistsAtPath:[repository.fileURL.path stringByAppendingPathComponent:@"MERGE_HEAD"]]) {
[[repository windowController] showMessageSheet:@"Cannot commit merges" infoText:@"GitX cannot commit merges yet. Please commit your changes from the command line."];
return;
}
if ([[cachedFilesController arrangedObjects] count] == 0) {
[[repository windowController] showMessageSheet:@"No changes to commit" infoText:@"You must first stage some changes before committing"];
return;
+1 -1
View File
@@ -79,7 +79,7 @@
// Check if it exists globally. In that case, write it as a global
NSArray *arguments = [NSArray arrayWithObjects:@"config", @"--global", @"--get", path];
NSArray *arguments = [NSArray arrayWithObjects:@"config", @"--global", @"--get", path, nil];
int ret;
[PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:nil retValue:&ret];
if (!ret) // It exists globally
+7
View File
@@ -42,6 +42,13 @@
- (IBAction) openSelectedFile: sender;
- (void) updateQuicklookForce: (BOOL) force;
// Context menu methods
- (NSMenu *)contextMenuForTreeView;
- (NSArray *)menuItemsForPaths:(NSArray *)paths;
- (void)showCommitsFromTree:(id)sender;
- (void)showInFinderAction:(id)sender;
- (void)openFilesAction:(id)sender;
- (void) copyCommitInfo;
- (BOOL) hasNonlinearPath;
+73
View File
@@ -208,6 +208,7 @@
[super removeView];
}
#pragma mark Table Column Methods
- (NSMenu *)tableColumnMenu
{
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Table columns menu"];
@@ -223,4 +224,76 @@
return menu;
}
#pragma mark Tree Context Menu Methods
- (void)showCommitsFromTree:(id)sender
{
// TODO: Enable this from webview as well!
NSMutableArray *filePaths = [NSMutableArray arrayWithObjects:@"HEAD", @"--", NULL];
[filePaths addObjectsFromArray:[sender representedObject]];
PBGitRevSpecifier *revSpec = [[PBGitRevSpecifier alloc] initWithParameters:filePaths];
repository.currentBranch = [repository addBranch:revSpec];
}
- (void)showInFinderAction:(id)sender
{
NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"];
NSString *path;
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
for (NSString *filePath in [sender representedObject]) {
path = [workingDirectory stringByAppendingPathComponent:filePath];
[ws selectFile: path inFileViewerRootedAtPath:path];
}
}
- (void)openFilesAction:(id)sender
{
NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"];
NSString *path;
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
for (NSString *filePath in [sender representedObject]) {
path = [workingDirectory stringByAppendingPathComponent:filePath];
[ws openFile:path];
}
}
- (NSMenu *)contextMenuForTreeView
{
NSArray *filePaths = [[treeController selectedObjects] valueForKey:@"fullPath"];
NSMenu *menu = [[NSMenu alloc] init];
for (NSMenuItem *item in [self menuItemsForPaths:filePaths])
[menu addItem:item];
return menu;
}
- (NSArray *)menuItemsForPaths:(NSArray *)paths
{
BOOL multiple = [paths count] != 1;
NSMenuItem *historyItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Show history of files" : @"Show history of file"
action:@selector(showCommitsFromTree:)
keyEquivalent:@""];
NSMenuItem *finderItem = [[NSMenuItem alloc] initWithTitle:@"Show in Finder"
action:@selector(showInFinderAction:)
keyEquivalent:@""];
NSMenuItem *openFilesItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Open Files" : @"Open File"
action:@selector(openFilesAction:)
keyEquivalent:@""];
NSArray *menuItems = [NSArray arrayWithObjects:historyItem, finderItem, openFilesItem, nil];
for (NSMenuItem *item in menuItems) {
[item setTarget:self];
[item setRepresentedObject:paths];
}
return menuItems;
}
@end
+9 -3
View File
@@ -9,7 +9,7 @@
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="237"/>
<integer value="27"/>
<integer value="2"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -2953,7 +2953,7 @@
</object>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{178, 79}, {852, 432}}</string>
<string>{{358, 67}, {852, 432}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2988,7 +2988,7 @@
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">274</int>
<int key="maxID">286</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -3028,6 +3028,8 @@
<string>setDetailedView:</string>
<string>setRawView:</string>
<string>setTreeView:</string>
<string>showCommitsFromTree:</string>
<string>showInFinderAction:</string>
<string>toggleQuickView:</string>
</object>
<object class="NSMutableArray" key="dict.values">
@@ -3038,6 +3040,8 @@
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
@@ -3048,6 +3052,7 @@
<string>commitList</string>
<string>fileBrowser</string>
<string>searchField</string>
<string>treeContextMenu</string>
<string>treeController</string>
<string>webView</string>
</object>
@@ -3057,6 +3062,7 @@
<string>NSTableView</string>
<string>NSOutlineView</string>
<string>NSSearchField</string>
<string>NSMenu</string>
<string>NSTreeController</string>
<string>id</string>
</object>
+27 -30
View File
@@ -157,8 +157,18 @@
return [commitController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-files", [self contextParameter], @"--", file.path, nil]];
}
- (void) forceRevertChangesForFiles:(NSArray *)files
- (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force
{
if(!force) {
int ret = [[NSAlert alertWithMessageText:@"Discard changes"
defaultButton:nil
alternateButton:@"Cancel"
otherButton:nil
informativeTextWithFormat:@"Are you sure you wish to discard the changes to this file?\n\nYou cannot undo this operation."] runModal];
if (ret != NSAlertDefaultReturn)
return;
}
NSArray *paths = [files valueForKey:@"path"];
NSString *input = [paths componentsJoinedByString:@"\0"];
@@ -166,7 +176,7 @@
int ret = 1;
[commitController.repository outputForArguments:arguments inputString:input retValue:&ret];
if (ret) {
[[commitController.repository windowController] showMessageSheet:@"Reverting changes failed" infoText:[NSString stringWithFormat:@"Reverting changes failed with error code %i", ret]];
[[commitController.repository windowController] showMessageSheet:@"Discarding changes failed" infoText:[NSString stringWithFormat:@"Discarding changes failed with error code %i", ret]];
return;
}
@@ -174,19 +184,6 @@
file.hasUnstagedChanges = NO;
}
- (void) revertChangesForFiles:(NSArray *)files
{
int ret = [[NSAlert alertWithMessageText:@"Revert changes"
defaultButton:nil
alternateButton:@"Cancel"
otherButton:nil
informativeTextWithFormat:@"Are you sure you wish to revert changes?\n\nYou cannot undo this operation."] runModal];
if (ret == NSAlertDefaultReturn)
[self forceRevertChangesForFiles:files];
}
# pragma mark Context Menu methods
- (BOOL) allSelectedCanBeIgnored:(NSArray *)selectedFiles
{
@@ -244,19 +241,19 @@
if (!file.hasUnstagedChanges)
return menu;
NSMenuItem *revertItem = [[NSMenuItem alloc] initWithTitle:@"Revert Changes" action:@selector(revertFilesAction:) keyEquivalent:@""];
[revertItem setTarget:self];
[revertItem setAlternate:NO];
[revertItem setRepresentedObject:selectedFiles];
NSMenuItem *discardItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes" action:@selector(discardFilesAction:) keyEquivalent:@""];
[discardItem setTarget:self];
[discardItem setAlternate:NO];
[discardItem setRepresentedObject:selectedFiles];
[menu addItem:revertItem];
[menu addItem:discardItem];
NSMenuItem *revertForceItem = [[NSMenuItem alloc] initWithTitle:@"Revert Changes" action:@selector(forceRevertFilesAction:) keyEquivalent:@""];
[revertForceItem setTarget:self];
[revertForceItem setAlternate:YES];
[revertForceItem setRepresentedObject:selectedFiles];
[revertForceItem setKeyEquivalentModifierMask:NSAlternateKeyMask];
[menu addItem:revertForceItem];
NSMenuItem *discardForceItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes" action:@selector(forceDiscardFilesAction:) keyEquivalent:@""];
[discardForceItem setTarget:self];
[discardForceItem setAlternate:YES];
[discardForceItem setRepresentedObject:selectedFiles];
[discardForceItem setKeyEquivalentModifierMask:NSAlternateKeyMask];
[menu addItem:discardForceItem];
return menu;
}
@@ -288,18 +285,18 @@
[commitController refresh:NULL];
}
- (void) revertFilesAction:(id) sender
- (void)discardFilesAction:(id) sender
{
NSArray *selectedFiles = [sender representedObject];
if ([selectedFiles count] > 0)
[self revertChangesForFiles:selectedFiles];
[self discardChangesForFiles:selectedFiles force:FALSE];
}
- (void) forceRevertFilesAction:(id) sender
- (void)forceDiscardFilesAction:(id) sender
{
NSArray *selectedFiles = [sender representedObject];
if ([selectedFiles count] > 0)
[self forceRevertChangesForFiles:selectedFiles];
[self discardChangesForFiles:selectedFiles force:TRUE];
}
- (void) showInFinderAction:(id) sender
+26 -22
View File
@@ -72,10 +72,12 @@ NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain";
return repositoryURL;
}
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError
// NSFileWrapper is broken and doesn't work when called on a directory containing a large number of directories and files.
//because of this it is safer to implement readFromURL than readFromFileWrapper.
//Because NSFileManager does not attempt to recursively open all directories and file when fileExistsAtPath is called
//this works much better.
- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
{
BOOL success = NO;
if (![PBGitBinary path])
{
if (outError) {
@@ -86,30 +88,32 @@ NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain";
return NO;
}
if (![fileWrapper isDirectory]) {
BOOL isDirectory = FALSE;
[[NSFileManager defaultManager] fileExistsAtPath:[absoluteURL path] isDirectory:&isDirectory];
if (!isDirectory) {
if (outError) {
NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Reading files is not supported.", [fileWrapper filename]]
forKey:NSLocalizedRecoverySuggestionErrorKey];
NSDictionary* userInfo = [NSDictionary dictionaryWithObject:@"Reading files is not supported."
forKey:NSLocalizedRecoverySuggestionErrorKey];
*outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo];
}
} else {
NSURL* gitDirURL = [PBGitRepository gitDirForURL:[self fileURL]];
if (gitDirURL) {
[self setFileURL:gitDirURL];
success = YES;
} else if (outError) {
NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ does not appear to be a git repository.", [fileWrapper filename]]
forKey:NSLocalizedRecoverySuggestionErrorKey];
*outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo];
}
if (success) {
[self setup];
[self readCurrentBranch];
}
return NO;
}
return success;
NSURL* gitDirURL = [PBGitRepository gitDirForURL:[self fileURL]];
if (!gitDirURL) {
if (outError) {
NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ does not appear to be a git repository.", [self fileName]]
forKey:NSLocalizedRecoverySuggestionErrorKey];
*outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo];
}
return NO;
}
[self setFileURL:gitDirURL];
[self setup];
[self readCurrentBranch];
return YES;
}
- (void) setup
+19
View File
@@ -58,6 +58,25 @@
return fileNames;
}
- (NSMenu *)menuForEvent:(NSEvent *)theEvent
{
if ([theEvent type] == NSRightMouseDown)
{
// get the current selections for the outline view.
NSIndexSet *selectedRowIndexes = [self selectedRowIndexes];
// select the row that was clicked before showing the menu for the event
NSPoint mousePoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
int row = [self rowAtPoint:mousePoint];
// figure out if the row that was just clicked on is currently selected
if ([selectedRowIndexes containsIndex:row] == NO)
[self selectRow:row byExtendingSelection:NO];
}
return [controller contextMenuForTreeView];
}
/* Implemented to satisfy datasourcee protocol */
- (BOOL) outlineView: (NSOutlineView *)ov
isItemExpandable: (id)item { return NO; }
+15 -13
View File
@@ -76,21 +76,23 @@ contextMenuItemsForElement:(NSDictionary *)element
{
DOMNode *node = [element valueForKey:@"WebElementDOMNode"];
// If clicked on the text, select the containing div
if ([[node className] isEqualToString:@"DOMText"])
while (node) {
// Every ref has a class name of 'refs' and some other class. We check on that to see if we pressed on a ref.
if ([[node className] hasPrefix:@"refs "]) {
NSString *selectedRefString = [[[node childNodes] item:0] textContent];
for (PBGitRef *ref in historyController.webCommit.refs)
{
if ([[ref shortName] isEqualToString:selectedRefString])
return [contextMenuDelegate menuItemsForRef:ref commit:historyController.webCommit];
}
NSLog(@"Could not find selected ref!");
return defaultMenuItems;
}
if ([node hasAttributes] && [[node attributes] getNamedItem:@"representedFile"])
return [historyController menuItemsForPaths:[NSArray arrayWithObject:[[[node attributes] getNamedItem:@"representedFile"] value]]];
node = [node parentNode];
// Every ref has a class name of 'refs' and some other class. We check on that to see if we pressed on a ref.
if (![[node className] hasPrefix:@"refs "])
return defaultMenuItems;
NSString *selectedRefString = [[[node childNodes] item:0] textContent];
for (PBGitRef *ref in historyController.webCommit.refs)
{
if ([[ref shortName] isEqualToString:selectedRefString])
return [contextMenuDelegate menuItemsForRef:ref commit:historyController.webCommit];
}
NSLog(@"Could not find selected ref!");
return defaultMenuItems;
}
+1 -1
View File
@@ -40,7 +40,7 @@ end
<%= @body %>
</div>
<div id="footer">
© Some rights reserved. <a href="http://github.com/pieter/gitx/tree/master/COPYING" title="GPL">GPL v2</a>. <a href="mailto:frimmirf+gitx@gmail.com">Pieter de Bie</a> is the GitX maintainer. Website design by <a href="http://www.ai.rug.nl/~kdoes/index.php?id=webdesign">Kim Does</a>.
© Some rights reserved. <a href="http://github.com/pieter/gitx/tree/master/COPYING" title="GPL">GPL v2</a>. <a href="mailto:frimmirf+gitx@gmail.com">Pieter de Bie</a> (<a href="http://twitter.com/pdebie">@pdebie</a>) is the GitX maintainer. Website design by <a href="http://www.ai.rug.nl/~kdoes/index.php?id=webdesign">Kim Does</a>.
</div>
</div>
<script type="text/javascript">
+3 -1
View File
@@ -4,4 +4,6 @@ Contact
The GitX repository can be found on Github at [http://github.com/pieter/gitx](http://www.github.com/pieter/gitx/wikis).
GitX has a mailing list at [gitx@googlegroups.com](mailto:gitx@googlegroups.com) to which you can email any questions or patches. GitX is actively maintained by Pieter de Bie. You can email him at [frimmirf@gmail.com](mailto:frimmirf+gitx@gmail.com).
GitX has a mailing list at [gitx@googlegroups.com](mailto:gitx@googlegroups.com) to which you can email any questions or patches.
GitX is actively maintained by Pieter de Bie. You can email him at [frimmirf@gmail.com](mailto:frimmirf+gitx@gmail.com) or on twitter <a href="http://twitter.com/pdebie">@pdebie</a>
+1 -1
View File
@@ -133,7 +133,7 @@ int main(int argc, const char** argv)
NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:argc];
int i = 0;
for (i = 0; i < argc; i++)
[arguments addObject: [NSString stringWithCString:argv[i]]];
[arguments addObject: [NSString stringWithUTF8String:argv[i]]];
if (!isatty(STDIN_FILENO) && fdopen(STDIN_FILENO, "r"))
handleSTDINDiff(proxy);
+17 -8
View File
@@ -184,15 +184,18 @@ var loadCommit = function(commitObject, currentRef) {
}
}
// Scroll to top
scroll(0, 0);
if (!commit.parents)
return;
for (var i = 0; i < commit.parents.length; i++) {
var newRow = $("commit_header").insertRow(-1);
newRow.innerHTML = "<td class='property_name'>Parent:</td><td>" +
"<a href='' onclick='selectCommit(this.innerHTML); return false;'>" +
commit.parents[i] + "</a></td>";
}
// Scroll to top
scroll(0, 0);
}
var showDiff = function() {
@@ -202,26 +205,32 @@ var showDiff = function() {
var link = document.createElement("a");
link.setAttribute("href", "#" + id);
p.appendChild(link);
var buttonType = ""
var buttonType = "";
var finalFile = "";
if (name1 == name2) {
buttonType = "changed"
link.appendChild(document.createTextNode(name1));
finalFile = name1;
if (mode_change)
p.appendChild(document.createTextNode(" mode " + old_mode + " -> " + new_mode));
}
else if (name1 == "/dev/null") {
buttonType = "created";
link.appendChild(document.createTextNode(name2));
finalFile = name2;
}
else if (name2 == "/dev/null") {
buttonType = "deleted";
link.appendChild(document.createTextNode(name1));
finalFile = name1;
}
else {
buttonType = "renamed";
link.appendChild(document.createTextNode(name2));
finalFile = name2;
p.insertBefore(document.createTextNode(name1 + " -> "), link);
}
link.appendChild(document.createTextNode(finalFile));
button.setAttribute("representedFile", finalFile);
link.setAttribute("representedFile", finalFile);
button.setAttribute("class", "button " + buttonType);
button.appendChild(document.createTextNode(buttonType));
$("files").appendChild(button);