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