├── .dockerignore ├── .env ├── .eslintignore ├── .eslintrc ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── jsconfig.json ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── App.js ├── assets │ ├── fonts │ │ └── OpenSans │ │ │ ├── OpenSans-Bold.ttf │ │ │ ├── OpenSans-ExtraBold.ttf │ │ │ ├── OpenSans-Light.ttf │ │ │ ├── OpenSans-Regular.ttf │ │ │ └── OpenSans-SemiBold.ttf │ └── images │ │ ├── clipboard.svg │ │ ├── gitcommand.png │ │ ├── github-green.svg │ │ └── github.svg ├── components │ ├── footer.js │ ├── index.js │ ├── nav.js │ └── toggle.js ├── data │ ├── index.js │ ├── primary-options.js │ ├── secondary-options.js │ └── tertiary-options.js ├── index.js ├── scss │ ├── components │ │ ├── _copy.scss │ │ ├── _footer.scss │ │ ├── _nav.scss │ │ └── _toggle.scss │ ├── core │ │ ├── _fonts.scss │ │ ├── _mixins.scss │ │ ├── _reboot.scss │ │ └── _variables.scss │ ├── main.css │ ├── main.scss │ ├── pages │ │ └── _home.scss │ └── utilities │ │ ├── _overrides.scss │ │ ├── _text.scss │ │ └── _utils.scss └── serviceWorker.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | coverage -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_NODE_PATH=src/ 2 | NODE_PATH=src/ -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": ["eslint:recommended", "plugin:react/recommended", "airbnb-base"], 4 | "settings": { 5 | "react": { 6 | "pragma": "React", 7 | "version": "16.6.0" 8 | } 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 9, 12 | "sourceType": "module", 13 | "ecmaFeatures": { 14 | "jsx": true 15 | } 16 | }, 17 | "env": { 18 | "browser": true, 19 | "node": true, 20 | "es6": true 21 | }, 22 | "rules": { 23 | "comma-dangle": 0, 24 | "global-require": "off", 25 | "prefer-spread": "off", 26 | "no-console": "off", 27 | "no-restricted-syntax": "off", 28 | "no-param-reassign": "off", 29 | "no-plusplus": "off", 30 | "react/prop-types": ["error", { "ignore": ["navigation"] }], 31 | "import/prefer-default-export": "off", 32 | "import/no-extraneous-dependencies": "off", 33 | "no-underscore-dangle": "off", 34 | "no-use-before-define": "off", 35 | "class-methods-use-this": "off", 36 | "import/no-cycle": "off", 37 | "no-nested-ternary": "off", 38 | "consistent-return": "off", 39 | "react/jsx-filename-extension": "off", 40 | "guard-for-in": "off", 41 | "import/no-unresolved": "off", 42 | "no-return-assign": "off" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | package-lock.json 23 | 24 | .history/ 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine as builder 2 | ENV NODE_ENV=development \ 3 | PORT=3000 \ 4 | SHELL=/bin/bash 5 | RUN apk update && apk add --no-cache make git gnupg bash 6 | 7 | # Create app directory 8 | WORKDIR /usr/app 9 | 10 | # Install app dependencies 11 | COPY . ./ 12 | RUN npm set progress=false && npm install 13 | VOLUME /usr/app 14 | EXPOSE $PORT 15 | CMD ["npm", "run", "start"] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Summitech Computing Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitExplorer 2 | Table of Contents 3 | 4 | [Intro](https://github.com/summitech/gitexplorer/blob/master/README.md#intro) 5 | 6 | [Tech Stack](https://github.com/summitech/gitexplorer/blob/master/README.md#tech-stack) 7 | 8 | [Installation](https://github.com/summitech/gitexplorer/blob/master/README.md#installation) 9 | 10 | [Makers](https://github.com/summitech/gitexplorer/blob/master/README.md#makers) 11 | 12 | [Contribute](https://github.com/summitech/gitexplorer/blob/master/README.md#contribute) 13 | 14 | [Donate](https://github.com/summitech/gitexplorer/blob/master/README.md#donate) 15 | 16 | [RoadMap](https://github.com/summitech/gitexplorer/blob/master/README.md#roadmap) 17 | 18 | ### Intro 19 | Last year, we came across [Sarah Drasner's array explorer](https://github.com/sdras/array-explorer). It is a really cool resource for anyone to easily figure out the best JS array method to use. We loved it and decided to build something similar for Git. 20 | 21 | Website: [Click to find the right git commands without digging through the web.](https://gitexplorer.com) 22 | 23 | Explore and Enjoy! 24 | 25 | You can reach us on [the official git explorer twitter handle](https://twitter.com/gitexplorer) or on [Summitech's twitter handle](https://twitter.com/summitechng). 26 | 27 | 28 | ### Tech Stack 29 | 30 | - React 31 | - Netlify 32 | - Our first hosting platform was [Surge](https://surge.sh). Super easy to set up and very reliable :+1:. 100% recommend! 33 | 34 | 35 | ### Installation 36 | ``` 37 | yarn (Install all dependencies) 38 | 39 | yarn start 40 | ``` 41 | 42 | To try GitExplorer in a docker container, run this: 43 | ``` 44 | docker-compose up 45 | ``` 46 | 47 | ### Makers 48 | Awesome devs and designer at [Summitech](https://summitech.ng) 49 | 50 | ### Contribute 51 | Thank you for contributing to GitExplorer! 52 | 53 | Please follow the below instructions to send a Pull Request (Search the website to make sure that this command doesn't already exist). 54 | 55 | The data folder (inside the src directory) is where you will be operating from. The three files you should be concerned with are the `primary-options.js`, `secondary-options.js` and `tertiary-options.js` files. 56 | 57 | These three files are responsible for the options a user can pick. 58 | 59 | `primary-options.js` contains an array of objects responsible for the options of the first select box. 60 | `secondary-options.js` contains an object. This object houses an arrays of objects (a mouthful :smile:), this is responsible for the second set of options a user sees when they select a primary option. 61 | `tertiary-option.js` file is responsible for cases where there needs to be a third & final select box. 62 | 63 | ###### Steps to add a new command 64 | 0. Please ensure you are not on the master branch. Checkout to a new branch entirely. 65 | 1. Add an object to the array in the `primary-options.js` file. Sample Format: 66 | ``` 67 | { value: 'show', label: 'show/view' } 68 | ``` 69 | 2. Add an array to the `secondary-options` file. Sample Format: 70 | ``` 71 | show: [ 72 | { 73 | value: 'repo-status', 74 | label: 'status of project including staged, unstaged and untracked files', 75 | usage: 'git status' 76 | nb: 'To know about this command, "run git status --help"' 77 | }, 78 | { 79 | value: 'logs', 80 | label: 'commit logs/history' 81 | }, 82 | ``` 83 | The `nb` is optional. It is responsible for what the user sees in the notes section. 84 | 85 | `\n` is used to insert newline. 86 | 87 | 3. To add tertiary options, remove the `usage` and `nb` key/value pair for that command in the `secondary-options.js` file e.g.. 88 | 89 | ``` 90 | show: [ 91 | { 92 | value: 'logs', 93 | label: 'commit logs/history' 94 | }, 95 | ``` 96 | 97 | then supply `tertiary-options.js` file the necessary data e.g. 98 | ``` 99 | logs: [ 100 | { 101 | value: 'all', 102 | label: 'all', 103 | usage: 'git log', 104 | nb: 'Type q in the terminal to exit the logs' 105 | }, 106 | { 107 | value: 'last-n-commit', 108 | label: 'for last xxx number of commits', 109 | usage: 'git log -n', 110 | nb: 'Replace n with number of commits e.g. git log -2' 111 | }, 112 | { 113 | value: 'particular-period', 114 | label: 'since a particular period', 115 | usage: 'git log --since=period', 116 | nb: 'Replace period with intended timeframe e.g git log --since=3days. You can use dates like 2018-12-31.\n\n Similar flags are --until, --before, --after' 117 | } 118 | ] 119 | ``` 120 | 4. Once you are done, add, commit, push and create a PR to Master. 121 | 122 | ### Donate 123 | 124 | You can also contribute to the continued success of the project via donation. Please click this [link](https://rave.flutterwave.com/donate/bavfmdlomzs2). 125 | 126 | ### RoadMap 127 | 128 | - [x] Enforce HTTPs & offline capabilities 129 | - [x] Open source 130 | - [ ] PWA 131 | - [ ] Shareable commands 132 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.2" 2 | services: 3 | gitexplorer: 4 | restart: always 5 | build: 6 | context: ./ 7 | cache_from: 8 | - node:alpine 9 | container_name: gitexplorer 10 | hostname: gitexplorer 11 | network_mode: bridge 12 | volumes: 13 | - ./:/usr/app 14 | ports: 15 | - 3000:3000 16 | environment: 17 | - SHELL=/bin/bash 18 | - NODE_ENV=development 19 | - PORT=3000 20 | command: 21 | sh -c 'npm i && npm run start' 22 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src" 4 | }, 5 | "include": ["src"] 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitexplorer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "classnames": "^2.2.6", 7 | "node-sass-chokidar": "^1.4.0", 8 | "npm-run-all": "^4.1.5", 9 | "prop-types": "^15.7.2", 10 | "react": "^16.12.0", 11 | "react-device-detect": "^1.11.4", 12 | "react-dom": "^16.12.0", 13 | "react-scripts": "3.4.0", 14 | "react-select": "^2.1.1", 15 | "react-typist": "^2.0.5" 16 | }, 17 | "scripts": { 18 | "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/", 19 | "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive", 20 | "start-js": "react-scripts start", 21 | "start": "npm-run-all -p watch-css start-js", 22 | "build": "react-scripts build && cp -r src/assets/images/ build/images", 23 | "test": "react-scripts test --env=jsdom", 24 | "eject": "react-scripts eject", 25 | "lint:check": "eslint . --ext=js,jsx; exit 0", 26 | "lint:fix": "eslint . --ext=js,jsx --fix; exit 0", 27 | "install:clean": "rm -rf node_modules/ && rm -rf package-lock.json && rm -rf yarn.lock && npm install && npm start" 28 | }, 29 | "eslintConfig": { 30 | "extends": "react-app" 31 | }, 32 | "browserslist": [ 33 | ">0.2%", 34 | "not dead", 35 | "not ie <= 11", 36 | "not op_mini all" 37 | ], 38 | "devDependencies": { 39 | "eslint-config-airbnb": "^17.1.0", 40 | "eslint-config-airbnb-base": "^13.1.0", 41 | "eslint-plugin-react": "^7.11.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summitech/gitexplorer/ffe8b11b692ee93aa07078d105a9a2dab07e50f7/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 23 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | Git Explorer 61 | 62 | 63 | 64 | 65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Git Explorer", 3 | "name": "Git Explorer", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Footer, Nav } from 'components'; 3 | import Typist from 'react-typist'; 4 | import { isMobile } from 'react-device-detect'; 5 | import { optionsFirst, optionsSecond, optionsThird } from 'data'; 6 | import Select from 'react-select'; 7 | import clipboard from 'assets/images/clipboard.svg'; 8 | import classnames from 'classnames'; 9 | 10 | class App extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | dark: JSON.parse(localStorage.getItem('dark')) || false, 15 | fastType: JSON.parse(localStorage.getItem('fastType')) || false, 16 | firstOption: null, 17 | showSecond: false, 18 | secondOption: null, 19 | showThird: false, 20 | thirdOption: null, 21 | nb: '', 22 | usage: '', 23 | copied: false 24 | }; 25 | } 26 | 27 | handleToggle = (evt) => { 28 | const { id } = evt.target; 29 | 30 | this.setState( 31 | prevState => ({ [id]: !prevState[id] }), 32 | () => { 33 | localStorage.setItem(id, this.state[id]); 34 | } 35 | ); 36 | }; 37 | 38 | onFirstChange = (selectedOption) => { 39 | if (this.state.secondOption) { 40 | this.setState({ 41 | firstOption: selectedOption, 42 | showSecond: true, 43 | secondOption: null, 44 | showThird: false, 45 | nb: '', 46 | usage: '' 47 | }); 48 | } else if (optionsSecond[selectedOption.value].length === 1) { 49 | this.setState({ firstOption: selectedOption, showSecond: true }); 50 | this.onSecondChange(optionsSecond[selectedOption.value][0]); 51 | } else { 52 | this.setState({ firstOption: selectedOption, showSecond: true }); 53 | } 54 | }; 55 | 56 | onSecondChange = (selectedOption) => { 57 | if (selectedOption.usage) { 58 | this.setState({ nb: '', usage: '' }, () => { 59 | this.setState({ 60 | secondOption: selectedOption, 61 | showThird: false, 62 | nb: selectedOption.nb, 63 | usage: selectedOption.usage, 64 | thirdOption: null 65 | }); 66 | }); 67 | } else if (optionsThird[selectedOption.value].length === 1) { 68 | this.setState({ 69 | secondOption: selectedOption, 70 | showThird: true, 71 | thirdOption: null, 72 | nb: '', 73 | usage: '' 74 | }); 75 | this.onThirdChange(optionsThird[selectedOption.value][0]); 76 | } else { 77 | this.setState({ 78 | secondOption: selectedOption, 79 | showThird: true, 80 | thirdOption: null, 81 | nb: '', 82 | usage: '' 83 | }); 84 | } 85 | }; 86 | 87 | onThirdChange = (selectedOption) => { 88 | this.setState({ nb: '', usage: '' }, () => { 89 | this.setState({ 90 | thirdOption: selectedOption, 91 | nb: selectedOption.nb, 92 | usage: selectedOption.usage 93 | }); 94 | }); 95 | }; 96 | 97 | onCopy = () => { 98 | this.setState({ copied: true }, () => { 99 | if (this.timeout) { 100 | clearInterval(this.timeout); 101 | } 102 | this.timeout = setTimeout(() => { 103 | this.setState({ copied: false }); 104 | }, 1000); 105 | }); 106 | }; 107 | 108 | copyUsage = () => { 109 | const el = document.createElement('textarea'); 110 | el.value = this.state.usage; 111 | el.setAttribute('readonly', ''); 112 | el.style.position = 'absolute'; 113 | el.style.left = '-9999px'; 114 | document.body.appendChild(el); 115 | const selected = document.getSelection().rangeCount > 0 116 | ? document.getSelection().getRangeAt(0) 117 | : false; 118 | el.select(); 119 | document.execCommand('copy'); 120 | document.body.removeChild(el); 121 | this.onCopy(); 122 | 123 | if (selected) { 124 | document.getSelection().removeAllRanges(); 125 | document.getSelection().addRange(selected); 126 | } 127 | }; 128 | 129 | render() { 130 | const { 131 | dark, 132 | firstOption, 133 | secondOption, 134 | thirdOption, 135 | showSecond, 136 | showThird, 137 | fastType, 138 | nb, 139 | usage, 140 | copied 141 | } = this.state; 142 | const avgTypingDelay = fastType ? 0 : 50; 143 | 144 | return ( 145 |
146 |
147 |