快速,安全,没有头痛。您一生中遗漏的Git界面终于到了。
GIT最近庆祝了其10周年纪念日,但大多数工程师仍然对其复杂性感到困惑(堆栈溢出的有史以来5个问题中的3个与Git有关)。由于GIT甚至将简单的动作变成了神秘的命令(“ git add”到舞台与“ git重置头”以拆除任何人吗?拧紧他们的仓库!
Gitup是发明新的GIT交互模型的赌注,该模型使各个层次的工程师都可以快速,安全且无头痛。它与其他任何GIT客户端不同(与磁盘上的GIT数据库直接交互)不同于其工作方式(您可以操纵存储库图而不是操纵提交)。
使用Gitup,您将获得Mac的真正高效的Git客户端:
Gitup是由@Swisspol在2014年底创建的,目的是重塑开发人员与GIT互动的方式。经过几个月的工作,它在2015年初发布的预发行前提供,并进入了Hacker News的顶端,并由产品Hunt和Daring Fireball饰演。后来有30,000行代码,Gitup于2015年8月中旬达到1.0,并被公开发售,作为向开发人员社区的礼物。
brew install homebrew/cask/gitup
(注意:已经有一个称为Gitup的公式,因此必须指定全名!)阅读文档并使用GitHub问题进行支持和反馈。
发行说明可在https://github.com/git-up/gitup/releases上找到。用v
标记的构建(例如v1.2.3
)在“稳定”通道上释放,而用b
(例如b1234
)标记的构建仅在“连续”通道上释放。您可以更改GITUP在应用程序首选项中使用的更新频道。
要自己构建gitup,只需运行命令git clone --recursive https://github.com/git-up/GitUp.git
在终端中,然后打开GitUp/GitUp.xcodeproj
xcode xcode project和hit rum。
重要的是:如果您没有带有代码签名Mac应用程序的开发人员帐户的Apple ID,则该构建将在代码签名错误时失败。只需删除“应用程序”目标的“代码签名身份”构建设置,以解决问题:
另外,如果您确实有一个开发人员帐户,则可以创建以下构建设置为其内容:
development_team = [您的TeamID]
有关此的更详细说明,您可以在文件“ Xcode-configurations/base.xcconfig”的末尾查看注释。
gitup是在可重复使用的通用git工具包上的薄层中构建的,称为“ gitupkit”。这意味着您可以使用相同的gitupkit框架来构建自己的git UI!
Gitupkit的目标与Objectivesgit的目标截然不同。 Gitupkit不用对Libgit2提供广泛的原始绑定,而仅使用Libgit2的最小子集,并在其顶部重新实现所有其他内容(例如,它具有自己的“ Rebase Engine”)。这使其可以暴露出非常紧密且一致的API,该API完全遵循OBJ-C约定,并掩盖了Libgit2的复杂性,有时甚至不一致。 Gitupkit在此之上添加了许多独家和强大的功能,从撤消/重做和时间机(如快照)到整个UI组件。
GitupKit源代码是2个独立层,仅通过使用公共API来通信:
基层(仅取决于基础,与OS X和iOS兼容)
Core/
:包装器围绕LibGit2所需的最小功能,然后在其上实现了Gitup所需的所有GIT功能(请注意,Gitup使用libgit2的稍微自定义的叉子)Extensions/
: Core
类别上的类别以添加仅使用公共API实现的便利性功能UI层(取决于AppKit,仅与OS X兼容)
Interface/
:低级视图类,例如GIGraphView
呈现gitup地图视图Utilities/
:接口实用程序类,例如基本视图控制器类GIViewController
Components/
:可重复使用的单视图控制器,例如GIDiffContentsViewController
渲染差异Views/
:高级可重复使用的多视图视图视图控制器,例如GIAdvancedCommitViewController
实现整个GITUP高级提交视图重要:如果在构建gitupkit时将预处理器常数DEBUG
定义为非零值(这是在“调试”配置中构建时默认值),则在运行时启用了许多额外的一致性检查以及额外的记录。请注意,此开销会严重影响性能。
使用gitupkit API应该非常简单,因为它是由功能(例如存储库,分支,提交,接口组件等)组织的,并且已经为清晰的命名函数做出了最大的努力。
关于“核心” API,学习它们的最佳方法是仔细阅读相关的单元测试 - 例如,请参阅分支API的分支测试。
这是一些示例代码可以使您开始(将错误处理作为练习给读者):
打开和浏览一个存储库:
// Open repo
GCRepository* repo = [[GCRepository alloc ] initWithExistingLocalRepository: <PATH> error: NULL ];
// Make sure repo is clean
assert ([repo checkClean: kGCCleanCheckOption_IgnoreUntrackedFiles error: NULL ]);
// List all branches
NSArray * branches = [repo listAllBranches: NULL ];
NSLog ( @" %@ " , branches);
// Lookup HEAD
GCLocalBranch* headBranch; // This would be nil if the HEAD is detached
GCCommit* headCommit;
[repo lookupHEADCurrentCommit: &headCommit branch: &headBranch error: NULL ];
NSLog ( @" %@ = %@ " , headBranch, headCommit);
// Load the *entire* repo history in memory for fast access, including all commits, branches and tags
GCHistory* history = [repo loadHistoryUsingSorting: kGCHistorySorting_ReverseChronological error: NULL ];
assert (history);
NSLog ( @" %lu commits total " , history.allCommits.count);
NSLog ( @" %@ n %@ " , history.rootCommits, history.leafCommits);
修改存储库:
// Take a snapshot of the repo
GCSnapshot* snapshot = [repo takeSnapshot: NULL ];
// Create a new branch and check it out
GCLocalBranch* newBranch = [repo createLocalBranchFromCommit: headCommit withName: @" temp " force: NO error: NULL ];
NSLog ( @" %@ " , newBranch);
assert ([repo checkoutLocalBranch: newBranch options: 0 error: NULL ]);
// Add a file to the index
[[ NSData data ] writeToFile: [repo.workingDirectoryPath stringByAppendingPathComponent: @" empty.data " ] atomically: YES ];
assert ([repo addFileToIndex: @" empty.data " error: NULL ]);
// Check index status
GCDiff* diff = [repo diffRepositoryIndexWithHEAD: nil options: 0 maxInterHunkLines: 0 maxContextLines: 0 error: NULL ];
assert (diff.deltas.count == 1 );
NSLog ( @" %@ " , diff);
// Create a commit
GCCommit* newCommit = [repo createCommitFromHEADWithMessage: @" Added file " error: NULL ];
assert (newCommit);
NSLog ( @" %@ " , newCommit);
// Restore repo to saved snapshot before topic branch and commit were created
BOOL success = [repo restoreSnapshot: snapshot withOptions: kGCSnapshotOption_IncludeAll reflogMessage: @" Rolled back " didUpdateReferences: NULL error: NULL ];
assert (success);
// Make sure topic branch is gone
assert ([repo findLocalBranchWithName: @" temp " error: NULL ] == nil );
// Update workdir and index to match HEAD
assert ([repo resetToHEAD: kGCResetMode_Hard error: NULL ]);
Gitdown是一个非常基本的应用程序,它可以提示用户进行存储库,并显示其藏匿处的交互式和实时更新的列表(所有内容都在-[AppDelegate applicationDidFinishLaunching:]
)中:
通过gitupkit,此基本应用程序还可以免费获得无限制的撤消/重做,统一和并排的差异,文本选择和复制,键盘快捷键等...
该源代码还演示了如何使用其他一些gitupkit视图控制器以及构建自定义的控制器。
Gitdiff演示了如何创建一个视图控制器,该视图控制器显示了HEAD
和WorkDiràlagit git diff HEAD
之间的实时更新差异:
Gity是使用GitupKit构建的Gitx克隆,少于200行代码:
IGIT是一个测试iOS应用程序,仅使用gitupkit克隆github repo并执行提交。
参见贡献。
也要非常感谢gitup永远不会存在的优秀libgit2贡献者!
Gitup是2015-2018 Pierre-Olivier Latour的版权,并获得GPL V3许可证。有关更多信息,请参见项目中的许可证文件。
重要的是: Gitup还包括其他一些开源项目,此类项目仍遵守自己的许可。