mirror of
https://github.com/tj/git-extras.git
synced 2026-09-11 16:06:20 -04:00
Compare commits
No commits in common. "main" and "3.0.0" have entirely different histories.
|
|
@ -1,30 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.patch]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.html]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
[*.bats]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[tests/*.sh]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
2
.github/.ignore_words
vendored
2
.github/.ignore_words
vendored
|
|
@ -1,2 +0,0 @@
|
|||
gool
|
||||
Cant
|
||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,12 +0,0 @@
|
|||
<!--
|
||||
|
||||
Note
|
||||
|
||||
* Mark the PR as draft until it's ready to be reviewed.
|
||||
* Please update the documentation to reflect the changes made in the PR.
|
||||
* If the command is covered by tests under ./tests, please add/update tests for any changes unless you have a good reason.
|
||||
* Make a new commit to resolve conversations instead of `push -f`.
|
||||
* To resolve merge conflicts, merge from the `main` branch instead of rebasing over `main`.
|
||||
* Please wait for the reviewer to mark a conversation as resolved.
|
||||
|
||||
-->
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
|
|
@ -1,6 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
116
.github/workflows/ci.yml
vendored
116
.github/workflows/ci.yml
vendored
|
|
@ -1,116 +0,0 @@
|
|||
name: ci
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code.
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Get Changed Files'
|
||||
id: 'files'
|
||||
run: |
|
||||
CHANGED_FILES=$(git diff --name-only "${BASE_SHA:-$BEFORE_SHA}...${HEAD_SHA:-$GITHUB_REF}")
|
||||
echo "::notice::Changed files: $CHANGED_FILES"
|
||||
{
|
||||
echo "added_modified<<EOF"
|
||||
echo "$CHANGED_FILES"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
BEFORE_SHA: "${{ github.event.before }}"
|
||||
- name: 'Check Generated Manuals'
|
||||
run: |
|
||||
failed=0
|
||||
while IFS= read -r source; do
|
||||
case "$source" in
|
||||
man/git-*.md)
|
||||
stem=${source%.md}
|
||||
for generated in "$stem.1" "$stem.html"; do
|
||||
if ! grep -Fqx -- "$generated" <<< "$CHANGED_FILES"; then
|
||||
echo "::error file=$source::$generated must be updated with $source"
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
;;
|
||||
esac
|
||||
done <<< "$CHANGED_FILES"
|
||||
exit "$failed"
|
||||
env:
|
||||
CHANGED_FILES: "${{ steps.files.outputs.added_modified }}"
|
||||
- uses: 'actions/setup-go@v7'
|
||||
with:
|
||||
go-version: '1.20'
|
||||
- name: 'Install EditorConfig Lint'
|
||||
run: go install 'github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@latest'
|
||||
- name: 'Check EditorConfig Lint'
|
||||
run: echo "$FILES" | xargs ~/go/bin/editorconfig-checker
|
||||
env:
|
||||
# NOTE: use env to pass the output in order to avoid possible injection attacks
|
||||
FILES: "${{ steps.files.outputs.added_modified }}"
|
||||
- name: checkstyle
|
||||
run: ./scripts/checkstyle.py
|
||||
- name: Shellcheck
|
||||
run: shellcheck --severity=error bin/* ./*.sh
|
||||
|
||||
typo:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code.
|
||||
uses: actions/checkout@v7
|
||||
- name: Install codespell
|
||||
run: python3 -m pip install codespell==2.4
|
||||
- name: spell check
|
||||
run: |
|
||||
git grep --cached -l '' . | \
|
||||
grep -v -e 'History\.md' -e 'AUTHORS' -e 'man/.*\.1' -e 'man/.*\.html' | \
|
||||
xargs codespell --ignore-words=.github/.ignore_words
|
||||
|
||||
test-bats:
|
||||
name: 'Test with Bats'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Bats
|
||||
id: setup-bats
|
||||
uses: bats-core/bats-action@4.0.0
|
||||
with:
|
||||
bats-version: 'v1.8.1'
|
||||
- name: Test with Bats
|
||||
env:
|
||||
BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }}
|
||||
TERM: xterm
|
||||
run: bats ./tests
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Check out code.
|
||||
uses: actions/checkout@v7
|
||||
- name: Linux Install
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
run: sudo apt-get install -y bsdmainutils
|
||||
- name: Script
|
||||
run: ./check_integrity.sh
|
||||
- name: Brew release
|
||||
if: matrix.platform == 'macos-latest'
|
||||
run: |
|
||||
mkdir ../release && git archive --format=tar.gz HEAD > ../release/git-extras-release.tar.gz
|
||||
cd ../release
|
||||
tar -xzf git-extras-release.tar.gz && make PREFIX=$(pwd) INSTALL_VIA=brew
|
||||
./bin/git-extras update | grep "brew upgrade git-extras"
|
||||
18
.github/workflows/stale.yaml
vendored
18
.github/workflows/stale.yaml
vendored
|
|
@ -1,18 +0,0 @@
|
|||
name: "Maintenance: Close Stale PRs"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
permissions:
|
||||
pull-requests: "write"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: "ubuntu-latest"
|
||||
if: github.repository_owner == 'tj'
|
||||
steps:
|
||||
- uses: "actions/stale@v11"
|
||||
with:
|
||||
close-pr-message: "This PR was closed because it has been stalled for 365 days with no activity. Feel free to make a new PR if you wish to continue"
|
||||
days-before-pr-stale: 350
|
||||
days-before-pr-close: 15
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,3 +0,0 @@
|
|||
.venv/
|
||||
__pycache__/
|
||||
coverage/
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "vendor/bats-all"]
|
||||
path = vendor/bats-all
|
||||
url = https://github.com/hyperupcall/bats-all
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[pytest]
|
||||
minversion = 7.4
|
||||
addopts = -ra -q
|
||||
testpaths = tests
|
||||
faulthandler_timeout = 5
|
||||
301
AUTHORS
301
AUTHORS
|
|
@ -1,296 +1,33 @@
|
|||
git-extras is written and maintained by various contributors:
|
||||
git-extras is written and maintained by TJ Holowaychuk and
|
||||
various contributors:
|
||||
|
||||
Development Lead
|
||||
````````````````
|
||||
|
||||
Maintainers
|
||||
```````````
|
||||
- TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Maintainer
|
||||
````````````````
|
||||
- Hemanth.HM <hemanth.hm@gmail.com>
|
||||
- Nimit Kalra <me@nimit.io>
|
||||
- Nicolai Skogheim <nicolai.skogheim@gmail.com>
|
||||
- spacewander <spacewanderlzx@gmail.com>
|
||||
- Edwin Kofler <edwin@kofler.dev>
|
||||
- Leroy <vanpipy@gmail.com>
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
- Jonhnny Weslley
|
||||
- Tobias Fendin
|
||||
- LeeW
|
||||
- timfeirg
|
||||
- Niklas Schlimm
|
||||
- nickl-
|
||||
- CJ
|
||||
- Paul Wise
|
||||
- Damien Tardy-Panis
|
||||
|
||||
- Mark Eissler
|
||||
- Yuriy VG
|
||||
- Leila Muhtasib
|
||||
- Richard Fearn
|
||||
- Bill Wood
|
||||
- Jesús Espino
|
||||
- Rémy HUBSCHER
|
||||
- go2null
|
||||
- Andrew Janke
|
||||
- David Baumgold
|
||||
- Jan Schulz
|
||||
- Chern Jie
|
||||
- Kenneth Reitz
|
||||
- Mark Pitman
|
||||
- Peter Benjamin
|
||||
- Günther Grill
|
||||
- Luke Childs
|
||||
- Sasha Khamkov
|
||||
- equt
|
||||
- oikarinen
|
||||
- vyas
|
||||
- Don Harper
|
||||
- Pierre Ayoub
|
||||
- Robin Winslow
|
||||
- Ross Smith II
|
||||
- Yi EungJun
|
||||
- grindhold
|
||||
- wyattscarpenter
|
||||
- Aggelos Orfanakos
|
||||
- Camille Reynders
|
||||
- Carlos Prado
|
||||
- Chris Hall
|
||||
- Damian Krzeminski
|
||||
- NANRI
|
||||
- Alexander Krasnukhin
|
||||
- Phally
|
||||
- Rico Sta. Cruz
|
||||
- Takuma Yamaguchi
|
||||
- thomas menzel
|
||||
- vr8ce
|
||||
- Brian J Brennan
|
||||
- Dominik Gedon
|
||||
- Hogan Long
|
||||
- Ivan Malopinsky
|
||||
- John Bachir
|
||||
- Jonathan "Duke" Leto
|
||||
- Julio Napurí
|
||||
- Justin Dugger
|
||||
- Nils Winkler
|
||||
- Philipp Klose
|
||||
- Raphael Boidol
|
||||
- Richard Russon
|
||||
- Sam Bostock
|
||||
- Vladimir Jimenez
|
||||
- gisphm
|
||||
- jacobherrington
|
||||
- overengineer
|
||||
- phigoro
|
||||
- wooorm
|
||||
- Alexis GRIMALDI
|
||||
- Allan Odgaard
|
||||
- Andre Cerqueira
|
||||
- Austin Ziegler
|
||||
- Beth Skurrie
|
||||
- Brice Dutheil
|
||||
- Curtis McEnroe
|
||||
- Daniele Paolella
|
||||
- David Rogers
|
||||
- Devin Withers
|
||||
- Domenico Rotiroti
|
||||
- Evan Grim
|
||||
- Florian H
|
||||
- Gert Van Gool
|
||||
- Guillaume Seren
|
||||
- Hozefa
|
||||
- Igor Ostapenko
|
||||
- Jean Jordaan
|
||||
- Joshua Li
|
||||
- Justin Guenther
|
||||
- Kylie McClain
|
||||
- Marc Harter
|
||||
- Matiss
|
||||
- Nate Jones
|
||||
- Newell Zhu
|
||||
- Patryk Małek
|
||||
- Paul Schreiber
|
||||
- Richard Littauer
|
||||
- Tjaart van der Walt
|
||||
- Tom Vincent
|
||||
- Wil Moore III
|
||||
- William Montgomery
|
||||
- Ye Lin Aung
|
||||
- luozexuan
|
||||
- roxchgt
|
||||
- soffolk
|
||||
- 0xflotus
|
||||
- Abdullah
|
||||
- Adam Parkin
|
||||
- Adriaan Zonnenberg
|
||||
- Akim Demaille
|
||||
- Alessandro Pagiaro
|
||||
- Alex McHale
|
||||
- Aloxaf
|
||||
- Amir Tocker
|
||||
- Ammar Najjar
|
||||
- Amory Meltzer
|
||||
- Andreas Duering
|
||||
- Andrei Petcu
|
||||
- Andrew Griffiths
|
||||
- Andrew Marcinkevičius
|
||||
- Andrew Starr-Bochicchio
|
||||
- Andrew Sullivan Cant
|
||||
- Andrey Elizarov
|
||||
- Angel Aguilera
|
||||
- Antoine Beaupré
|
||||
- Aurélien Scoubeau
|
||||
- Balazs Nadasdi
|
||||
- Ben Parnell
|
||||
- Beni Ben Zikry
|
||||
- Brandon Zylstra
|
||||
- Brian Goad
|
||||
- Brian Murrell
|
||||
- Bruno Sutic
|
||||
- Caleb Maclennan
|
||||
- Carl Casbolt
|
||||
- Carlos Rodríguez
|
||||
- Christophe Badoit
|
||||
- Ciro Nunes
|
||||
- CleanMachine1
|
||||
- CodeByZach
|
||||
- Craig MacGregor
|
||||
- Dan Goodliffe
|
||||
- Dan Jackson
|
||||
- Dan Kilman
|
||||
- Daniel Bruder
|
||||
- Daniel Lublin
|
||||
- Daniel Schildt
|
||||
- Dave James Miller
|
||||
- David Hartmann
|
||||
- Dominic Barnes
|
||||
- Dung Quang
|
||||
- Edward Betts
|
||||
- Eli Schwartz
|
||||
- Emil Kjelsrud
|
||||
- Erik Skultety
|
||||
- François M
|
||||
- George Crabtree
|
||||
- Gerrit-K
|
||||
- Greg Allen
|
||||
- Guilhem Saurel
|
||||
- Guillermo Rauch
|
||||
- Gunnlaugur Thor Briem
|
||||
- Hasse Ramlev Hansen
|
||||
- Heiko Becker
|
||||
- Hugo Ruiz-Mireles
|
||||
- Isaac Mungai
|
||||
- J.C. Yamokoski
|
||||
- Jack O. Wasey
|
||||
- James Manning
|
||||
- James Zhu
|
||||
- Jan Krueger
|
||||
- Jared Baur
|
||||
- Jarod Stewart
|
||||
- Jason Young
|
||||
- Jens K. Mueller
|
||||
- Jeremy Lin
|
||||
- Jesse Sipprell
|
||||
- Jianjin Fan
|
||||
- Jobin Kurian
|
||||
- Johannes Ewald
|
||||
- John Evans
|
||||
- John Hoffmann
|
||||
- Jon Ander Peñalba
|
||||
- Josh McKinney
|
||||
- Joshua Appelman
|
||||
- José María Gutiérrez @TheTechOddBug
|
||||
- Katrin Leinweber
|
||||
- Kevin Woo
|
||||
- Konstantin Schukraft
|
||||
- Kurban Mallachiev
|
||||
- Leandro López
|
||||
- Lone
|
||||
- Luis Miguel Hernanz
|
||||
- Maarten Winter
|
||||
- Marco Glasbergen
|
||||
- Matan Rosenberg
|
||||
- Mathieu D. (MatToufoutu)
|
||||
- Matt
|
||||
- Matt Colyer
|
||||
- Matt Headley
|
||||
- Matthew Avant
|
||||
- Mattias Andersson
|
||||
- Michael Komitee
|
||||
- Michael Matuzak
|
||||
- Michele Bologna
|
||||
- Montak Oleg
|
||||
- Moritz Grauel
|
||||
- Nate Eagleson
|
||||
- Nate Fischer
|
||||
- Nathan Rajlich
|
||||
- Nick Campbell
|
||||
- Nick Payne
|
||||
- Nicolas Kosinski
|
||||
- Niklas Fiekas
|
||||
- Oliver Kopp
|
||||
- Orestis
|
||||
- Peter Bittner
|
||||
- Piotr Górski
|
||||
- Prayag Verma
|
||||
- Przemek Kitszel
|
||||
- R. Martinho Fernandes
|
||||
- Raphael Fleischlin
|
||||
- Rasmus Wriedt Larsen
|
||||
- René
|
||||
- Revisor
|
||||
- Ricardo Fernández Serrata
|
||||
- Riceball LEE
|
||||
- Rob Kennedy
|
||||
- Robin von Bülow
|
||||
- Russ
|
||||
- Ryan Bohn
|
||||
- Sachin Gupta
|
||||
- Sam Thursfield
|
||||
- Sandro
|
||||
- Sascha Andres
|
||||
- SchleimKeim
|
||||
- Sean Molenaar
|
||||
- Sebastian Gniazdowski
|
||||
- Sebastián Mancilla
|
||||
- Simon Tate
|
||||
- Stefan VanBuren
|
||||
- Stephen Mathieson
|
||||
- Steve Mao
|
||||
- Stu Feldt
|
||||
- SukkaW
|
||||
- Tim Preston
|
||||
- Timothy Hwang
|
||||
- Tin Lai
|
||||
- Jonhnny Weslley
|
||||
- Alex McHale
|
||||
- Gert Van Gool
|
||||
- Devin Withers
|
||||
- David Baumgold
|
||||
- Leila Muhtasib
|
||||
- Duane Johnson
|
||||
- Todd Wolfson
|
||||
- Tom Andrade
|
||||
- Tom Ashworth
|
||||
- Tony
|
||||
- TweeKane
|
||||
- Valérian Galliat
|
||||
- Vitaly Chikunov
|
||||
- Wei Lee
|
||||
- Wei Wu
|
||||
- Wen Sun
|
||||
- Wiktor Żurawik
|
||||
- Xavier Krantz
|
||||
- Xiaopei Li
|
||||
- Zeeshan Ahmed
|
||||
- ax1036
|
||||
- creynders
|
||||
- dead-horse
|
||||
- eszabpt
|
||||
- fengkx
|
||||
- johnpyp
|
||||
- jykntr
|
||||
- kang
|
||||
- meza
|
||||
- neydroid
|
||||
- nulltask
|
||||
- phanium
|
||||
- sgleizes
|
||||
- tfendin
|
||||
- tiemonl
|
||||
- zentarul
|
||||
- zeroDivisible
|
||||
- zhiyanfoo
|
||||
- Áron Hoffmann
|
||||
- Étienne BERSAC
|
||||
- ☃ pitr
|
||||
- 单元源
|
||||
- Niklas Fiekas
|
||||
- Paul Schreiber
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
Thanks for contributing! Please read this document before you make a PR.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
Any changes must support the following platforms:
|
||||
|
||||
- macOS
|
||||
- Linux
|
||||
- OpenBSD (You may need to browse their man page)
|
||||
|
||||
Your change must also be compatible with the dependency constraints that we specify in [Installation](./Installation.md). If you aren't sure if a feature is compatible, check the manual or release notes. For example, the Bash changelog is [here](https://git.savannah.gnu.org/cgit/bash.git/tree/NEWS?h=devel).
|
||||
|
||||
If you aren't able to test your new command on a platform, make that clear in your PR; someone else may be able to test it on their system.
|
||||
|
||||
## Adding a New Command
|
||||
|
||||
Let's say you wish to add a new command. Assuming your new command is named `foo`:
|
||||
|
||||
1. Write a bash script under `./bin` called `git-foo`. The script should be started with `#!/usr/bin/env bash`.
|
||||
2. Read `./man/Readme.md` and write documentation for `git-foo`.
|
||||
3. Don't forget to introduce it in `Commands.md`.
|
||||
4. Update `./etc/git-extras-completion.zsh`. Just follow existing code.
|
||||
5. (Optional) Update `./etc/bash_completion.sh`.
|
||||
6. (Optional) Update `./etc/git-extras.fish`.
|
||||
7. (Optional) Add a test under `./tests`.
|
||||
8. Run `./check_integrity.sh foo` to check if all done. (You may also run `./check_integrity.sh` to check all commands.)
|
||||
|
||||
You are welcome to open up an issue to discuss new commands or features before opening a pull request.
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
Please follow the suggestion in `./.github/PULL_REQUEST_TEMPLATE.md`.
|
||||
1041
Commands.md
1041
Commands.md
File diff suppressed because it is too large
Load diff
1193
History.md
1193
History.md
File diff suppressed because it is too large
Load diff
184
Installation.md
184
Installation.md
|
|
@ -1,196 +1,24 @@
|
|||
# Installation
|
||||
|
||||
## Dependencies
|
||||
|
||||
Some commands require extra dependencies which are unavailable in some platforms.
|
||||
You may need to install them manually. They are:
|
||||
|
||||
* bash 4.0+
|
||||
* Git 2.17+
|
||||
* `column`
|
||||
* `awk`
|
||||
* `find`
|
||||
* `tput`
|
||||
* cURL (only required for `git-fork`, `git-ignore-io`, `git-pull-request`)
|
||||
* `ps` (only required for `git-changelog`)
|
||||
* `rsync` (only required for `git-rscp`, `git-scp`)
|
||||
* `xargs` (only required for `git-delete-merged-branches`, `git-force-clone`, `git-sed`, `git-scp`)
|
||||
|
||||
If `bash --version` shows a lower version, you need to update it.
|
||||
For example, the default bash in Mac is usually too old and you may need to update it via `brew install bash`.
|
||||
|
||||
## Installing with a package manager
|
||||
|
||||
Note that only the Homebrew package is maintained by the git-extras developers directly.
|
||||
Other packages are maintained by the distribution's packagers or third-party volunteers.
|
||||
|
||||
[](https://repology.org/project/git-extras/versions)
|
||||
|
||||
### Debian
|
||||
## Debian
|
||||
|
||||
```bash
|
||||
$ sudo $apt_pref update
|
||||
$ sudo $apt_pref install git-extras
|
||||
```
|
||||
|
||||
### Fedora
|
||||
|
||||
```bash
|
||||
$ sudo dnf install git-extras
|
||||
```
|
||||
|
||||
### openSUSE
|
||||
|
||||
Substitute your openSUSE version in the command below (in this case we are considering openSUSE Leap 15.6):
|
||||
|
||||
```bash
|
||||
$ sudo zypper ar https://download.opensuse.org/repositories/devel:/tools:/scm/15.6/devel:tools:scm.repo
|
||||
```
|
||||
|
||||
and install it:
|
||||
|
||||
```bash
|
||||
$ sudo zypper in -y git-extras
|
||||
```
|
||||
|
||||
### RHEL/CentOS (requires [EPEL](https://fedoraproject.org/wiki/EPEL))
|
||||
|
||||
```bash
|
||||
$ sudo yum install git-extras
|
||||
```
|
||||
|
||||
### Ubuntu
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install git-extras
|
||||
```
|
||||
|
||||
### Nix/NixOS
|
||||
|
||||
```bash
|
||||
$ nix-env -i git-extras
|
||||
```
|
||||
|
||||
### CRUX
|
||||
|
||||
[Abdullah](https://github.com/AWAN) has written a [Pkgfile](https://abdullah.today/ports/git-extras/Pkgfile) for his beloved [distro](https://crux.nu).
|
||||
|
||||
### Homebrew
|
||||
## Mac
|
||||
|
||||
```bash
|
||||
$ brew install git-extras
|
||||
```
|
||||
|
||||
Installing from Homebrew will not give you the option omit certain `git-extras` if they conflict with existing git aliases. To have this option, build from source.
|
||||
|
||||
### Arch Linux
|
||||
|
||||
* [git-extras](https://aur.archlinux.org/packages/git-extras/)
|
||||
* [git-extras-git](https://aur.archlinux.org/packages/git-extras-git/)
|
||||
|
||||
### Windows
|
||||
|
||||
First, please install `Git for Windows 2.x` from 'https://github.com/git-for-windows/git/releases'.
|
||||
Second, clone the `git-extras` repo into any folder you like.
|
||||
## Clone / Tarball:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/tj/git-extras.git
|
||||
# checkout the latest tag (optional)
|
||||
git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
|
||||
$ make install
|
||||
```
|
||||
|
||||
After that, execute the `install.cmd` in the command prompt. If you installed
|
||||
git as admin, you need to run this prompt as admin, too. Per default, it finds
|
||||
a `Git for Windows 2.x` if it's in the path (first path in `where git.exe` wins)
|
||||
or installed in the default location `%ProgramFiles%\Git`. The fallback is
|
||||
`C:\SCM\PortableGit`. If you didn't install git into one of these dirs, you have
|
||||
to supply the path for the install location, e.g. if git is installed
|
||||
in `c:\git`:
|
||||
|
||||
```batch
|
||||
install.cmd "C:\git"
|
||||
```
|
||||
|
||||
Last, to use `git summary` and `git ignore-io`, you need to copy
|
||||
`column.exe` from a [msys2][1] installation from `folder-your-msys2-installed/usr/bin`
|
||||
to `folder-your-git-installed/usr/bin` or wait for git 2.7.1, which will include column.exe.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
```bash
|
||||
$ pkg install git-extras
|
||||
```
|
||||
|
||||
### BSD
|
||||
|
||||
Use the instructions to build from source below. Make sure you are using `gmake` (GNU `make`) instead of `make`.
|
||||
|
||||
## Building from source
|
||||
|
||||
Read [Dependencies](#dependencies) and ensure they are installed.
|
||||
|
||||
Obtain the git-extras source by cloning [its GitHub repo](https://github.com/tj/git-extras.git) or downloading a tarball of a [release](https://github.com/tj/git-extras/releases). Then install it by doing `make install` from the source tree.
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/tj/git-extras.git
|
||||
$ cd git-extras
|
||||
# checkout the latest tag
|
||||
$ git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
|
||||
$ [sudo] make install
|
||||
```
|
||||
|
||||
By default, git-extras is installed under `/usr/local`. To install it at an
|
||||
alternate location, specify a `PREFIX` when calling `make`.
|
||||
|
||||
```bash
|
||||
# Non-root users can install under their home directory
|
||||
make install PREFIX=$HOME/.local
|
||||
|
||||
# For third-party software kept under /opt
|
||||
make install PREFIX=/opt
|
||||
```
|
||||
|
||||
If you want to relocate the bulk of the installation but still have configuration
|
||||
files installed to the system `/etc` dir or other alternate location, you can
|
||||
specify `SYSCONFDIR` in addition to `PREFIX`.
|
||||
|
||||
```sh
|
||||
$ sudo make install PREFIX=/usr/local SYSCONFDIR=/etc
|
||||
```
|
||||
|
||||
See the Makefile for details on advanced configuration options.
|
||||
|
||||
## Installing from network
|
||||
|
||||
One-liner:
|
||||
|
||||
```bash
|
||||
curl -sSL https://raw.githubusercontent.com/tj/git-extras/main/install.sh | sudo bash /dev/stdin
|
||||
```
|
||||
|
||||
## Installing as Zsh plugin
|
||||
|
||||
[ZInit](https://github.com/zdharma/zinit) can install git-extras by using:
|
||||
|
||||
```zsh
|
||||
zinit ice as"program" pick"$ZPFX/bin/git-*" src"etc/git-extras-completion.zsh" make"PREFIX=$ZPFX"
|
||||
zinit light tj/git-extras
|
||||
|
||||
# or with the for syntax + async load
|
||||
zinit lucid wait'0a' for \
|
||||
as"program" pick"$ZPFX/bin/git-*" src"etc/git-extras-completion.zsh" make"PREFIX=$ZPFX" tj/git-extras
|
||||
|
||||
```
|
||||
|
||||
`$ZPFX` is `~/.zinit/polaris` by default. Use `zinit update tj/git-extras` to update.
|
||||
This method installs in `$HOME`, so you don't need to ask administrator to install package.
|
||||
|
||||
## Updating
|
||||
|
||||
If you installed git-extras with a package manager, use that package manager's tools to update it.
|
||||
|
||||
If you installed git-extras from source, you can run `git-extras update` to update it to the latest release. Be aware that this may lose any configuration options you specified during the original installation.
|
||||
|
||||
Enjoy `git-extras`!
|
||||
|
||||
[1]: https://sourceforge.net/projects/msys2/
|
||||
$ (cd /tmp && git clone --depth 1 https://github.com/tj/git-extras.git && cd git-extras && sudo make install)
|
||||
```
|
||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
(The MIT License)
|
||||
|
||||
Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca> and Contributors
|
||||
Copyright (c) 2015 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
|
|
|||
123
Makefile
123
Makefile
|
|
@ -1,80 +1,37 @@
|
|||
PREFIX ?= /usr/local
|
||||
BINPREFIX ?= "$(PREFIX)/bin"
|
||||
SYSCONFDIR ?= $(PREFIX)/etc
|
||||
SHELL := bash
|
||||
|
||||
OS = $(shell uname)
|
||||
ifeq ($(OS), FreeBSD)
|
||||
MANPREFIX ?= "$(PREFIX)/man/man1"
|
||||
ifeq ($(MANPREFIX), /usr/local)
|
||||
MANPREFIX = "/usr/local/man/man1"
|
||||
endif
|
||||
else
|
||||
MANPREFIX ?= "$(PREFIX)/share/man/man1"
|
||||
endif
|
||||
ifeq ($(OS), Darwin)
|
||||
COMPL_DIR ?= "$(DESTDIR)$(SYSCONFDIR)/bash_completion.d"
|
||||
else ifeq ($(OS), FreeBSD)
|
||||
COMPL_DIR ?= "$(DESTDIR)$(SYSCONFDIR)/bash_completion.d"
|
||||
else
|
||||
COMPL_DIR ?= "$(DESTDIR)$(SYSCONFDIR)/bash-completion/completions"
|
||||
endif
|
||||
|
||||
MANPREFIX ?= "$(PREFIX)/share/man/man1"
|
||||
BINS = $(wildcard bin/git-*)
|
||||
MANS = $(wildcard man/git-*.md)
|
||||
MAN_BINS = $(filter-out man/git-extras.md, $(MANS))
|
||||
MAN_HTML = $(MANS:.md=.html)
|
||||
MAN_PAGES = $(MANS:.md=.1)
|
||||
CODE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
INSTALL_VIA ?= source
|
||||
# Libraries used by all commands
|
||||
LIB = "helper/reset-env" "helper/git-extra-utility"
|
||||
|
||||
COMMANDS = $(subst bin/, , $(BINS))
|
||||
|
||||
default: install
|
||||
COMMANDS_USED_WITHOUT_GIT_REPO = git-alias git-extras git-fork git-setup
|
||||
COMMANDS_USED_WITH_GIT_REPO = $(filter-out $(COMMANDS_USED_WITHOUT_GIT_REPO), \
|
||||
$(subst bin/, , $(BINS)))
|
||||
|
||||
docs: $(MAN_HTML) $(MAN_PAGES)
|
||||
|
||||
check:
|
||||
@echo "Check dependencies before installation"
|
||||
@./check_dependencies.sh
|
||||
@echo
|
||||
|
||||
install: check
|
||||
@if [ "$(INSTALL_VIA)" = brew ]; then \
|
||||
git apply brew-release.patch || { echo "Can't apply brew release patch"; exit 1; } \
|
||||
fi
|
||||
mkdir -p $(DESTDIR)$(MANPREFIX)
|
||||
mkdir -p $(DESTDIR)$(BINPREFIX)
|
||||
@echo '... installing bins to '$(DESTDIR)$(BINPREFIX)
|
||||
@echo '... installing man pages to '$(DESTDIR)$(MANPREFIX)
|
||||
$(eval TEMPFILE := $(shell mktemp -q $${TMPDIR:-/tmp}/git-extras.XXXXXX 2>/dev/null || mktemp -q))
|
||||
install:
|
||||
@mkdir -p $(DESTDIR)$(MANPREFIX)
|
||||
@mkdir -p $(DESTDIR)$(BINPREFIX)
|
||||
@echo "... installing bins to $(DESTDIR)$(BINPREFIX)"
|
||||
@echo "... installing man pages to $(DESTDIR)$(MANPREFIX)"
|
||||
$(eval TEMPFILE := $(shell mktemp -t git-extra.XXX))
|
||||
@# chmod from rw-------(default) to rwxrwxr-x, so that users can exec the scripts
|
||||
@chmod 775 $(TEMPFILE)
|
||||
$(eval EXISTED_ALIASES := $(shell \
|
||||
git config --get-regexp 'alias\..*' | awk '{print "git-" substr($$1, 7)}'))
|
||||
@$(foreach COMMAND, $(COMMANDS), \
|
||||
should_install='yes'; \
|
||||
if [ ! -z "$(filter $(COMMAND), $(EXISTED_ALIASES))" ] && [ "$$SKIP_CONFLICT_CHECK" != yes ]; then \
|
||||
read -p "$(COMMAND) conflicts with an alias, still install it? [y/n]: " answer; \
|
||||
if [ "$$answer" = 'n' ] || [ "$$answer" = 'N' ]; then \
|
||||
should_install="no"; \
|
||||
fi; \
|
||||
fi; \
|
||||
if [ "$$should_install" = 'yes' ]; then \
|
||||
echo "... installing $(COMMAND)"; \
|
||||
head -1 bin/$(COMMAND) > $(TEMPFILE); \
|
||||
cat $(LIB) >> $(TEMPFILE); \
|
||||
if ! grep "$(COMMAND)" not_need_git_repo >/dev/null; then \
|
||||
cat ./helper/is-git-repo >> $(TEMPFILE); \
|
||||
fi; \
|
||||
if grep "$(COMMAND)" need_git_commit >/dev/null; then \
|
||||
cat ./helper/has-git-commit >> $(TEMPFILE); \
|
||||
fi; \
|
||||
tail -n +2 bin/$(COMMAND) >> $(TEMPFILE); \
|
||||
cp -f $(TEMPFILE) $(DESTDIR)$(BINPREFIX)/$(COMMAND); \
|
||||
fi; \
|
||||
@$(foreach COMMAND, $(COMMANDS_USED_WITH_GIT_REPO), \
|
||||
echo "... installing $(COMMAND)"; \
|
||||
head -1 bin/$(COMMAND) | cat - $(LIB) ./helper/is-git-repo > $(TEMPFILE); \
|
||||
tail -n +2 bin/$(COMMAND) >> $(TEMPFILE); \
|
||||
cp -f $(TEMPFILE) $(DESTDIR)$(BINPREFIX)/$(COMMAND); \
|
||||
)
|
||||
@$(foreach COMMAND, $(COMMANDS_USED_WITHOUT_GIT_REPO), \
|
||||
echo "... installing $(COMMAND)"; \
|
||||
head -1 bin/$(COMMAND) | cat - $(LIB) > $(TEMPFILE); \
|
||||
tail -n +2 bin/$(COMMAND) >> $(TEMPFILE); \
|
||||
cp -f $(TEMPFILE) $(DESTDIR)$(BINPREFIX)/$(COMMAND); \
|
||||
)
|
||||
@if [ -z "$(wildcard man/git-*.1)" ]; then \
|
||||
echo "WARNING: man pages not created, use 'make docs' (which requires 'ronn' ruby lib)"; \
|
||||
|
|
@ -82,35 +39,10 @@ install: check
|
|||
cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX); \
|
||||
echo "cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX)"; \
|
||||
fi
|
||||
@mkdir -p $(COMPL_DIR)
|
||||
cp -f etc/bash_completion.sh $(COMPL_DIR)/git-extras
|
||||
@echo ""
|
||||
@echo "If you are a zsh user, you may want to 'source $(CODE_DIR)etc/git-extras-completion.zsh'" \
|
||||
"and put this line into ~/.zshrc to enable zsh completion"
|
||||
@echo "If you are a fish user, you may want to copy or link '$(CODE_DIR)etc/git-extras.fish'" \
|
||||
"to '~/.config/fish/completions/'"
|
||||
@mkdir -p $(DESTDIR)/etc/bash_completion.d
|
||||
cp -f etc/bash_completion.sh $(DESTDIR)/etc/bash_completion.d/git-extras
|
||||
|
||||
man/index.txt: $(MANS)
|
||||
echo '# manuals' > $@.tmp
|
||||
for file in $(sort $^) ; do \
|
||||
extra=$${file%.md} ; \
|
||||
extra=$${extra#man/} ; \
|
||||
echo "$$extra(1) $$extra" >> $@.tmp ; \
|
||||
done
|
||||
mv -f $@.tmp $@
|
||||
|
||||
man/git-extras.md: $(MAN_BINS)
|
||||
ln=$$(awk '/## COMMANDS/{print NR};' $@) ; \
|
||||
awk "NR <= $$ln+1" $@ > $@.tmp
|
||||
for file in $(sort $^) ; do \
|
||||
head -n1 $$file | \
|
||||
sed 's/^/ - **/;s/ -- /** /' >> $@.tmp ; \
|
||||
done
|
||||
ln=$$(awk '/## AUTHOR/{print NR};' $@) ; \
|
||||
awk "NR >= $$ln-1" $@ >> $@.tmp
|
||||
mv -f $@.tmp $@
|
||||
|
||||
man/%.html: man/%.md man/index.txt
|
||||
man/%.html: man/%.md
|
||||
ronn \
|
||||
--manual "Git Extras" \
|
||||
--html \
|
||||
|
|
@ -132,7 +64,7 @@ uninstall:
|
|||
echo "... uninstalling $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN))"; \
|
||||
rm -f $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN)); \
|
||||
)
|
||||
rm -f $(COMPL_DIR)/git-extras
|
||||
rm -f $(DESTDIR)/etc/bash_completion.d/git-extras
|
||||
|
||||
clean: docclean
|
||||
|
||||
|
|
@ -140,7 +72,4 @@ docclean:
|
|||
rm -f man/*.1
|
||||
rm -f man/*.html
|
||||
|
||||
test:
|
||||
bats ./tests
|
||||
|
||||
.PHONY: default docs check install uninstall clean docclean test
|
||||
.PHONY: docs clean docclean install uninstall
|
||||
|
|
|
|||
21
Readme.md
21
Readme.md
|
|
@ -6,29 +6,14 @@ Little git extras.
|
|||
|
||||
Just getting started? Check out these screencasts:
|
||||
|
||||
* [introduction](https://vimeo.com/45506445) ([archive.org link](https://web.archive.org/web/20230219181235id_/vod-progressive.akamaized.net/exp=1676834145~acl=%2Fvimeo-prod-skyfire-std-us%2F01%2F4101%2F1%2F45506445%2F107111328.mp4~hmac=065b68f23c4b6a222463097b36d1c346995a9559baa9b819972da95550018471/vimeo-prod-skyfire-std-us/01/4101/1/45506445/107111328.mp4)) -- covers `git-ignore`, `git-setup`, `git-changelog`, `git-release`, `git-effort` and more
|
||||
* [introduction](https://vimeo.com/45506445) -- covering git-ignore, git-setup, git-changelog, git-release, git-effort and more
|
||||
|
||||
## Installation
|
||||
|
||||
See the [Installation](Installation.md) page.
|
||||
See [Installation](Installation.md) page.
|
||||
|
||||
## Commands
|
||||
|
||||
Go to the [Commands](Commands.md) page for basic usage and examples.
|
||||
Go to [Commands](Commands.md) page for basic usage and examples.
|
||||
|
||||
__GIT utilities__ -- repo summary, repl, changelog population, author commit percentages and more
|
||||
|
||||
## Contributing
|
||||
|
||||
Interested in contributing? Awesome!
|
||||
|
||||
Please read [Contributing](CONTRIBUTING.md) before you make a PR, thanks!
|
||||
|
||||
## The change of the default branch
|
||||
|
||||
As of Git Extras 6.4 the assumed default branch name changed from `master` to `main`.
|
||||
This affects the Git Extras commands `git archive-file`, `git delete-merged-branches`, `git delta`, `git pull-request`, `git show-merged-branches`, `git show-unmerged-branches`, and `git squash`.
|
||||
|
||||
To change the default branch name to `master`: change either the configuration `git-extras.default-branch` or `init.defaultBranch` to `master`; the former takes precedence.
|
||||
|
||||
For example, `git config git-extras.default-branch master`.
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# git rev-parse emits an error if not in a git repo so only need to bail out
|
||||
gitdir="$(git rev-parse --git-dir)" || exit
|
||||
|
||||
discover_op() {
|
||||
local op
|
||||
for op in cherry_pick merge rebase revert ; do
|
||||
if [ -f "${gitdir}/${op^^}_HEAD" ]; then
|
||||
echo "${op/_/-}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
validate_op() {
|
||||
local op="$1"
|
||||
if [ -z "$op" ]; then
|
||||
echo "No active operation found" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$(echo "$op" | wc -l)" -gt 1 ]]; then
|
||||
echo "Multiple active operations found:" >&2
|
||||
for o in $op; do
|
||||
echo " - $o: HEAD: $(cat "${gitdir}/${o}_HEAD")" >&2
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
discover_action() {
|
||||
local action=${1/git-/}
|
||||
if [ "$action" != "abort" ] && [ "$action" != "continue" ]; then
|
||||
echo "Invalid action: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$action"
|
||||
}
|
||||
|
||||
action=$(discover_action "$(basename "$0")")
|
||||
op=$(discover_op)
|
||||
validate_op "$op"
|
||||
|
||||
git "$op" "--$action"
|
||||
|
|
@ -1,45 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
options=""
|
||||
|
||||
usage() {
|
||||
cat <<HERE
|
||||
usage: git alias [options] # list all aliases
|
||||
or: git alias [options] <search-pattern> # show aliases matching pattern
|
||||
or: git alias [options] <alias-name> <command> # alias a command
|
||||
options:
|
||||
|
||||
--global Show or create alias in the system config
|
||||
--local Show or create alias in the repository config
|
||||
HERE
|
||||
}
|
||||
|
||||
if [[ "$1" == "--local" || "$1" == "--global" ]]; then
|
||||
options=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
case $# in
|
||||
0)
|
||||
if [[ -z "$options" ]]; then
|
||||
git config --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort
|
||||
else
|
||||
git config "$options" --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort
|
||||
fi
|
||||
;;
|
||||
1)
|
||||
if [[ -z "$options" ]]; then
|
||||
git config --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort | grep -e "$1"
|
||||
else
|
||||
git config "$options" --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort | grep -e "$1"
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
if [[ -z "$options" ]]; then
|
||||
git config alias."$1" "$2"
|
||||
else
|
||||
git config "$options" alias."$1" "$2"
|
||||
fi
|
||||
;;
|
||||
*) >&2 echo "error: too many arguments." && usage && exit 1 ;;
|
||||
0) git config --get-regexp 'alias.*' | colrm 1 6 | sed 's/[ ]/ = /' | sort ;;
|
||||
1) git alias | grep -e -- "$1" ;;
|
||||
*) git config --global "alias.$1" "$2" ;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -1,37 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# extract current branch name
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
BRANCH=$(git name-rev HEAD 2> /dev/null | awk "{ print \$2 }")
|
||||
|
||||
# get name of the most top folder of current directory, used for the
|
||||
# output filename
|
||||
ARCHIVE_NAME=$(basename "$(git rev-parse --show-toplevel)")
|
||||
ARCHIVE_NAME=$(basename $(pwd))
|
||||
|
||||
if [[ $BRANCH = tags* ]]; then
|
||||
BRANCH=$(git describe)
|
||||
echo Building for tag "\"$BRANCH\""
|
||||
FILENAME=$ARCHIVE_NAME.$BRANCH.zip
|
||||
BRANCH=$(git describe)
|
||||
echo Building for tag \"$BRANCH\"
|
||||
FILENAME=$ARCHIVE_NAME.$BRANCH.zip
|
||||
else
|
||||
echo Building archive on branch "\"$BRANCH\""
|
||||
# get a version string, so archives will not be overwritten when creating
|
||||
# many of them
|
||||
VERSION=$(git describe --always --long)
|
||||
# if not on master append branch name into the filename
|
||||
if [ "$BRANCH" = "$(git_extra_default_branch)" ]; then
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.zip
|
||||
else
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip
|
||||
fi
|
||||
echo Building archive on branch \"$BRANCH\"
|
||||
# get a version string, so archives will not be overwritten when creating
|
||||
# many of them
|
||||
VERSION=$(git describe --always --long)
|
||||
# if not on master append branch name into the filename
|
||||
if [ "$BRANCH" = "master" ]; then
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.zip
|
||||
else
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip
|
||||
fi
|
||||
fi
|
||||
|
||||
# rename invalid chars for the file path
|
||||
FILENAME=${FILENAME//\//-}
|
||||
FILENAME=${FILENAME//\\/-}
|
||||
# combine path and filename
|
||||
OUTPUT=$PWD/$FILENAME
|
||||
OUTPUT=$(pwd)/$FILENAME
|
||||
|
||||
# building archive
|
||||
git archive --format zip --output "$OUTPUT" "$BRANCH"
|
||||
git archive --format zip --output $OUTPUT $BRANCH
|
||||
|
||||
# also display size of the resulting file
|
||||
echo Saved to \""$FILENAME"\" \("$(du -h "$OUTPUT" | cut -f1)"\)
|
||||
echo Saved to \"$FILENAME\" \(`du -h $OUTPUT | cut -f1`\)
|
||||
|
|
|
|||
|
|
@ -1,29 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
LIST=false
|
||||
NO_EMAIL=false
|
||||
FILE=""
|
||||
EDITOR=$(git var GIT_EDITOR)
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-l|--list )
|
||||
LIST=true
|
||||
shift
|
||||
;;
|
||||
--no-email )
|
||||
NO_EMAIL=true
|
||||
shift
|
||||
;;
|
||||
* )
|
||||
break
|
||||
esac
|
||||
done
|
||||
|
||||
if ! $LIST; then
|
||||
if test "$1" = "-l" || test "$1" = "--list"; then
|
||||
LIST=true
|
||||
else
|
||||
FILE=$1
|
||||
if [ -z "$FILE" ]; then
|
||||
FILE=$(find . -mindepth 1 -maxdepth 1 \( -iname '*authors*' -o -iname '*contributors*' \) | head -n1)
|
||||
if [ -z "$FILE" ]; then
|
||||
if test "$FILE" = ""; then
|
||||
FILE=`ls | egrep 'authors|contributors' -i|head -n1`
|
||||
if test "$FILE" = ""; then
|
||||
FILE='AUTHORS'
|
||||
fi
|
||||
fi
|
||||
|
|
@ -34,13 +21,7 @@ fi
|
|||
#
|
||||
|
||||
authors() {
|
||||
if $NO_EMAIL; then
|
||||
# email will be used to uniq authors.
|
||||
git shortlog HEAD -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' \
|
||||
| awk -F'<' '{gsub(/ +$/, "", $1); print $1}'
|
||||
else
|
||||
git shortlog HEAD -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++'
|
||||
fi
|
||||
git shortlog -sne | awk '{$1=""; sub(" ", ""); print}'
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -48,7 +29,8 @@ authors() {
|
|||
#
|
||||
|
||||
if $LIST; then
|
||||
authors
|
||||
echo "$(authors)"
|
||||
else
|
||||
authors >> "$FILE"
|
||||
authors >> $FILE
|
||||
test -n "$EDITOR" && $EDITOR $FILE
|
||||
fi
|
||||
|
|
|
|||
11
bin/git-back
Executable file
11
bin/git-back
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if test $# -eq 0; then
|
||||
git reset --soft HEAD~1
|
||||
else
|
||||
if `echo $1 | grep -q [^[:digit:]]`; then
|
||||
echo "$1 is not a number" 1>&2
|
||||
else
|
||||
git reset --soft HEAD~$1
|
||||
fi
|
||||
fi
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
remote=${1:-""}
|
||||
branch=""
|
||||
filename=${2:-""}
|
||||
line1=${3:-""}
|
||||
line2=${4:-""}
|
||||
|
||||
# get remote name
|
||||
if [[ $remote == "" ]]; then
|
||||
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
|
||||
remote=$(git config branch."${branch}".remote || echo "origin")
|
||||
fi
|
||||
|
||||
if [[ $remote == "" ]]; then
|
||||
echo "Remote not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
remote_url=$(git remote get-url "$remote") || exit $?
|
||||
|
||||
if [[ $remote_url = git@* ]]; then
|
||||
url=$(echo "$remote_url" | sed -E -e 's/:/\//' -e 's/\.git$//' -e 's/.*@(.*)/http:\/\/\1/')
|
||||
elif [[ $remote_url = http* ]]; then
|
||||
url=${remote_url%.git}
|
||||
fi
|
||||
|
||||
# construct urls
|
||||
commit_hash=$(git rev-parse HEAD 2>/dev/null)
|
||||
commit_or_branch=${commit_hash:-${branch}}
|
||||
|
||||
if [[ $remote_url =~ gitlab ]]; then
|
||||
# construct gitlab urls
|
||||
# https://gitlab.com/<user_or_group>/<repo>/-/blob/<commit_or_branch>/<filename>#L<line1>-<line2>
|
||||
if [[ -n ${filename} ]]; then
|
||||
url="${url}/-/blob/${commit_or_branch}/${filename}"
|
||||
if [[ -n "${line1}" ]]; then
|
||||
url="${url}#L${line1}"
|
||||
if [[ -n "${line2}" ]]; then
|
||||
url="${url}-${line2}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
elif [[ $remote_url =~ github ]]; then
|
||||
# construct github urls
|
||||
# https://github.com/<user_or_org>/<repo>/blob/<commit_or_branch>/<filename>#L<line1>-L<line2>
|
||||
if [[ -n "${filename}" ]]; then
|
||||
url="${url}/blob/${commit_or_branch}/${filename}"
|
||||
if [[ -n "${line1}" ]]; then
|
||||
url="${url}#L${line1}"
|
||||
if [[ -n "${line2}" ]]; then
|
||||
url="${url}-L${line2}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
elif [[ $remote_url =~ bitbucket ]]; then
|
||||
# construct bitbucket urls
|
||||
# https://bitbucket.org/<user_or_org>/<repo>/src/<commit_or_branch>/<filename>#lines-<line1>:<line2>
|
||||
if [[ -n ${filename} ]]; then
|
||||
url=${url}/src/${commit_or_branch}/${filename}
|
||||
if [[ -n "${line1}" ]]; then
|
||||
url="${url}#lines-${line1}"
|
||||
if [[ -n "${line2}" ]]; then
|
||||
url="${url}:${line2}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# open url
|
||||
case "$OSTYPE" in
|
||||
darwin*)
|
||||
# MacOS
|
||||
open "$url"
|
||||
;;
|
||||
msys)
|
||||
# Git-Bash on Windows
|
||||
start "$url"
|
||||
;;
|
||||
linux*)
|
||||
# Handle WSL on Windows
|
||||
if uname -a | grep -i -q Microsoft && command -v powershell.exe; then
|
||||
powershell.exe -NoProfile start "$url"
|
||||
else
|
||||
xdg-open "$url"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# fall back to xdg-open for BSDs, etc.
|
||||
xdg-open "$url"
|
||||
;;
|
||||
esac
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
if [[ $1 == "" ]]
|
||||
then
|
||||
branch=$(git rev-parse --abbrev-ref HEAD &> /dev/null)
|
||||
remote=$(git config branch."${branch}".remote || echo "origin")
|
||||
else
|
||||
remote=$1
|
||||
fi
|
||||
|
||||
if [[ -z $remote ]]
|
||||
then
|
||||
echo "Remote not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
remote_url=$(git remote get-url "${remote}") || exit $?
|
||||
|
||||
if [[ $remote_url = git@* ]]
|
||||
then
|
||||
url=$(echo "${remote_url}" | sed -E -e 's/:/\//' -e 's/\.git$//' -e 's/.*@(.*)/http:\/\/\1/')
|
||||
elif [[ $remote_url = http* ]]
|
||||
then
|
||||
url=${remote_url%.git}
|
||||
fi
|
||||
|
||||
if [[ $url =~ github ]]
|
||||
then
|
||||
ci_url=${url}/actions
|
||||
elif [[ $url =~ gitlab ]]
|
||||
then
|
||||
ci_url=${url}/-/pipelines
|
||||
elif [[ $url =~ bitbucket ]]
|
||||
then
|
||||
ci_url=${url}/addon/pipelines/home
|
||||
fi
|
||||
case "$OSTYPE" in
|
||||
darwin*)
|
||||
# MacOS
|
||||
open "${ci_url}"
|
||||
;;
|
||||
msys)
|
||||
# Git-Bash on Windows
|
||||
start "${ci_url}"
|
||||
;;
|
||||
linux*)
|
||||
# Handle WSL on Windows
|
||||
if uname -a | grep -i -q Microsoft && command -v powershell.exe
|
||||
then
|
||||
powershell.exe -NoProfile start "${ci_url}"
|
||||
else
|
||||
xdg-open "${ci_url}"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# fall back to xdg-open for BSDs, etc.
|
||||
xdg-open "${ci_url}"
|
||||
;;
|
||||
esac
|
||||
81
bin/git-brv
81
bin/git-brv
|
|
@ -1,81 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# A sorted prettier branch -vv
|
||||
|
||||
if ! (( BASH_VERSINFO[0] > 4 ||
|
||||
BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 2 )); then
|
||||
printf >&2 'This script requires bash 4.2 or newer\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# allow the user to set a default reverse option in an environment variable
|
||||
reverse=$(git config --get git-extras.brv.reverse || echo false)
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-r | --reverse)
|
||||
reverse=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
shopt -s checkwinsize
|
||||
COLUMNS=$(tput cols)
|
||||
color_branch_local=$(git config --get-color color.branch.local normal)
|
||||
color_branch_current=$(git config --get-color color.branch.current green)
|
||||
color_diff_commit=$(git config --get-color color.diff.commit yellow)
|
||||
color_branch_upstream=$(git config --get-color color.branch.upstream blue)
|
||||
reset=$(tput sgr0)
|
||||
fi
|
||||
|
||||
|
||||
declare -A upstream date hash message
|
||||
eval "$(
|
||||
git for-each-ref --format='upstream[%(refname:short)]=%(upstream:short)' \
|
||||
--shell 'refs/heads/**'
|
||||
)"
|
||||
|
||||
for b in "${!upstream[@]}"; do
|
||||
blen=${#b} ulen=${#upstream[$b]}
|
||||
(( bwidth = blen > bwidth ? blen : bwidth ))
|
||||
(( uwidth = ulen > uwidth ? ulen : uwidth ))
|
||||
IFS=/ read -r 'date[$b]' 'hash[$b]' 'message[$b]' < <(
|
||||
git log --no-walk=unsorted --format=%ct/%h/%s "$b" --
|
||||
)
|
||||
hlen=${#hash[$b]}
|
||||
(( hwidth = hlen > hwidth ? hlen : hwidth ))
|
||||
done
|
||||
|
||||
mapfile -t ordered < <(
|
||||
# the reverse option of git-brv causes the sort to be sorted normally rather than in reverse
|
||||
reverse_opt=""
|
||||
if [[ $reverse = false ]]; then
|
||||
reverse_opt="-r"
|
||||
fi
|
||||
for b in "${!date[@]}"; do
|
||||
printf '%d\t%s\n' "${date[$b]}" "$b"
|
||||
done | sort -n $reverse_opt | cut -f2-
|
||||
)
|
||||
|
||||
current=$(git symbolic-ref -q --short HEAD)
|
||||
|
||||
for b in "${ordered[@]}"; do
|
||||
branch_color=$color_branch_local
|
||||
if [[ $b = "$current" ]]; then
|
||||
branch_color=$color_branch_current
|
||||
fi
|
||||
if [[ -t 1 ]]; then
|
||||
msg=${message[$b]:0:COLUMNS-bwidth-uwidth-hwidth-14}
|
||||
else
|
||||
msg=${message[$b]}
|
||||
fi
|
||||
printf '%(%Y-%m-%d)T %s%*s%s %s%*s%s %s%*s%s %s\n' \
|
||||
"${date[$b]}" \
|
||||
"$branch_color" "-$bwidth" "$b" "$reset" \
|
||||
"$color_branch_upstream" "-$uwidth" "${upstream[$b]}" "$reset" \
|
||||
"$color_diff_commit" "-$hwidth" "${hash[$b]}" "$reset" \
|
||||
"$msg"
|
||||
done
|
||||
15
bin/git-bug
Executable file
15
bin/git-bug
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if test "$1" = "finish"; then
|
||||
test -z $2 && echo "bug <name> required." 1>&2 && exit 1
|
||||
branch=bug/$2
|
||||
git merge --no-ff $branch && git delete-branch $branch
|
||||
else
|
||||
test -z $1 && echo "bug <name> required." 1>&2 && exit 1
|
||||
if test -n "$2"; then
|
||||
echo "too many arguments; if not finishing a bug, only <name> of new bug is expected" && exit 1
|
||||
fi
|
||||
branch=bug/$1
|
||||
git checkout -b $branch &> /dev/null
|
||||
test -n $? && git checkout $branch &> /dev/null
|
||||
fi
|
||||
231
bin/git-bulk
231
bin/git-bulk
|
|
@ -1,231 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
inverse=$(tput rev)
|
||||
reset=$(tput sgr0)
|
||||
txtbld=$(tput bold)
|
||||
bldred=${txtbld}$(tput setaf 1)
|
||||
|
||||
# default option settings
|
||||
guardedmode=false
|
||||
singlemode=false
|
||||
allwsmode=false
|
||||
quiet=false
|
||||
no_follow_symlinks=false
|
||||
no_follow_hidden=false
|
||||
|
||||
#
|
||||
# print usage message
|
||||
#
|
||||
usage() {
|
||||
echo 1>&2 "usage: git bulk [--no-follow-symlinks] [--no-follow-hidden] [-q|--quiet] [-g] ([-a]|[-w <ws-name>]) <git command>"
|
||||
echo 1>&2 " git bulk --addworkspace <ws-name> <ws-root-directory> (--from <URL or file>)"
|
||||
echo 1>&2 " git bulk --removeworkspace <ws-name>"
|
||||
echo 1>&2 " git bulk --addcurrent <ws-name>"
|
||||
echo 1>&2 " git bulk --purge"
|
||||
echo 1>&2 " git bulk --listall"
|
||||
}
|
||||
|
||||
cdfail() {
|
||||
echo 1>&2 "failed to change directory: $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# add another workspace to global git config
|
||||
addworkspace() {
|
||||
git config --global bulkworkspaces."$wsname" "$wsdir";
|
||||
if [ -n "$source" ]; then
|
||||
if [ ! -d "$wsdir" ]; then echo 1>&2 "Path of workspace doesn't exist, make it first."; exit 1; fi
|
||||
regex='http(s)?://|ssh://|(git@)?.*:.*/.*'
|
||||
if [[ "$source" =~ $regex ]]; then
|
||||
pushd "$wsdir" > /dev/null || cdfail "$wsdir"
|
||||
git clone "$source"
|
||||
popd > /dev/null || cdfail "$OLDPWD"
|
||||
else
|
||||
source=$(realpath "$source" 2>/dev/null)
|
||||
if [ -f "$source" ]; then
|
||||
pushd "$wsdir" > /dev/null || cdfail "$wsdir"
|
||||
while IFS= read -r line; do
|
||||
if [ -n "$line" ]; then
|
||||
# the git clone command to take the complete line in the repository.txt as separate argument. This facilitated the cloning of the repository with a custom folder name.
|
||||
# shellcheck disable=SC2086
|
||||
git clone $line;
|
||||
fi
|
||||
done < "$source"
|
||||
popd > /dev/null || cdfail "$OLDPWD"
|
||||
else
|
||||
echo 1>&2 "format of URL or file unknown"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# add current directory
|
||||
addcurrent() { git config --global bulkworkspaces."$wsname" "$PWD"; }
|
||||
|
||||
# remove workspace from global git config
|
||||
removeworkspace() { checkWSName && git config --global --unset bulkworkspaces."$wsname"; }
|
||||
|
||||
# remove workspace from global git config
|
||||
purge() { git config --global --remove-section bulkworkspaces; }
|
||||
|
||||
# list all current workspace locations defined
|
||||
listall() { git config --global --get-regexp bulkworkspaces; }
|
||||
|
||||
# guarded execution of a git command in one specific repository
|
||||
guardedExecution () {
|
||||
if [ "${quiet?}" != "true" ] || $guardedmode; then
|
||||
echo 1>&2 "${bldred}->${reset} executing ${inverse}git $gitcommand${reset} in repository ${leadingpath%/*}/${bldred}${curdir##*/}${reset}"
|
||||
fi
|
||||
|
||||
if $guardedmode; then
|
||||
echo 1>&2 -n " Execute command here (y/n)? "
|
||||
read -n 1 -r </dev/tty; echo 1>&2
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
git "$@"
|
||||
fi
|
||||
else
|
||||
git "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# check if the passed command is known as a core git command
|
||||
checkGitCommand () {
|
||||
if git help -a | grep -o -q "\b${corecommand}\b"; then
|
||||
echo 1>&2 "Core command \"$corecommand\" accepted."
|
||||
else
|
||||
if git config --get-regexp alias | grep -o -q "\.${corecommand} "; then
|
||||
echo 1>&2 "Alias ${corecommand} accepted."
|
||||
else
|
||||
usage && echo 1>&2 "error: unknown GIT command: $corecommand" && exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# check if workspace name is registered
|
||||
checkWSName () {
|
||||
while read -r workspace; do
|
||||
parseWsName "$workspace"
|
||||
if [[ $rwsname == "$wsname" ]]; then return; fi
|
||||
done <<< "$(listall)"
|
||||
# when here the ws name was not found
|
||||
usage && echo 1>&2 "error: unknown workspace name: $wsname" && exit 1
|
||||
}
|
||||
|
||||
# parse out wsname from workspacespec
|
||||
parseWsName () {
|
||||
local wsspec="$1"
|
||||
# Get the workspace value from its specification in the `.gitconfig`.
|
||||
# May be an absolute path or a variable name of the form: `$VARNAME`
|
||||
rwsdir=${wsspec#* }
|
||||
if [[ ${rwsdir:0:1} == '$' ]]; then
|
||||
# Dereference the `rwsdir` value which is a variable name.
|
||||
rwsdir_varname=${rwsdir:1}
|
||||
rwsdir=${!rwsdir_varname}
|
||||
if [[ -z "${rwsdir}" ]]; then
|
||||
echo 1>&2 "error: bad environment variable: $rwsdir_varname" && exit 1
|
||||
fi
|
||||
fi
|
||||
rwsname=${wsspec#*.} && rwsname=${rwsname%% *}
|
||||
}
|
||||
|
||||
# detects the wsname of the current directory
|
||||
wsnameToCurrent () {
|
||||
while read -r workspace; do
|
||||
if [ -z "$workspace" ]; then continue; fi
|
||||
parseWsName "$workspace"
|
||||
if echo "$PWD" | grep -o -q "$rwsdir"; then wsname="$rwsname" && return; fi
|
||||
done <<< "$(listall)"
|
||||
# when here then not in workspace dir
|
||||
echo 1>&2 "error: you are not in a workspace directory. your registered workspaces are:" && \
|
||||
wslist="$(listall)" && echo 1>&2 "${wslist:-'<no workspaces defined yet>'}" && exit 1
|
||||
}
|
||||
|
||||
# helper to check number of arguments.
|
||||
allowedargcount () {
|
||||
if [ "$paramcount" -ne "${1:-0}" ] && [ "$paramcount" -ne "${2:-0}" ]; then
|
||||
echo 1>&2 "error: wrong number of arguments" && usage;
|
||||
exit 1;
|
||||
fi
|
||||
}
|
||||
|
||||
# execute the bulk operation
|
||||
executBulkOp () {
|
||||
checkGitCommand
|
||||
if ! $allwsmode && ! $singlemode; then wsnameToCurrent; fi # by default git bulk works within the 'current' workspace
|
||||
listall | while read -r workspacespec; do
|
||||
parseWsName "$workspacespec"
|
||||
if [[ -n $wsname ]] && [[ $rwsname != "$wsname" ]]; then continue; fi
|
||||
cd "$rwsdir" || exit 1
|
||||
local actual=$PWD
|
||||
[ "${quiet?}" != "true" ] && echo 1>&2 "Executing bulk operation in workspace ${inverse}$actual${reset}"
|
||||
|
||||
# build `find` flags depending on command-line options
|
||||
local find_flags=()
|
||||
if [[ "$no_follow_symlinks" == true ]]; then
|
||||
find_flags+=(-P)
|
||||
else
|
||||
find_flags+=(-L)
|
||||
fi
|
||||
# find all git repositories under the workspace on which we want to operate
|
||||
readarray allGitFolders < <(find "${find_flags[@]}" . -name ".git" 2>/dev/null)
|
||||
|
||||
for line in "${allGitFolders[@]}"; do
|
||||
local gitrepodir=${line::${#line}-5} # cut the .git part of find results to have the root git directory of that repository
|
||||
cd "$gitrepodir" || exit 1 # into git repo location
|
||||
local curdir=$PWD
|
||||
local leadingpath=${curdir#"${actual}"}
|
||||
# do not execute if we do not want to consider a ".git" directory under a hidden directory
|
||||
if [ $no_follow_hidden = false ] || ! [[ "$leadingpath" =~ "/." ]]; then
|
||||
guardedExecution "$@"
|
||||
fi
|
||||
cd "$rwsdir" || exit 1 # back to origin location of last find command
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
paramcount="${#}"
|
||||
|
||||
# if no arguments show usage
|
||||
if [[ $paramcount -le 0 ]]; then usage; fi
|
||||
|
||||
# parse command parameters
|
||||
while [ "${#}" -ge 1 ] ; do
|
||||
case "$1" in
|
||||
--quiet|-q) quiet='true' ;;
|
||||
--listall|--purge)
|
||||
butilcommand="${1:2}" && break ;;
|
||||
--removeworkspace|--addcurrent|--addworkspace)
|
||||
butilcommand="${1:2}" && wsname="$2" && wsdir="$3" && if [ "$4" = "--from" ]; then source="$5"; fi && break ;;
|
||||
--no-follow-symlinks)
|
||||
no_follow_symlinks=true ;;
|
||||
--no-follow-hidden)
|
||||
no_follow_hidden=true ;;
|
||||
-a)
|
||||
allwsmode=true ;;
|
||||
-g)
|
||||
guardedmode=true ;;
|
||||
-w)
|
||||
singlemode=true && shift && wsname="$1" && checkWSName ;;
|
||||
--*)
|
||||
usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
|
||||
-*)
|
||||
usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
|
||||
*) # git core commands
|
||||
butilcommand="executBulkOp" && corecommand="$1" && gitcommand="$*" && break ;;
|
||||
esac && shift
|
||||
done
|
||||
|
||||
# check option compatibility
|
||||
if $allwsmode && $singlemode; then echo 1>&2 "error: options -w and -a are incompatible" && exit 1; fi
|
||||
|
||||
# if single mode check the supplied workspace name
|
||||
if $singlemode; then echo 1>&2 "Selected single workspace mode in workspace: $wsname" && checkWSName; fi
|
||||
|
||||
# check right number of arguments
|
||||
case $butilcommand in
|
||||
listall|purge) allowedargcount 1;;
|
||||
addcurrent|removeworkspace) allowedargcount 2;;
|
||||
addworkspace) allowedargcount 3 5;;
|
||||
esac
|
||||
|
||||
# pass the origin arguments to the 'executBulkOp'
|
||||
$butilcommand "$@" # run user command
|
||||
|
|
@ -4,20 +4,9 @@ DEF_TAG_RECENT="n.n.n"
|
|||
GIT_LOG_OPTS="$(git config changelog.opts)"
|
||||
GIT_LOG_FORMAT="$(git config changelog.format)"
|
||||
[[ -z "$GIT_LOG_FORMAT" ]] && GIT_LOG_FORMAT=' * %s'
|
||||
GIT_MERGELOG_FORMAT="$(git config changelog.mergeformat)"
|
||||
[[ -z "$GIT_MERGELOG_FORMAT" ]] && GIT_MERGELOG_FORMAT=' * %s%n%w(64,4,4)%b'
|
||||
GIT_EDITOR="$(git var GIT_EDITOR)"
|
||||
PROGNAME="git-changelog"
|
||||
|
||||
git_editor() {
|
||||
if test -z "${GIT_EDITOR:+set}"
|
||||
then
|
||||
GIT_EDITOR="$(git var GIT_EDITOR)" || return $?
|
||||
fi
|
||||
|
||||
eval "$GIT_EDITOR" '"$@"'
|
||||
}
|
||||
|
||||
_usage() {
|
||||
cat << EOF
|
||||
usage: $PROGNAME options [file]
|
||||
|
|
@ -31,14 +20,12 @@ tags exist, then all commits are output; if tags exist, then only the most-
|
|||
recent commits are output up to the last identified tag.
|
||||
|
||||
OPTIONS:
|
||||
-a, --all Retrieve all commits (ignores --start-tag/commit, --final-tag)
|
||||
-a, --all Retrieve all commits (ignores --start-tag, --final-tag)
|
||||
-l, --list Display commits as a list, with no titles
|
||||
-t, --tag Tag label to use for most-recent (untagged) commits
|
||||
-f, --final-tag Newest tag to retrieve commits from in a range
|
||||
-s, --start-tag Oldest tag to retrieve commits from in a range
|
||||
--start-commit Like --start-tag but use commit instead of tag
|
||||
-n, --no-merges Suppress commits from merged branches
|
||||
-m, --merges-only Only uses merge commits (uses both subject and body of commit)
|
||||
-p, --prune-old Replace existing Changelog entirely with new content
|
||||
-x, --stdout Write output to stdout instead of to a Changelog file
|
||||
-h, --help, ? Show this message
|
||||
|
|
@ -155,16 +142,14 @@ _fetchCommitRange() {
|
|||
local start_tag="$2"
|
||||
local final_tag="$3"
|
||||
|
||||
# This shellcheck disable is applied to the whole if-body
|
||||
# shellcheck disable=SC2086
|
||||
if [[ "$list_all" == true ]]; then
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}"
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${GIT_LOG_FORMAT}"
|
||||
elif [[ -n "$final_tag" && "$start_tag" == "null" ]]; then
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}" "${final_tag}"
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${GIT_LOG_FORMAT}" "${final_tag}"
|
||||
elif [[ -n "$final_tag" ]]; then
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}" "${start_tag}"'..'"${final_tag}"
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${GIT_LOG_FORMAT}" "${start_tag}"'..'"${final_tag}"
|
||||
elif [[ -n "$start_tag" ]]; then
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}" "${start_tag}"'..'
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${GIT_LOG_FORMAT}" "${start_tag}"'..'
|
||||
fi | sed 's/^ \* \*/ */g'
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +157,7 @@ _formatCommitPlain() {
|
|||
local start_tag="$1"
|
||||
local final_tag="$2"
|
||||
|
||||
printf "%s\n" "$(_fetchCommitRange "false" "$start_tag" "$final_tag")"
|
||||
printf "%s" "$(_fetchCommitRange "false" "$start_tag" "$final_tag")"
|
||||
}
|
||||
|
||||
_formatCommitPretty() {
|
||||
|
|
@ -199,8 +184,7 @@ commitList() {
|
|||
local title_tag="$1"; shift
|
||||
local start_tag="$1"; shift
|
||||
local final_tag="$1"; shift
|
||||
local list_style="${1:-false}"; shift # enable/disable list format
|
||||
local start_commit="$1"; shift
|
||||
local list_style="${1:-false}" # enable/disable list format
|
||||
local changelog="$FILE"
|
||||
local title_date="$(date +'%Y-%m-%d')"
|
||||
local tags_list=()
|
||||
|
|
@ -208,19 +192,6 @@ commitList() {
|
|||
local defaultIFS="$IFS"
|
||||
local IFS="$defaultIFS"
|
||||
|
||||
if [[ -n "$start_commit" && "$final_tag" == "null" \
|
||||
&& "$start_tag" == "null" ]]; then
|
||||
# if there is not tag after $start_commit,
|
||||
# output directly without fetch all tags
|
||||
if [[ "$list_style" == true ]]; then
|
||||
_formatCommitPlain "${start_commit}~"
|
||||
else
|
||||
_formatCommitPretty "$title_tag" "$title_date" "$start_commit~"
|
||||
fi
|
||||
|
||||
return
|
||||
fi
|
||||
|
||||
#
|
||||
# Tags look like this:
|
||||
#
|
||||
|
|
@ -243,18 +214,18 @@ commitList() {
|
|||
|
||||
# fetch our tags
|
||||
local _ref _date _tag _tab='%x09'
|
||||
local _tag_regex='tag: *'
|
||||
while IFS=$'\t' read -r _ref _date _tag; do
|
||||
local _tag_regex='^[[:alnum:][:blank:][:punct:]]+/.*'
|
||||
while IFS=$'\t' read _ref _date _tag; do
|
||||
[[ -z "${_tag}" ]] && continue
|
||||
# strip out tags form ()
|
||||
# git v2.2.0+ supports '%D', like '%d' without the " (", ")" wrapping. One day we should use it instead.
|
||||
_tag="${_tag# }"; _tag="${_tag//[()]/}"
|
||||
# trap tag if it points to last commit (HEAD)
|
||||
_tag="${_tag#HEAD*, }"
|
||||
# strip out branches
|
||||
[[ ! "${_tag}" =~ ${_tag_regex} ]] && continue
|
||||
_tag="${_tag##HEAD, }"
|
||||
# strip out any additional tags pointing to same commit, remove tag label
|
||||
_tag="${_tag%%,*}"; _tag="${_tag#tag: }"
|
||||
# strip out tags that are actually feature branches (git-flow support)
|
||||
[[ "${_tag}" =~ ${_tag_regex} ]] && continue
|
||||
# add tag to assoc array; copy tag to tag_list_keys for ordered iteration
|
||||
tags_list+=( "${_tag}:${_ref}=>${_date}" )
|
||||
tags_list_keys+=( "${_tag}" )
|
||||
|
|
@ -264,35 +235,13 @@ commitList() {
|
|||
unset _ref _date _tag _tab
|
||||
|
||||
local _tags_list_keys_length="${#tags_list_keys[@]}"
|
||||
if [[ "${_tags_list_keys_length}" -eq 0 ]]
|
||||
then
|
||||
unset _tags_list_keys_length
|
||||
if [[ "$list_style" == true ]]; then
|
||||
printf "%s" "$(_fetchCommitRange "true")"
|
||||
else
|
||||
local title="$title_tag / $title_date"
|
||||
local title_underline=""
|
||||
|
||||
local i
|
||||
for i in $(seq ${#title}); do
|
||||
title_underline+="="
|
||||
done
|
||||
unset i
|
||||
|
||||
printf '\n%s\n%s\n' "$title" "$title_underline"
|
||||
printf "\n%s\n" "$(_fetchCommitRange "true")"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
local _final_tag_found=false
|
||||
local _start_tag_found=false
|
||||
local i
|
||||
for (( i=0; i<"${_tags_list_keys_length}"; i++ )); do
|
||||
local __curr_tag="${tags_list_keys[$i]}"
|
||||
local __prev_tag="${tags_list_keys[$i+1]:-null}"
|
||||
local __curr_date
|
||||
__curr_date="$(_valueForKeyFakeAssocArray "${__curr_tag}" "${tags_list[*]}")"
|
||||
local __curr_date="$(_valueForKeyFakeAssocArray "${__curr_tag}" "${tags_list[*]}")"
|
||||
__curr_date="${__curr_date##*=>}"
|
||||
|
||||
# output latest commits, up until the most-recent tag, these are all
|
||||
|
|
@ -317,23 +266,7 @@ commitList() {
|
|||
# find the specified start tag, break when found
|
||||
if [[ -n "$start_tag" ]]; then
|
||||
[[ "$start_tag" == "${__curr_tag}" ]] && _start_tag_found=true
|
||||
if [[ "${_start_tag_found}" == true ]]; then
|
||||
if [[ -n "$start_commit" ]]; then
|
||||
|
||||
# output commits after start_commit to its closest tag
|
||||
if [[ "$list_style" == true ]]; then
|
||||
_formatCommitPlain "$start_commit~" "${__curr_tag}"
|
||||
else
|
||||
_formatCommitPretty "${__curr_tag}" "${__curr_date}" \
|
||||
"$start_commit~" "${__curr_tag}"
|
||||
fi
|
||||
|
||||
break
|
||||
fi
|
||||
|
||||
[[ "$start_tag" != "${__curr_tag}" ]] && break
|
||||
|
||||
fi
|
||||
[[ "$start_tag" != "${__curr_tag}" && "${_start_tag_found}" == true ]] && break
|
||||
fi
|
||||
|
||||
# output commits made between prev_tag and curr_tag, these are all of the
|
||||
|
|
@ -359,9 +292,8 @@ commitListPlain() {
|
|||
local list_all="${1:-false}"
|
||||
local start_tag="$2"
|
||||
local final_tag="$3"
|
||||
local start_commit="$4"
|
||||
|
||||
commitList "$list_all" "" "$start_tag" "$final_tag" "true" "$start_commit"
|
||||
commitList "$list_all" "" "$start_tag" "$final_tag" "true"
|
||||
}
|
||||
|
||||
commitListPretty() {
|
||||
|
|
@ -369,48 +301,11 @@ commitListPretty() {
|
|||
local title_tag="$2"
|
||||
local start_tag="$3"
|
||||
local final_tag="$4"
|
||||
local start_commit="$5"
|
||||
local title_date="$(date +'%Y-%m-%d')"
|
||||
|
||||
commitList "$list_all" "$title_tag" "$start_tag" "$final_tag" "false" \
|
||||
"$start_commit"
|
||||
commitList "$list_all" "$title_tag" "$start_tag" "$final_tag"
|
||||
}
|
||||
|
||||
_exit() {
|
||||
local pid_list=()
|
||||
local defaultIFS="$IFS"
|
||||
local IFS="$defaultIFS"
|
||||
|
||||
stty sane; echo; echo "caught signal, shutting down"
|
||||
|
||||
IFS=$'\n'
|
||||
# The format of `ps` is different between Windows and other platforms,
|
||||
# so we need to calculate the total column number(COL_NUM) of header first.
|
||||
# Why don't we just use the last column?
|
||||
# Because the body of CMD column may contain space and be treated as multiple fields.
|
||||
pid_list=( $(ps -f |
|
||||
awk -v ppid=$$ 'NR == 1 {
|
||||
COL_NUM = NF
|
||||
}
|
||||
$3 == ppid {
|
||||
# filter out temp processes created in this subshell
|
||||
if ($COL_NUM != "ps" && $COL_NUM != "awk" && $COL_NUM !~ "bash$")
|
||||
print $2
|
||||
}')
|
||||
)
|
||||
IFS="$defaultIFS"
|
||||
|
||||
local _pid
|
||||
for _pid in "${pid_list[@]}"; do
|
||||
echo "killing: ${_pid}"
|
||||
kill -TERM "${_pid}"
|
||||
done
|
||||
|
||||
wait; stty sane; exit 1
|
||||
}
|
||||
|
||||
trap '_exit' SIGINT SIGQUIT SIGTERM
|
||||
|
||||
main() {
|
||||
local start_tag="null" # empty string and "null" mean two different things!
|
||||
local final_tag="null"
|
||||
|
|
@ -420,7 +315,6 @@ main() {
|
|||
"list_style:false"
|
||||
"title_tag:$DEF_TAG_RECENT"
|
||||
"start_tag:"
|
||||
"start_commit:"
|
||||
"final_tag:"
|
||||
"output_file:"
|
||||
"use_stdout:false"
|
||||
|
|
@ -457,17 +351,8 @@ main() {
|
|||
option=( $(_setValueForKeyFakeAssocArray "start_tag" "$2" "${option[*]}") )
|
||||
shift
|
||||
;;
|
||||
--start-commit )
|
||||
option=( $(_setValueForKeyFakeAssocArray "start_commit" "$2" "${option[*]}") )
|
||||
shift
|
||||
;;
|
||||
-n | --no-merges )
|
||||
GIT_LOG_OPTS='--no-merges'
|
||||
CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
||||
;;
|
||||
-m | --merges-only )
|
||||
GIT_LOG_OPTS='--merges'
|
||||
CUR_GIT_LOG_FORMAT="$GIT_MERGELOG_FORMAT"
|
||||
;;
|
||||
-p | --prune-old )
|
||||
option=( $(_setValueForKeyFakeAssocArray "prune_old" true "${option[*]}") )
|
||||
|
|
@ -487,44 +372,14 @@ main() {
|
|||
shift
|
||||
done
|
||||
|
||||
# The default log format unless already set
|
||||
[[ -z "$CUR_GIT_LOG_FORMAT" ]] && CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
||||
|
||||
local _tag="$(_valueForKeyFakeAssocArray "start_tag" "${option[*]}")"
|
||||
local start_commit="$(_valueForKeyFakeAssocArray "start_commit" "${option[*]}")"
|
||||
|
||||
if [[ -n "$start_commit" ]]; then
|
||||
if [[ -n "${_tag}" ]]; then
|
||||
_error "--start-tag could not use with --start-commit!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
start_tag="$(git describe --tags --contains "$start_commit" 2>/dev/null || echo 'null')"
|
||||
if [[ -z "$start_tag" ]]; then
|
||||
_error "Could find the associative tag for the start-commit!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# remove suffix from the $start_tag when no tag matched exactly
|
||||
start_tag="${start_tag%%~*}"
|
||||
# also remove "^0" added sometimes when tag matched exactly
|
||||
start_tag="${start_tag%%^0}"
|
||||
|
||||
elif [[ -n "${_tag}" ]]; then
|
||||
if [[ -n "${_tag}" ]]; then
|
||||
start_tag="$(git describe --tags --abbrev=0 "${_tag}" 2>/dev/null)"
|
||||
if [[ -z "$start_tag" ]]; then
|
||||
_error "Specified start-tag does not exist!"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${_tag}" ]]; then
|
||||
if [[ -n "$start_commit" ]]; then
|
||||
_error "--start-tag could not use with --start-commit!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
fi
|
||||
unset _tag
|
||||
|
||||
local _tag="$(_valueForKeyFakeAssocArray "final_tag" "${option[*]}")"
|
||||
|
|
@ -540,29 +395,26 @@ main() {
|
|||
#
|
||||
# generate changelog
|
||||
#
|
||||
local tmpfile changelog title_tag
|
||||
tmpfile="$(git_extra_mktemp)"
|
||||
changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
|
||||
title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
|
||||
local tmpfile="$(git_extra_mktemp)"
|
||||
local changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
|
||||
local title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
|
||||
|
||||
if [[ "$(_valueForKeyFakeAssocArray "list_style" "${option[*]}")" == true ]]; then
|
||||
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
||||
commitListPlain "true" >> "$tmpfile"
|
||||
else
|
||||
commitListPlain "false" "$start_tag" "$final_tag" \
|
||||
"$start_commit" >> "$tmpfile"
|
||||
commitListPlain "false" "$start_tag" "$final_tag" >> "$tmpfile"
|
||||
fi
|
||||
else
|
||||
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
||||
commitListPretty "true" "$title_tag" >> "$tmpfile"
|
||||
else
|
||||
commitListPretty "false" "$title_tag" "$start_tag" "$final_tag" \
|
||||
"$start_commit" >> "$tmpfile"
|
||||
commitListPretty "false" "$title_tag" "$start_tag" "$final_tag" >> "$tmpfile"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$changelog" ]]; then
|
||||
changelog="$(find . -mindepth 1 -maxdepth 1 \( -iname '*change*' -o -iname '*history*' \) | head -n1)"
|
||||
changelog="$(ls | egrep 'change|history' -i | head -n1)"
|
||||
if [[ -z "$changelog" ]]; then
|
||||
changelog="History.md";
|
||||
fi
|
||||
|
|
@ -579,14 +431,8 @@ main() {
|
|||
cat "$tmpfile"
|
||||
rm -f "$tmpfile"
|
||||
else
|
||||
cp -f "$tmpfile" "$changelog"
|
||||
rm -f "$tmpfile"
|
||||
|
||||
if [[ -n "$GIT_EDITOR" ]]; then
|
||||
git_editor "$changelog" || _exit
|
||||
else
|
||||
less "$changelog" || _exit
|
||||
fi
|
||||
mv -f "$tmpfile" "$changelog"
|
||||
[[ -n "$GIT_EDITOR" ]] && $GIT_EDITOR "$changelog"
|
||||
fi
|
||||
|
||||
return
|
||||
|
|
|
|||
15
bin/git-chore
Normal file
15
bin/git-chore
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if test "$1" = "finish"; then
|
||||
test -z $2 && echo "chore <name> required." 1>&2 && exit 1
|
||||
branch=chore/$2
|
||||
git merge --no-ff $branch && git delete-branch $branch
|
||||
else
|
||||
test -z $1 && echo "chore <name> required." 1>&2 && exit 1
|
||||
if test -n "$2"; then
|
||||
echo "too many arguments; if not finishing a chore, only <name> of new chore is expected" && exit 1
|
||||
fi
|
||||
branch=chore/$1
|
||||
git checkout -b $branch &> /dev/null
|
||||
test -n $? && git checkout $branch &> /dev/null
|
||||
fi
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
PROGNAME="git-clear"
|
||||
FORCE=0
|
||||
|
||||
_usage() {
|
||||
cat << EOF
|
||||
usage: $PROGNAME options
|
||||
usage: $PROGNAME -h|help|?
|
||||
|
||||
clear git repository
|
||||
|
||||
OPTIONS:
|
||||
-f, --force Force clear without questioning user
|
||||
-h, --help, ? Show this message
|
||||
EOF
|
||||
}
|
||||
|
||||
# Read arguments
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-f|--force)
|
||||
FORCE=1
|
||||
;;
|
||||
-h|--help|?)
|
||||
_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
# Only wait for answer if not forced by user
|
||||
if [[ $FORCE == 0 ]]; then
|
||||
echo -n "Sure? - THIS COMMAND MAY DELETE FILES THAT CANNOT BE RECOVERED, including those in .gitignore [y/N]: "
|
||||
read -r clean
|
||||
else
|
||||
clean=y
|
||||
fi
|
||||
|
||||
if [ "$clean" = "y" ]; then
|
||||
git clean -d -f -x && git reset --hard
|
||||
fi
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo -n "Sure? - This command may delete files that cannot be recovered. Files and directories in .gitignore will be preserved [y/N]: "
|
||||
read -r answer
|
||||
if [ "$answer" = "y" ]
|
||||
then git clean -d -f && git reset --hard
|
||||
fi
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
coauthor=$1
|
||||
email=$2
|
||||
|
||||
test -z "$coauthor" && echo "co-author required." 1>&2 && exit 1
|
||||
test -z "$email" && echo "co-author email required." 1>&2 && exit 1
|
||||
|
||||
old_message="$(git log --format=%B -n1)"
|
||||
if [[ "$old_message" == *"Co-authored-by:"* ]]; then
|
||||
git commit --amend -m "$old_message
|
||||
Co-authored-by: $coauthor <$email>"
|
||||
else
|
||||
git commit --amend -m "$old_message" -m "Co-authored-by: $coauthor <$email>"
|
||||
fi
|
||||
|
|
@ -1,36 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
REF=""
|
||||
SINCE=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-r|--ref)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "error: option $1 requires an argument" >&2
|
||||
exit 1
|
||||
fi
|
||||
REF="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
# non-flag args accumulation, e.g. "last " + "week" -> "last week"
|
||||
SINCE="${SINCE:+$SINCE }$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "$REF" && -n "$SINCE" ]]; then
|
||||
echo "error: '--ref <ref>' and '<date>' are mutually exclusive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SINCE="${SINCE:-last week}"
|
||||
|
||||
if [[ -n "$REF" ]]; then
|
||||
git log --pretty='%h %an - %s' "$REF..HEAD"
|
||||
else
|
||||
git log --pretty='%h %an - %s' --after="@{$SINCE}"
|
||||
fi
|
||||
SINCE="last week"
|
||||
test $# -ne 0 && SINCE=$@
|
||||
echo "... commits since $SINCE" >&2
|
||||
git log --pretty='%an - %s' --after="@{$SINCE}"
|
||||
|
|
@ -1 +0,0 @@
|
|||
git-abort
|
||||
|
|
@ -4,4 +4,6 @@ user="$*"
|
|||
|
||||
test -z "$user" && echo "user name required." 1>&2 && exit 1
|
||||
|
||||
git shortlog --format=format:"%h %s" --author="$user"
|
||||
count=`git log --oneline --pretty="format: %an" | grep -- "$user" | wc -l`
|
||||
test $count -eq 0 && echo "$user did not contribute." && exit 1
|
||||
git shortlog | grep -- "$user (" -A $count
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if test "$1" = "--all"; then
|
||||
git shortlog -n -s | awk '{print substr($0,index($0,$2)) " (" $1 ")"}'
|
||||
git shortlog -n -s $@ | awk '{print substr($0,index($0,$2)) " (" $1 ")"}'
|
||||
echo
|
||||
fi
|
||||
|
||||
echo total "$(git rev-list --count HEAD)"
|
||||
echo total `git rev-list --count HEAD`
|
||||
|
|
|
|||
78
bin/git-cp
78
bin/git-cp
|
|
@ -1,78 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PROGRAM=$0
|
||||
CURRENT_FILENAME=""
|
||||
DESTINATION_FILENAME=""
|
||||
|
||||
function usage()
|
||||
{
|
||||
echo 1>&2 "USAGE: ${PROGRAM} CURRENT_FILENAME DESTINATION_FILENAME"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
|
||||
if [[ "$CURRENT_FILENAME" == "" ]]; then
|
||||
CURRENT_FILENAME=$key
|
||||
elif [[ "$DESTINATION_FILENAME" == "" ]]; then
|
||||
DESTINATION_FILENAME=$key
|
||||
else
|
||||
usage
|
||||
exit 30 # Error during arguments parsing
|
||||
fi
|
||||
shift # past argument or value
|
||||
done
|
||||
|
||||
if [[ "$DESTINATION_FILENAME" == "" ]]; then
|
||||
usage
|
||||
exit 20 # Missing arguments CURRENT_FILENAME
|
||||
elif [[ "$CURRENT_FILENAME" == "" ]]; then
|
||||
usage
|
||||
exit 10 # Missing arguments CURRENT_FILENAME
|
||||
else
|
||||
if [ -e "$DESTINATION_FILENAME" ]; then
|
||||
echo 1>&2 "$DESTINATION_FILENAME already exists."
|
||||
echo 1>&2 "Make sure to remove the destination first."
|
||||
exit 40
|
||||
fi
|
||||
|
||||
if [[ "$DESTINATION_FILENAME" == */ ]]; then
|
||||
echo 1>&2 "$DESTINATION_FILENAME is not a file path."
|
||||
exit 80
|
||||
fi
|
||||
if [[ "$DESTINATION_FILENAME" == */* ]]; then
|
||||
DESTINATION_DIR="${DESTINATION_FILENAME%/*}"
|
||||
if ! mkdir -p "$DESTINATION_DIR"; then
|
||||
echo 1>&2 "Failed to create destination directory: $DESTINATION_DIR"
|
||||
exit 160
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
echo "Copying $CURRENT_FILENAME into $DESTINATION_FILENAME"
|
||||
|
||||
# Pre-check that the source and destination will work
|
||||
git mv --dry-run "${CURRENT_FILENAME}" "${DESTINATION_FILENAME}"
|
||||
|
||||
# Make a new branch and switch to it
|
||||
BRANCH_NAME="git-cp-$(date +%s)"
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
|
||||
# Move the original file to the new destination, in this branch
|
||||
git mv "${CURRENT_FILENAME}" "${DESTINATION_FILENAME}"
|
||||
git commit -nm "--Duplicate $CURRENT_FILENAME history into $DESTINATION_FILENAME"
|
||||
|
||||
# Restore the original file to keep it in the history
|
||||
git checkout HEAD~ "${CURRENT_FILENAME}"
|
||||
git commit -nm "--Restore $CURRENT_FILENAME"
|
||||
|
||||
# Switch to the original branch and merge this back in.
|
||||
git checkout -
|
||||
git merge --no-ff "$BRANCH_NAME" -m "Copy $CURRENT_FILENAME into $DESTINATION_FILENAME"
|
||||
|
||||
# We're now done with the branch, so delete it.
|
||||
# We shouldn't need -D here, as we've already merged it back in.
|
||||
git branch -d "$BRANCH_NAME"
|
||||
fi
|
||||
|
|
@ -1,76 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
test $# -eq 0 && echo "branch argument required." 1>&2 && exit 1
|
||||
branch=$1
|
||||
test -z $branch && echo "branch required." 1>&2 && exit 1
|
||||
|
||||
# preference takes lowest priority; look for remote from prefs first
|
||||
REMOTE_PREF=$(git config git-extras.create-branch.remote)
|
||||
if [ -n "$REMOTE_PREF" ]; then
|
||||
REMOTE=$REMOTE_PREF
|
||||
fi
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case $1 in
|
||||
-r|--remote)
|
||||
if [[ -n $2 ]]
|
||||
then
|
||||
REMOTE=$2
|
||||
shift
|
||||
else
|
||||
REMOTE=origin
|
||||
fi
|
||||
;;
|
||||
--from)
|
||||
if [[ -n $2 ]]; then
|
||||
START_POINT=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
BRANCH=$1
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# handle ambiguous `-r` option argument by shift
|
||||
if [[ -z $BRANCH ]] && [[ -n $REMOTE ]]
|
||||
then
|
||||
BRANCH=$REMOTE
|
||||
REMOTE=origin
|
||||
fi
|
||||
|
||||
test -z "$BRANCH" && echo "branch argument required." 1>&2 && exit 1
|
||||
|
||||
if [[ -n $REMOTE ]]
|
||||
then
|
||||
stderr=$(git_extra_mktemp)
|
||||
git ls-remote --exit-code "$REMOTE" 1>/dev/null 2>"$stderr"
|
||||
REMOTE_EXIT=$?
|
||||
REMOTE_ERROR=$(<"$stderr")
|
||||
rm -f "$stderr"
|
||||
if [ $REMOTE_EXIT -eq 0 ]
|
||||
then
|
||||
if [[ -n $START_POINT ]]; then
|
||||
git fetch "$REMOTE"
|
||||
git checkout --track -b "$BRANCH" "$START_POINT"
|
||||
git push "$REMOTE" "HEAD:refs/heads/$BRANCH"
|
||||
else
|
||||
git push "$REMOTE" "HEAD:refs/heads/$BRANCH"
|
||||
git fetch "$REMOTE"
|
||||
git checkout --track -b "$BRANCH" "$REMOTE/$BRANCH"
|
||||
fi
|
||||
exit $?
|
||||
else
|
||||
echo
|
||||
echo " Error connecting to remote '$REMOTE': $REMOTE_ERROR"
|
||||
echo
|
||||
exit $REMOTE_EXIT
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n $START_POINT ]]
|
||||
then
|
||||
git checkout -b "$BRANCH" "$START_POINT"
|
||||
else
|
||||
git checkout -b "$BRANCH"
|
||||
fi
|
||||
git push origin HEAD:refs/heads/$branch
|
||||
git fetch origin
|
||||
git checkout --track -b $branch origin/$branch
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Assert there is at least one branch provided
|
||||
test -z "$1" && echo "branch required." 1>&2 && exit 1
|
||||
test -z $1 && echo "branch required." 1>&2 && exit 1
|
||||
|
||||
for branch in "$@"
|
||||
do
|
||||
remote=$(git config "branch.$branch.remote" || echo "origin")
|
||||
ref=$(git config "branch.$branch.merge" || echo "refs/heads/$branch")
|
||||
remote=$(git config branch.$branch.remote)
|
||||
test -z $remote && remote="origin"
|
||||
ref=$(git config branch.$branch.merge)
|
||||
test -z $ref && ref="refs/heads/$branch"
|
||||
|
||||
git branch -D "$branch" || true
|
||||
# Avoid deleting local upstream
|
||||
[ "$remote" = "." ] && continue
|
||||
git branch -d -r "$remote/$branch" || continue
|
||||
git push "$remote" ":$ref"
|
||||
git branch -D $branch
|
||||
git branch -d -r $remote/$branch
|
||||
git push $remote :$ref
|
||||
done
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
dry_run=false
|
||||
force=false
|
||||
prune=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-n|--dry-run)
|
||||
dry_run=true
|
||||
shift
|
||||
;;
|
||||
-f|--force)
|
||||
force=true
|
||||
shift
|
||||
;;
|
||||
-p|--prune)
|
||||
prune=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Usage: git delete-gone-branches [-n|--dry-run] [-f|--force] [-p|--prune]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$prune" = true ]; then
|
||||
git fetch --prune
|
||||
fi
|
||||
|
||||
gone_branches=$(git for-each-ref --format \
|
||||
'%(if:equals=gone)%(upstream:track,nobracket)%(then)%(refname:short)%(end)' refs/heads/ | sed '/^$/d')
|
||||
|
||||
if [ -z "$gone_branches" ]; then
|
||||
echo "No branches with a gone remote found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$dry_run" = true ]; then
|
||||
echo "Would delete the following branches:"
|
||||
echo "$gone_branches"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "The following branches will be deleted:"
|
||||
echo "$gone_branches"
|
||||
|
||||
if [ "$force" = false ]; then
|
||||
read -r -p "Delete these branches? [y/N] " confirm
|
||||
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
|
||||
echo "Aborted."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$gone_branches" | xargs git branch -D
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
branches=$(git branch --no-color --merged | grep -vE "^(\*|\+)" | grep -v "$(git_extra_default_branch)" | grep -v svn)
|
||||
if [ -n "$branches" ]
|
||||
then
|
||||
echo "$branches" | xargs git branch -d
|
||||
fi
|
||||
git branch --no-color --merged | grep -v "\*" | grep -v master | grep -v svn | xargs -n 1 git branch -d
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
proceed=false
|
||||
if [[ $# -gt 0 && ("$1" == "--proceed" || "$1" == "-p") ]]; then
|
||||
proceed=true
|
||||
shift
|
||||
fi
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
targetBranch=$(git rev-parse --abbrev-ref HEAD)
|
||||
else
|
||||
targetBranch=$1
|
||||
git checkout "$targetBranch"
|
||||
fi
|
||||
|
||||
git for-each-ref refs/heads/ "--format=%(refname:short)" | while read -r branch; do
|
||||
mergeBase=$(git merge-base "$targetBranch" "$branch")
|
||||
|
||||
if [[ $(git cherry "$targetBranch" "$(git commit-tree "$(git rev-parse "$branch^{tree}")" -p "$mergeBase" -m _)") == "-"* ]]; then
|
||||
if [[ $proceed == true ]]; then
|
||||
git branch -D "$branch" || true
|
||||
else
|
||||
git branch -D "$branch"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
|
@ -1,50 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
abort() {
|
||||
error="$1" && shift
|
||||
echo "ERROR: $*" 1>&2
|
||||
test -z "$FORCE" && exit "$error"
|
||||
}
|
||||
submodule=$1
|
||||
|
||||
# Don't abort on failures. This allows to cleanup after a previous failure.
|
||||
[ "$1" = '--force' ] && FORCE=1 && shift
|
||||
test -z $submodule && echo "submodule required" 1>&2 && exit 1
|
||||
test ! -f .gitmodules && echo ".gitmodules file not found" 1>&2 && exit 2
|
||||
|
||||
test -z "$1" && abort 1 'Submodule required'
|
||||
cd "$(git root)" || abort 5 'Cannot change to repository root'
|
||||
test ! -f '.gitmodules' && abort 2 '.gitmodules file not found'
|
||||
NAME=$(echo $submodule | sed 's/\/$//g')
|
||||
test -z $(git config --file=.gitmodules submodule.$NAME.url) && echo "submodule not found" 1>&2 && exit 3
|
||||
|
||||
NAME="${1%/}"
|
||||
test -z "$(git config --file='.gitmodules' "submodule.$NAME.url")" \
|
||||
&& abort 3 'Submodule not found'
|
||||
|
||||
# 1. Handle the .git directory
|
||||
# 1.a. Delete the relevant section from .git/config
|
||||
git submodule deinit -f "$NAME" || abort 4 "Failed to deinitialize $NAME"
|
||||
# 1.b. Delete empty submodule directory
|
||||
git rm -f "$NAME"
|
||||
|
||||
# 2. Handle .gitmodules file
|
||||
# 2.a. Delete the relevant line from .gitmodules
|
||||
git config --file='.gitmodules' --remove-section "submodule.$NAME" 2>/dev/null || :
|
||||
# 2.b and stage changes
|
||||
git add '.gitmodules'
|
||||
# 2.c. Delete empty .gitmodules
|
||||
[ "$(wc -l '.gitmodules' | cut -d' ' -f1)" = '0' ] && git rm -f '.gitmodules'
|
||||
|
||||
# 3. Need to confirm and commit the changes for yourself
|
||||
git_status_text="$(git submodule status 2>&1)"
|
||||
git_status_exit=$?
|
||||
if [ "$git_status_exit" -eq 0 ] \
|
||||
&& printf '%s' "DUMMY$git_status_text" | grep -v "$NAME" > /dev/null; then
|
||||
# grep fails when piping in an empty string, so we add a DUMMY prefix
|
||||
|
||||
echo "Successfully deleted $NAME."
|
||||
else
|
||||
abort 6 "Failed to delete $NAME with error:\n$git_status_text"
|
||||
fi
|
||||
printf '\n%s\n' '== git submodule status =='
|
||||
printf '%s\n' "$git_status_text"
|
||||
printf '%s\n' '=========================='
|
||||
# shellcheck disable=SC2016
|
||||
echo 'Confirm the output of `git submodule status` above (if any)' \
|
||||
'and then commit the changes.'
|
||||
git submodule deinit $NAME
|
||||
git rm --cached $NAME
|
||||
rm -rf .git/modules/$NAME
|
||||
|
|
|
|||
|
|
@ -1,16 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Assert there is at least one tag provided
|
||||
test -z "$1" && echo "tag required." 1>&2 && exit 1
|
||||
|
||||
# Detect the default remote exists or not
|
||||
default_remote=$(git config git-extras.default-remote)
|
||||
|
||||
if [[ -n "$default_remote" ]]; then
|
||||
origin="$default_remote"
|
||||
else
|
||||
origin=origin
|
||||
fi
|
||||
test -z $1 && echo "tag required." 1>&2 && exit 1
|
||||
|
||||
# Concatenate all the tag references
|
||||
local_tags=""
|
||||
|
|
@ -22,7 +13,5 @@ do
|
|||
done
|
||||
|
||||
# Delete all the tags
|
||||
# shellcheck disable=SC2086
|
||||
git tag -d $local_tags
|
||||
# shellcheck disable=SC2086
|
||||
git push "$origin" $origin_refs
|
||||
git push origin $origin_refs
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
branch=$(git_extra_default_branch)
|
||||
branch=master
|
||||
filter=ACM
|
||||
|
||||
if test $# -eq 1; then
|
||||
|
|
@ -12,4 +12,4 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
git diff --name-only --diff-filter="$filter" "$branch"
|
||||
git diff --name-only --diff-filter=$filter $branch
|
||||
193
bin/git-effort
193
bin/git-effort
|
|
@ -1,29 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
tmp=$(git_extra_mktemp)
|
||||
above=0
|
||||
# if the output won't be printed to tty, disable the color
|
||||
test -t 1 && to_tty=true
|
||||
above='0'
|
||||
color=
|
||||
|
||||
#
|
||||
# print usage message
|
||||
# get date for the given <commit>
|
||||
#
|
||||
usage() {
|
||||
echo 1>&2 "usage: git effort [--above <value>] [<path>...] [-- [<log options>...]]"
|
||||
}
|
||||
|
||||
#
|
||||
# get dates for the given <commit>
|
||||
#
|
||||
dates() {
|
||||
eval "git log $args_to_git_log --pretty='format: %ad' --date=short -- \"$1\""
|
||||
}
|
||||
|
||||
# tput, being quiet about unknown capabilities
|
||||
tputq() {
|
||||
tput "$@" 2>/dev/null
|
||||
return 0
|
||||
date() {
|
||||
git log --pretty='format: %ai' $1 | cut -d ' ' -f 2
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -31,7 +17,7 @@ tputq() {
|
|||
#
|
||||
|
||||
hide_cursor() {
|
||||
tputq civis
|
||||
printf '\033[?25l'
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -39,10 +25,10 @@ hide_cursor() {
|
|||
#
|
||||
|
||||
show_cursor_and_cleanup() {
|
||||
tputq cnorm
|
||||
tputq sgr0
|
||||
printf '\033[?25h'
|
||||
printf '\033[m\n'
|
||||
rm "$tmp" > /dev/null 2>&1
|
||||
exit 0
|
||||
exit 1
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -50,7 +36,10 @@ show_cursor_and_cleanup() {
|
|||
#
|
||||
|
||||
active_days() {
|
||||
echo "$1" | sort -r | uniq | wc -l
|
||||
date $1 | uniq | awk '
|
||||
{ sum += 1 }
|
||||
END { print sum }
|
||||
'
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -58,65 +47,45 @@ active_days() {
|
|||
#
|
||||
|
||||
color_for() {
|
||||
if [ "$to_tty" = true ]; then
|
||||
if [ "$1" -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)"
|
||||
elif [ "$1" -gt 150 ]; then color="$(tputq setaf 1)" # red
|
||||
elif [ "$1" -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)"
|
||||
elif [ "$1" -gt 100 ]; then color="$(tputq setaf 2)" # green
|
||||
elif [ "$1" -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)"
|
||||
elif [ "$1" -gt 50 ]; then color="$(tputq setaf 5)" # purplish
|
||||
elif [ "$1" -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)"
|
||||
elif [ "$1" -gt 10 ]; then color="$(tputq setaf 3)" # yellow
|
||||
else color="$(tputq sgr0)" # default color
|
||||
fi
|
||||
else
|
||||
color=""
|
||||
fi
|
||||
test $1 -gt 10 && color='33'
|
||||
test $1 -gt 25 && color='33;1'
|
||||
test $1 -gt 50 && color='93'
|
||||
test $1 -gt 75 && color='93;1'
|
||||
test $1 -gt 100 && color='31'
|
||||
test $1 -gt 125 && color='31;1'
|
||||
test $1 -gt 150 && color='91'
|
||||
test $1 -gt 200 && color='91;1'
|
||||
}
|
||||
|
||||
#
|
||||
# compute the effort of the given <path ...>
|
||||
# compute the effort of the given <file ...>
|
||||
#
|
||||
|
||||
effort() {
|
||||
path=$1
|
||||
local commit_dates
|
||||
local color reset_color commits len dot f_dot i msg active
|
||||
reset_color=""
|
||||
test "$to_tty" = true && reset_color="$(tputq sgr0)"
|
||||
if ! commit_dates=$(dates "$path"); then
|
||||
exit 255
|
||||
fi
|
||||
|
||||
# Ensure it's not just an empty line
|
||||
if [ -z "$(head -c 1 <<<"$commit_dates")" ]
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
commits=$(wc -l <<<"$commit_dates")
|
||||
for file in "$@"; do
|
||||
commits=`git log --oneline "$file" | wc -l`
|
||||
color='90'
|
||||
|
||||
# ignore <= --above
|
||||
test "$commits" -le "$above" && exit 0
|
||||
test $commits -le $above && continue
|
||||
|
||||
# commits
|
||||
color_for $(( commits - above ))
|
||||
len=${#path}
|
||||
color_for $commits
|
||||
len=${#file}
|
||||
dot="."
|
||||
f_dot="$path"
|
||||
i=0 ; while test $i -lt $(( columns - len )) ; do
|
||||
f_dot="$file"
|
||||
i=0 ; while test $i -lt $((45-$len)) ; do
|
||||
f_dot=$f_dot$dot
|
||||
i=$((i+1))
|
||||
i=$(($i+1))
|
||||
done
|
||||
|
||||
msg=$(printf " ${color}%s %-10d" "$f_dot" "$commits")
|
||||
printf " \033[${color}m%s %-10d" $f_dot $commits
|
||||
|
||||
# active days
|
||||
active=$(active_days "$commit_dates")
|
||||
color_for $(( active - above ))
|
||||
msg="$msg $(printf "${color} %d${reset_color}\n" "$active")"
|
||||
echo "$msg"
|
||||
active=`active_days "$file"`
|
||||
color_for $active
|
||||
printf "\033[${color}m %d\033[0m\n" $active
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -125,7 +94,7 @@ effort() {
|
|||
|
||||
heading() {
|
||||
echo
|
||||
printf " %-${columns}s %-10s %s\n" 'path' 'commits' 'active days'
|
||||
printf " %-45s %-10s %s\n" 'file' 'commits' 'active days'
|
||||
echo
|
||||
}
|
||||
|
||||
|
|
@ -135,100 +104,42 @@ heading() {
|
|||
|
||||
sort_effort() {
|
||||
clear
|
||||
echo " "
|
||||
echo
|
||||
heading
|
||||
< "$tmp" sort -rn -k 2
|
||||
cat $tmp | sort -rn -k 2
|
||||
}
|
||||
|
||||
#
|
||||
# get processor count, default 8
|
||||
#
|
||||
procs() {
|
||||
if ! (nproc --all || getconf NPROCESSORS_ONLN || getconf _NPROCESSORS_ONLN || grep -c processor /proc/cpuinfo) 2>/dev/null; then
|
||||
echo 8
|
||||
fi
|
||||
}
|
||||
|
||||
declare -a paths=()
|
||||
while [ "${#}" -ge 1 ] ; do
|
||||
|
||||
case "$1" in
|
||||
--above)
|
||||
shift
|
||||
above=$1
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
args_to_git_log=$(printf " %q" "${@:1}")
|
||||
break
|
||||
;;
|
||||
--*)
|
||||
usage
|
||||
echo 1>&2 "error: unknown argument $1"
|
||||
echo 1>&2 "error: if that argument was meant for git-log,"
|
||||
echo 1>&2 "error: please put it after two dashes ( -- )."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
paths+=( "$1" )
|
||||
;;
|
||||
esac
|
||||
# --above <n-commits>
|
||||
|
||||
if test "$1" = "--above"; then
|
||||
shift; above=$1
|
||||
shift
|
||||
done
|
||||
|
||||
# Exit if above-value is not an int
|
||||
if [ -z "${above##*[!0-9]*}" ] ; then
|
||||
echo "error: argument to --above was not an integer" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# remove empty quotes that appear when there are no arguments
|
||||
args_to_git_log="${args_to_git_log#\ \'\'}"
|
||||
export args_to_git_log
|
||||
# [file ...]
|
||||
|
||||
# [path ...]
|
||||
|
||||
if test "${#paths}" -eq 0; then
|
||||
declare -a files=()
|
||||
if test $# -ge 1; then
|
||||
files=("$@")
|
||||
else
|
||||
save_ifs=$IFS
|
||||
IFS=$(echo -en "\n\b")
|
||||
paths=($(git ls-files))
|
||||
IFS=$save_ifs
|
||||
IFS=`echo -en "\n\b"`
|
||||
files=(`git ls-files`)
|
||||
IFS=$safe_ifs
|
||||
unset save_ifs
|
||||
fi
|
||||
|
||||
# set column width to match longest filename
|
||||
max=0
|
||||
for path in "${paths[@]}"; do
|
||||
cur=${#path}
|
||||
if [[ $max -lt $cur ]]; then
|
||||
max=$cur
|
||||
fi
|
||||
done
|
||||
columns=$(( max + 5 ))
|
||||
export columns
|
||||
|
||||
# hide cursor
|
||||
|
||||
hide_cursor
|
||||
trap show_cursor_and_cleanup INT
|
||||
|
||||
# loop files
|
||||
|
||||
heading
|
||||
|
||||
# send paths to effort
|
||||
nPaths=${#paths[@]}
|
||||
nProcs=$(procs)
|
||||
nJobs="\j"
|
||||
for ((i=0; i<nPaths; ++i))
|
||||
do
|
||||
while (( ${nJobs@P} >= nProcs )); do
|
||||
wait -n
|
||||
done
|
||||
effort "${paths[i]}" &
|
||||
done|tee "$tmp"
|
||||
|
||||
# if more than one path, sort and print
|
||||
test "$(wc -l "$tmp" | awk '{print $1}')" -gt 1 && sort_effort
|
||||
effort "${files[@]}" | tee $tmp
|
||||
test "$(wc -l $tmp | cut -d' ' -f1 )" -gt 1 && sort_effort
|
||||
echo
|
||||
|
||||
show_cursor_and_cleanup
|
||||
|
|
|
|||
|
|
@ -1,54 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
VERSION="7.6.0-dev"
|
||||
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
|
||||
VERSION="3.0.0"
|
||||
|
||||
update() {
|
||||
local bin="$(command -v git-extras)"
|
||||
local bin=$(which git-extras)
|
||||
local prefix=${bin%/*/*}
|
||||
local orig=$PWD
|
||||
|
||||
curl -s $INSTALL_SCRIPT | PREFIX="$prefix" bash /dev/stdin \
|
||||
cd /tmp \
|
||||
&& rm -fr ./git-extras \
|
||||
&& git clone --depth 1 https://github.com/tj/git-extras.git \
|
||||
&& cd git-extras \
|
||||
&& PREFIX="$prefix" make install \
|
||||
&& cd "$orig" \
|
||||
&& echo "... updated git-extras $VERSION -> $(git extras --version)"
|
||||
}
|
||||
|
||||
updateForWindows() {
|
||||
local bin="$(command -v git-extras)"
|
||||
local prefix=${bin%/*/*}
|
||||
local orig=$PWD
|
||||
|
||||
# we need to clean up /tmp manually on windows
|
||||
cd /tmp \
|
||||
&& rm -rf ./git-extras \
|
||||
&& echo "Setting up 'git-extras'...." \
|
||||
&& git clone https://github.com/tj/git-extras.git &> /dev/null \
|
||||
&& cd git-extras \
|
||||
&& git checkout \
|
||||
$(git describe --tags $(git rev-list --tags --max-count=1)) \
|
||||
&> /dev/null \
|
||||
&& ./install.cmd "$prefix" \
|
||||
&& cd "$orig" \
|
||||
&& echo "... updated git-extras $VERSION -> $(git extras --version)"
|
||||
rm -rf /tmp/git-extras &> /dev/null
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
-v|--version)
|
||||
echo $VERSION && exit 0
|
||||
;;
|
||||
update)
|
||||
platform=$(uname -s)
|
||||
if [ "${platform::9}" = "CYGWIN_NT" ] || \
|
||||
[ "${platform::5}" = "MINGW" ] || \
|
||||
[ "${platform::7}" = "MSYS_NT" ]
|
||||
then
|
||||
updateForWindows
|
||||
else
|
||||
update
|
||||
fi
|
||||
update
|
||||
;;
|
||||
*)
|
||||
git extras --help
|
||||
man git-extras
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
116
bin/git-feature
116
bin/git-feature
|
|
@ -1,105 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
branch_prefix=$(git config --get git-extras.feature.prefix)
|
||||
|
||||
if [ -z "$branch_prefix" ]; then
|
||||
branch_prefix="feature"
|
||||
fi
|
||||
|
||||
branch_separator=$(git config --get git-extras.feature.separator)
|
||||
|
||||
if [ -z "$branch_separator" ]; then
|
||||
branch_separator="/"
|
||||
fi
|
||||
|
||||
merge_mode="--no-ff"
|
||||
finish=false
|
||||
declare -a argv
|
||||
while test $# != 0
|
||||
do
|
||||
case $1 in
|
||||
-a|--alias )
|
||||
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||
then
|
||||
shift # shift -a|-alias
|
||||
branch_prefix=$1
|
||||
else
|
||||
echo >&2 "option $1 requires a value"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
-r|--remote )
|
||||
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||
then
|
||||
remote=$2
|
||||
shift
|
||||
else
|
||||
remote="origin"
|
||||
fi
|
||||
;;
|
||||
-s|--separator )
|
||||
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||
then
|
||||
branch_separator=$2
|
||||
shift
|
||||
else
|
||||
echo >&2 "option $1 requires a value"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--squash )
|
||||
merge_mode="--squash"
|
||||
;;
|
||||
--from )
|
||||
start_point=$2
|
||||
shift
|
||||
;;
|
||||
-- )
|
||||
# terminate argument parsing
|
||||
shift
|
||||
argv+=("$@")
|
||||
break
|
||||
;;
|
||||
finish )
|
||||
finish=true
|
||||
;;
|
||||
* )
|
||||
argv+=("$1")
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
concatargs() {
|
||||
str=$(IFS='-'; echo "$*")
|
||||
branch="$branch_prefix$branch_separator$str"
|
||||
concatargs(){
|
||||
delim='-'
|
||||
str=
|
||||
for i in "$@"; do
|
||||
str="$str$delim$i"
|
||||
done
|
||||
branch=feature/${str:1}
|
||||
}
|
||||
|
||||
test -z "${argv[0]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
|
||||
|
||||
concatargs "${argv[@]}"
|
||||
|
||||
if "${finish}"
|
||||
then
|
||||
git merge ${merge_mode} "$branch" && git delete-branch "$branch"
|
||||
if test "$1" = "finish"; then
|
||||
test -z $2 && echo "feature <name> required." 1>&2 && exit 1
|
||||
branch=feature/$2
|
||||
git merge --no-ff $branch && git delete-branch $branch
|
||||
else
|
||||
if [[ -n $remote ]] && [[ -z $start_point ]]
|
||||
then
|
||||
git create-branch -r "$remote" "$branch"
|
||||
fi
|
||||
|
||||
if [[ -z $remote ]] && [[ -z $start_point ]]
|
||||
then
|
||||
git create-branch "$branch"
|
||||
fi
|
||||
|
||||
if [[ -n $remote ]] && [[ -n $start_point ]]
|
||||
then
|
||||
git create-branch -r "$remote" --from "$start_point" "$branch"
|
||||
fi
|
||||
|
||||
if [[ -z $remote ]] && [[ -n $start_point ]]
|
||||
then
|
||||
git create-branch --from "$start_point" "$branch"
|
||||
test -z $1 && echo "feature <name> required." 1>&2 && exit 1
|
||||
if test -n "$2"; then
|
||||
concatargs ${@:2}
|
||||
else
|
||||
branch=feature/$1
|
||||
fi
|
||||
git checkout -b $branch &> /dev/null
|
||||
test -n $? && git checkout $branch &> /dev/null
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
_usage() {
|
||||
echo "
|
||||
Usage:
|
||||
git-force-clone -b branch remote_url destination_path
|
||||
|
||||
Example:
|
||||
git-force-clone -b master git@github.com:me/repo.git ./repo_dir
|
||||
|
||||
Provides the basic functionality of 'git clone', but if the destination git
|
||||
repository already exists it will force-reset it to resemble a clone of the
|
||||
remote.
|
||||
|
||||
Because it doesn't actually delete the directory, it is usually significantly
|
||||
faster than the alternative of deleting the directory and cloning the
|
||||
repository from scratch.
|
||||
|
||||
**CAUTION**: If the repository exists, this will destroy *all* local work:
|
||||
changed files will be reset, local branches and other remotes will be removed.
|
||||
|
||||
OPTIONS:
|
||||
-b, --branch The branch to pull from the remote
|
||||
-h, --help Display this help message
|
||||
"
|
||||
}
|
||||
|
||||
_check() {
|
||||
if [ -z "$1" ]; then
|
||||
echo "Error: Missing ${2}"
|
||||
_usage
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
while [[ -n "${1:-}" ]] && [[ "${1:0:1}" == "-" ]]; do
|
||||
case $1 in
|
||||
-b | --branch )
|
||||
branch=${2:-}
|
||||
shift
|
||||
;;
|
||||
-h | --help )
|
||||
_usage
|
||||
exit 0
|
||||
;;
|
||||
* )
|
||||
echo "Error: Invalid option: $1" >>/dev/stderr
|
||||
_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
remote_url=${1:-}
|
||||
destination_path=${2:-}
|
||||
|
||||
_check "${remote_url}" "remote_url"
|
||||
_check "${destination_path}" "destination_path"
|
||||
|
||||
if [ -d "${destination_path}/.git" ]; then
|
||||
(
|
||||
cd "${destination_path}"
|
||||
|
||||
# Delete all remotes
|
||||
for remote in $(git remote); do
|
||||
git remote rm "${remote}"
|
||||
done
|
||||
|
||||
# Add origin
|
||||
git remote add origin "${remote_url}"
|
||||
git fetch origin
|
||||
|
||||
# Set default branch
|
||||
if [ -z "${branch:-}" ]; then
|
||||
branch=$(LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$')
|
||||
git remote set-head origin "${branch}"
|
||||
else
|
||||
git remote set-head origin -a
|
||||
fi
|
||||
|
||||
# Make sure current branch is clean
|
||||
git clean -fd
|
||||
git reset --hard HEAD
|
||||
|
||||
# Get on the desired branch
|
||||
git checkout "${branch}"
|
||||
git reset --hard "origin/${branch}"
|
||||
|
||||
# Delete all other branches
|
||||
# shellcheck disable=SC2063
|
||||
branches=$(git branch | grep -v '*' | xargs)
|
||||
if [ -n "${branches}" ]; then
|
||||
git branch -D "${branches}"
|
||||
fi
|
||||
)
|
||||
elif [ -n "${branch:-}" ]; then
|
||||
git clone -b "${branch}" "${remote_url}" "${destination_path}"
|
||||
else
|
||||
git clone "${remote_url}" "${destination_path}"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
exit 0
|
||||
69
bin/git-fork
69
bin/git-fork
|
|
@ -1,67 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
abort() {
|
||||
echo "$@"
|
||||
echo $@
|
||||
exit 1
|
||||
}
|
||||
|
||||
url="$1"
|
||||
test -z "$url" && url=$(git remote get-url origin 2> /dev/null) && origin=true
|
||||
# validate repo url
|
||||
test -z "$url" && abort "github repo needs to be specified as an argument"
|
||||
test -z "$1" && abort "github repo needs to be specified as an argument"
|
||||
|
||||
# validate user
|
||||
echo "Enter your github username"
|
||||
read -r user
|
||||
[ -n "$user" ] || abort "git username required"
|
||||
# personal access token
|
||||
# config name is github-personal-access-token '_' is not allowed in git config
|
||||
|
||||
github_personal_access_token=$(git config --default "$GITHUB_TOKEN" git-extras.github-personal-access-token)
|
||||
|
||||
test -z "$github_personal_access_token" && abort "GITHUB_TOKEN, or git config git-extras.github-personal-access-token required"
|
||||
read user
|
||||
[ "$user" = 'n' ] && abort "git username required"
|
||||
|
||||
# extract owner + project from repo url
|
||||
project=${url##*/}
|
||||
owner=${url%/"$project"}
|
||||
project=${project%.git}
|
||||
if [[ $owner == git@* ]]; then
|
||||
owner=${owner##*:}
|
||||
else
|
||||
owner=${owner##*/}
|
||||
fi
|
||||
project=${1##*/}
|
||||
owner=${1%/$project}
|
||||
owner=${owner##*/}
|
||||
|
||||
# validate
|
||||
[[ -z "$project" || -z "$owner" ]] && abort "github repo needs to be specified as an argument"
|
||||
[ -z "$project" -o -z "$owner" ] && abort "github repo needs to be specified as an argument"
|
||||
|
||||
# create fork
|
||||
if ! curl -qsf \
|
||||
-X POST \
|
||||
-u "$user:$github_personal_access_token" \
|
||||
-H "X-GitHub-OTP: $MFA_CODE" \
|
||||
"https://api.github.com/repos/$owner/$project/forks"
|
||||
then
|
||||
abort "fork failed"
|
||||
fi
|
||||
# create pull request
|
||||
curl -X POST -u "$user" "https://api.github.com/repos/$owner/$project/forks"
|
||||
[ $? = 0 ] || abort "fork failed"
|
||||
|
||||
echo "Add GitHub remote branch via SSH (you will be prompted to verify the server's credentials)? (y/n)"
|
||||
read -r use_ssh
|
||||
# Check if user has ssh configured with GitHub
|
||||
if [ -n "$use_ssh" ] && ssh -T git@github.com 2>&1 | grep -qi 'success'; then
|
||||
remote_prefix="git@github.com:"
|
||||
else
|
||||
remote_prefix="https://github.com/"
|
||||
fi
|
||||
# clone forked repo into current dir
|
||||
git clone "git@github.com:$user/$project" "$project"
|
||||
|
||||
if [ "$origin" = true ]; then
|
||||
git remote rename origin upstream
|
||||
git remote add origin "${remote_prefix}${user}/${project}.git"
|
||||
git fetch origin
|
||||
else
|
||||
# clone forked repo into current dir
|
||||
git clone "${remote_prefix}${user}/${project}.git" "$project"
|
||||
# add reference to origin fork so can merge in upstream changes
|
||||
cd "$project" || exit
|
||||
git remote add upstream "${remote_prefix}${owner}/${project}.git"
|
||||
git fetch upstream
|
||||
fi
|
||||
# add reference to origin fork so can merge in upstream changes
|
||||
cd "$project"
|
||||
git remote add upstream "git@github.com:$owner/$project"
|
||||
git fetch upstream
|
||||
|
|
@ -2,21 +2,21 @@
|
|||
|
||||
branch=$1
|
||||
|
||||
test -z "$branch" && echo "branch required." 1>&2 && exit 1
|
||||
test -z $branch && echo "branch required." 1>&2 && exit 1
|
||||
|
||||
changes=$(git status --porcelain)
|
||||
changes=`git status --porcelain`
|
||||
|
||||
clean()
|
||||
{
|
||||
git symbolic-ref HEAD "refs/heads/$branch"
|
||||
git symbolic-ref HEAD refs/heads/$branch
|
||||
rm .git/index
|
||||
git clean -fdx
|
||||
}
|
||||
|
||||
if [ -n "$changes" ]; then
|
||||
read -rp "All untracked changes will be lost. Continue [y/N]? " res
|
||||
if [ ! -z "$changes" ]; then
|
||||
read -p "All untracked changes will be lost. Continue [y/N]? " res
|
||||
case $res in
|
||||
[Yy]* ) ;;
|
||||
[Yy]* ) clean; break;;
|
||||
* ) exit 0;;
|
||||
esac
|
||||
fi
|
||||
|
|
|
|||
40
bin/git-get
40
bin/git-get
|
|
@ -1,40 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
_usage() {
|
||||
printf '%s\n' "usage: ${0##*/} <url>
|
||||
usage: ${0##*/} --help
|
||||
|
||||
Clone a repository in a particular directory."
|
||||
}
|
||||
|
||||
if (( $# == 0 )); then
|
||||
_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for arg; do
|
||||
if [ "$arg" = '-h' ] || [ "$arg" = '--help' ]; then
|
||||
_usage
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
url=$1
|
||||
if ! shift; then
|
||||
printf 'ERROR: Failed to shift' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
clone_path=$(git config --get git-extras.get.clone-path)
|
||||
|
||||
if [ -z "$clone_path" ]; then
|
||||
printf 'ERROR: %s\n' "Git configuration key 'git-extras.get.clone-path' must be set to a directory to clone under" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dirname=${url%/}
|
||||
dirname=${dirname%.git}
|
||||
dirname=${dirname##*/}
|
||||
|
||||
mkdir -p "$clone_path"
|
||||
git clone "$url" "$clone_path/$dirname" "$@"
|
||||
|
|
@ -4,10 +4,10 @@ echo 'setting up gh-pages'
|
|||
echo '-------------------'
|
||||
|
||||
echo 'Tell me your github account username: '
|
||||
read -r username
|
||||
read username
|
||||
|
||||
echo 'Now, tell me your repository name: '
|
||||
read -r repository
|
||||
read repository
|
||||
|
||||
git stash \
|
||||
&& git checkout -b 'gh-pages' \
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
src=$1
|
||||
dst=$2
|
||||
|
||||
test -z "$src" && echo "source branch required." 1>&2 && exit 1
|
||||
test -z "$dst" && echo "destination branch required." 1>&2 && exit 1
|
||||
test -z $src && echo "source branch required." 1>&2 && exit 1
|
||||
|
||||
git checkout "$dst" \
|
||||
&& git merge --no-ff "$src" \
|
||||
&& git branch -d "$src"
|
||||
git checkout $dst \
|
||||
&& git merge --no-ff $src \
|
||||
&& git branch -d $src
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
|
||||
for param in "$@"
|
||||
for param in $*
|
||||
do
|
||||
case $param in
|
||||
-h)
|
||||
-h|--help )
|
||||
echo 'Usage: git-guilt [<options>] <since> <until>'
|
||||
echo 'Calculates the change in blame between two revisions'
|
||||
echo 'Example: git guilt HEAD~3 HEAD'
|
||||
|
|
@ -31,7 +31,7 @@ do
|
|||
esac
|
||||
done
|
||||
|
||||
cd "$(git-root)" || exit # cd for git blame
|
||||
cd "$(git-root)" # cd for git blame
|
||||
MERGED_LOG=$(git_extra_mktemp)
|
||||
if [[ $EMAIL == '-e' ]]
|
||||
then
|
||||
|
|
@ -44,13 +44,11 @@ for file in $(git diff --name-only "$@")
|
|||
do
|
||||
test -n "$DEBUG" && echo "git blame $file"
|
||||
# $1 - since $2 - until
|
||||
# shellcheck disable=SC2086
|
||||
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
|
||||
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/- \1/' >> "$MERGED_LOG"
|
||||
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
|
||||
sed -n "$PATTERN" | sort | uniq -c | sed 's/^\(.\)/- \1/' >> $MERGED_LOG
|
||||
# if $2 not given, use current commit as "until"
|
||||
# shellcheck disable=SC2086
|
||||
git blame $NOT_WHITESPACE --line-porcelain "${2-@}" -- "$file" 2> /dev/null |
|
||||
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/+ \1/' >> "$MERGED_LOG"
|
||||
sed -n "$PATTERN" | sort | uniq -c | sed 's/^\(.\)/+ \1/' >> $MERGED_LOG
|
||||
done
|
||||
|
||||
DEBUG="$DEBUG" awk '
|
||||
|
|
@ -73,15 +71,20 @@ END {
|
|||
printf("%d %s\n", contributors[people], people)
|
||||
}
|
||||
}
|
||||
}' "$MERGED_LOG" | sort -nr | # only gawk supports built-in sort function
|
||||
while read -r line
|
||||
}' $MERGED_LOG | sort -nr | # only gawk supports built-in sort function
|
||||
while read line
|
||||
do
|
||||
people=${line#* }
|
||||
num=${line%% *}
|
||||
|
||||
if [[ $num -gt 0 ]]
|
||||
then
|
||||
printf "%-29s \033[00;32m" "$people"
|
||||
if [[ ${#people} -ge 29 ]]
|
||||
then
|
||||
printf "%-29s \033[00;32m" "$people"
|
||||
else
|
||||
printf "%-29s\t\033[00;32m" "$people"
|
||||
fi
|
||||
if [[ $num -ge 50 ]]
|
||||
then
|
||||
len=${#num}
|
||||
|
|
@ -89,7 +92,7 @@ do
|
|||
do
|
||||
printf "+"
|
||||
done
|
||||
printf "(%s)" "$num"
|
||||
printf "(%s)" $num
|
||||
else
|
||||
for (( i = 0; i < num; i++ ))
|
||||
do
|
||||
|
|
@ -97,7 +100,12 @@ do
|
|||
done
|
||||
fi
|
||||
else
|
||||
printf "%-29s \033[00;31m" "$people"
|
||||
if [[ ${#people} -ge 29 ]]
|
||||
then
|
||||
printf "%-29s \033[00;31m" "$people"
|
||||
else
|
||||
printf "%-29s\t\033[00;31m" "$people"
|
||||
fi
|
||||
if [[ $num -le -50 ]]
|
||||
then
|
||||
len=${#num}
|
||||
|
|
@ -105,7 +113,7 @@ do
|
|||
do
|
||||
printf "-"
|
||||
done
|
||||
printf "(%s)" "$num"
|
||||
printf "(%s)" $num
|
||||
else
|
||||
for (( i = 0; i > num; i-- ))
|
||||
do
|
||||
|
|
|
|||
277
bin/git-ignore
277
bin/git-ignore
|
|
@ -1,28 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
|
||||
|
||||
# awk script that takes 2 input files. The first one is newline deilmeted
|
||||
# string of patterns to look for, second one is the file to remove them from.
|
||||
# outfile has to be passed with -v upon call and is the file result is
|
||||
# written to.
|
||||
read -r -d '' AWK_SCRIPT <<'EOF'
|
||||
# process the first "file" (newline split argument list)
|
||||
NR==FNR { a[b++]=$0; next; }
|
||||
# process file
|
||||
{
|
||||
found=0;
|
||||
for (i=0; i != b; i++) {
|
||||
if (a[i] == $0) { found=1; }
|
||||
}
|
||||
if (found == 1) { print "... removing '"$0"'"; }
|
||||
else { print $0 > outfile }
|
||||
}
|
||||
EOF
|
||||
|
||||
|
||||
|
||||
show_contents() {
|
||||
function show_contents {
|
||||
local file="${2/#~/$HOME}"
|
||||
if [ -f "$file" ]; then
|
||||
echo "$1 gitignore: $2" && cat "$file"
|
||||
|
|
@ -31,255 +9,50 @@ show_contents() {
|
|||
fi
|
||||
}
|
||||
|
||||
cd_to_git_root() {
|
||||
local error_level="$1"
|
||||
|
||||
if ! git rev-parse --git-dir &>/dev/null; then
|
||||
if [ "$error_level" = '--warn' ]; then
|
||||
echo "Warning: Not currently in a Git repository" >&2
|
||||
elif [ "$error_level" = '--error' ]; then
|
||||
echo "Error: Not currently in a Git repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local result=
|
||||
if result=$(git rev-parse --show-toplevel 2>/dev/null); then
|
||||
cd "$result" || exit
|
||||
fi
|
||||
function show_global {
|
||||
show_contents Global `git config --global core.excludesfile`
|
||||
}
|
||||
|
||||
global_ignore() {
|
||||
if ! git config --global core.excludesFile 2>/dev/null; then
|
||||
if [ -f "$HOME/.gitignore" ]; then
|
||||
echo "$HOME/.gitignore"
|
||||
else
|
||||
echo "${XDG_CONFIG_HOME:-$HOME/.config}/git/ignore"
|
||||
fi
|
||||
fi
|
||||
function add_global {
|
||||
add_patterns `git config --global core.excludesfile` "$@"
|
||||
}
|
||||
|
||||
show_global() {
|
||||
show_contents Global "$(global_ignore)"
|
||||
}
|
||||
|
||||
add_global() {
|
||||
local global_gitignore
|
||||
global_gitignore="$(global_ignore)"
|
||||
if [ -z "$global_gitignore" ]; then
|
||||
echo "Can't find global .gitignore."
|
||||
echo ""
|
||||
echo "Use 'git config --global --add core.excludesfile ~/.gitignore-global' to set the path to your global gitignore file to '~/.gitignore-global'."
|
||||
echo ""
|
||||
else
|
||||
add_patterns "$global_gitignore" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
show_local() {
|
||||
cd_to_git_root --warn
|
||||
function show_local {
|
||||
cd "$(git root)"
|
||||
show_contents Local .gitignore
|
||||
}
|
||||
|
||||
add_local() {
|
||||
cd_to_git_root --warn
|
||||
function add_local {
|
||||
cd "$(git root)"
|
||||
add_patterns .gitignore "$@"
|
||||
}
|
||||
|
||||
show_private() {
|
||||
cd_to_git_root --error
|
||||
show_contents Private "${GIT_DIR}/info/exclude"
|
||||
}
|
||||
|
||||
add_private() {
|
||||
cd_to_git_root --error
|
||||
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
|
||||
add_patterns "${GIT_DIR}/info/exclude" "$@"
|
||||
}
|
||||
|
||||
add_patterns() {
|
||||
function add_patterns {
|
||||
echo "Adding pattern(s) to: $1"
|
||||
local file="${1/#~/$HOME}"
|
||||
dir_name=$(dirname "$file")
|
||||
if [ ! -d "$dir_name" ]; then
|
||||
mkdir -p "$dir_name"
|
||||
fi
|
||||
if [ -s "$file" ]; then
|
||||
# If the content of $file doesn't end with a newline, add one
|
||||
test "$(tail -c 1 "$file")" != "" && echo "" >> "$file"
|
||||
fi
|
||||
for pattern in "${@:2}"; do
|
||||
echo "... adding '$pattern'"
|
||||
(test -f "$file" && test "$pattern" && grep -q -F -x -- "$pattern" "$file") || echo "$pattern" >> "$file"
|
||||
(test -f "$file" && test "$pattern" && grep -q -- "$pattern" "$file") || echo "$pattern" >> "$file"
|
||||
done
|
||||
}
|
||||
|
||||
edit_file() {
|
||||
local file="${2/#~/$HOME}"
|
||||
if [ -f "$file" ]; then
|
||||
echo "Editing $1 gitignore: ($2)" && eval "$(git var GIT_EDITOR) $(printf '%q' "$file")"
|
||||
echo "Done."
|
||||
else
|
||||
echo "There is no $1 .gitignore yet." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
_usage() {
|
||||
cat << EOF
|
||||
Usage: git ignore [OPTION]... [PATTERN]...
|
||||
Modify or display local global or private gitignore files.
|
||||
|
||||
OPTIONs set context (local, global, private) and action (display, add,
|
||||
remove, edit). In case of collision, last flag overwrites preceding ones.
|
||||
Defaults are: local context, action is display (see man git ignore for
|
||||
more details).
|
||||
Context examples:
|
||||
local: .../repo/.gitignore
|
||||
private: .../repo/.git/info/exclude
|
||||
global: \$HOME/.gitignore
|
||||
|
||||
PATTERNs are the gitignore patterns as specified in git documentation.
|
||||
|
||||
Options:
|
||||
-l, --local Set context to local gitignore
|
||||
-g, --global Set context to global gitignore
|
||||
-p, --private Set context to private gitignore
|
||||
-e, --edit Set action to edit
|
||||
-r, --remove Set action to remove
|
||||
-h, --help Print this message
|
||||
-- End of options delimiter
|
||||
EOF
|
||||
}
|
||||
|
||||
remove_patterns() {
|
||||
declare -a args
|
||||
local tmpfile
|
||||
args=( "${@:3}" )
|
||||
local file="${2/#~/$HOME}"
|
||||
if [ -f "$file" ]; then
|
||||
tmpfile="$(mktemp -q "${TMPDIR:-/tmp}"/git-extras-ignore.XXXXXX 2>/dev/null)"
|
||||
echo "Removing patterns from $1 gitignore: $2"
|
||||
awk -v outfile="$tmpfile" "${AWK_SCRIPT}"\
|
||||
<(printf '%s\n' "${args[@]}") "$file"
|
||||
cat "$tmpfile" > "$file"
|
||||
rm "$tmpfile"
|
||||
else
|
||||
echo "There is no $1 .gitignore yet"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# can have only 1 action and 1 level, if conflicting flags are passed
|
||||
# latter ones overwrite former
|
||||
# sanity of args not tested (e.g. there should be no args if -e is supplied)
|
||||
if test $# -eq 0; then
|
||||
show_global
|
||||
echo "---------------------------------"
|
||||
show_local
|
||||
echo "---------------------------------"
|
||||
show_private
|
||||
else
|
||||
# parse flags and args
|
||||
level="none"
|
||||
action="none"
|
||||
declare -a args
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
_usage
|
||||
exit 0
|
||||
;;
|
||||
-l|--local)
|
||||
level="local"
|
||||
;;
|
||||
-g|--global)
|
||||
level="global"
|
||||
;;
|
||||
-p|--private)
|
||||
level="private"
|
||||
;;
|
||||
-e|--edit)
|
||||
action="edit"
|
||||
;;
|
||||
-r|--remove)
|
||||
action="remove"
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
args+=("$@")
|
||||
break
|
||||
;;
|
||||
*)
|
||||
args+=("$1")
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
# compatible with previous version
|
||||
if [ "$action" = "none" ]; then
|
||||
case "$level" in
|
||||
local)
|
||||
test ${#args[@]} -ne 0 && add_local "${args[@]}" && echo
|
||||
show_local
|
||||
;;
|
||||
global)
|
||||
test ${#args[@]} -ne 0 && add_global "${args[@]}" && echo
|
||||
show_global
|
||||
;;
|
||||
private)
|
||||
test ${#args[@]} -ne 0 && add_private "${args[@]}" && echo
|
||||
show_private
|
||||
;;
|
||||
none)
|
||||
add_local "${args[@]}"
|
||||
# maybe show_local here too?
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
# open file in editor
|
||||
elif [ "$action" = "edit" ]; then
|
||||
case "$level" in
|
||||
local|none)
|
||||
cd_to_git_root --warn
|
||||
edit_file 'local' .gitignore && echo
|
||||
;;
|
||||
global)
|
||||
global_gitignore="$(global_ignore)"
|
||||
edit_file global "$global_gitignore" && echo
|
||||
;;
|
||||
private)
|
||||
cd_to_git_root --error
|
||||
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
|
||||
edit_file private "${GIT_DIR}/info/exclude" && echo
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
# remove entries
|
||||
else
|
||||
if [ ${#args[@]} -eq 0 ]; then
|
||||
echo "Nothing to remove."
|
||||
exit 0
|
||||
fi
|
||||
case "$level" in
|
||||
local|none)
|
||||
cd_to_git_root --warn
|
||||
remove_patterns 'local' .gitignore "${args[@]}" && echo
|
||||
show_local
|
||||
;;
|
||||
global)
|
||||
global_gitignore="$(global_ignore)"
|
||||
remove_patterns global "$global_gitignore" "${args[@]}" && echo
|
||||
show_global
|
||||
;;
|
||||
private)
|
||||
cd_to_git_root --error
|
||||
remove_patterns private "${GIT_DIR}/info/exclude" "${args[@]}" && echo
|
||||
show_private
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
|
||||
fi
|
||||
case "$1" in
|
||||
-l|--local)
|
||||
test $# -gt 1 && add_local "${@:2}" && echo
|
||||
show_local
|
||||
;;
|
||||
-g|--global)
|
||||
test $# -gt 1 && add_global "${@:2}" && echo
|
||||
show_global
|
||||
;;
|
||||
*)
|
||||
add_local "$@"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,151 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
gitignore_io_url="https://www.toptal.com/developers/gitignore/api/"
|
||||
default_path="$HOME/.gi_list"
|
||||
if [[ -n "$XDG_CACHE_HOME" ]]; then
|
||||
default_path="$XDG_CACHE_HOME/git-extras/gi_list"
|
||||
mkdir -p "$XDG_CACHE_HOME/git-extras"
|
||||
fi
|
||||
|
||||
update_gi_list() {
|
||||
curl -L -s "${gitignore_io_url}/list" > "$default_path"
|
||||
}
|
||||
|
||||
print_in_alphabetical_order() {
|
||||
local first_character previous_first_character ignorable
|
||||
local first=true
|
||||
for ignorable in $(echo "$gi_list" | sort);
|
||||
do
|
||||
first_character=${ignorable:0:1}
|
||||
if [[ $first_character = "$previous_first_character" ]]; then
|
||||
printf " %s" "$ignorable"
|
||||
elif [[ $first = true ]]; then
|
||||
previous_first_character=$first_character
|
||||
first=false
|
||||
printf "%s" "$ignorable"
|
||||
else
|
||||
previous_first_character=$first_character
|
||||
printf "\n%s" "$ignorable"
|
||||
fi
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
print_in_table_format() {
|
||||
echo "$gi_list" | column
|
||||
}
|
||||
|
||||
search() {
|
||||
for type in $gi_list;
|
||||
do
|
||||
if [[ "$type" == *$1* ]]
|
||||
then
|
||||
echo "$type"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
print_last_modified_time() {
|
||||
if ! gi_list_date=$(stat -c "%y" "$default_path" 2> /dev/null); then
|
||||
if ! gi_list_date=$(stat -f "%t%Sm" "$default_path" 2> /dev/null); then
|
||||
if ! gi_list_date=$(date -r "$default_path" +%s 2> /dev/null); then
|
||||
gi_list_date=0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo "Last update time: $gi_list_date"
|
||||
}
|
||||
|
||||
gi() {
|
||||
curl -L -s $gitignore_io_url/"$1"
|
||||
}
|
||||
|
||||
gi_replace() {
|
||||
gi "$1" > .gitignore
|
||||
}
|
||||
|
||||
gi_append() {
|
||||
gi "$1" >> .gitignore
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "Usage:"
|
||||
echo " git ignore-io <types>... Show gitignore template"
|
||||
echo " [-a|--append] <types>... Append new .gitignore content to .gitignore under the current directory"
|
||||
echo " [-r|--replace] <types>... Export new .gitignore to the current directory (The old one will be replaced)"
|
||||
echo " [-l|--list-in-table] Print available types in table format"
|
||||
echo " [-L|--list-alphabetically] Print available types in alphabetical order "
|
||||
echo " [-s|--search] <word> Search word in available types"
|
||||
echo " [-t|--show-update-time] Show the last modified time of $default_path (where the list of available types is stored)"
|
||||
echo " [-u|--update-list] Update $default_path"
|
||||
}
|
||||
|
||||
|
||||
check_list_exist() {
|
||||
if ! [ -f "$default_path" ]; then
|
||||
echo "-----Initial gitignore.io list----"
|
||||
update_gi_list
|
||||
echo "-----Save to $default_path-----"
|
||||
echo
|
||||
fi
|
||||
gi_list=$(tr "," "\n" < "$default_path" 2>/dev/null)
|
||||
}
|
||||
|
||||
check_list_exist
|
||||
if [[ $# -eq 0 ]]; then
|
||||
show_usage
|
||||
else
|
||||
case $1 in
|
||||
-a|--append|-r|--replace)
|
||||
opt=$1
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "There should be at least one type"
|
||||
echo
|
||||
show_usage
|
||||
exit
|
||||
fi
|
||||
|
||||
gi_to_curl=$(echo "$@" | tr " " ",")
|
||||
case $opt in
|
||||
-a|--append)
|
||||
gi_append "$gi_to_curl"
|
||||
;;
|
||||
-r|--replace)
|
||||
gi_replace "$gi_to_curl"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit
|
||||
;;
|
||||
-t|--show-update-time)
|
||||
print_last_modified_time
|
||||
;;
|
||||
-u|--update-list)
|
||||
update_gi_list
|
||||
;;
|
||||
-s|--search)
|
||||
opt=$1
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
show_usage
|
||||
exit
|
||||
fi
|
||||
search "$1"
|
||||
;;
|
||||
-L|--list-alphabetically)
|
||||
print_in_alphabetical_order
|
||||
;;
|
||||
-l|--list-in-table)
|
||||
print_in_table_format
|
||||
;;
|
||||
-*)
|
||||
echo No Such option
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
gi_to_curl=$(echo "$@" | tr " " ",")
|
||||
gi "$gi_to_curl"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
58
bin/git-info
58
bin/git-info
|
|
@ -1,40 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
GREEN="$(tput setaf 2)"
|
||||
NORMAL="$(tput sgr0)"
|
||||
if [ "$1" = "--color" ] || [ "$2" = "--color" ] || \
|
||||
[ "$1" = "-c" ] || [ "$2" = "-c" ] ; then
|
||||
COLOR_TITLE="$GREEN"
|
||||
else
|
||||
COLOR_TITLE="$NORMAL"
|
||||
fi
|
||||
|
||||
HIDE_CONFIG=
|
||||
if [ "$1" != "--no-config" ] && [ "$2" != "--no-config" ]; then
|
||||
HIDE_CONFIG=1
|
||||
fi
|
||||
|
||||
get_config() {
|
||||
cmd_get_config="$(git config --get-all git-extras.info.config-grep)"
|
||||
if [ -z "$cmd_get_config" ]; then
|
||||
git config --list
|
||||
else
|
||||
eval "$cmd_get_config"
|
||||
fi
|
||||
git config --list
|
||||
}
|
||||
|
||||
most_recent_commit() {
|
||||
cmd_get_log="$(git config --get-all git-extras.info.log)"
|
||||
if [ -z "$cmd_get_log" ]; then
|
||||
git log --max-count=1 --pretty=short
|
||||
else
|
||||
eval "$cmd_get_log"
|
||||
fi
|
||||
}
|
||||
|
||||
submodules() {
|
||||
# short sha1
|
||||
git submodule status | sed 's/\([^abcdef0-9]\{0,2\}\)\([abcdef0-9]\{7\}\)\([abcdef0-9]\{33\}\)\(.*\)/\1\2\4/'
|
||||
git log --max-count=1 --pretty=short
|
||||
}
|
||||
|
||||
local_branches() {
|
||||
|
|
@ -50,30 +21,27 @@ remote_urls() {
|
|||
}
|
||||
|
||||
echon() {
|
||||
echo "$@"
|
||||
echo
|
||||
echo "$@"
|
||||
echo
|
||||
}
|
||||
|
||||
# Show info similar to svn
|
||||
|
||||
echo
|
||||
echon "${COLOR_TITLE}## Remote URLs:${NORMAL}"
|
||||
echon "## Remote URLs:"
|
||||
echon "$(remote_urls)"
|
||||
|
||||
echon "${COLOR_TITLE}## Remote Branches:${NORMAL}"
|
||||
echon "## Remote Branches:"
|
||||
echon "$(remote_branches)"
|
||||
|
||||
echon "${COLOR_TITLE}## Local Branches:${NORMAL}"
|
||||
echon "## Local Branches:"
|
||||
echon "$(local_branches)"
|
||||
|
||||
SUBMODULES_LOG=$(submodules)
|
||||
if [ -n "$SUBMODULES_LOG" ]; then
|
||||
echon "${COLOR_TITLE}## Submodule(s):${NORMAL}"
|
||||
echon "$SUBMODULES_LOG"
|
||||
fi
|
||||
|
||||
echon "${COLOR_TITLE}## Most Recent Commit:${NORMAL}"
|
||||
echon "## Most Recent Commit:"
|
||||
echon "$(most_recent_commit)"
|
||||
echon "Type 'git log' for more commits, or 'git show <commit id>' for full commit details."
|
||||
|
||||
if [ -n "$HIDE_CONFIG" ]; then
|
||||
echon "${COLOR_TITLE}## Configuration (.git/config):${NORMAL}"
|
||||
if test "$1" != "--no-config"; then
|
||||
echon "## Configuration (.git/config):"
|
||||
echon "$(get_config)"
|
||||
fi
|
||||
|
|
|
|||
60
bin/git-line-summary
Executable file
60
bin/git-line-summary
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
project=${PWD##*/}
|
||||
|
||||
#
|
||||
# list the last modified author for each line
|
||||
#
|
||||
function single_file {
|
||||
while read data
|
||||
do
|
||||
if [[ $(file $data) = *text* ]]; then #
|
||||
git blame --line-porcelain $data | sed -n 's/^author //p';
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# list the author for all file
|
||||
#
|
||||
function lines {
|
||||
git ls-files | single_file
|
||||
}
|
||||
|
||||
#
|
||||
# count the line count
|
||||
#
|
||||
function count {
|
||||
lines | wc -l
|
||||
}
|
||||
|
||||
#
|
||||
# sort by author modified lines
|
||||
#
|
||||
function authors {
|
||||
lines | sort | uniq -c | sort -rn
|
||||
}
|
||||
|
||||
#
|
||||
# list as percentage for author modified lines
|
||||
#
|
||||
function result {
|
||||
authors | awk '
|
||||
{ args[NR] = $0; sum += $0 }
|
||||
END {
|
||||
printf " %-9s: %i\n", "lines", sum
|
||||
printf " %-9s: \n", "authors"
|
||||
for (i = 1; i <= NR; ++i) {
|
||||
printf " %-30s %2.1f%%\n", args[i], 100 * args[i] / sum
|
||||
}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
# summary
|
||||
|
||||
echo
|
||||
echo " project : $project"
|
||||
result
|
||||
echo
|
||||
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
git log "@{upstream}..@" "$@"
|
||||
ref=$(git symbolic-ref HEAD)
|
||||
branch=${ref#refs/heads/}
|
||||
|
||||
git log origin/${branch}..${branch} $*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
filename="$1"
|
||||
test -z "$filename" && echo "filename required." 1>&2 && exit 1
|
||||
git update-index --skip-worktree "$filename"
|
||||
filename=$1
|
||||
test -z $filename && echo "filename required." 1>&2 && exit 1
|
||||
git update-index --skip-worktree $filename
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
# Bash unofficial strict mode
|
||||
set -eo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
USAGE='git-magic [-a] [-m msg] [-e] [-p] [-f]'
|
||||
|
||||
ALL=false
|
||||
PUSH=false
|
||||
FORCE=''
|
||||
ARGS=()
|
||||
|
||||
while getopts "m:eapfh" arg; do
|
||||
case "${arg}" in
|
||||
m)
|
||||
ARGS+=("-m")
|
||||
ARGS+=("${OPTARG}")
|
||||
;;
|
||||
e)
|
||||
ARGS+=("-e")
|
||||
;;
|
||||
a)
|
||||
ALL=true
|
||||
;;
|
||||
p)
|
||||
PUSH=true
|
||||
;;
|
||||
f)
|
||||
FORCE='--force-with-lease'
|
||||
;;
|
||||
h)
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
;;
|
||||
?)
|
||||
echo "${USAGE}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [[ $# != 0 ]]; then
|
||||
echo "Unknown arguments: $*"
|
||||
echo "${USAGE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -- "${ARGS[@]}" # restore positional parameters
|
||||
|
||||
if [[ $ALL == true ]]; then
|
||||
|
||||
# Check if there is no changes to stage
|
||||
if [[ -z $(git status --porcelain) ]]; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get confirmation from user
|
||||
git status
|
||||
echo "Everything will be added"
|
||||
read -rp "Press enter to continue"
|
||||
|
||||
# Restore staging area so that, for example,
|
||||
# add and modify will not be separate entries in status
|
||||
git restore --staged . || true
|
||||
git add .
|
||||
fi
|
||||
|
||||
# Commit with generated message
|
||||
git commit --no-edit "$@" -m "$(git status --porcelain -uno)"
|
||||
|
||||
if [[ $PUSH == true ]]; then
|
||||
git push $FORCE
|
||||
fi
|
||||
|
||||
# --no-edit by default. use option -e to override this
|
||||
|
||||
# Arguments are passed with quoted "$@" to avoid misparsing
|
||||
|
||||
# Generated message comes after user provided arguments
|
||||
# so that user can insert title before it
|
||||
|
|
@ -1,24 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
|
||||
current_branch() {
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: git merge-into [src] dest [--ff-only]"
|
||||
echo "Usage: git merge-into [src] dest [--ff]"
|
||||
}
|
||||
|
||||
cur_branch=$(current_branch)
|
||||
|
||||
stashed=0
|
||||
if [ -n "$(git status --porcelain)" ];
|
||||
then
|
||||
echo "Local modifications detected, stashing"
|
||||
stashed=1
|
||||
git stash
|
||||
fi
|
||||
|
||||
if [ "${!#}" = '--ff-only' ]; then
|
||||
if [ "${!#}" == '--ff' ]; then
|
||||
case $# in
|
||||
2 ) # dest --ff
|
||||
git push "$(git rev-parse --show-toplevel)" "$cur_branch":"$1";;
|
||||
|
|
@ -41,8 +32,3 @@ else
|
|||
usage
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ $stashed -eq 1 ];
|
||||
then
|
||||
git stash pop;
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ branch=$2
|
|||
prefix=$3
|
||||
flat=0
|
||||
|
||||
if test "$prefix" = "."; then
|
||||
if test $prefix = "."; then
|
||||
prefix=$(mktemp -u 'git-merge-repo.XXXXXXX')
|
||||
flat=1
|
||||
fi
|
||||
|
|
@ -16,10 +16,10 @@ message=$(git log -1 --pretty=%B)
|
|||
|
||||
if test $flat -eq 1; then
|
||||
git stash -u
|
||||
mv -i "$prefix"/* ./
|
||||
mv -i $prefix/* ./
|
||||
git undo
|
||||
git add .
|
||||
git commit -am "$message"
|
||||
git stash apply
|
||||
rm -drf "$prefix"
|
||||
rm -drf $prefix
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,52 +1,21 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
usage() {
|
||||
echo 1>&2 "usage: git missing [<first branch>] <second branch> [<git log options>] [[--] <path>...]"
|
||||
}
|
||||
|
||||
if [ "${#}" -lt 1 ]
|
||||
then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare -a git_log_args=()
|
||||
declare -a branches=()
|
||||
declare -a pathspec=()
|
||||
declare parse_path=false
|
||||
|
||||
for arg in "$@" ; do
|
||||
|
||||
if [[ $parse_path == true ]]; then
|
||||
pathspec+=("$@")
|
||||
break
|
||||
# grab first two non-option arguments
|
||||
for arg in $*; do
|
||||
if [ -n "${arg}" ]; then
|
||||
test -z "$firstbranch" && firstbranch="${arg}" && continue
|
||||
test -z "$secondbranch" && secondbranch="${arg}" && continue
|
||||
else
|
||||
# add anything else to a passthrough
|
||||
passthrough="$passthrough $arg"
|
||||
fi
|
||||
|
||||
case "$arg" in
|
||||
--)
|
||||
parse_path=true
|
||||
;;
|
||||
--*)
|
||||
git_log_args+=( "$arg" )
|
||||
;;
|
||||
*)
|
||||
branches+=( "$arg" )
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
firstbranch=
|
||||
secondbranch=
|
||||
if [ ${#branches[@]} -eq 2 ]
|
||||
then
|
||||
firstbranch="${branches[0]}"
|
||||
secondbranch="${branches[1]}"
|
||||
elif [ ${#branches[@]} -eq 1 ]
|
||||
then
|
||||
secondbranch="${branches[0]}"
|
||||
else
|
||||
echo >&2 "error: at least one branch required"
|
||||
exit 1
|
||||
test -z "$firstbranch" && echo "at least one branch required" && exit 1
|
||||
|
||||
if test -z "$secondbranch"; then
|
||||
secondbranch=$firstbranch
|
||||
firstbranch=
|
||||
fi
|
||||
|
||||
git log "${git_log_args[@]}" "$firstbranch"..."$secondbranch" --format="%m %h %s" --left-right -- "${pathspec[@]}"
|
||||
git log $passthrough $firstbranch...$secondbranch --format="%m %h %s" --left-right
|
||||
|
|
|
|||
36
bin/git-mr
36
bin/git-mr
|
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
if [ -z "${1-}" ] ; then
|
||||
echo "mr number or URL required. See --help for usage." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
remote_ref_kind=merge-requests
|
||||
|
||||
if test "$1" = "clean"; then
|
||||
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read -r ref; do
|
||||
git branch -D "${ref#refs/heads/}"
|
||||
done
|
||||
exit 0
|
||||
elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then
|
||||
remote=${BASH_REMATCH[1]}.git
|
||||
id=${BASH_REMATCH[3]}
|
||||
elif [[ $1 =~ ^(https?://[^/]+/(.+))/pulls/([0-9]+).*$ ]]; then
|
||||
# Forgejo/Codeberg pull request URL, e.g.
|
||||
# https://codeberg.org/owner/repository/pulls/453
|
||||
remote=${BASH_REMATCH[1]}.git
|
||||
id=${BASH_REMATCH[3]}
|
||||
remote_ref_kind=pull
|
||||
else
|
||||
id=$1
|
||||
remote=${2:-origin}
|
||||
fi
|
||||
|
||||
branch=mr/$id
|
||||
remote_ref=refs/$remote_ref_kind/$id/head
|
||||
git fetch -fu "$remote" "$remote_ref:$branch"
|
||||
git checkout "$branch"
|
||||
git config --local --replace "branch.$branch.merge" "$remote_ref"
|
||||
git config --local --replace "branch.$branch.remote" "$remote"
|
||||
|
|
@ -1,23 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
file=""
|
||||
range=""
|
||||
for i in "$@"
|
||||
do
|
||||
# use '--' to separate file list and rev-list arguments
|
||||
test "$i" == '--' && shift && break
|
||||
file="$file"' '"$i"
|
||||
shift
|
||||
done
|
||||
test -n "$*" && range=("$@")
|
||||
|
||||
file="$1"
|
||||
test -z "$file" && echo "file required." 1>&2 && exit 1
|
||||
if [ -z "${range[*]}" ]
|
||||
then
|
||||
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
|
||||
--prune-empty --tag-name-filter cat -- --all
|
||||
else
|
||||
# $range is an array so that we can forward multiple rev-list arguments
|
||||
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
|
||||
--prune-empty --tag-name-filter cat -- "${range[@]}"
|
||||
fi
|
||||
git filter-branch -f --index-filter "git rm -r --cached '$file' --ignore-unmatch" --prune-empty --tag-name-filter cat -- --all
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
if ! command -v pastebinit &>/dev/null; then
|
||||
echo >&2 "To run 'git paste', you need to install pastebinit in your system"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test $# -ne 0 || set -- '@{u}'
|
||||
git format-patch --stdout "$@" | pastebinit -f diff
|
||||
76
bin/git-pr
76
bin/git-pr
|
|
@ -1,77 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
# Based on https://gist.github.com/gnarf/5406589 and https://gist.github.com/jhnns/d654d9d6da6d3b749986
|
||||
TO_MERGE=
|
||||
|
||||
pull() {
|
||||
local remote="$1"
|
||||
local id="$2"
|
||||
local branch="$3"
|
||||
local ref="refs/pull/$id/head"
|
||||
|
||||
if [ -n "$TO_MERGE" ]; then
|
||||
ref="refs/pull/$id/merge"
|
||||
fi
|
||||
|
||||
git fetch -fu "$remote" "$ref:$branch" && \
|
||||
git checkout "$branch" && \
|
||||
git config --local --replace "branch.$branch.merge" "$ref" && \
|
||||
git config --local --replace "branch.$branch.remote" "$remote"
|
||||
}
|
||||
|
||||
pull_pr_if_matched() {
|
||||
if [[ $1 =~ ^(.*):([0-9]+)|(https?://[^/]+/.+)/pull/([0-9]+).*$ ]]; then
|
||||
if [[ -n ${BASH_REMATCH[2]} ]]; then
|
||||
remote="${BASH_REMATCH[1]:-origin}"
|
||||
id="${BASH_REMATCH[2]}"
|
||||
else
|
||||
remote="${BASH_REMATCH[3]}.git"
|
||||
id="${BASH_REMATCH[4]}"
|
||||
fi
|
||||
|
||||
branch=pr/$id
|
||||
pull "$remote" "$id" "$branch"
|
||||
return $?
|
||||
fi
|
||||
|
||||
echo "$1 doesn't match the pr id pattern."
|
||||
return 1
|
||||
}
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-m|--merge)
|
||||
TO_MERGE=1
|
||||
;;
|
||||
*)
|
||||
# set the argument back
|
||||
set -- "$@" "$arg"
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
test -z "$1" && echo "pr number required." 1>&2 && exit 1
|
||||
# Based on https://gist.github.com/gnarf/5406589
|
||||
|
||||
if test "$1" = "clean"; then
|
||||
git for-each-ref refs/heads/pr/* --format='%(refname)' | while read -r ref; do
|
||||
git branch -D "${ref#refs/heads/}"
|
||||
git for-each-ref refs/heads/pr/* --format='%(refname)' | while read ref; do
|
||||
git branch -D ${ref#refs/heads/}
|
||||
done
|
||||
|
||||
elif [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
remote_pref=${2:-$(git config --get git-extras.pr.remote)}
|
||||
remote=${remote_pref:-origin}
|
||||
id=$1
|
||||
branch=pr/$id
|
||||
pull "$remote" "$id" "$branch"
|
||||
|
||||
else
|
||||
rc=1
|
||||
while [ "$1" != "" ]; do
|
||||
pull_pr_if_matched "$1"
|
||||
rc=$?
|
||||
shift
|
||||
done
|
||||
|
||||
exit "$rc"
|
||||
test -z $1 && echo "pr number required." 1>&2 && exit 1
|
||||
git fetch -fu ${2:-origin} refs/pull/$1/head:pr/$1 && git checkout pr/$1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
PROGRAM=$0
|
||||
PRIMARY_BRANCH=""
|
||||
SECONDARY_BRANCH=""
|
||||
FF="--ff"
|
||||
CONTINUE="no"
|
||||
|
||||
current_branch() {
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
}
|
||||
|
||||
function usage()
|
||||
{
|
||||
echo "USAGE: ${PROGRAM} PRIMARY_BRANCH [SECONDARY_BRANCH] [--no-ff]"
|
||||
echo "USAGE: ${PROGRAM} --continue"
|
||||
echo ""
|
||||
echo "OPTIONS:"
|
||||
echo " --no-ff: Force rebase commit."
|
||||
echo " -c|--continue: Continue after the user updates conflicts."
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
|
||||
case $key in
|
||||
--no-ff)
|
||||
FF="--no-ff"
|
||||
;;
|
||||
-c|--continue)
|
||||
CONTINUE=yes
|
||||
;;
|
||||
*)
|
||||
if [[ "$PRIMARY_BRANCH" == "" ]]; then
|
||||
PRIMARY_BRANCH=$key
|
||||
elif [[ "$SECONDARY_BRANCH" == "" ]]; then
|
||||
SECONDARY_BRANCH=$key
|
||||
else
|
||||
usage
|
||||
exit 20 # Error during arguments parsing
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift # past argument or value
|
||||
done
|
||||
|
||||
if [[ "$SECONDARY_BRANCH" == "" ]]; then
|
||||
SECONDARY_BRANCH=$(current_branch)
|
||||
fi
|
||||
|
||||
if [[ "$CONTINUE" == "yes" ]]; then
|
||||
TARGET_BRANCH=$(current_branch)
|
||||
SECONDARY_BRANCH=${TARGET_BRANCH%"-rebased-on-top-of-"*}
|
||||
PRIMARY_BRANCH=${TARGET_BRANCH#*"-rebased-on-top-of-"}
|
||||
if [[ "${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}" != $TARGET_BRANCH ]]; then
|
||||
echo "Couldn't continue rebasing on ${TARGET_BRANCH}"
|
||||
exit 30 # Impossible to detect PRIMARY_BRANCH AND SECONDARY_BRANCH
|
||||
fi
|
||||
|
||||
echo "Continuing rebasing of $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
||||
git commit || exit 51
|
||||
git branch -d "${SECONDARY_BRANCH}" || exit 52
|
||||
git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 53
|
||||
|
||||
elif [[ "$PRIMARY_BRANCH" == "" ]]; then
|
||||
usage
|
||||
exit 10 # Missing arguments PRIMARY_BRANCH
|
||||
else
|
||||
echo "Rebasing $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
||||
TARGET_BRANCH="${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}"
|
||||
|
||||
git checkout "${PRIMARY_BRANCH}" || exit 41
|
||||
git checkout -b "${TARGET_BRANCH}" || exit 42
|
||||
|
||||
if git merge "${SECONDARY_BRANCH}" ${FF} \
|
||||
-m "Psycho-rebased branch ${SECONDARY_BRANCH} on top of ${PRIMARY_BRANCH}"; then
|
||||
git branch -d "${SECONDARY_BRANCH}" || exit 43
|
||||
git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 44
|
||||
else
|
||||
echo "Resolve the conflict and run ``${PROGRAM} --continue``."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
#
|
||||
|
||||
abort() {
|
||||
echo >&2 "$@"
|
||||
echo $@
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
|
@ -14,72 +14,51 @@ abort() {
|
|||
#
|
||||
|
||||
json() {
|
||||
local title="${1//\"/\\\"}"
|
||||
local body="${2//\"/\\\"}"
|
||||
local head="${3//\"/\\\"}"
|
||||
local base="${4//\"/\\\"}"
|
||||
cat <<EOF
|
||||
{
|
||||
"title": "$title",
|
||||
"body": "$body",
|
||||
"head": "$head",
|
||||
"base": "$base"
|
||||
}
|
||||
"title": "$1",
|
||||
"body": "$2",
|
||||
"head": "$3",
|
||||
"base": "$4"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# personal access token
|
||||
# config name is github-personal-access-token '_' is not allowed in git config
|
||||
# user
|
||||
|
||||
github_personal_access_token=$(git config --default "$GITHUB_TOKEN" git-extras.github-personal-access-token)
|
||||
|
||||
test -z "$github_personal_access_token" && abort "GITHUB_TOKEN or git config git-extras.github-personal-access-token required"
|
||||
user=$(git config --global user.email)
|
||||
test -z "$user" && abort "git config user.email required"
|
||||
|
||||
# branch
|
||||
|
||||
branch=${1-$(git symbolic-ref HEAD | sed 's/refs\/heads\///')}
|
||||
remote=$(git config branch."$branch".remote)
|
||||
if [ -z "$remote" ]; then
|
||||
echo 'no upstream found, push to origin as default'
|
||||
remote="origin"
|
||||
fi
|
||||
[ "$remote" = "." ] && abort "the upstream should be a remote branch."
|
||||
|
||||
# make sure it's pushed
|
||||
|
||||
git push "$remote" "$branch" || abort "failed to push $branch"
|
||||
git push origin $branch || abort "failed to push $branch"
|
||||
|
||||
remote_url=$(git config remote."$remote".url)
|
||||
if [[ "$remote_url" == git@* ]]; then
|
||||
project=${remote_url##*:}
|
||||
else
|
||||
project=${remote_url#https://*/}
|
||||
fi
|
||||
project=${project%.git}
|
||||
# lame hack to get project
|
||||
|
||||
project=$(git config remote.origin.url | sed 's/^.*://' | sed 's/\.git$//')
|
||||
|
||||
# prompt
|
||||
|
||||
echo
|
||||
echo " create pull-request for $project '$branch'"
|
||||
echo
|
||||
printf " title: " && read -r title
|
||||
printf " body: " && read -r body
|
||||
printf " base [%s]: " "$(git_extra_default_branch)" && read -r base
|
||||
printf " GitHub two-factor authentication code (leave blank if not set up): " && read -r mfa_code
|
||||
printf " title: " && read title
|
||||
printf " body: " && read body
|
||||
printf " base [master]: " && read base
|
||||
echo
|
||||
|
||||
# create pull request
|
||||
|
||||
if [ -z "$base" ]
|
||||
then
|
||||
base="$(git_extra_default_branch)"
|
||||
base="master"
|
||||
fi
|
||||
|
||||
body=$(json "$title" "$body" "$branch" "$base")
|
||||
body=$(json "$title" "$body" $branch "$base")
|
||||
|
||||
curl -u "$user" "https://api.github.com/repos/$project/pulls" -d "$body"
|
||||
|
||||
curl \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: token $github_personal_access_token" \
|
||||
-H "X-GitHub-OTP: $mfa_code" \
|
||||
"https://api.github.com/repos/$project/pulls" -d "$body"
|
||||
|
|
|
|||
161
bin/git-reauthor
161
bin/git-reauthor
|
|
@ -1,161 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
init_variables() {
|
||||
COMMAND=${0#*-}
|
||||
|
||||
CONFIG=false
|
||||
ALL=false
|
||||
unset OLD_EMAIL
|
||||
unset CORRECT_EMAIL
|
||||
unset CORRECT_NAME
|
||||
TYPE='both'
|
||||
}
|
||||
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
usage: git ${COMMAND} [<options>]
|
||||
|
||||
Options
|
||||
-a, --all rewrite all identities in commits and tags
|
||||
-c, --use-config define correct values from user Git config
|
||||
-e, --correct-email <email> define the correct email to set
|
||||
-n, --correct-name <name> define the correct name to set
|
||||
-o, --old-email <email> rewrite identities matching old email in commits and tags
|
||||
-t, --type <id> define the type of identities affected by the rewrite
|
||||
author, committer, both (default)
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
error() {
|
||||
if [[ -n "$1" ]]; then
|
||||
local msg=$( echo "error: $1" | sed 's/\\n/\\n /g' )
|
||||
echo -e "${msg}" >&2
|
||||
fi
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
reauthor() {
|
||||
local author='
|
||||
if ${ALL} || [ "${GIT_AUTHOR_EMAIL}" = "${OLD_EMAIL}" ]; then
|
||||
[ -z "${CORRECT_EMAIL+x}" ] || export GIT_AUTHOR_EMAIL="${CORRECT_EMAIL}"
|
||||
[ -z "${CORRECT_NAME+x}" ] || export GIT_AUTHOR_NAME="${CORRECT_NAME}"
|
||||
fi
|
||||
'
|
||||
local committer='
|
||||
if ${ALL} || [ "${GIT_COMMITTER_EMAIL}" = "${OLD_EMAIL}" ]; then
|
||||
[ -z "${CORRECT_EMAIL+x}" ] || export GIT_COMMITTER_EMAIL="${CORRECT_EMAIL}"
|
||||
[ -z "${CORRECT_NAME+x}" ] || export GIT_COMMITTER_NAME="${CORRECT_NAME}"
|
||||
fi
|
||||
'
|
||||
local filter
|
||||
|
||||
case "${TYPE}" in
|
||||
author) filter="${author}" ;;
|
||||
committer) filter="${committer}" ;;
|
||||
both) filter="${author} ${committer}" ;;
|
||||
esac
|
||||
|
||||
export ALL
|
||||
export OLD_EMAIL
|
||||
export CORRECT_EMAIL
|
||||
export CORRECT_NAME
|
||||
|
||||
git filter-branch --force --env-filter "${filter}" \
|
||||
--tag-name-filter cat -- --branches --tags
|
||||
}
|
||||
|
||||
|
||||
parse_options() {
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--all|-a)
|
||||
ALL=true
|
||||
shift
|
||||
;;
|
||||
--correct-email|-e)
|
||||
[[ -n "${2+x}" ]] || error 'Missing correct-email value'
|
||||
CORRECT_EMAIL="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--correct-name|-n)
|
||||
[[ -n "${2+x}" ]] || error 'Missing correct-name value'
|
||||
CORRECT_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--old-email|-o)
|
||||
[[ -n "${2+x}" ]] || error 'Missing old-email value'
|
||||
OLD_EMAIL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--type|-t)
|
||||
[[ -n "${2+x}" ]] || error 'Missing type value'
|
||||
TYPE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--use-config|-c)
|
||||
CONFIG=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
error "invalid option '$1'"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ${CONFIG}; then
|
||||
# use config values if not explicitly already defined
|
||||
[[ -n "${CORRECT_EMAIL+x}" ]] || CORRECT_EMAIL=$( git config user.email )
|
||||
[[ -n "${CORRECT_NAME+x}" ]] || CORRECT_NAME=$( git config user.name )
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
validate_options() {
|
||||
# Either OLD_EMAIL should be set or ALL should be true
|
||||
if [[ -z "${OLD_EMAIL+x}" ]] && ! ${ALL}; then
|
||||
msg="missing target of the rewrite"
|
||||
msg="${msg}\nuse either --old-email option or --all flag"
|
||||
error "${msg}"
|
||||
fi
|
||||
|
||||
# OLD_EMAIL shouldn't be set if ALL is true as well to prevent misuse
|
||||
if [[ -n "${OLD_EMAIL+x}" ]] && ${ALL}; then
|
||||
msg="ambiguous target of the rewrite"
|
||||
msg="${msg}\nuse either --old-email option or --all flag"
|
||||
error "${msg}"
|
||||
fi
|
||||
|
||||
# CORRECT_NAME should be either unset or set to non-empty string
|
||||
[[ -n "${CORRECT_NAME-x}" ]] || error "empty name is not allowed"
|
||||
|
||||
# Either CORRECT_EMAIL or CORRECT_NAME should be set
|
||||
if [[ -z "${CORRECT_EMAIL+x}" ]] && [[ -z "${CORRECT_NAME+x}" ]]; then
|
||||
msg="missing correct email and/or name to set"
|
||||
msg="${msg}\nuse --correct-email and/or --correct-name options"
|
||||
msg="${msg}\nor --use-config flag with user values set in Git config"
|
||||
error "${msg}"
|
||||
fi
|
||||
|
||||
# TYPE should be a valid identifier
|
||||
if [[ "${TYPE}" != 'both' ]] \
|
||||
&& [[ "${TYPE}" != 'author' ]] \
|
||||
&& [[ "${TYPE}" != 'committer' ]]; then
|
||||
error "invalid type '${TYPE}'"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
init_variables
|
||||
parse_options "$@"
|
||||
validate_options
|
||||
|
||||
reauthor
|
||||
|
|
@ -17,26 +17,26 @@ fi
|
|||
# Use a temporary index.
|
||||
index=$(git_extra_mktemp)
|
||||
cleanup() {
|
||||
rm "$index"
|
||||
rm $index
|
||||
exit 2
|
||||
}
|
||||
trap cleanup INT
|
||||
trap cleanup 2
|
||||
|
||||
# Go back in history while parent commits are available.
|
||||
echo "Trying to find a commit the patch applies to..."
|
||||
rev=$(git rev-parse HEAD)
|
||||
while [ $? = 0 ]
|
||||
do
|
||||
GIT_INDEX_FILE=$index git read-tree "$rev"
|
||||
GIT_INDEX_FILE=$index git read-tree $rev
|
||||
|
||||
# Try to apply the patch.
|
||||
GIT_INDEX_FILE=$index git apply --cached "$1" &>/dev/null
|
||||
GIT_INDEX_FILE=$index git apply --cached $1 >/dev/null 2>&1
|
||||
patch_failed=$?
|
||||
|
||||
# Do it again, but show the error, if the problem is the patch itself.
|
||||
if [ $patch_failed = 128 ]
|
||||
then
|
||||
GIT_INDEX_FILE=$index git apply --index --check "$1"
|
||||
GIT_INDEX_FILE=$index git apply --index --check $1
|
||||
exit $patch_failed
|
||||
fi
|
||||
|
||||
|
|
@ -45,19 +45,19 @@ do
|
|||
then
|
||||
# Manufacture a commit.
|
||||
tree=$(GIT_INDEX_FILE=$index git write-tree)
|
||||
commit=$(git commit-tree "$tree" -p "$rev" -m "$1")
|
||||
rm "$index"
|
||||
commit=$(git commit-tree $tree -p $rev -m "$1")
|
||||
rm $index
|
||||
|
||||
echo "Patch applied to $(git rev-parse --short "$rev") as $(git rev-parse --short "$commit")"
|
||||
echo "Patch applied to $(git rev-parse --short $rev) as $(git rev-parse --short $commit)"
|
||||
|
||||
git cherry-pick "$commit"
|
||||
git cherry-pick $commit
|
||||
exit $?
|
||||
fi
|
||||
|
||||
rev=$(git rev-parse --verify -q "$rev^")
|
||||
rev=$(git rev-parse --verify -q $rev^)
|
||||
done
|
||||
|
||||
# No compatible commit found. Restore.
|
||||
echo "Failed to find a commit the patch applies to."
|
||||
rm "$index"
|
||||
rm $index
|
||||
exit 1
|
||||
|
|
|
|||
16
bin/git-refactor
Executable file
16
bin/git-refactor
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
# Removed unnecessary git checkout for bug, feature and refactor
|
||||
|
||||
if test "$1" = "finish"; then
|
||||
test -z $2 && echo "refactor <name> required." 1>&2 && exit 1
|
||||
branch=refactor/$2
|
||||
git merge --no-ff $branch && git delete-branch $branch
|
||||
else
|
||||
test -z $1 && echo "refactor <name> required." 1>&2 && exit 1
|
||||
if test -n "$2"; then
|
||||
echo "too many arguments; if not finishing a refactor, only <name> of new refactoring is expected" && exit 1
|
||||
fi
|
||||
branch=refactor/$1
|
||||
git checkout -b $branch &> /dev/null
|
||||
test -n $? && git checkout $branch &> /dev/null
|
||||
fi
|
||||
125
bin/git-release
125
bin/git-release
|
|
@ -1,137 +1,48 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
#!/usr/bin/env sh
|
||||
|
||||
hook() {
|
||||
local hook=.git/hooks/$1.sh
|
||||
# compat without extname
|
||||
if test ! -f "$hook"; then
|
||||
if test ! -f $hook; then
|
||||
hook=.git/hooks/$1
|
||||
fi
|
||||
|
||||
if test -f "$hook"; then
|
||||
if test -f $hook; then
|
||||
echo "... $1"
|
||||
shift
|
||||
if test -x "$hook"; then
|
||||
$hook "$@"
|
||||
if test -x $hook; then
|
||||
$hook
|
||||
else
|
||||
. "$hook" "$@"
|
||||
. $hook
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
exit_with_msg() {
|
||||
>&2 echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if test $# -gt 0; then
|
||||
version=$1
|
||||
shift #remove version from arguments list
|
||||
remote=''
|
||||
|
||||
hook pre-release
|
||||
echo "... releasing $version"
|
||||
# check for flags
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-c) need_changelog=true;;
|
||||
-c) git-changelog -t "$version" ;;
|
||||
-r) remote=$2; shift ;;
|
||||
-m) msg=$2; shift ;;
|
||||
-s)
|
||||
test -n "$keyid" &&
|
||||
exit_with_msg "Please use '-s' OR '-u'"
|
||||
sign=true
|
||||
;;
|
||||
-u)
|
||||
test -n "$sign" &&
|
||||
exit_with_msg "Please use '-s' OR '-u'"
|
||||
keyid=$2
|
||||
shift
|
||||
;;
|
||||
--semver)
|
||||
test -z "$2" &&
|
||||
exit_with_msg "major/minor/patch required for --semver option"
|
||||
semver=$2
|
||||
shift
|
||||
;;
|
||||
--prefix)
|
||||
test -z "$2" &&
|
||||
exit_with_msg "prefix string required for --prefix option"
|
||||
prefix="$2"
|
||||
shift
|
||||
;;
|
||||
--no-empty-commit) no_empty_commit=true;;
|
||||
--) shift; hook_args="$hook_args $*"; break;;
|
||||
*) test -z "$version" && version=$1 ;;
|
||||
--) shift; break;;
|
||||
*) usage ;;
|
||||
esac
|
||||
|
||||
shift
|
||||
|
||||
done
|
||||
|
||||
if [ -n "$semver" ]; then
|
||||
if [ -z "$(git tag)" ]; then
|
||||
echo "there is no tag in the git repo" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
|
||||
|
||||
if [[ ! "$latest_tag" =~ \
|
||||
^$prefix([^0-9]*)([1-9][0-9]+|[0-9])\.([1-9][0-9]+|[0-9])\.([1-9][0-9]+|[0-9])(.*) ]]; then
|
||||
echo "the latest tag doesn't match semver format requirement" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$semver" in
|
||||
major ) version="${BASH_REMATCH[2]}" ;;
|
||||
minor ) version="${BASH_REMATCH[3]}" ;;
|
||||
patch ) version="${BASH_REMATCH[4]}" ;;
|
||||
* ) echo "invalid semver argument given: $semver" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
(( ++version ))
|
||||
|
||||
case "$semver" in
|
||||
major ) version="$prefix${BASH_REMATCH[1]}$version.0.0${BASH_REMATCH[5]}" ;;
|
||||
minor ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.$version.0${BASH_REMATCH[5]}" ;;
|
||||
patch ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.${BASH_REMATCH[3]}.$version${BASH_REMATCH[5]}" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
hook_args="$version"
|
||||
|
||||
if [ -z "$msg" ]; then
|
||||
msg="Release ${version}"
|
||||
msg="Release ${version}"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
hook pre-release $hook_args \
|
||||
|| exit_with_msg "pre-release hook failed! Cancelling release."
|
||||
echo "... releasing $version"
|
||||
if [ "$need_changelog" = true ]; then
|
||||
git-changelog -t "$version"
|
||||
fi
|
||||
|
||||
if [ "$no_empty_commit" = true ]; then
|
||||
git commit -a -m "$msg" || true
|
||||
else
|
||||
git commit -a -m "$msg" --allow-empty
|
||||
fi
|
||||
|
||||
declare -a sign_args
|
||||
if [ "$sign" = true ]; then
|
||||
sign_args=("-s")
|
||||
fi
|
||||
|
||||
if [ -n "$keyid" ]; then
|
||||
sign_args=("-u" "$keyid")
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
git tag "${sign_args[@]}" $version -a -m "$msg" \
|
||||
&& git push $remote --tags \
|
||||
git commit -a -m "$msg"
|
||||
git tag $version -a -m "$msg" \
|
||||
&& git push $remote \
|
||||
&& hook post-release $hook_args \
|
||||
&& git push $remote --tags \
|
||||
&& hook post-release \
|
||||
&& echo "... complete"
|
||||
else
|
||||
echo "tag required" 1>&2 && exit 1
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Assert there is at least one branch provided
|
||||
test -z "$1" && echo "new branch name required." 1>&2 && exit 1
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
new_branch="$1"
|
||||
old_branch="$(git symbolic-ref --short -q HEAD)"
|
||||
else
|
||||
new_branch="$2"
|
||||
old_branch="$1"
|
||||
fi
|
||||
|
||||
remote=$(git config branch."$old_branch".remote; true)
|
||||
|
||||
git branch -m "$old_branch" "$new_branch"
|
||||
# check if the branch is tracking a remote branch
|
||||
if [[ -n "$remote" && "$remote" != "." ]]
|
||||
then
|
||||
git push "$remote" :"$old_branch"
|
||||
git push --set-upstream "$remote" "$new_branch"
|
||||
fi
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Usage function
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: git rename-file [OPTIONS] <source> <destination>
|
||||
|
||||
Description:
|
||||
Rename a file or directory and ensure Git recognizes the change, regardless of filesystem case-sensitivity.
|
||||
It combines the functionality of the "mv" command and "git mv". This is particularly useful for renaming files or directories
|
||||
to change only their case, which might not be detected by Git on case-insensitive filesystems.
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message and exit.
|
||||
|
||||
Examples:
|
||||
git rename-file old_filename new_filename
|
||||
git rename-file old_directory new_directory
|
||||
EOF
|
||||
}
|
||||
|
||||
# Check for help option
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for correct number of arguments
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Error: Incorrect number of arguments."
|
||||
echo ""
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Assign variables
|
||||
SOURCE="$1"
|
||||
DESTINATION="$2"
|
||||
TEMP_NAME="${SOURCE}.temp"
|
||||
|
||||
# Function to check if a file or directory exists in a case-sensitive manner
|
||||
check_case_sensitive_exists() {
|
||||
local path="$1"
|
||||
local dir
|
||||
local base
|
||||
|
||||
dir=$(dirname "$path")
|
||||
base=$(basename "$path")
|
||||
|
||||
if [ -e "$dir" ]; then
|
||||
if (cd "$dir" && find . -maxdepth 1 -name "$base" | grep -q "$base"); then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if source exists and is under version control
|
||||
if ! check_case_sensitive_exists "$SOURCE"; then
|
||||
echo "Error: Source '$SOURCE' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! git ls-files --error-unmatch "$SOURCE" > /dev/null 2>&1; then
|
||||
echo "Error: Source '$SOURCE' is not under version control. If file or directory is new, it must at least be staged."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if destination already exists
|
||||
if check_case_sensitive_exists "$DESTINATION"; then
|
||||
echo "Error: Destination '$DESTINATION' already exists."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the destination directory exists
|
||||
DEST_DIR=$(dirname "$DESTINATION")
|
||||
if ! check_case_sensitive_exists "$DEST_DIR"; then
|
||||
echo "Error: Destination directory '$DEST_DIR' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create a rollback function
|
||||
rollback() {
|
||||
echo "Rolling back changes..."
|
||||
if [ -e "$TEMP_NAME" ]; then
|
||||
git mv -f "$TEMP_NAME" "$SOURCE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Trap errors to trigger rollback
|
||||
trap 'rollback' ERR
|
||||
|
||||
# Move the file to a temporary name within the Git repository
|
||||
git mv "$SOURCE" "$TEMP_NAME"
|
||||
|
||||
# Move the temporary file to the desired destination
|
||||
git mv "$TEMP_NAME" "$DESTINATION"
|
||||
|
||||
echo "Successfully renamed '$SOURCE' to '$DESTINATION'."
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
old=$1
|
||||
new=$2
|
||||
|
||||
test -z "$old" && echo "old remote name required." 1>&2 && exit 1
|
||||
test -z "$new" && echo "new remote name required." 1>&2 && exit 1
|
||||
|
||||
if ! git config --get "remote.$old.fetch" > /dev/null; then
|
||||
echo "remote $old doesn't exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if git config --get "remote.$new.fetch" > /dev/null; then
|
||||
git remote remove "$new"
|
||||
fi
|
||||
git remote rename "$old $new"
|
||||
git remote -v
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
old=$1
|
||||
new=$2
|
||||
|
||||
test -z "$old" && echo "old tag name required." 1>&2 && exit 1
|
||||
test -z "$new" && echo "new tag name required." 1>&2 && exit 1
|
||||
test -z $old && echo "old tag name required." 1>&2 && exit 1
|
||||
test -z $new && echo "new tag name required." 1>&2 && exit 1
|
||||
|
||||
git tag "$new" "$old"
|
||||
git tag -d "$old"
|
||||
git push origin "$new"
|
||||
git push origin ":refs/tags/$old"
|
||||
git tag $new $old
|
||||
git tag -d $old
|
||||
git push origin $new
|
||||
git push origin :refs/tags/$old
|
||||
|
|
|
|||
82
bin/git-repl
82
bin/git-repl
|
|
@ -1,99 +1,43 @@
|
|||
#!/usr/bin/env bash
|
||||
shopt -s extglob
|
||||
|
||||
git version
|
||||
echo "git-extras version ""$(git-extras -v)"
|
||||
echo "Type 'ls' to ls files below current directory; '!command' to execute any command or just 'subcommand' to execute any git subcommand; 'quit', 'exit', 'q', ^D, or ^C to exit the git repl."
|
||||
|
||||
HISTIGNORE=${HISTIGNORE:-+([[:space:]])}
|
||||
HISTCONTROL=${HISTCONTROL:-ignoredups}
|
||||
use_local_history=$(git config --get --default 'false' git-extras.repl.use-local-history)
|
||||
if [[ "$use_local_history" == "true" ]]; then
|
||||
HISTFILE="$(git rev-parse --show-toplevel)/.git_extras_repl_history"
|
||||
else
|
||||
HISTFILE=${XDG_STATE_HOME:-$HOME/.local/state}/git_extras_repl_history
|
||||
fi
|
||||
|
||||
# file doesn't exist, is empty, or contains only whitespace
|
||||
if [[ ! -f "$HISTFILE" ]] || [[ ! -s "$HISTFILE" ]] || ! grep -q '[^[:space:]]' "$HISTFILE"; then
|
||||
# `history -r` .... `history -a` are not happy with an empty initial file in bash 3.2.57, which is what MacOS 26 ships with
|
||||
echo '!echo welcome to git-repl!' >> "$HISTFILE"
|
||||
fi
|
||||
history -r
|
||||
echo "type 'ls' to ls files below current directory, '!command' to execute any command or just 'subcommand' to execute any git subcommand"
|
||||
|
||||
while true; do
|
||||
# Current branch
|
||||
cur=$(git symbolic-ref HEAD 2> /dev/null | cut -d/ -f3-)
|
||||
cur=`git symbolic-ref HEAD 2> /dev/null | cut -d/ -f3-`
|
||||
|
||||
# Prompt
|
||||
if test -n "$cur"; then
|
||||
cur_string=" ($cur)"
|
||||
prompt="git ($cur)> "
|
||||
else
|
||||
cur_string=""
|
||||
fi
|
||||
if test -n "$exit_status" && test "$exit_status" -ne 0; then
|
||||
es_string=$' \e[31m['"$exit_status"$']\e[0m'
|
||||
else
|
||||
es_string=""
|
||||
fi
|
||||
prompt_character=$(git config --get --default '>' git-extras.repl.prompt-character)
|
||||
|
||||
prefix=$(git config --get --default 'git' git-extras.repl.prefix)
|
||||
|
||||
show_project_name=$(git config --get --default 'false' git-extras.repl.show-project-name)
|
||||
if [[ "$show_project_name" == "true" ]]; then
|
||||
project_name=" $(basename "$(git rev-parse --show-toplevel)" .git)"
|
||||
else
|
||||
project_name=""
|
||||
prompt="git> "
|
||||
fi
|
||||
|
||||
prompt_base="$prefix$project_name$cur_string$es_string$prompt_character"
|
||||
prompt_base_stripped=$(echo "$prompt_base" | awk '{$1=$1};1')
|
||||
prompt="$prompt_base_stripped "
|
||||
# Readline
|
||||
read -e -r -p "$prompt" cmd
|
||||
|
||||
# Use arguments as a command if any are provided.
|
||||
if [ $# -ne 0 ]; then
|
||||
cmd=$*
|
||||
set --
|
||||
echo "$prompt" "$cmd" # It is as though you had entered a command.
|
||||
else
|
||||
# Readline
|
||||
read -e -r -p "$prompt" cmd
|
||||
# Check for EOF, and end the program if so (handles ^D).
|
||||
test $? -ne 0 && break
|
||||
fi
|
||||
# EOF
|
||||
test $? -ne 0 && break
|
||||
|
||||
# Add command to history if it is not all whitespace
|
||||
if [[ ! "$cmd" =~ ^[[:space:]]*$ ]]; then
|
||||
history -s "$cmd"
|
||||
fi
|
||||
# History
|
||||
history -s "$cmd"
|
||||
|
||||
# Built-in commands
|
||||
case $cmd in
|
||||
ls) cmd=ls-files;;
|
||||
"")
|
||||
on_enter_cmd=$(git config --get --default '' git-extras.repl.on-enter-command)
|
||||
if test -n "$on_enter_cmd"; then
|
||||
cmd="$on_enter_cmd"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
quit|exit|q) break;;
|
||||
"") continue;;
|
||||
quit) break;;
|
||||
esac
|
||||
|
||||
history -a
|
||||
|
||||
if [[ $cmd == !* ]]; then
|
||||
# shellcheck disable=SC2086
|
||||
eval ${cmd:1}
|
||||
eval ${cmd:1}
|
||||
elif [[ $cmd == git* ]]; then
|
||||
# shellcheck disable=SC2086
|
||||
eval $cmd
|
||||
else
|
||||
eval git "$cmd"
|
||||
fi
|
||||
exit_status=$?
|
||||
done
|
||||
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ file="$1"
|
|||
commit="$2"
|
||||
|
||||
if [[ -f $file ]]; then
|
||||
git rm --cached -q -f -- "$file"
|
||||
if [[ -z $commit ]]; then
|
||||
git checkout HEAD -- "$file"
|
||||
git rm --cached -q -f -- $file
|
||||
if [[ -z $commit ]]; then
|
||||
git checkout HEAD -- $file
|
||||
echo "Reset '$1' to HEAD"
|
||||
else
|
||||
git checkout "$commit" -- "$file"
|
||||
git checkout $commit -- $file
|
||||
echo "Reset '$1' to $commit"
|
||||
fi
|
||||
else
|
||||
echo "File '$1' not found in $PWD"
|
||||
else
|
||||
echo "File '$1' not found in `pwd`"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -6,13 +6,7 @@ git_root() {
|
|||
|
||||
# get the relative path of current path according to root of repo
|
||||
git_root_relative() {
|
||||
rel=$(git rev-parse --show-prefix)
|
||||
if [ -z "$rel" ]; then
|
||||
# git rev-parse --show-prefix will output empty string when we are in the root dir
|
||||
echo "."
|
||||
else
|
||||
echo "$rel"
|
||||
fi
|
||||
git rev-parse --show-prefix
|
||||
}
|
||||
|
||||
if test $# -eq 0; then
|
||||
|
|
|
|||
181
bin/git-scp
181
bin/git-scp
|
|
@ -1,10 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
COLOR_RED() { test -t 1 && echo -n "$(tput setaf 1)"; }
|
||||
COLOR_GREEN() { test -t 1 && echo -n "$(tput setaf 2)"; }
|
||||
COLOR_YELLOW(){ test -t 1 && echo -n "$(tput setaf 3)"; }
|
||||
COLOR_BLUE() { test -t 1 && echo -n "$(tput setaf 4)"; }
|
||||
COLOR_RESET() { test -t 1 && echo -n "$(tput sgr 0)"; }
|
||||
COLOR_RED() { echo -en "\033[31m"; }
|
||||
COLOR_GREEN() { echo -en "\033[32m"; }
|
||||
COLOR_YELLOW(){ echo -en "\033[33m"; }
|
||||
COLOR_BLUE() { echo -en "\033[34m"; }
|
||||
COLOR_RESET() { echo -en "\033[0m"; }
|
||||
|
||||
function _test_git_scp()
|
||||
{
|
||||
|
|
@ -18,7 +18,7 @@ function _test_git_scp()
|
|||
function set_remote()
|
||||
{
|
||||
remote=$1
|
||||
if [ "$(git remote | grep -c -- "^$remote$")" -eq 0 ]
|
||||
if [ $(git remote | grep -c -- ^$remote$) -eq 0 ]
|
||||
then
|
||||
COLOR_RED
|
||||
echo "Remote $remote does not exist in your git config"
|
||||
|
|
@ -39,9 +39,8 @@ function php_lint()
|
|||
test ! -f "$i" && continue
|
||||
case "$i" in
|
||||
*\.php|*\.phtml)
|
||||
if ! php -l "$i" > /dev/null; then
|
||||
error_count[${#error_count[@]}]="$i"
|
||||
fi
|
||||
php -l "$i" > /dev/null
|
||||
[ $? -gt 0 ] && error_count[${#error_count[@]}]="$i"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -59,17 +58,17 @@ function php_lint()
|
|||
|
||||
function _dos2unix()
|
||||
{
|
||||
command -v dos2unix > /dev/null && dos2unix "$@"
|
||||
command -v dos2unix > /dev/null && dos2unix $@
|
||||
return 0
|
||||
}
|
||||
|
||||
function _sanitize()
|
||||
{
|
||||
git config --get-all extras.scp.sanitize | while read -r i
|
||||
git config --get-all extras.scp.sanitize | while read i
|
||||
do
|
||||
case $i in
|
||||
php_lint) php_lint "$@";; # git config --global --add extras.scp.sanitize php_lint
|
||||
dos2unix) _dos2unix "$@";; # git config --global --add extras.scp.sanitize dos2unix
|
||||
php_lint) php_lint $@;; # git config --global --add extras.scp.sanitize php_lint
|
||||
dos2unix) _dos2unix $@;; # git config --global --add extras.scp.sanitize dos2unix
|
||||
esac
|
||||
done
|
||||
return $?
|
||||
|
|
@ -77,26 +76,14 @@ function _sanitize()
|
|||
|
||||
function scp_and_stage
|
||||
{
|
||||
local verbose=0 interactive=0 dry_run=0
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
-v|--verbose) verbose=1; shift;;
|
||||
-i|--interactive) verbose=1; interactive=1; shift;;
|
||||
-n|--dry-run) verbose=1; dry_run=1; shift;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
|
||||
set_remote "$1"
|
||||
set_remote $1
|
||||
shift
|
||||
|
||||
local refhead
|
||||
refhead="$(git rev-parse --quiet --verify "$1")"
|
||||
local refhead="$(git rev-parse --quiet --verify $1)"
|
||||
if [ -n "$refhead" ]
|
||||
then
|
||||
shift
|
||||
[ "$(git branch --contains "$refhead" | grep -c '\*')" -eq 0 ] &&
|
||||
[ $(git branch --contains "$refhead" | grep -c '\*') -eq 0 ] &&
|
||||
_error "refhead provided is not part of current branch"
|
||||
fi
|
||||
|
||||
|
|
@ -105,117 +92,46 @@ function scp_and_stage
|
|||
list=$(git ls-files "$@")" "$(git ls-files -o "$@")
|
||||
elif [ -n "$refhead" ]
|
||||
then
|
||||
[ "$verbose" -eq 1 ] && git --no-pager diff --stat "$refhead"
|
||||
list=$(git diff "$refhead" --name-only)
|
||||
git diff --stat $refhead
|
||||
list=$(git diff $refhead --name-only)
|
||||
else
|
||||
[ "$verbose" -eq 1 ] && git --no-pager diff
|
||||
git diff
|
||||
list=$(git diff --name-only)
|
||||
fi
|
||||
|
||||
deleted=$(for i in $list; do [ -f "$i" ] || echo "$i"; done)
|
||||
list=$(for i in $list; do [ -f "$i" ] && echo "$i"; done)
|
||||
|
||||
if [ "$interactive" -eq 1 ] && [ "$dry_run" -eq 0 ] && { [ -n "$list" ] || [ -n "$deleted" ]; }
|
||||
then
|
||||
local reply
|
||||
read -r -p "Proceed with sync to $remote? [y/N] " reply
|
||||
case "$reply" in
|
||||
y|Y|yes|YES) ;;
|
||||
*) _info "Aborted, nothing synced."; exit 0;;
|
||||
esac
|
||||
fi
|
||||
|
||||
local status=0
|
||||
deleted=$(for i in $list; do [ -f $i ] || echo $i; done)
|
||||
list=$(for i in $list; do [ -f $i ] && echo $i; done)
|
||||
|
||||
if [ -n "$list" ]
|
||||
then
|
||||
local _TMP=${0///}
|
||||
# shellcheck disable=SC2086
|
||||
echo "$list" > "$_TMP"
|
||||
if [ "$dry_run" -eq 1 ] || _sanitize $list
|
||||
then
|
||||
_info "Pushing to $remote ($(git config "remote.$remote.url"))"
|
||||
if [ "$dry_run" -eq 1 ]
|
||||
then
|
||||
rsync -rlDvn --files-from="$_TMP" ./ "$(git config "remote.$remote.url")/"
|
||||
else
|
||||
rsync -rlDv --files-from="$_TMP" ./ "$(git config "remote.$remote.url")/" &&
|
||||
git add --force $list
|
||||
fi
|
||||
status=$?
|
||||
else
|
||||
status=1
|
||||
fi
|
||||
rm "$_TMP"
|
||||
echo "$list" > $_TMP &&
|
||||
_sanitize $list &&
|
||||
_info Pushing to $remote \($(git config remote.$remote.url)\) &&
|
||||
rsync -rlDv --files-from=$_TMP ./ "$(git config remote.$remote.url)/" &&
|
||||
git add --force $list &&
|
||||
rm $_TMP
|
||||
fi
|
||||
|
||||
deleted=$(for i in $deleted; do echo "$(git config "remote.$remote.url" | cut -d: -f2)/$i"; done)
|
||||
deleted=$(for i in $deleted; do echo $(git config remote.$remote.url | cut -d: -f2)/$i; done)
|
||||
|
||||
if [ -n "$deleted" ]
|
||||
then
|
||||
COLOR_RED
|
||||
echo Deleted remote files
|
||||
if [ "$dry_run" -eq 1 ]
|
||||
then
|
||||
echo "$deleted"
|
||||
else
|
||||
if ssh "$(git config "remote.$remote.url" | cut -d: -f1)" -t "rm $deleted"
|
||||
then
|
||||
echo "$deleted"
|
||||
else
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
COLOR_RESET
|
||||
fi
|
||||
|
||||
return "$status"
|
||||
[ -n "$deleted" ] &&
|
||||
COLOR_RED &&
|
||||
echo Deleted remote files &&
|
||||
ssh $(git config remote.$remote.url | cut -d: -f1) -t "rm $deleted" &&
|
||||
echo "$deleted"
|
||||
COLOR_RESET
|
||||
}
|
||||
|
||||
function reverse_scp()
|
||||
{
|
||||
local verbose=0 interactive=0 dry_run=0
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
-v|--verbose) verbose=1; shift;;
|
||||
-i|--interactive) verbose=1; interactive=1; shift;;
|
||||
-n|--dry-run) verbose=1; dry_run=1; shift;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
|
||||
set_remote "$1"
|
||||
set_remote $1
|
||||
shift
|
||||
|
||||
local _TMP=${0///}
|
||||
echo "$@" > "$_TMP"
|
||||
|
||||
local status=0
|
||||
if [ "$verbose" -eq 1 ]
|
||||
then
|
||||
rsync -rlDvn --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./
|
||||
status=$?
|
||||
fi
|
||||
|
||||
if [ "$dry_run" -eq 1 ]
|
||||
then
|
||||
rm "$_TMP"
|
||||
return "$status"
|
||||
fi
|
||||
|
||||
if [ "$interactive" -eq 1 ]
|
||||
then
|
||||
local reply
|
||||
read -r -p "Proceed with copy from $remote? [y/N] " reply
|
||||
case "$reply" in
|
||||
y|Y|yes|YES) ;;
|
||||
*) _info "Aborted, nothing copied."; rm "$_TMP"; exit 0;;
|
||||
esac
|
||||
fi
|
||||
|
||||
rsync -rlDv --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./ &&
|
||||
rm "$_TMP"
|
||||
echo $@ > $_TMP &&
|
||||
rsync -rlDv --files-from=$_TMP "$(git config remote.$remote.url)/" ./ &&
|
||||
rm $_TMP
|
||||
}
|
||||
|
||||
function _info()
|
||||
|
|
@ -229,18 +145,13 @@ function _usage()
|
|||
{
|
||||
echo "Usage:
|
||||
git scp -h|help|?
|
||||
git scp [options] <remote> [ref|file..] # scp and stage your files to specified remote
|
||||
git scp [options] <remote> [<ref>] # show diff relative to <ref> and upload unstaged files to <remote>
|
||||
git rscp [options] <remote> [<file|directory>] # copy <remote> files to current working directory
|
||||
|
||||
OPTIONS:
|
||||
-v, --verbose print what will be synced before syncing
|
||||
-i, --interactive prompt for confirmation before syncing (implies --verbose)
|
||||
-n, --dry-run show what would be synced, change nothing (implies --verbose; no staging, no remote writes/deletes)
|
||||
git scp <remote> [ref|file..] # scp and stage your files to specified remote
|
||||
git scp <remote> [<ref>] # show diff relative to <ref> and upload unstaged files to <remote>
|
||||
git rscp <remote> [<file|directory>] # copy <remote> files to current working directory
|
||||
"
|
||||
|
||||
case $1 in
|
||||
-v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' "$0" ;;
|
||||
-v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' $0;;
|
||||
esac
|
||||
exit
|
||||
}
|
||||
|
|
@ -250,18 +161,18 @@ function _error()
|
|||
[ $# -eq 0 ] && _usage && exit 0
|
||||
|
||||
echo
|
||||
echo "ERROR: $*"
|
||||
echo ERROR: "$@"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
### OPTIONS ###
|
||||
case $(basename "$0") in
|
||||
case $(basename $0) in
|
||||
git-scp)
|
||||
case $1 in
|
||||
''|-h|'?'|help|--help) shift; _test_git_scp; _usage "$@";;
|
||||
*) scp_and_stage "$@";;
|
||||
''|-h|?|help|--help) shift; _test_git_scp; _usage $@;;
|
||||
*) scp_and_stage $@;;
|
||||
esac
|
||||
;;
|
||||
git-rscp) reverse_scp "$@";;
|
||||
git-rscp) reverse_scp $@;;
|
||||
esac
|
||||
|
|
|
|||
104
bin/git-sed
104
bin/git-sed
|
|
@ -1,104 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: git sed [ -c ] [ -f <flags> ] <search> <replacement> [ <flags> ]
|
||||
|
||||
Run git grep and then send results to sed for replacement with the
|
||||
given flags, if they are provided via -f or as the third argument.
|
||||
|
||||
Also runs git commit if -c is provided.
|
||||
EOF
|
||||
}
|
||||
|
||||
# don't commit by default
|
||||
do_commit() {
|
||||
true
|
||||
}
|
||||
|
||||
pathspec=
|
||||
while [ "$1" != "" ]; do
|
||||
case "$1" in
|
||||
-c|--commit)
|
||||
if git status --porcelain | grep .; then
|
||||
echo "you need to commit your changes before running with --commit"
|
||||
exit 1
|
||||
fi
|
||||
do_commit() {
|
||||
git commit -m"replace $search with $replacement
|
||||
|
||||
actual command:
|
||||
|
||||
$command" -a
|
||||
}
|
||||
;;
|
||||
-f|--flags)
|
||||
if [ "$2" = "" ]; then
|
||||
usage
|
||||
echo "missing argument for $1"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
flags=$1
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
--)
|
||||
pathspec="$*"
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
echo "unknown flag: $1"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ "$search" = "" ]; then
|
||||
search="$1"
|
||||
elif [ "$replacement" = "" ]; then
|
||||
replacement="$1"
|
||||
elif [ "$flags" = "" ]; then
|
||||
flags="$1"
|
||||
else
|
||||
usage
|
||||
echo "too many arguments: $1"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
all="$search$replacement$flags"
|
||||
case "$all" in
|
||||
*/*)
|
||||
ascii="$(for((i=32;i<=127;i++)) do printf '%b' "\\$(printf '%03o' "$i")"; done)"
|
||||
escaped="${all//-/\\-}"
|
||||
escaped="${escaped//[/\\[}"
|
||||
sep="$(printf '%s' "$ascii" | tr -d "$escaped")"
|
||||
sep="$(printf %.1s "$sep")"
|
||||
if [ "$sep" = "" ] ; then
|
||||
echo 'could not find an unused character for sed separator character'
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
sep=/
|
||||
;;
|
||||
esac
|
||||
|
||||
r=$(xargs -r false < /dev/null > /dev/null 2>&1 && echo r)
|
||||
need_bak=$(sed -i s/hello/world/ "$(git_extra_mktemp)" > /dev/null 2>&1 || echo true)
|
||||
|
||||
if [ "$need_bak" ]; then
|
||||
command="git grep -lz '$search' $pathspec | xargs -0$r sed -i '' 's$sep$search$sep$replacement$sep$flags'"
|
||||
# shellcheck disable=SC2086
|
||||
git grep -lz "$search" $pathspec | xargs -0"$r" sed -i '' "s$sep$search$sep$replacement$sep$flags"
|
||||
else
|
||||
command="git grep -lz '$search' $pathspec | xargs -0$r sed -i 's$sep$search$sep$replacement$sep$flags'"
|
||||
# shellcheck disable=SC2086
|
||||
git grep -lz "$search" $pathspec | xargs -0"$r" sed -i "s$sep$search$sep$replacement$sep$flags"
|
||||
fi
|
||||
do_commit
|
||||
|
|
@ -1,12 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
COMMIT_MESSAGE='Initial commit'
|
||||
|
||||
if [ "$1" = "-m" ]; then
|
||||
COMMIT_MESSAGE=$2
|
||||
shift; shift
|
||||
fi
|
||||
|
||||
gitdirexists(){
|
||||
if [ -d ".git" ]; then
|
||||
echo ".git directory already exists, aborting"
|
||||
|
|
@ -20,4 +13,4 @@ mkdir -p "$dir" \
|
|||
&& gitdirexists \
|
||||
&& git init \
|
||||
&& git add . \
|
||||
&& git commit --allow-empty -m "$COMMIT_MESSAGE"
|
||||
&& git commit --allow-empty -m 'Initial commit'
|
||||
|
|
|
|||
2
bin/git-show-merged-branches
Executable file → Normal file
2
bin/git-show-merged-branches
Executable file → Normal file
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
git branch --no-color --merged | grep -v "\*" | grep -v "$(git_extra_default_branch)" | tr -d ' '
|
||||
git branch --no-color --merged | grep -v "\*" | grep -v master | tr -d ' '
|
||||
|
|
|
|||
2
bin/git-show-unmerged-branches
Executable file → Normal file
2
bin/git-show-unmerged-branches
Executable file → Normal file
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
git branch --no-color --no-merged | grep -v "\*" | grep -v "$(git_extra_default_branch)" | tr -d ' '
|
||||
git branch --no-color --no-merged | grep -v "\*" | grep -v master | tr -d ' '
|
||||
|
|
|
|||
|
|
@ -1,26 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SQUASH_MSG=
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--squash-msg)
|
||||
SQUASH_MSG=1
|
||||
;;
|
||||
*)
|
||||
# set the argument back
|
||||
set -- "$@" "$arg"
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
src="$1"
|
||||
msg="$2"
|
||||
if [[ -n "$msg" ]] && [[ -n "$SQUASH_MSG" ]]; then
|
||||
>&2 echo "When commit message is given, --squash-msg is not allowed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
is_branch() {
|
||||
git show-ref --verify --quiet "refs/heads/$src"
|
||||
|
|
@ -31,8 +12,7 @@ is_commit_reference() {
|
|||
}
|
||||
|
||||
is_on_current_branch() {
|
||||
local commit_sha
|
||||
commit_sha=$(git rev-parse "$src")
|
||||
local commit_sha=`git rev-parse "$src"`
|
||||
git rev-list HEAD |
|
||||
grep -q -- "$commit_sha"
|
||||
}
|
||||
|
|
@ -43,8 +23,8 @@ commit_if_msg_provided() {
|
|||
fi
|
||||
}
|
||||
|
||||
prompt_continuation_if_squashing_default_branch() {
|
||||
if [[ $src == $(git_extra_default_branch) ]]; then
|
||||
prompt_continuation_if_squashing_master() {
|
||||
if [[ $src =~ ^master$ ]]; then
|
||||
read -p "Warning: squashing '$src'! Continue [y/N]? " -r
|
||||
if ! [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Exiting"
|
||||
|
|
@ -54,26 +34,19 @@ prompt_continuation_if_squashing_default_branch() {
|
|||
}
|
||||
|
||||
squash_branch() {
|
||||
prompt_continuation_if_squashing_default_branch
|
||||
if [ -n "$SQUASH_MSG" ]; then
|
||||
base=$(git merge-base "$src" @)
|
||||
msg=$(git log "$base".."$src" --format="%s%n%n%b" --no-merges --reverse)
|
||||
fi
|
||||
prompt_continuation_if_squashing_master
|
||||
git merge --squash "$src" || exit 1 # quits if `git merge` fails
|
||||
commit_if_msg_provided
|
||||
}
|
||||
|
||||
squash_current_branch() {
|
||||
if [ -n "$SQUASH_MSG" ]; then
|
||||
msg=$(git log "$src"..@ --format="%s%n%n%b" --no-merges --reverse)
|
||||
fi
|
||||
git reset --soft "$src" || exit 1 # quits if `git reset` fails
|
||||
commit_if_msg_provided
|
||||
}
|
||||
|
||||
if is_branch; then
|
||||
if `is_branch`; then
|
||||
squash_branch
|
||||
elif is_commit_reference && is_on_current_branch; then
|
||||
elif `is_commit_reference` && `is_on_current_branch`; then
|
||||
squash_current_branch
|
||||
else
|
||||
echo "Source branch or commit reference required." 1>&2 && exit 1
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
init_variables() {
|
||||
COMMAND=${0#*-}
|
||||
|
||||
REPLACE=false
|
||||
unset ID
|
||||
unset MSG
|
||||
}
|
||||
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
usage: git ${COMMAND} [<options>] <id> [<messages>]
|
||||
|
||||
Options:
|
||||
-r, --replace replace all previous stamps with same id
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
error() {
|
||||
if [[ -n "$1" ]]; then
|
||||
echo "error: $1" >&2
|
||||
fi
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
stamp() {
|
||||
local commit_msg=$( git log -1 --pretty=%B )
|
||||
local stamp_msg
|
||||
[[ -n "${MSG}" ]] && stamp_msg="${ID} ${MSG}" || stamp_msg="${ID}"
|
||||
|
||||
if ${REPLACE}; then
|
||||
# remove previous stamps with same ID from the commit message
|
||||
commit_msg=$(
|
||||
echo "${commit_msg}" \
|
||||
| grep --ignore-case --invert-match "^${ID}\b" \
|
||||
| cat -s
|
||||
)
|
||||
fi
|
||||
|
||||
# append the stamp to the commit message in a new paragraph
|
||||
git commit --amend \
|
||||
--message "${commit_msg}" \
|
||||
--message "${stamp_msg}" \
|
||||
> /dev/null
|
||||
|
||||
# show result
|
||||
git log -1 --pretty=full
|
||||
}
|
||||
|
||||
|
||||
parse_options() {
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--replace|-r)
|
||||
REPLACE=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
ID="$1"
|
||||
MSG="${*:2}"
|
||||
}
|
||||
|
||||
|
||||
validate_options() {
|
||||
# ID should be set to non-empty string
|
||||
if [[ -z "${ID}" ]]; then
|
||||
error "missing stamp identifier"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
init_variables
|
||||
parse_options "$@"
|
||||
validate_options
|
||||
|
||||
stamp
|
||||
301
bin/git-standup
301
bin/git-standup
|
|
@ -1,301 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Code modified from https://github.com/kamranahmedse/git-standup,
|
||||
# under the MIT LICENSE.
|
||||
usage() {
|
||||
cat <<EOS
|
||||
Usage:
|
||||
git standup [-a <author name>] [-w <weekstart-weekend>|-d <days-ago>] [-m <max-dir-depth>] [-D date-format] [-L] [-h] [-f] [-B] [-n <number-of-commits] [-F <gpg|authordate>]
|
||||
|
||||
-a - Specify author to restrict search to, default to current git user.
|
||||
Use "-a all" if you don't want the restriction.
|
||||
-w - Specify weekday range to limit search to
|
||||
-m - Specify the depth of recursive directory search
|
||||
-L - Toggle inclusion of symbolic links in recursive directory search
|
||||
-d - Specify the number of days back to include
|
||||
-D - Specify the date format for "git log" (default: relative)
|
||||
-h - Display this help screen
|
||||
-f - Fetch the latest commits beforehand
|
||||
-B - Display the commits in branch groups
|
||||
-n - Limit the number of commits displayed per group
|
||||
-F gpg - Show if commit is GPG signed (G) or not (N)
|
||||
-F authordate - Print author date instead of commit date
|
||||
|
||||
Examples:
|
||||
git standup -a "John Doe" -w "MON-FRI" -m 3
|
||||
EOS
|
||||
}
|
||||
|
||||
warn() {
|
||||
>&2 echo "${BOLD}${RED}WARNING: $1${NORMAL}"
|
||||
}
|
||||
|
||||
git rev-parse --show-toplevel > /dev/null 2>&1
|
||||
in_git_repo=$?
|
||||
|
||||
# Use colors, but only if connected to a terminal, and that terminal
|
||||
# supports them.
|
||||
if command -v tput &>/dev/null; then
|
||||
ncolors=$(tput colors)
|
||||
fi
|
||||
if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
|
||||
RED="$(tput setaf 1)"
|
||||
GREEN="$(tput setaf 2)"
|
||||
YELLOW="$(tput setaf 3)"
|
||||
BOLD="$(tput bold)"
|
||||
NORMAL="$(tput sgr0)"
|
||||
BOLD=$(tput bold)
|
||||
UNDERLINE=$(tput smul)
|
||||
NORMAL=$(tput sgr0)
|
||||
COLOR=always
|
||||
else
|
||||
RED=""
|
||||
GREEN=""
|
||||
YELLOW=""
|
||||
BOLD=""
|
||||
NORMAL=""
|
||||
BOLD=""
|
||||
UNDERLINE=""
|
||||
NORMAL=""
|
||||
COLOR=never
|
||||
fi
|
||||
|
||||
# Only enable exit-on-error after the non-critical colorization stuff,
|
||||
# which may fail on systems lacking tput or terminfo
|
||||
set -e
|
||||
|
||||
RANGE_SPECIFIED=
|
||||
COMMIT_DATE_FORMAT=%cd
|
||||
USE_GPG_FORMAT=no
|
||||
|
||||
while getopts "hgfF:Bd:a:w:m:D:n:L" opt; do
|
||||
case $opt in
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
a)
|
||||
if [[ "$OPTARG" = 'all' ]] ; then
|
||||
AUTHOR=".*"
|
||||
else
|
||||
AUTHOR="$OPTARG"
|
||||
fi
|
||||
;;
|
||||
d)
|
||||
test -n "$RANGE_SPECIFIED" && warn "-d option is conflict with -w"
|
||||
RANGE_SPECIFIED=yes
|
||||
if [ "$OPTARG" -lt 1 ]; then
|
||||
>&2 echo "Specify days less than one is invalid"
|
||||
exit 1
|
||||
fi
|
||||
SINCE="$OPTARG days ago"
|
||||
;;
|
||||
w)
|
||||
if [ -n "$RANGE_SPECIFIED" ]; then
|
||||
warn "-w option is conflict with -d"
|
||||
continue
|
||||
fi
|
||||
RANGE_SPECIFIED=yes
|
||||
|
||||
week_range=${OPTARG}
|
||||
week_start="${week_range%%-*}"
|
||||
week_start="${week_start:="Mon"}"
|
||||
week_end="${week_range##*-}"
|
||||
week_end=${week_end:="Fri"}
|
||||
|
||||
## In case it is the start of week, we need to
|
||||
## show the commits since the last weekend
|
||||
shopt -s nocasematch
|
||||
if [[ "$week_start" == "$(LC_ALL=C date +%a)" ]] ; then
|
||||
SINCE="last $week_end";
|
||||
fi
|
||||
;;
|
||||
f)
|
||||
FETCH_LAST_COMMIT=true
|
||||
;;
|
||||
m)
|
||||
MAXDEPTH=$((OPTARG + 1))
|
||||
if [ "$MAXDEPTH" -lt 1 ]; then
|
||||
>&2 echo "Specify depth less than one is invalid"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
L)
|
||||
INCLUDE_LINKS=-L
|
||||
;;
|
||||
D)
|
||||
GIT_DATE_FORMAT=${OPTARG}
|
||||
;;
|
||||
g)
|
||||
warn "-g option is deprecated, use '-F gpg' instead"
|
||||
USE_GPG_FORMAT=yes
|
||||
;;
|
||||
B)
|
||||
GROUP_BY_BRANCHES=true
|
||||
;;
|
||||
n)
|
||||
MAX_COMMIT_NUM=${OPTARG}
|
||||
;;
|
||||
F)
|
||||
case $OPTARG in
|
||||
gpg)
|
||||
USE_GPG_FORMAT=yes
|
||||
;;
|
||||
authordate)
|
||||
COMMIT_DATE_FORMAT=%ad
|
||||
;;
|
||||
*)
|
||||
warn "Invalid argument for -F: $OPTARG"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
\?)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
warn "please upgrade to new-style interface. Run 'git help standup' to get more info."
|
||||
if [[ $# -gt 3 ]] ; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
AUTHOR=$1
|
||||
SINCE=$2
|
||||
UNTIL=$3
|
||||
fi
|
||||
|
||||
AUTHOR=${AUTHOR:="$(git config user.name || echo '')"}
|
||||
if [ -z "${AUTHOR}" ]; then
|
||||
warn "please configure an author with 'git config user.name' or specify an author via '-a'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FETCH_LAST_COMMIT=${FETCH_LAST_COMMIT:=false}
|
||||
MAXDEPTH=${MAXDEPTH:=2}
|
||||
GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen(${COMMIT_DATE_FORMAT}) %C(bold blue)<%an>%Creset"
|
||||
GIT_DATE_FORMAT=${GIT_DATE_FORMAT:=relative}
|
||||
if [[ "$USE_GPG_FORMAT" == 'yes' ]]; then
|
||||
GIT_PRETTY_FORMAT="$GIT_PRETTY_FORMAT %C(yellow)gpg: %G?%Creset"
|
||||
fi
|
||||
|
||||
# Handle config of implicit week
|
||||
IMPLICIT_WEEK=$(git config --get git-extras.standup.implicit-week || echo '')
|
||||
if [[ -z "$RANGE_SPECIFIED" ]] && [[ -n "${IMPLICIT_WEEK}" ]]; then
|
||||
week_start=${IMPLICIT_WEEK%%-*}
|
||||
week_end=${IMPLICIT_WEEK##*-}
|
||||
shopt -s nocasematch
|
||||
if [[ "$week_start" == "$(LC_ALL=C date +%a)" ]]; then
|
||||
SINCE="last $week_end"
|
||||
fi
|
||||
UNTIL=today
|
||||
else
|
||||
SINCE=${SINCE:=yesterday}
|
||||
UNTIL=${UNTIL:=today}
|
||||
fi
|
||||
|
||||
GIT_LOG_COMMAND="git --no-pager log \
|
||||
--no-merges
|
||||
--since \"$SINCE\"
|
||||
--until \"$UNTIL\"
|
||||
--author=\"$AUTHOR\"
|
||||
--abbrev-commit
|
||||
--oneline
|
||||
--color=$COLOR
|
||||
--pretty=format:'$GIT_PRETTY_FORMAT'
|
||||
--date='$GIT_DATE_FORMAT'"
|
||||
if [[ -n "$MAX_COMMIT_NUM" ]]; then
|
||||
GIT_LOG_COMMAND="$GIT_LOG_COMMAND --max-count=$MAX_COMMIT_NUM"
|
||||
fi
|
||||
|
||||
git_output() {
|
||||
if [ "$GROUP_BY_BRANCHES" = true ]; then
|
||||
local branches
|
||||
branches=$(git branch --sort=-committerdate | awk '{print substr($0, 3)}')
|
||||
for branch in $branches; do
|
||||
# shellcheck disable=SC2086
|
||||
if output=$(eval $GIT_LOG_COMMAND "$branch"); then
|
||||
if [[ -n "$output" ]] ; then
|
||||
echo "${GREEN}${branch}${NORMAL}"
|
||||
echo "$output"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
# TODO optimize:return if the latest commit of a branch is earlier than the 'since' day
|
||||
done
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
eval $GIT_LOG_COMMAND --all
|
||||
fi
|
||||
}
|
||||
|
||||
## For when the command has been run in a non-repo directory
|
||||
if [[ $in_git_repo != 0 ]]; then
|
||||
## Set delimiter to newline for the loop
|
||||
IFS=$'\n'
|
||||
## Recursively search for git repositories
|
||||
# shellcheck disable=SC2086
|
||||
PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git)
|
||||
|
||||
# Fetch the latest commits, if required
|
||||
if [ "$FETCH_LAST_COMMIT" = true ]; then
|
||||
|
||||
echo "${BOLD}${GREEN}Fetching commits ..${NORMAL}"
|
||||
|
||||
# Foreach of the project directories, fetch the commits
|
||||
for DIR in $PROJECT_DIRS; do
|
||||
DIR="$(dirname "$DIR")"
|
||||
pushd "$DIR" > /dev/null
|
||||
|
||||
if [[ -d ".git" ]] ; then
|
||||
echo " ${YELLOW}$(basename "$DIR")${NORMAL}"
|
||||
git fetch --all > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
done
|
||||
fi
|
||||
|
||||
# Get the standup details for each of the projects
|
||||
for DIR in $PROJECT_DIRS; do
|
||||
DIR="$(dirname "$DIR")"
|
||||
pushd "$DIR" > /dev/null
|
||||
## Show the detail only if it is a git repository
|
||||
if [[ -d ".git" || -f ".git" ]] ; then
|
||||
if GITOUT=$(git_output); then
|
||||
## Only output if there is some activities
|
||||
if [[ -n "$GITOUT" ]] ; then
|
||||
echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}"
|
||||
echo "$GITOUT"
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
echo "Repository under $DIR could not be queried." >&2
|
||||
fi
|
||||
fi
|
||||
popd > /dev/null
|
||||
done
|
||||
else
|
||||
if [ "$FETCH_LAST_COMMIT" = true ]; then
|
||||
echo "${GREEN}Fetching commits ..${NORMAL}"
|
||||
git fetch --all > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if GITOUT=$(git_output); then
|
||||
if [[ -n "$GITOUT" ]] ; then
|
||||
echo "$GITOUT"
|
||||
else
|
||||
if [[ $AUTHOR = '.*' ]] ; then
|
||||
AUTHOR="all the contributors"
|
||||
fi
|
||||
|
||||
echo "${YELLOW}Seems like $AUTHOR did nothing!${NORMAL}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
247
bin/git-summary
247
bin/git-summary
|
|
@ -1,81 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
source "$(git --exec-path)/git-sh-setup"
|
||||
cd_to_toplevel
|
||||
|
||||
cd "$(git root)" || { echo "Can't cd to top level directory";exit 1; }
|
||||
|
||||
PROJECT_FULL_PATH=
|
||||
SUMMARY_BY_LINE=
|
||||
DEDUP_BY_EMAIL=
|
||||
MERGES_ARG=
|
||||
OUTPUT_STYLE=
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--full-path)
|
||||
PROJECT_FULL_PATH=1
|
||||
;;
|
||||
--line)
|
||||
SUMMARY_BY_LINE=1
|
||||
;;
|
||||
--dedup-by-email)
|
||||
DEDUP_BY_EMAIL=1
|
||||
;;
|
||||
--no-merges)
|
||||
MERGES_ARG="--no-merges"
|
||||
;;
|
||||
--output-style)
|
||||
OUTPUT_STYLE="$2"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
>&2 echo "unknown argument $arg found"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
# set the argument back
|
||||
set -- "$@" "$arg"
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -n "$DEDUP_BY_EMAIL" ] && [ -n "$SUMMARY_BY_LINE" ]; then
|
||||
>&2 echo "--dedup-by-email used with --line is not supported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$MERGES_ARG" ] && [ -n "$SUMMARY_BY_LINE" ]; then
|
||||
>&2 echo "--no-merges used with --line is not supported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
commit="HEAD"
|
||||
if [ -n "$SUMMARY_BY_LINE" ]; then
|
||||
paths=( "$@" )
|
||||
else
|
||||
[ $# -ne 0 ] && commit=$*
|
||||
fi
|
||||
|
||||
if [[ -n "$PROJECT_FULL_PATH" ]]; then
|
||||
project=${PWD/${HOME}/\~}
|
||||
else
|
||||
project=${PWD##*/}
|
||||
fi
|
||||
commit=""
|
||||
test $# -ne 0 && commit=$@
|
||||
project=${PWD##*/}
|
||||
|
||||
#
|
||||
# get date for the given <commit>
|
||||
#
|
||||
commit_date() {
|
||||
# the $1 can be empty
|
||||
# shellcheck disable=SC2086
|
||||
git log $MERGES_ARG --pretty='format: %ai' "$1" | cut -d ' ' -f 2
|
||||
|
||||
date() {
|
||||
git log --pretty='format: %ai' $1 | cut -d ' ' -f 2
|
||||
}
|
||||
|
||||
#
|
||||
# get active days for the given <commit>
|
||||
#
|
||||
|
||||
active_days() {
|
||||
commit_date "$1" | sort -r | uniq | awk '
|
||||
date $1 | uniq | awk '
|
||||
{ sum += 1 }
|
||||
END { print sum }
|
||||
'
|
||||
|
|
@ -84,176 +30,59 @@ active_days() {
|
|||
#
|
||||
# get the commit total
|
||||
#
|
||||
|
||||
commit_count() {
|
||||
# shellcheck disable=SC2086
|
||||
git rev-list $MERGES_ARG --count "$commit"
|
||||
git log --oneline $commit | wc -l | tr -d ' '
|
||||
}
|
||||
|
||||
#
|
||||
# total file count
|
||||
#
|
||||
|
||||
file_count() {
|
||||
git ls-files | wc -l | tr -d ' '
|
||||
}
|
||||
|
||||
#
|
||||
# remove duplicate authors who belong to the same email address
|
||||
#
|
||||
dedup_by_email() {
|
||||
# in:
|
||||
# 27 luo zexuan <LuoZexuan@xxx.com>
|
||||
# 7 罗泽轩 <luozexuan@xxx.com>
|
||||
# out:
|
||||
# 34 luo zexuan
|
||||
LC_ALL=C awk '
|
||||
{
|
||||
sum += $1
|
||||
last_field = tolower($NF)
|
||||
if (last_field in emails) {
|
||||
emails[last_field] += $1
|
||||
} else {
|
||||
email = last_field
|
||||
emails[email] = $1
|
||||
# set commits/email to empty
|
||||
$1=$NF=""
|
||||
sub(/^[[:space:]]+/, "", $0)
|
||||
sub(/[[:space:]]+$/, "", $0)
|
||||
name = $0
|
||||
if (name in names) {
|
||||
# when the same name is associated with existed email,
|
||||
# merge the previous email into the later one.
|
||||
emails[email] += emails[names[name]]
|
||||
emails[names[name]] = 0
|
||||
}
|
||||
names[name] = email
|
||||
}
|
||||
}
|
||||
END {
|
||||
for (name in names) {
|
||||
email = names[name]
|
||||
printf "%6d\t%s\n", emails[email], name
|
||||
}
|
||||
}' | sort -rn -k 1
|
||||
}
|
||||
|
||||
#
|
||||
# list authors
|
||||
#
|
||||
format_authors() {
|
||||
# a rare unicode character is used as separator to avoid conflicting with
|
||||
# author name. However, Linux column utility will escape tab if separator
|
||||
# specified, so we do unesaping after it.
|
||||
LC_ALL=C awk '
|
||||
|
||||
authors() {
|
||||
git shortlog -n -s $commit | awk '
|
||||
{ args[NR] = $0; sum += $0 }
|
||||
END {
|
||||
for (i = 1; i <= NR; ++i) {
|
||||
printf "%s♪%2.1f%%\n", args[i], 100 * args[i] / sum
|
||||
printf "%s,%2.1f%%\n", args[i], 100 * args[i] / sum
|
||||
}
|
||||
}
|
||||
' | column -t -s♪ | sed "s/\\\x09/\t/g"
|
||||
' | column -t -s,
|
||||
}
|
||||
|
||||
#
|
||||
# fetch repository age from oldest commit
|
||||
#
|
||||
|
||||
repository_age() {
|
||||
git log --reverse --pretty=oneline --format="%ar" | head -n 1 | LC_ALL=C sed 's/ago//'
|
||||
git log --reverse --pretty=oneline --format="%ar" | head -n 1 | sed 's/ago//'
|
||||
}
|
||||
|
||||
#
|
||||
# fetch repository age of the latest commit
|
||||
#
|
||||
last_active() {
|
||||
git log --pretty=oneline --format="%ar" -n 1
|
||||
}
|
||||
# summary
|
||||
|
||||
#
|
||||
# list the last modified author for each line
|
||||
#
|
||||
single_file() {
|
||||
while read -r data
|
||||
do
|
||||
if [[ $(file "$data") = *text* ]]; then
|
||||
git blame --line-porcelain "$data" 2>/dev/null | grep "^author " | LC_ALL=C sed -n 's/^author //p';
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
current_branch_name() {
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
}
|
||||
|
||||
#
|
||||
# list the author for all file
|
||||
#
|
||||
lines() {
|
||||
git ls-files -- "$@" | single_file
|
||||
}
|
||||
|
||||
#
|
||||
# get the number of the lines
|
||||
#
|
||||
line_count() {
|
||||
lines "$@" | wc -l
|
||||
}
|
||||
|
||||
uncommitted_changes_count() {
|
||||
git status --porcelain | wc -l
|
||||
}
|
||||
|
||||
|
||||
COLUMN_CMD_DELIMTER="¬" # Hopefully, this symbol is not used in branch names... I use it as a separator for columns
|
||||
SP="$COLUMN_CMD_DELIMTER|"
|
||||
|
||||
print_summary_by_line() {
|
||||
if [ "$OUTPUT_STYLE" = "tabular" ]; then
|
||||
tabular_headers="# Repo $SP Lines"
|
||||
echo -e "$tabular_headers\n$project $SP $(line_count "${paths[@]}")" | column -t -s "$COLUMN_CMD_DELIMTER"
|
||||
elif [ "$OUTPUT_STYLE" = "oneline" ]; then
|
||||
echo "$project / lines: $(line_count "${paths[@]}")"
|
||||
elif [ -n "$SUMMARY_BY_LINE" ]; then
|
||||
echo
|
||||
echo " project : $project"
|
||||
echo " lines : $(line_count "${paths[@]}")"
|
||||
echo " authors :"
|
||||
lines "${paths[@]}" | sort | uniq -c | sort -rn | format_authors
|
||||
fi
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
if [ "$OUTPUT_STYLE" = "tabular" ]; then
|
||||
tabular_headers="# Repo $SP Age $SP Last active $SP Active on $SP Commits $SP Uncommitted $SP Branch"
|
||||
echo -e "$tabular_headers\n$project $SP $(repository_age) $SP $(last_active) $SP $(active_days "$commit") days $SP $(commit_count "$commit") $SP $(uncommitted_changes_count) $SP $(current_branch_name)" | column -t -s "$COLUMN_CMD_DELIMTER"
|
||||
elif [ "$OUTPUT_STYLE" = "oneline" ]; then
|
||||
echo "$project / age: $(repository_age) / last active: $(last_active) / active on $(active_days "$commit") days / commits: $(commit_count "$commit") / uncommitted: $(uncommitted_changes_count) / branch: $(current_branch_name)"
|
||||
else
|
||||
echo
|
||||
echo " project : $project"
|
||||
echo " repo age : $(repository_age)"
|
||||
echo " branch: : $(current_branch_name)"
|
||||
echo " last active : $(last_active)"
|
||||
echo " active on : $(active_days "$commit") days"
|
||||
echo " commits : $(commit_count "$commit")"
|
||||
|
||||
# The file count doesn't support passing a git ref so ignore it if a ref is given
|
||||
if [ "$commit" = "HEAD" ]; then
|
||||
echo " files : $(file_count)"
|
||||
fi
|
||||
echo " uncommitted : $(uncommitted_changes_count)"
|
||||
echo " authors : "
|
||||
if [ -n "$DEDUP_BY_EMAIL" ]; then
|
||||
# the $commit can be empty
|
||||
# shellcheck disable=SC2086
|
||||
git shortlog $MERGES_ARG -n -s -e "$commit" | dedup_by_email | format_authors
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
git shortlog $MERGES_ARG -n -s "$commit" | format_authors
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -n "$SUMMARY_BY_LINE" ]; then
|
||||
print_summary_by_line
|
||||
if test "$1" = "--line"; then
|
||||
git line-summary
|
||||
echo
|
||||
else
|
||||
print_summary
|
||||
|
||||
echo
|
||||
echo " project : $project"
|
||||
echo " repo age :" $(repository_age)
|
||||
echo " active :" $(active_days) days
|
||||
echo " commits :" $(commit_count)
|
||||
if test "$commit" = ""; then
|
||||
echo " files :" $(file_count)
|
||||
fi
|
||||
echo " authors : "
|
||||
authors
|
||||
echo
|
||||
|
||||
fi
|
||||
|
|
|
|||
101
bin/git-sync
101
bin/git-sync
|
|
@ -1,101 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function _usage()
|
||||
{
|
||||
local command="git sync"
|
||||
cat << EOS
|
||||
Usage:
|
||||
${command} [<remote> <branch>]
|
||||
${command} -h | --help
|
||||
${command} -s | --soft
|
||||
${command} -f | --force
|
||||
|
||||
Sync local branch with <remote>/<branch>.
|
||||
When <remote> and <branch> are not specified on the command line, upstream of local branch will be used by default.
|
||||
All changes and untracked files and directories will be removed unless you add -s(--soft).
|
||||
|
||||
Examples:
|
||||
Sync with upstream of local branch:
|
||||
${command}
|
||||
|
||||
Sync with origin/master:
|
||||
${command} origin master
|
||||
|
||||
Sync without cleaning untracked files:
|
||||
${command} -s
|
||||
|
||||
Sync without interaction:
|
||||
${command} -f
|
||||
EOS
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-h | --help)
|
||||
_usage
|
||||
exit
|
||||
;;
|
||||
-s | --soft)
|
||||
local soft="true"
|
||||
;;
|
||||
-f | --force)
|
||||
local force="YES"
|
||||
;;
|
||||
* )
|
||||
if [ "${remote}" = "" ]; then
|
||||
local remote="$1"
|
||||
elif [ "${branch}" = "" ]; then
|
||||
local branch="$1"
|
||||
else
|
||||
echo -e "Error: too many arguments.\n"
|
||||
_usage
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
local remote_branch
|
||||
if [ -z "${remote}" ]; then
|
||||
if ! remote_branch="$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null)"; then
|
||||
echo "There is no upstream information of local branch."
|
||||
exit 1
|
||||
fi
|
||||
local branch="$(git rev-parse --abbrev-ref --symbolic-full-name @)"
|
||||
local remote=$(git config "branch.${branch}.remote")
|
||||
elif [ -z "${branch}" ]; then
|
||||
echo -e "Error: too few arguments.\n"
|
||||
_usage
|
||||
exit 1
|
||||
else
|
||||
remote_branch="${remote}/${branch}"
|
||||
fi
|
||||
|
||||
if [ "${force}" != "YES" ]; then
|
||||
if [ "${soft}" = "true" ]; then
|
||||
echo -n "Are you sure you want to sync with '${remote_branch}'? [y/N]: "
|
||||
else
|
||||
echo -n "Are you sure you want to clean all changes & sync with '${remote_branch}'? [y/N]: "
|
||||
fi
|
||||
local force
|
||||
read -r force
|
||||
fi
|
||||
case "${force}" in
|
||||
"Y" | "y" | "yes" | "Yes" | "YES" )
|
||||
if [ "${soft}" = "true" ]; then
|
||||
git fetch "${remote}" "${branch}" && git reset --hard "${remote_branch}"
|
||||
else
|
||||
git fetch "${remote}" "${branch}" && git reset --hard "${remote_branch}" && git clean -d -f -x
|
||||
fi
|
||||
;;
|
||||
* )
|
||||
echo "Canceled."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
|
|
@ -1,13 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
USAGE(){
|
||||
echo "git touch <filename> [ <filename> . . .]" 1>&2
|
||||
exit 1
|
||||
}
|
||||
filename="$*"
|
||||
|
||||
test $# -lt 1 && USAGE
|
||||
test -z "$filename" && echo "filename required" 1>&2 && exit 1
|
||||
|
||||
for filename in "$@"; do
|
||||
touch "$filename" \
|
||||
&& git add "$filename"
|
||||
done
|
||||
touch "$filename" \
|
||||
&& git add "$filename"
|
||||
|
|
|
|||
69
bin/git-undo
69
bin/git-undo
|
|
@ -1,72 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cstr="commits"
|
||||
ccnt=$(git rev-list --count HEAD)
|
||||
if [ "$ccnt" -eq 1 ]; then cstr="commit"; fi
|
||||
parm3=""
|
||||
|
||||
function _undo()
|
||||
{
|
||||
type=${1:-soft}
|
||||
undo_cnt=${2:-1}
|
||||
reset=${3:-""}
|
||||
|
||||
if [ "$undo_cnt" -gt "$ccnt" ]; then
|
||||
echo "Only $ccnt $cstr, cannot undo $undo_cnt"
|
||||
elif [ "$type" = "hard" ] && [ "$ccnt" -eq "$undo_cnt" ]; then
|
||||
echo "Cannot hard undo all commits"
|
||||
elif [ "$type" = "soft" ] && [ "$ccnt" -eq 1 ]; then
|
||||
git update-ref -d HEAD
|
||||
else
|
||||
git reset "--$type" "HEAD~$undo_cnt"
|
||||
fi
|
||||
if [ "$reset" != "" ]; then git reset; fi
|
||||
}
|
||||
back="^"
|
||||
|
||||
case "$1" in
|
||||
-h)
|
||||
cat << EOL
|
||||
This will erase any changes since your last commit.
|
||||
If you want to get help info, run "git undo --help" instead.
|
||||
Do you want to continue? [yN]"
|
||||
EOL
|
||||
read -r res
|
||||
case "${res}" in
|
||||
"Y" | "y")
|
||||
parm1=hard
|
||||
parm2=${2:-1}
|
||||
;;
|
||||
* )
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--hard)
|
||||
parm1=hard
|
||||
parm2=${2:-1}
|
||||
-h|--hard)
|
||||
test $2 -gt 1 > /dev/null 2>&1 && back="~$2"
|
||||
git reset --hard HEAD$back && exit 0;
|
||||
;;
|
||||
-s|--soft)
|
||||
parm1=soft
|
||||
parm2=${2:-1}
|
||||
parm3=reset
|
||||
;;
|
||||
"")
|
||||
parm1=soft
|
||||
parm2=1
|
||||
;;
|
||||
*[!0-9]*)
|
||||
echo "Invalid parameter: $1"
|
||||
exit 1
|
||||
test $2 -gt 1 > /dev/null 2>&1 && back="~$2"
|
||||
;;
|
||||
*)
|
||||
parm1=soft
|
||||
parm2="$1"
|
||||
test $1 -gt 1 > /dev/null 2>&1 && back="~$1"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ ! $parm2 =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo "Invalid undo count: $parm2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
_undo "$parm1" "$parm2" "$parm3"
|
||||
git reset --soft HEAD$back
|
||||
git reset
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
filename="$1"
|
||||
test -z "$filename" && echo "filename required." 1>&2 && exit 1
|
||||
git update-index --no-skip-worktree "$filename"
|
||||
filename=$1
|
||||
test -z $filename && echo "filename required." 1>&2 && exit 1
|
||||
git update-index --no-skip-worktree $filename
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Based on scripts from git-utils:
|
||||
# * https://github.com/ddollar/git-utils/blob/master/git-unwip
|
||||
|
||||
# Check if the last commit is a 'WIP' commit
|
||||
LAST_COMMIT=`git log -1 --pretty=%B | tr -d '[:space:]'`
|
||||
|
||||
if [ 'WIP' != $LAST_COMMIT ]; then
|
||||
echo 'Last commit is not a WIP commit, so it will not be unWIP-ed.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git undo --soft
|
||||
131
bin/git-utimes
131
bin/git-utimes
|
|
@ -1,131 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Change files modification time to their last commit date
|
||||
#
|
||||
|
||||
# Bash unofficial strict mode
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
if [[ "${1:-}" == "--newer" ]]; then
|
||||
op=le
|
||||
shift
|
||||
else
|
||||
op=eq
|
||||
fi
|
||||
|
||||
# BSD systems
|
||||
if date -j &>/dev/null; then
|
||||
stat_flags="-f %m"
|
||||
date_flags="-r"
|
||||
else
|
||||
# Non-BSD systems
|
||||
stat_flags="-c %Y"
|
||||
date_flags="-d@"
|
||||
fi
|
||||
|
||||
bash_opts=()
|
||||
if bash --help 2>&1 | grep -q -- '--noprofile'; then
|
||||
bash_opts+=(--noprofile)
|
||||
fi
|
||||
if bash --help 2>&1 | grep -q -- '--norc'; then
|
||||
bash_opts+=(--norc)
|
||||
fi
|
||||
|
||||
status_opts=(--porcelain --short)
|
||||
# %ct: committer date, UNIX timestamp / %at: author date, UNIX timestamp
|
||||
log_opts=(--format='%ct')
|
||||
if git status --help 2>&1 | grep -q -- "--no-renames"; then
|
||||
status_opts+=(--no-renames)
|
||||
log_opts+=(--no-renames)
|
||||
fi
|
||||
if git status --help 2>&1 | grep -q -- "--untracked-files"; then
|
||||
status_opts+=(--untracked-files=no)
|
||||
fi
|
||||
if git status --help 2>&1 | grep -q -- "--ignored"; then
|
||||
status_opts+=(--ignored=no)
|
||||
fi
|
||||
|
||||
prefix="$(git rev-parse --show-prefix) "
|
||||
strip="${#prefix}"
|
||||
|
||||
tmpfile=$(mktemp)
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -f '${tmpfile}'" 0
|
||||
|
||||
awk_flags=(
|
||||
-F'\t'
|
||||
-v date_flags="${date_flags}"
|
||||
-v op="${op}"
|
||||
-v stat_flags="${stat_flags}"
|
||||
-v strip="${strip}"
|
||||
-v tmpfile="${tmpfile}"
|
||||
)
|
||||
|
||||
# sanity check, not required:
|
||||
if awk --help 2>&1 | grep -q -- '--posix'; then
|
||||
awk_flags+=(--posix)
|
||||
fi
|
||||
|
||||
read -r -d '' awk_script <<"EOF" || true
|
||||
BEGIN {
|
||||
seen[""]=1
|
||||
print "#!/usr/bin/env bash"
|
||||
print "set +e"
|
||||
print "t() {"
|
||||
print " test -e \"$2\" || return 0"
|
||||
printf(" test \"$(stat %s \"$2\" 2>/dev/null)\" -%s \"$1\" && return 0\n", stat_flags, op)
|
||||
if (date_flags == "-d@") {
|
||||
print " echo \"+ touch -h -d@$1 $2\""
|
||||
print " touch -h -d@$1 \"$2\""
|
||||
} else {
|
||||
print " t=$(date -r$1 \"+%Y%m%d%H%M.%S\")"
|
||||
print " echo \"+ touch -h -t $t $2\""
|
||||
print " touch -h -t $t \"$2\""
|
||||
}
|
||||
print "}"
|
||||
}
|
||||
FILENAME==tmpfile {
|
||||
skip[$1]=1
|
||||
next
|
||||
}
|
||||
# skip blank lines
|
||||
!/^$/ {
|
||||
# skip deletes
|
||||
if (substr($1, length($1), 1) ~ /D/) {
|
||||
next
|
||||
}
|
||||
if (NF == 1) {
|
||||
ct=$1
|
||||
next
|
||||
}
|
||||
$2 = substr($2, strip, length($2)- strip + 1)
|
||||
if ($2 in seen) {
|
||||
next
|
||||
}
|
||||
if ($2 in skip) {
|
||||
next
|
||||
}
|
||||
seen[$2]=1
|
||||
# remove enclosing double quotes that git adds:
|
||||
if (substr($2, 1, 1) == "\"" && substr($2, length($2), 1) == "\"") {
|
||||
$2 = substr($2, 2, length($2) - 2)
|
||||
# unescape remaining double quotes
|
||||
gsub(/\\"/, "\"", $2)
|
||||
# unescape escaped backslashes
|
||||
gsub(/\\\\/, "\\", $2)
|
||||
}
|
||||
# escape apostrophes: ' => '\''
|
||||
gsub(/'/, "'\\''", $2)
|
||||
printf("t %s '%s'\n", ct, $2)
|
||||
}
|
||||
EOF
|
||||
|
||||
# prefix is stripped:
|
||||
git --no-pager status "${status_opts[@]}" . \
|
||||
| cut -c 4- >"${tmpfile}"
|
||||
|
||||
# prefix is not stripped:
|
||||
git --no-pager log --raw --no-merges "${log_opts[@]}" . \
|
||||
| awk "${awk_flags[@]}" "${awk_script}" "${tmpfile}" - \
|
||||
| BASH_ENV='' bash "${bash_opts[@]}" -
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Based on scripts from git-utils:
|
||||
# * https://github.com/ddollar/git-utils/blob/master/git-wip
|
||||
# * https://github.com/ddollar/git-utils/blob/master/git-addremove
|
||||
|
||||
git add --all
|
||||
git commit --all --message="WIP"
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
diff --git a/bin/git-extras b/bin/git-extras
|
||||
index e49cd24..4ae28b5 100755
|
||||
--- a/bin/git-extras
|
||||
+++ b/bin/git-extras
|
||||
@@ -4,13 +4,12 @@ VERSION="4.3.0"
|
||||
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
|
||||
|
||||
update() {
|
||||
- local bin="$(command -v git-extras)"
|
||||
- local prefix=${bin%/*/*}
|
||||
- local orig=$PWD
|
||||
-
|
||||
- curl -s $INSTALL_SCRIPT | PREFIX="$prefix" bash /dev/stdin \
|
||||
- && cd "$orig" \
|
||||
- && echo "... updated git-extras $VERSION -> $(git extras --version)"
|
||||
+ echo "This git-extras installation is managed by Homebrew."
|
||||
+ echo "If you'd like to update git-extras, run the following:"
|
||||
+ echo
|
||||
+ echo " brew upgrade git-extras"
|
||||
+ echo
|
||||
+ return 1
|
||||
}
|
||||
|
||||
updateForWindows() {
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
err() {
|
||||
echo >&2 "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ! command -v column >/dev/null 2>&1; then
|
||||
err "Need to install dependency 'column' before installation (can be found in bsdmainutils)"
|
||||
fi
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue