mirror of https://github.com/sgoudham/git-view.git
Merge pull request #1 from sgoudham/v0.1.0
commit
70bc6ff544
@ -0,0 +1,21 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'src/**'
|
||||
- 'tests/**'
|
||||
- '.github/**'
|
||||
branches:
|
||||
- '**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'src/**'
|
||||
- 'tests/**'
|
||||
- '.github/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: 'echo "No build required"'
|
@ -0,0 +1,18 @@
|
||||
name: Generate CHANGELOG
|
||||
on:
|
||||
release:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
generate-changelog:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: BobAnkh/auto-generate-changelog@master
|
||||
with:
|
||||
ACCESS_TOKEN: ${{secrets.CHANGELOG}}
|
||||
PATH: 'CHANGELOG.md'
|
||||
COMMIT_MESSAGE: 'docs(CHANGELOG): Update release notes'
|
||||
TYPE: 'feat:Feature,fix:Bug Fixes,docs:Documentation,refactor:Refactor,perf:Performance Improvements,tests:Tests'
|
@ -0,0 +1,12 @@
|
||||
# Contributing to mdbook-template
|
||||
|
||||
Thank you for your interest in contributing to this project! I really appreciate it!
|
||||
|
||||
# Got a Question?
|
||||
|
||||
Please raise an issue with the `question` label. This ensures that bugs are separated from genuine questions.
|
||||
|
||||
# Found an Issue or Bug?
|
||||
|
||||
Please raise an issue on if you think you have found any issues or bugs. Even better, it would be great
|
||||
to submit a Pull Request including the fix.
|
@ -1,3 +1,186 @@
|
||||
# git-browser
|
||||
# git-view
|
||||
|
||||
A git sub-command to open your git repository in the web browser... written in Rust :D
|
||||
[![build](https://github.com/sgoudham/git-view/actions/workflows/build.yml/badge.svg)](https://github.com/sgoudham/git-view/actions/workflows/build.yml)
|
||||
[![crates.io](https://img.shields.io/crates/v/git-view)](https://crates.io/crates/git-view)
|
||||
[![downloads](https://img.shields.io/crates/d/git-view)](https://crates.io/crates/git-view)
|
||||
[![license](https://img.shields.io/github/license/sgoudham/git-view)](LICENSE)
|
||||
|
||||
> A git sub-command to view your git repository in the web browser!
|
||||
|
||||
## About
|
||||
|
||||
Are you _also_ frustrated from moving your hands away from the keyboard to view your git repository in the browser?
|
||||
|
||||
> Me too!
|
||||
|
||||
`git-view` alleviates that pain by allowing you to chuck away your mouse!
|
||||
|
||||
> (n)vim users rejoice :P
|
||||
|
||||
**_Important Note: You should always use `git view -h` instead of `git view --help` as the manpage/html files are NOT included._**
|
||||
|
||||
## Features
|
||||
|
||||
- [x] GitHub & BitBucket
|
||||
- [x] View Branches, Commits & Issues
|
||||
- [x] Custom Suffix
|
||||
- [x] Custom Remote
|
||||
- [ ] View Profile
|
||||
- [ ] View Current Directory
|
||||
|
||||
Feel free to raise any issues or pull requests (after having read the [CONTRIBUTING.md](./CONTRIBUTING.md)!) for any additional features
|
||||
that you want!
|
||||
|
||||
## Usage
|
||||
|
||||
![Usage](./docs/images/usage.png "Displays different usages of `git-view`")
|
||||
|
||||
## Installation
|
||||
|
||||
### Cargo
|
||||
|
||||
The _preferred_ way of installation is to manually install the provided binaries and update your `$PATH` variable to enable
|
||||
the usage as `git view` globally. However, that being said, it also available on [crates.io](https://crates.io/crates/git-view) to allow installation
|
||||
through the use of Rust's build tool and package manager `cargo`.
|
||||
|
||||
> If you do not have `cargo` available on your machine, you can download it [here](https://www.rust-lang.org/tools/install)
|
||||
|
||||
```shell
|
||||
$ cargo install git-view
|
||||
```
|
||||
|
||||
Refresh terminal & verify installation
|
||||
|
||||
```shell
|
||||
$ git view --version
|
||||
git-view 0.1.0
|
||||
```
|
||||
|
||||
### Homebrew
|
||||
|
||||
For `macOS` users, installation through [Homebrew](https://brew.sh/) is recommended.
|
||||
|
||||
```shell
|
||||
$ brew tap sgoudham/tap
|
||||
$ brew install git-view
|
||||
```
|
||||
|
||||
Refresh terminal & verify installation
|
||||
|
||||
```shell
|
||||
$ git view --version
|
||||
git-view 0.1.0
|
||||
```
|
||||
|
||||
### Binaries
|
||||
|
||||
Pre-compiled binaries are _always_ available with every single [release](https://github.com/sgoudham/git-view/releases) for **Windows**, **macOS** and **Linux**.
|
||||
|
||||
The examples shown below will showcase the installation of the binaries living within the local `git` directory but realistically, any path will
|
||||
work if updated correctly within `$PATH`.
|
||||
|
||||
#### Windows
|
||||
|
||||
1. Download either `git-view-x86_64-pc-windows-msvc.zip` or `git-view-x86_64-pc-windows-gnu.zip`
|
||||
|
||||
2. Find local `git` directory
|
||||
|
||||
```shell
|
||||
# CMD
|
||||
$ where git
|
||||
C:\Program Files\Git\cmd\git.exe
|
||||
|
||||
# PowerShell
|
||||
$ (Get-Command git.exe).Path
|
||||
C:\Program Files\Git\cmd\git.exe
|
||||
```
|
||||
|
||||
3. `cd` into above path & extract downloaded binary zip
|
||||
|
||||
```shell
|
||||
$ cd 'C:\Program Files\Git\cmd'
|
||||
|
||||
$ tar -xf git-view-x86_64-pc-windows-msvc.zip
|
||||
# OR
|
||||
$ tar -xf git-view-x86_64-pc-windows-gnu.zip
|
||||
```
|
||||
|
||||
4. Ensure `%PATH%` is updated
|
||||
|
||||
```shell
|
||||
# Only required if git-view exists within a path not already included within %PATH%
|
||||
$ setx path "%path%;C:\your\path\here\bin"
|
||||
```
|
||||
|
||||
5. Refresh terminal and verify installation
|
||||
|
||||
```shell
|
||||
$ git view --version
|
||||
git-view 0.1.0
|
||||
```
|
||||
|
||||
#### Linux / macOS
|
||||
1. Download `git-view-x86_64-unknown-linux-gnu.tar.gz` or `git-view-x86_64-unknown-linux-musl.tar.gz`
|
||||
or `git-view-x86_64-apple-darwin.tar.gz`
|
||||
|
||||
2. Extract into your local directory
|
||||
|
||||
```shell
|
||||
# Linux
|
||||
$ tar -xf git-view-x86_64-unknown-linux-gnu.tar.gz
|
||||
$ tar -xf git-view-x86_64-unknown-linux-musl.tar.gz
|
||||
|
||||
# macOS
|
||||
$ tar -xf git-view-x86_64-apple-darwin.tar.gz
|
||||
```
|
||||
|
||||
3. Move into `~/bin`
|
||||
|
||||
```shell
|
||||
# Create ~/bin if it does not exist
|
||||
$ mkdir -p ~/bin
|
||||
$ mv git-view ~/bin
|
||||
```
|
||||
|
||||
4. Set permissions for executable
|
||||
|
||||
```shell
|
||||
$ chmod 755 ~/bin/git-view
|
||||
```
|
||||
|
||||
5. Ensure `$PATH` is updated
|
||||
|
||||
```shell
|
||||
# Only required if git-view exists within a path not already included within $PATH
|
||||
|
||||
# Linux
|
||||
$ echo 'export PATH=~/bin:$PATH' >> ~/.bashrc
|
||||
$ source ~/.bashrc
|
||||
|
||||
# macOS
|
||||
$ echo 'export PATH=~/bin:$PATH' >> ~/.bash_profile
|
||||
$ source ~/.bash_profile
|
||||
```
|
||||
|
||||
6. Verify installation
|
||||
|
||||
```shell
|
||||
$ git view --version
|
||||
git-view 0.1.0
|
||||
```
|
||||
|
||||
## Help
|
||||
|
||||
![help](./docs/images/help.png "Contents displayed when running `git view -h`")
|
||||
|
||||
## Contributing
|
||||
|
||||
First, thanks for your interest in contributing to this project! Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) before contributing!
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
The idea for this project came about from an existing project [git-open](https://github.com/paulirish/git-open/blob/master/git-open)
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
@ -0,0 +1,37 @@
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ErrorType {
|
||||
CommandFailed,
|
||||
CommandError,
|
||||
MissingGitRepository,
|
||||
MissingGitRemote,
|
||||
InvalidGitUrl,
|
||||
InvalidUtf8,
|
||||
IOError,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct AppError {
|
||||
pub error_type: ErrorType,
|
||||
pub error_str: String,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for AppError {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
AppError::new(ErrorType::IOError, error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::str::Utf8Error> for AppError {
|
||||
fn from(error: std::str::Utf8Error) -> Self {
|
||||
AppError::new(ErrorType::InvalidUtf8, error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
pub fn new(error_type: ErrorType, error_str: String) -> Self {
|
||||
Self {
|
||||
error_type,
|
||||
error_str,
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
use core::fmt;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
process::{Command, Output},
|
||||
};
|
||||
|
||||
use crate::error::AppError;
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum Local<'a> {
|
||||
Branch(Cow<'a, str>),
|
||||
NotBranch,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Domain {
|
||||
GitHub,
|
||||
BitBucket,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Url {
|
||||
pub(crate) protocol: String,
|
||||
pub(crate) domain: Domain,
|
||||
pub(crate) path: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Git;
|
||||
|
||||
pub(crate) enum GitCommand<'a> {
|
||||
IsValidRepository,
|
||||
LocalBranch,
|
||||
DefaultRemote,
|
||||
TrackedRemote(&'a str),
|
||||
UpstreamBranch(&'a str),
|
||||
DefaultBranch(&'a str),
|
||||
IsValidRemote(&'a str),
|
||||
CurrentTag,
|
||||
CurrentCommit,
|
||||
}
|
||||
|
||||
pub enum GitOutput {
|
||||
Ok(String),
|
||||
Err(String),
|
||||
}
|
||||
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait GitTrait {
|
||||
fn is_valid_repository(&self) -> Result<GitOutput, AppError>;
|
||||
fn get_local_branch(&self) -> Result<GitOutput, AppError>;
|
||||
fn get_default_remote(&self) -> Result<GitOutput, AppError>;
|
||||
fn get_tracked_remote(&self, tracked: &str) -> Result<GitOutput, AppError>;
|
||||
fn get_upstream_branch(&self, branch: &str) -> Result<GitOutput, AppError>;
|
||||
fn get_default_branch(&self, remote: &str) -> Result<GitOutput, AppError>;
|
||||
fn is_valid_remote(&self, remote: &str) -> Result<GitOutput, AppError>;
|
||||
fn get_current_tag(&self) -> Result<GitOutput, AppError>;
|
||||
fn get_current_commit(&self) -> Result<GitOutput, AppError>;
|
||||
}
|
||||
|
||||
impl GitTrait for Git {
|
||||
fn is_valid_repository(&self) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::IsValidRepository)?)
|
||||
}
|
||||
|
||||
fn get_local_branch(&self) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::LocalBranch)?)
|
||||
}
|
||||
|
||||
fn get_default_remote(&self) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::DefaultRemote)?)
|
||||
}
|
||||
|
||||
fn get_tracked_remote(&self, tracked: &str) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::TrackedRemote(tracked))?)
|
||||
}
|
||||
|
||||
fn get_upstream_branch(&self, branch: &str) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::UpstreamBranch(branch))?)
|
||||
}
|
||||
|
||||
fn get_default_branch(&self, remote: &str) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::DefaultBranch(remote))?)
|
||||
}
|
||||
|
||||
fn is_valid_remote(&self, remote: &str) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::IsValidRemote(remote))?)
|
||||
}
|
||||
|
||||
fn get_current_tag(&self) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::CurrentTag)?)
|
||||
}
|
||||
|
||||
fn get_current_commit(&self) -> Result<GitOutput, AppError> {
|
||||
execute(command(GitCommand::CurrentCommit)?)
|
||||
}
|
||||
}
|
||||
|
||||
fn command(git_command: GitCommand) -> Result<Output, std::io::Error> {
|
||||
match git_command {
|
||||
GitCommand::IsValidRepository => Command::new("git")
|
||||
.arg("rev-parse")
|
||||
.arg("--is-inside-work-tree")
|
||||
.output(),
|
||||
GitCommand::LocalBranch => Command::new("git")
|
||||
.arg("symbolic-ref")
|
||||
.arg("-q")
|
||||
.arg("--short")
|
||||
.arg("HEAD")
|
||||
.output(),
|
||||
GitCommand::DefaultRemote => Command::new("git")
|
||||
.arg("config")
|
||||
.arg("open.default.remote")
|
||||
.output(),
|
||||
GitCommand::TrackedRemote(branch) => Command::new("git")
|
||||
.arg("config")
|
||||
.arg(format!("branch.{}.remote", branch))
|
||||
.output(),
|
||||
GitCommand::UpstreamBranch(branch) => Command::new("git")
|
||||
.arg("config")
|
||||
.arg(format!("branch.{}.merge", branch))
|
||||
.output(),
|
||||
GitCommand::DefaultBranch(remote) => Command::new("git")
|
||||
.arg("rev-parse")
|
||||
.arg("--abbrev-ref")
|
||||
.arg(format!("{}/HEAD", remote))
|
||||
.output(),
|
||||
GitCommand::IsValidRemote(remote) => Command::new("git")
|
||||
.arg("ls-remote")
|
||||
.arg("--get-url")
|
||||
.arg(remote)
|
||||
.output(),
|
||||
GitCommand::CurrentTag => Command::new("git")
|
||||
.arg("describe")
|
||||
.arg("--tags")
|
||||
.arg("--exact-match")
|
||||
.output(),
|
||||
GitCommand::CurrentCommit => Command::new("git").arg("rev-parse").arg("HEAD").output(),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(output: Output) -> Result<GitOutput, AppError> {
|
||||
if output.status.success() {
|
||||
Ok(GitOutput::Ok(trim(&output.stdout)?))
|
||||
} else {
|
||||
Ok(GitOutput::Err(trim(&output.stderr)?))
|
||||
}
|
||||
}
|
||||
|
||||
fn trim(bytes: &[u8]) -> Result<String, AppError> {
|
||||
let mut utf_8_string = String::from(std::str::from_utf8(bytes)?.trim());
|
||||
|
||||
if utf_8_string.ends_with('\n') {
|
||||
utf_8_string.pop();
|
||||
if utf_8_string.ends_with('\r') {
|
||||
utf_8_string.pop();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(utf_8_string)
|
||||
}
|
||||
|
||||
impl Url {
|
||||
pub(crate) fn new(protocol: &str, domain: Domain, path: &str) -> Self {
|
||||
Self {
|
||||
protocol: protocol.into(),
|
||||
domain,
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Domain {
|
||||
pub(crate) fn from_str(s: &str) -> Self {
|
||||
if s == "bitbucket.org" {
|
||||
Domain::BitBucket
|
||||
} else {
|
||||
Domain::GitHub
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Domain {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.to_string() == other.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Domain {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Domain::GitHub => write!(f, "github.com"),
|
||||
Domain::BitBucket => write!(f, "bitbucket.org"),
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue