Compare commits

..

301 commits
6.2.0 ... main

Author SHA1 Message Date
dependabot[bot] 4cf28ec559
chore(deps): bump actions/stale from 10 to 11 (#1265)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / Test with Bats (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-01 12:25:44 -07:00
罗泽轩 09b77e8ed6
ci: add generated manuals check (#1268)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / Test with Bats (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2026-08-27 13:59:29 +08:00
Vijay Misal 4c38988ba9
feat(mr): support Forgejo/Codeberg pull request URLs (#1267)
* feat(mr): support Forgejo/Codeberg pull request URLs

Codeberg and other Forgejo instances expose pull requests under
refs/pull/<id>/head instead of GitLab's refs/merge-requests/<id>/head,
so passing a Codeberg pull request URL to git mr fetched the wrong
ref. Recognize the /pulls/<id> URL shape and switch to the matching
ref for that case, leaving the GitLab URL and bare numeric id
behavior unchanged.

Closes #1213

* test(git-mr): cover Forgejo/Codeberg URLs, regenerate man docs

Add a bats test that exercises the pulls URL branch via a local
insteadOf remote rewrite (no real HTTP endpoint needed), and bring
git-mr.1/git-mr.html in line with the git-mr.md changes.

---------

Co-authored-by: vjymisal0 <vijay.looprai@gmail.com>
2026-08-27 13:59:00 +08:00
Vijay Misal dbf68bac59
fix(is-git-repo): recognize bare repositories (#1266)
* fix(is-git-repo): recognize bare repositories

is_git_repo() used `git rev-parse --show-toplevel` to detect whether
the current directory is inside a git repository. --show-toplevel
fails for bare repositories since they have no working tree, so every
command that relies on this shared helper (e.g. `git browse`) reports
"Not a git repo!" when run from inside a bare repo, even though it
plainly is one.

Switch to `git rev-parse --git-dir`, which succeeds for both normal
and bare repositories and keeps the existing "not a repo" behavior
for non-repo directories.

Fixes #1238, reported and LGTM'd by maintainers there with this exact
fix; no PR had been opened for it yet.

Adds tests/is-git-repo.bats covering: a normal repo, a bare repo, and
a plain (non-repo) directory.

* Fix ruff lint failures in scripts/checkstyle.py

This PR's new Ruff CI job surfaced pre-existing style issues (unsorted
imports, deprecated typing.List/Dict, nested ifs, bare exit()) in
checkstyle.py. Cleaned these up so the newly added lint job passes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 14:15:48 +08:00
dependabot[bot] 03569856db
chore(deps): bump actions/setup-python from 6 to 7 (#1263)
* chore(deps): bump actions/setup-python from 6 to 7

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix lints

* Fix typo

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Edwin Kofler <edwin@kofler.dev>
Co-authored-by: spacewander <spacewanderlzx@gmail.com>
2026-08-14 12:27:16 +08:00
罗泽轩 d11e45f0b1
port python tests to bats (#1264)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2026-08-12 15:43:59 +08:00
dependabot[bot] fd2e03cf55
chore(deps): bump actions/setup-go from 6 to 7 (#1262)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-19 18:52:47 -07:00
AJ Côté 5a14757944
feat(scp): add verbose, interactive, and dry-run flags (#1261)
* feat(scp): add -v/--verbose and -i/--interactive flags

git scp/git rscp printed a diff via plain `git diff`/`git diff --stat`
with no `--no-pager` and no TTY check, blocking on the user's pager.
Add -v/--verbose to print the diff via --no-pager before syncing, and
-i/--interactive to also prompt for confirmation before the real
push/copy. Both apply to git scp and git rscp, and must precede
<remote>.

Updates man/git-scp.md and Commands.md.

* feat(scp): add -n/--dry-run flag

Add -n/--dry-run to preview a sync without changing anything: rsync
runs with -n, and git add --force / ssh ... rm are skipped outright.
Implies --verbose, since confirming or previewing with nothing shown
is useless.

Also restructures scp_and_stage's push/delete blocks from `&&`-chains
into if/then, needed to branch on dry-run, which incidentally fixes a
latent bug where a non-TTY COLOR_RESET could silently skip the real
rsync/git add/ssh rm calls.

Updates man/git-scp.md and Commands.md.

* test(scp): add coverage for -n/--dry-run and -i/--interactive abort

git-scp had no test coverage at all. Cover the two paths that need no
real remote destination: dry-run must preview without touching the
index, working tree, or remote path, and declining the interactive
confirmation must abort before anything syncs.

* feat(scp): Code review

* feat(scp): Code review
2026-07-14 19:43:00 +08:00
kdergachev b450b36036
Added edit/remove functionality to git-ignore (#1258)
* Added edit/remove functionality to git-ignore.

* Minor change to array-string assignment.

* Fixes array handling for remove.

* Suggested fixes (iteration over arguments, pattern removal, editor handling, tmp file location, symlinked gitignore handling).

* Changed pure bash editing of files to an awk script. Some other minor cosmetic changes.

* Updated docs.

* Modified autocomplete to work with new git-ignore

* Added help message to git-ignore.

* Added help flags to git-ignore's man.

* Made $file in eval safe. Should not do the same for editor since then all of the GIT_EDITOR will be treated as command (flags included).

* Typo fixes.
2026-07-14 19:42:02 +08:00
Acho Arnold 7f36d37283
fix: support Windows ARM64 (clangarm64) in install.cmd (#1259)
Some checks failed
ci / Test with Bats (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / Test with Pytest (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* fix: support Windows ARM64 (clangarm64) in install.cmd

Git for Windows on ARM64 uses a `clangarm64` directory instead of
`mingw64`. The installer now auto-detects `clangarm64` when
`mingw64` is not present, so it works out of the box on ARM64
Windows machines without requiring the user to manually supply
the path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: remove trailing whitespace from install.cmd

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: correctly resolve PREFIX when git.exe is under the arch dir in PATH

When 'where git.exe' resolves to ...\mingw64\bin\git.exe or
...\clangarm64\bin\git.exe, installdir becomes the architecture directory
itself. Detect that case by inspecting the leaf directory name instead of
appending mingw64/clangarm64 (which produced non-existent ...\mingw64\mingw64
paths) or using ..\ segments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-08 15:20:25 +08:00
dependabot[bot] 184d37e980
chore(deps): bump actions/checkout from 6 to 7 (#1260)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / Test with Pytest (push) Has been cancelled
ci / Test with Bats (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 14:05:50 +08:00
Utsav Sabharwal e92878b81b
feat: add git-delete-gone-branches command (#1239)
* feat: add git-delete-gone-branches command

Adds a new command to delete local branches whose remote-tracking
branch has been deleted (shown as [gone] in git branch -vv).

This is a common need after PRs are merged and remote branches are
cleaned up, leaving stale local branches behind.

Features:
- Deletes all local branches with a gone remote in one command
- --dry-run / -n flag to preview branches before deletion

Closes #1220

* Address PR review feedback on git-delete-gone-branches

- Use git for-each-ref instead of git branch -vv to avoid false
  positives from commit messages containing ': gone]'
- Add interactive confirmation prompt by default; skip with -f/--force
- Add -p/--prune flag to run git fetch --prune before checking
- Add generated man page .1 and .html files

* Use GitHub profile for author contact in man page

* Fix gone-branch detection with nobracket format
2026-06-09 10:36:27 +08:00
罗泽轩 b9e1d88f2c
doc: regenerate git-extras.1 (#1255)
This file has been reset since commit 032090e847

Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2026-05-26 12:28:42 +08:00
Ricardo Fernández Serrata 99207eee8e
refactor(delete-merged-branches): rm -n1 from xargs, fmt pipes (#1233) 2026-05-18 16:10:46 +08:00
罗泽轩 9d6319becb
Bump git-extras version to 7.6.0-dev (#1251) 2026-05-11 09:49:38 +08:00
罗泽轩 2bdeb59a73
Version 7.5.0 (#1250)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2026-05-10 10:19:14 +08:00
Russ c5a0fdf660
Use standard prefix for home dir installs (#1248) 2026-04-29 13:46:32 -07:00
Ricardo Fernández Serrata 8d1c970031
refactor(abort): common gitdir (#1244)
* refactor(abort): common gitdir

* un-readonly
2026-04-14 13:26:28 +08:00
罗泽轩 c06fad59d2
Fix man page usage docs (#1241) 2026-03-23 23:21:26 -07:00
phanium 65dd36ae62
Fix git-changelog to handle whitespace in command arguments (#1232) 2026-03-05 23:06:10 -08:00
dependabot[bot] 967e339f91
chore(deps): bump bats-core/bats-action from 3.0.1 to 4.0.0 (#1230)
Bumps [bats-core/bats-action](https://github.com/bats-core/bats-action) from 3.0.1 to 4.0.0.
- [Release notes](https://github.com/bats-core/bats-action/releases)
- [Commits](https://github.com/bats-core/bats-action/compare/3.0.1...4.0.0)

---
updated-dependencies:
- dependency-name: bats-core/bats-action
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-09 14:36:15 +08:00
罗泽轩 ae3d30fb6e
add details when multiple active operations found (#1229)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2026-02-06 15:03:50 +08:00
John Bachir 032090e847
Add persistent history to git-repl (#1226)
* persistent command history

* notes

* decision on which commands to save

* configurable history location

* fix history initialization in various scenarios

* documentation

* comment

* don't add whitespace or consecutive duplicates to history

* fix manpage output

* Update bin/git-repl

Co-authored-by: Edwin Kofler <edwin@kofler.dev>

* Update bin/git-repl

Co-authored-by: Edwin Kofler <edwin@kofler.dev>

* Update git-repl

* Update bin/git-repl

* Apply suggestions from code review

* Add `extglob` and generate docs

* Fix regex

* update docs

rm man/git-repl.{1,html}
touch man/git-repl.md
make man/git-repl.{1,html}

---------

Co-authored-by: Edwin Kofler <edwin@kofler.dev>
2026-02-05 11:05:01 -08:00
Erik Skultety 5367101f2e
Support passing ref to git-commits-since (#1228) 2026-02-02 21:39:48 -08:00
John Bachir 6ed2643252
ability to specify command when hitting enter (#1223)
* ability to specify command when hitting enter

* documentation

* man page

* generate man page and html

* run default command through same logic as other commands

impetuous: persist return val for display in promt if non-zero

also future-proofs for other benefits / DRYness. no need for another eval invocation.

* fix generated manpage
2026-01-20 14:11:45 +08:00
John Bachir b9bd309a87
Improve git-repl prompt (#1224)
* repl config ideas

* documentation

* Update bin/git-repl

Co-authored-by: Edwin Kofler <edwin@kofler.dev>

* separator

* man page and web docs

* always use dir for project name

---------

Co-authored-by: Edwin Kofler <edwin@kofler.dev>
2026-01-19 16:15:42 -08:00
johnpyp 6f4cf0c1bb
feat: delete-branch multiple unique branch names completions (#1221) 2025-12-18 12:40:24 -08:00
dependabot[bot] f5c48e4842
chore(deps): bump actions/checkout from 5 to 6 (#1219)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 10:42:06 +08:00
Hugo Ruiz-Mireles daf9d4148e
Made Git Magic use --force-with-lease (#1218)
This change simply overwrites the usage of `-f` with
`--force-with-lease` in git-magic because `--force-with-lease` is a
safer alternative.
2025-10-22 11:49:27 +08:00
dependabot[bot] eb34655aea
chore(deps): bump actions/stale from 9 to 10 (#1214)
Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 10:29:50 +08:00
dependabot[bot] 1f1320f4d6
chore(deps): bump actions/setup-go from 5 to 6 (#1215)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 10:29:28 +08:00
dependabot[bot] 4d05955bc5
chore(deps): bump actions/setup-python from 5 to 6 (#1216)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 10:28:51 +08:00
Daniele Paolella b57eb8b92a
Fix minor typo in git rename-branch man page (#1211)
* docs: fix symbols in `git rename-branch` man page

* Format doc page after others and rebuild artifacts
2025-09-04 10:51:48 +08:00
Daniele Paolella cf6d40d446
Improvements to Bash completion (#1210)
* Sort Bash completion functions

* feat: add Bash completion for `git rename-branch`

* feat: add Bash completion for `git rename-remote`

* feat: add Bash completion for `git rename-tag`

* fix: use default completion for `git rename-file`

* Refactor rename completion functions common logic
2025-08-29 18:29:31 +08:00
Edwin Kofler 14586c7da0
Fix use of git-whatchanged to git-log (#1212) 2025-08-29 18:27:55 +08:00
dependabot[bot] 5c9a7a2533
chore(deps): bump actions/checkout from 4 to 5 (#1209)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 11:15:38 +08:00
罗泽轩 215382b12d
Bump version to 7.5.0-dev (#1207)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2025-06-23 11:08:27 +08:00
罗泽轩 13cbdfc51e
Version 7.4.0 (#1206)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2025-06-20 11:12:58 +08:00
Edwin Kofler 3ddc315a76
Format comparisons, functions, and redirections to be consistent (#1201)
* Format comparisons and functions to be consistent

* Add `checkstyle.py` script and add check to CI

* Fix Ruff lints
2025-06-06 11:10:50 +08:00
Andrew Sullivan Cant c63b46a33b
add git-wip and git-unwip (#669)
* Upgrade codespell to v2.4.0

It is a lastet verison, and is needed so that I can use the inline
ignore support in a future commit.

After updating tests/pyproject.toml I ran the following commands:
```
cd tests
poetry lock --no-update
poetry install
```

I also fixed a spelling error in man/git-summary.md, which was not
caught by the old version of codespell. After updating the file I also
updated the related files by running the following commands:

```
cd man
make -C .. man/git-summary.{1,html}
```

* add git-wip and git-unwip

Updated/re-built the documentation with:
```
cd man
make -C .. man/git-unwip.{1,html}
make -C .. man/git-wip.{1,html}
make -C .. man/git-extras.{1,html}
```
2025-05-14 17:05:41 +08:00
dependabot[bot] d4cd272556
Merge pull request #1203 from tj/dependabot/github_actions/bats-core/bats-action-3.0.1 2025-03-31 05:00:48 +00:00
dependabot[bot] 783812b627
chore(deps): bump bats-core/bats-action from 3.0.0 to 3.0.1
Bumps [bats-core/bats-action](https://github.com/bats-core/bats-action) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/bats-core/bats-action/releases)
- [Commits](https://github.com/bats-core/bats-action/compare/3.0.0...3.0.1)

---
updated-dependencies:
- dependency-name: bats-core/bats-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-31 00:23:42 +00:00
Edwin Kofler c49ca70814
Mostly finish pytest to Bats conversion (#1200)
* Mostly finish pytest to Bats conversion

* Add Bats version note to testing docs

* Move Bats CI check to separate job
2025-03-26 11:19:30 +08:00
Edwin Kofler 18ecffef5e
Implement half of tests in Bats (#1187)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* Initial scaffold

* Finish first half of Bats tests

* Add Bats to CI

* Fix CI

* Fix CI

* Fix configuration to set git name and email

* Fix `GIT_CONFIG_{KEY,VALUE}` indexing

* Fix Git config override precedence

* Remove unused and untested function
2025-03-12 18:15:37 +08:00
Edwin Kofler aadf5f874b
Add stale bot for old PRs (#1186)
* Add stale bot for old PRs

* Update message

* Update .github/workflows/stale.yaml

Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com>

* Fix stale workflow

* Update .github/workflows/stale.yaml

Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com>

---------

Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com>
2025-03-12 18:14:53 +08:00
Pierre Ayoub 18d1017325
feat(git-bulk): add new option to not follow hidden directories (#1195)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* feat(git-bulk): add --no-follow-hidden flag

* docs(git-bulk): add --no-follow-hidden description

* docs(man): make ronn

* docs(git-bulk): put --no-follow-hidden at right place in usage()

* refactor(git-bulk): logic optimization

Remove unnecessary subshell

Co-authored-by: Edwin Kofler <edwin@kofler.dev>

* fix(git-bulk): bad test operator

With the Bash' regexp matching operator `=~`, we need to use the Bash
conditional expression evaluation command `[[ ]]`.

* docs(completion.zsh): add --no-follow-hidden and --no-follow-symlink

---------

Co-authored-by: Pierre Ayoub <pierre.ayoub@protonmail.com>
Co-authored-by: Edwin Kofler <edwin@kofler.dev>
2025-03-05 10:39:41 +08:00
Pierre Ayoub a841845d9a
Feat: allow git-summary showing full path of repository (#1193)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* feat(git-summary): add --full-path option

* docs(git-summary): add doc for --full-path option

* docs(man): make using ronn

* docs(git-extras-completion.zsh): add --full-path option of git-summary
2025-02-27 14:25:55 +08:00
Pierre Ayoub 8eed5f49ad
feat(git-bulk): add new option to no follow symlinks (#1194)
* feat(git-bulk): add --no-follow-symlinks flag

* docs(git-bulk): add --no-follow-symlinks description

* docs(man): make ronn

* docs(git-bulk): put --no-follow-symlinks at right place in usage()

* fix(git-bulk): use readarray for find command

1. Use `readarray` such that we can handle paths with spaces
2. Remove the unnecessary `eval`
2025-02-27 14:18:16 +08:00
Pierre Ayoub 64687599ae
docs(git-bulk): Add zsh completion (#1190)
* docs(git-bulk): Add zsh completion

* docs(git-bulk): Harmonize help strings

* docs(git-bulk): simplify previous bad logic

The previous logic was not allowing to use multiple options (which is
often the case). This was bad because this is my first time with zsh
automcompletion. Without it, the user can use any flag combination --
but at least, he has the freedom.
2025-02-26 12:34:04 +08:00
Pierre Ayoub 8ce63003c0
fix(git-bulk): fix workspace selection when cd fails (#1197)
* fix(git-bulk): fix workspace selection when cd fails

`cd` may fails for multiple reasons:
- mistake when editing `.gitconfig` manually
- previously existing workspace that have been removed
- ...

Currently, if `cd` fails, the `BulkOp` continue its execution ... in the
workspace defined in a higher directory that where the user, despite the
user specified a specific workspace (`-w`).

The user should be noticed of a failed `cd` (this is really not expected
for a valid configuration) and the operations should stop to not execute
something unexpected.

* fix(git-bulk): replace weak eval for better variable substitution

Get rid of poor `eval` syntax because they are vulnerable to command
injection, which may have unexpected side effects.

However, they enabled a useful feature: using environment variable
(*e.g.*, defined in a `.bashrc`) inside the `.gitconfig` to use dynamic
paths as  `bulk` workspaces.

As such, I keep this feature possible by using the Bash's ${!VAR}
syntax, which allows to get the value of one variable using the name of
a another variable. However, arbitrary command injection is not possible
anymore.

* fix(git-bulk): missing check about empty environnement variable

* style(git-bulk): typo

* docs(Commands.md): git-bulk env var feature

* docs(man/git-bulk): git-bulk env var feature

* docs(man/git-bulk): mention .gitconfig for config storage

* style(man/git-bulk): typo

* docs(man/git-bulk): run make/ronn for .1 and .html
2025-02-26 12:33:16 +08:00
Pierre Ayoub 1a9b0c2ab4
fix(git-bulk): quiet find errors by default (#1196)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2025-02-20 15:47:27 +08:00
Pierre Ayoub 82cc37d0f5
fix(git-bulk): fix a bad integer expression (#1198)
Fix a logic error inside the `allowedargcount()` function. This function
may be called with one or two arguments. However, no default values are
assigned to `$1` and `$2` that are used inside a numerical comparison.
Therefore, when using a bad number of arguments for the following lines:

```
listall|purge) allowedargcount 1;;
addcurrent|removeworkspace) allowedargcount 2;;
```

Then, we would get the error `[: : integer expression expected`. To fix
this, we assign the 0 default value to `$1` and `$2`, such that we
trigger the error message destined to the user without any integer error
when there is a bad number of argument and that the function is called
with only 1 argument instead of 2.
2025-02-20 15:41:42 +08:00
dependabot[bot] df53711fc7
chore(deps): bump astral-sh/ruff-action from 2 to 3 (#1189)
Some checks failed
ci / test (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
Bumps [astral-sh/ruff-action](https://github.com/astral-sh/ruff-action) from 2 to 3.
- [Release notes](https://github.com/astral-sh/ruff-action/releases)
- [Commits](https://github.com/astral-sh/ruff-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: astral-sh/ruff-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-29 18:13:50 -08:00
oikarinen e691826b4b
Fix all ShellCheck errors and add to CI (#1179)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-12-20 23:16:39 -08:00
dependabot[bot] fdaca2cd26
chore(deps): bump astral-sh/ruff-action from 1 to 2 (#1188)
Some checks failed
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
ci / lint (push) Has been cancelled
Bumps [astral-sh/ruff-action](https://github.com/astral-sh/ruff-action) from 1 to 2.
- [Release notes](https://github.com/astral-sh/ruff-action/releases)
- [Commits](https://github.com/astral-sh/ruff-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: astral-sh/ruff-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:06:50 +08:00
oikarinen 3db275cbe2
fix(ci): use poetry (#1183)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
For managing the python dependencies, running the unit tests and
spellchecker. This makes it easier for everyone to have same environment
as CI. Also includes few cosmetic edits for style for the ci.yml
2024-11-29 10:39:42 +08:00
Edwin Kofler 1345c5977d
Delete etc/test.fish (#1185) 2024-11-29 10:39:09 +08:00
oikarinen 2f4b57d52b
feat: add git-continue (#1176)
Some checks are pending
ci / lint (push) Waiting to run
ci / typo (push) Waiting to run
ci / test (push) Waiting to run
ci / build (macos-latest) (push) Waiting to run
ci / build (ubuntu-latest) (push) Waiting to run
* feat: add git-continue

Also refactor `git-abort` to parse action from the called file name.

There's a promise for this in #865 but that was a long time ago.

* fix: add git-continue

address review comments, fix the script, remove unnecessary testpath
import.
2024-11-28 16:50:29 +08:00
oikarinen c23da8ae9c
feat: add ruff linter with ci check (#1182)
Some checks are pending
ci / lint (push) Waiting to run
ci / typo (push) Waiting to run
ci / test (push) Waiting to run
ci / build (macos-latest) (push) Waiting to run
ci / build (ubuntu-latest) (push) Waiting to run
* feat: add ruff linter with ci check

Liniting the python files in tests directory would reduce whitespace etc
change and enforce basic coding standard for those. Kind of
opportunistic PR as there might be other opinions about how to deal with
this.

* fix: format with ruff
2024-11-28 10:30:14 +08:00
oikarinen f1f50ac5ba
fix(ci): missing dollar sign (#1184)
Some checks are pending
ci / lint (push) Waiting to run
ci / typo (push) Waiting to run
ci / test (push) Waiting to run
ci / build (macos-latest) (push) Waiting to run
ci / build (ubuntu-latest) (push) Waiting to run
Let's get the ci fixed!
2024-11-27 08:40:33 -08:00
oikarinen 0989b773de
fix(github-actions): changed files output for editorcondig-checker (#1180)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* fix(github-actions): changed files output for editorcondig-checker

Seems that ci.yml includes a dead dependency action just to get list of
changed files for the PR / push. This seems weird as the repo is all
about git. Propose removing this extra dep with straightforward
implemenation. Also: github runners do have `jq` preinstalled.

* feat(ci): print changed files to log in lint job
2024-11-26 11:53:50 +08:00
罗泽轩 65203312c1
Revert "feat: add ruff linter with ci check (#1178)" (#1181)
Some checks are pending
ci / lint (push) Waiting to run
ci / typo (push) Waiting to run
ci / test (push) Waiting to run
ci / build (macos-latest) (push) Waiting to run
ci / build (ubuntu-latest) (push) Waiting to run
This reverts commit a2c424aada.
2024-11-25 14:32:54 +08:00
oikarinen a2c424aada
feat: add ruff linter with ci check (#1178)
Some checks are pending
ci / lint (push) Waiting to run
ci / typo (push) Waiting to run
ci / test (push) Waiting to run
ci / build (macos-latest) (push) Waiting to run
ci / build (ubuntu-latest) (push) Waiting to run
* feat: add ruff linter with ci check

Liniting the python files in tests directory would reduce whitespace etc
change and enforce basic coding standard for those. Kind of
opportunistic PR as there might be other opinions about how to deal with
this.

* fix: format with ruff
2024-11-25 10:14:37 +08:00
Guilhem Saurel d291b2775c
Support GITHUB_TOKEN var for git-fork and git-pull-request (#1177)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
Co-authored-by: 罗泽轩 <4161644+spacewander@users.noreply.github.com>
2024-11-16 10:33:26 -08:00
罗泽轩 fb0bc6950b
Bump version to 7.4.0-dev (#1175)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2024-10-22 10:07:46 +08:00
罗泽轩 21a086c009
Version 7.3.0 (#1174)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2024-10-20 18:16:23 +08:00
Jared Baur 8eb1d72f07
Fix stripping trailing forward slash for git-get (#1172)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-10-18 10:22:05 +08:00
Mattias Andersson 233686d2a2
Change git-cp to use cleaner branch approach (#1169)
Some checks failed
ci / test (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* Change git-cp to use cleaner branch approach, per https://stackoverflow.com/a/46484848/32841 and https://devblogs.microsoft.com/oldnewthing/20190919-00/?p=102904

* Name the temporary branch to better fit in with git-extras

* Again, git-extras naming is "copy", not "split"

* Make the commit messages more clear
2024-10-08 11:40:43 +08:00
Edwin Kofler 5c3fb1b062
Improve warning for git clear (#1168)
Some checks failed
ci / test (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-09-30 10:07:52 +08:00
wyattscarpenter 5ca9360109
Enhance git-repl (#1160)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-09-22 20:22:58 -07:00
wyattscarpenter 16d1445271
Update some documentation that was out of sync (#1164)
Some checks are pending
ci / build (ubuntu-latest) (push) Waiting to run
ci / typo (push) Waiting to run
ci / test (push) Waiting to run
ci / lint (push) Waiting to run
ci / build (macos-latest) (push) Waiting to run
* make instructions easier to copy-paste

* update some documentation that was out of sync
2024-09-22 14:11:31 +08:00
wyattscarpenter 4d2d393142
Use filetimes in check_integrity (#1162)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* check the file times in check_integrity

* make no-arguments to check_integrity check all commands, and also make check_integrity run git-utimes first

this has the downside that the script now takes a little longer. But the upside that it guarantees more correctness.

* update git-extras

using the incredible power of the new script, I have detected this was out of date, out of sync, etc
2024-09-19 11:27:11 +08:00
wyattscarpenter ffee88a246
Update git-alias.md: add brs to prevent incorrect line behavior (#1161)
* Update git-alias.md: add brs to prevent incorrect line behavior

currently these all appear on one line, in the rendered markdown viewer and also in the man page. That isn't what's wanted. What's wanted is for each to be on its own line, which this change implements, using the strategy I found in git-bulk.

* run make on git-alias
2024-09-19 11:19:48 +08:00
wyattscarpenter da03b7b133
Update git-bulk.md: use correct stylization in synopsis (#1163)
* Update git-bulk.md: use correct stylization in synopsis

* update derived documents for git-bulk
2024-09-19 11:17:00 +08:00
wyattscarpenter f0671e2733
Update git-repl.md: typo: "let's" for "lets" (#1158)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-09-16 12:19:03 -07:00
Revisor 2c161aae02
Update instructions for the OpenSUSE installation (#1157)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-09-04 13:59:35 +08:00
Wiktor Żurawik f0b9e18e54
Add pathspec support in git-missing (#1156)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
* feat: Add pathspec support in git-missing

Allow to specify a path to limit the commit difference list. This
improvement allows users to focus on changes in specific directories or
files when comparing branches for missing commits.

* refactor: Improve pathspec handling in git-missing

- Change pathspec from string to array to support multiple pathspecs
- Remove unnecessary 'shift' command in argument processing loop
- Simplify git log command execution by using a single codepath

* chore: Update git-missing docs

* chore: Fix a typo in git-missing docs
2024-08-26 10:38:15 +08:00
CodeByZach c32869de37
feat: add rename-file command (#1149)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-08-02 01:19:35 -07:00
Matt Headley e1a4e7195d
Update Commands.md (#1148)
Some checks failed
ci / lint (push) Has been cancelled
ci / typo (push) Has been cancelled
ci / test (push) Has been cancelled
ci / build (macos-latest) (push) Has been cancelled
ci / build (ubuntu-latest) (push) Has been cancelled
2024-07-05 10:15:22 +08:00
Wen Sun b766a655ce
fix: git-summary commit count (#1147)
* fix: git-summary commit count

* fixup! fix: git-summary commit count
2024-06-18 11:13:07 +08:00
Raphael Boidol e34dea9b7a
tests: update dependencies (#1142) 2024-05-03 14:52:28 +08:00
dependabot[bot] 3bac05e690
chore(deps): bump masesgroup/retrieve-changed-files from 2 to 3 (#1144)
Bumps [masesgroup/retrieve-changed-files](https://github.com/masesgroup/retrieve-changed-files) from 2 to 3.
- [Release notes](https://github.com/masesgroup/retrieve-changed-files/releases)
- [Commits](https://github.com/masesgroup/retrieve-changed-files/compare/v2...v3)

---
updated-dependencies:
- dependency-name: masesgroup/retrieve-changed-files
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-29 09:59:50 +08:00
dependabot[bot] a7ad306a6b
chore(deps): bump actions/setup-go from 4 to 5 (#1143)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-29 09:59:43 +08:00
Raphael Boidol dcb8bd6c54
ci: add dependabot update for GitHub actions (#1141) 2024-04-28 10:06:37 +08:00
Raphael Boidol a1bf09e8e0
ci: update actions (#1140) 2024-04-27 18:04:27 +08:00
罗泽轩 615ea971cb
Bump version to 7.3.0-dev (#1139)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2024-04-25 10:53:37 +08:00
罗泽轩 e692b9619a
Version 7.2.0 (#1138)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2024-04-23 19:59:09 +08:00
Leroy a17d2fb15d
refactor: test utils and simplify the case code (#1136)
* doc(tests/README.md): explain the unit test process

* test(TempRepository): support to change origin and get filename

* refactor: simplify the case code

    * test_git_browse_ci.py
    * test_git_browse.py

* fix: rename `test_authors.py` to `test_git_authors.py`

* fix: typo

* fix: log the GitCommandError
2024-02-29 11:11:00 +08:00
Edwin Kofler 52897f73b9
Add --proceed flag to git-delete-squashed-branches (#1134) (#1135)
Co-authored-by: Norbert Kiesel <nk@iname.com>
2024-02-20 10:47:36 +08:00
Leroy 5f19424a50
test(browse-ci): add unit tests (#1130) 2024-01-30 10:43:32 +08:00
Leroy b54d8c7c50
test(git-browse): add unit tests (#1127)
* chore: add the dependency testpath@0.6.0

* test(git-browse): add unit tests

* fix: update origin name to `tj/git-extras`
2024-01-05 14:31:37 +08:00
Josh McKinney 9049967232
feat: add reverse option to git-brv (#1123)
* feat: add reverse option to git-brv

When there are a large amount of branches it is often useful to see the
most recent branches last as they end up closer to the prompt. This adds
a new --reverse / -r option and an environment variable to set the
default behavior (GIT_BRV_REVERSE).

* git-brv: change to git option instead of environment variable and fix typo

* wrap git option in backticks for git-brv and fix git-feature title

* update git-feature docs

* fix missing breaks in git-feature.md
2023-12-16 12:05:52 +08:00
Edwin Kofler cdbcef3227
Merge pull request #1124 from rasa/patch-3
fix(utimes): change /dev/stdin to -
2023-12-14 19:36:32 -08:00
Ross Smith II a4b637346d
fix(utimes): change /dev/stdin to -
As not all environments have /dev/stdin. See https://github.com/ish-app/ish/issues/944
2023-12-13 07:51:50 -08:00
Ross Smith II 1843afb38a
fix(utimes): Support filenames with backspaces, etc. (#1122) 2023-12-12 14:50:21 +08:00
Leroy 28b48c7432
chore: add poetry to handle the tests of the git extras (#1121)
* chore: add poetry to handle the tests of the git extras

* chore(ci): upgrade the deps version

* chore(CONTRIBUTING): remove the test description
2023-12-01 14:25:14 +08:00
Ross Smith II 2c2bd07693
Fix typo in ci.yml (#1120) 2023-11-25 14:02:35 +08:00
Ross Smith II e39b1ab6a6
Fix: harden utimes, use single quotes (#1109)
* Fix: harden utimes, use single quotes

* Remove passing --posix to bash

* Remove double-quotes and backslashes

* Fix: patch git-utimes (fixes #1118)

Fixes #1118

* Fix formatting issue in git-utimes

Per https://github.com/tj/git-extras/pull/1109#discussion_r1399965099
2023-11-23 11:39:56 +08:00
Ross Smith II 058cb0763e
Fix: patch git-utimes (fixes #1118) (#1119)
I don’t have a Mac/BSD system to test, but this should fix #1118
2023-11-23 11:38:33 +08:00
罗泽轩 3d964687fc
add pull request manner (#1113)
* add pull request manner

Signed-off-by: spacewander <spacewanderlzx@gmail.com>

* Update .github/PULL_REQUEST_TEMPLATE.md

Thanks for @hyperupcall

Co-authored-by: Edwin Kofler <edwin@kofler.dev>

---------

Signed-off-by: spacewander <spacewanderlzx@gmail.com>
Co-authored-by: Edwin Kofler <edwin@kofler.dev>
2023-11-21 10:39:44 +08:00
Edwin Kofler ca18c6e552
maintenance: Add my name as maintainer in AUTHORS (#1117) 2023-11-21 10:32:52 +08:00
Leroy cf9fe2d23f
test(git-authors): add unit test (#1098) 2023-11-18 10:20:19 +08:00
Edwin Kofler c13ab52138
Add more comprensive dependencies (#1111) 2023-11-18 10:19:10 +08:00
René 58c5b50514
fix: git-standup find error (tj#1106) (#1110) 2023-11-14 12:05:28 +08:00
Edwin Kofler 5020890c34
Improve defaults for testing suite (#1104) 2023-11-11 12:55:46 +08:00
Ross Smith II 4d8060e8cb
feat: Speed up utimes (#1108) 2023-11-11 12:52:04 +08:00
Edwin Kofler 7a65311b1c
Overhaul CONTRIBUTING document (#1103) 2023-11-11 12:50:50 +08:00
罗泽轩 186bbdcb87
Bump version to 7.2.0-dev (#1099)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2023-11-02 12:00:12 +08:00
罗泽轩 bfef91f30a
Version 7.1.0 (#1097) 2023-10-29 18:52:56 +08:00
罗泽轩 78c5ca28a0
git-cp: don't stop execution when merge.ff is not set (#1096)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2023-10-26 12:28:19 +08:00
Wei Wu 90221f2149
Allow git cp to create destination folder automatically (#1091)
* Allow `git cp` to create destination folder automatically

* Replace subshell command with builtin operation to infer DESTINATION_DIR

* Remove destination folder existence check and only create it when the path contains slash(s)
2023-10-25 12:03:36 +08:00
Edwin Kofler 263dcb4a79
fix: Use -iname and expressions instead of -iregex (#1094) 2023-10-24 21:52:59 +08:00
罗泽轩 cc1c0e1a38
change installation source from master to main (#1092)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2023-10-23 12:13:40 +08:00
Leroy 04bf403861
test(git-archive-file): add unit test (#1084) 2023-10-17 13:54:09 +08:00
Edwin Kofler 5fe4226865
ci: Use Go v1.20 for running editorconfig-checker (#1086) 2023-10-15 10:19:41 +08:00
Leroy bddb987383
test(git-alias): add its unit test (#1077) 2023-09-27 10:21:35 +08:00
Edwin Kofler df1997c0dc
makefile: Allow bypassing conflict check (#1080) 2023-09-26 11:35:24 +08:00
Edwin Kofler 954ba1fb7c
fix: Restore zsh completions to working completion (#1079) 2023-09-23 13:38:15 +08:00
Gerrit-K e4152db482
fix: add missing quote in git-extras-completion.zsh (#1078)
Broken in #1074
2023-09-22 19:31:09 +08:00
Edwin Kofler dee7c7f1d0
feat: Implement completion for git coauthor (#1074) 2023-09-22 11:58:01 +08:00
Edwin Kofler e793f8979b
fix: Move improvements via ShellCheck (#1075) 2023-09-22 11:57:48 +08:00
Leroy d2dde69d14
feat(auto-test): CI with pytest (#1066)
* feat(ci): add pull request action and pytest.ini

* test(git-abort): add its unit test
2023-09-19 11:11:43 +08:00
Austin Ziegler 49650eda62
git-feature: add configurable branch separator (#1072)
* git-feature: add configurable branch separator

Closes #1069

This allows the use of a separator other than `/` for feature or other
alias branches. While a command-line option has been provided (`-s` or
`--separator`), this will most often be used via
`git-extras.feature.separator`.

* Significant update to bin/git-feature

- Changed option parsing so that `--alias` requires an argument and will
  fail with an error unless provided. Applied the same logic to
  `--separator`.

- Changed `finish` parsing to capture it as a variable flag during
  argument parsing.

  This could be extended so that if `finish` is already true, a second
  `finish` results in the word being added to the argument list.

  ```console
  $ git feature -- finish remote
  $ git feature finish finish remote
  ```

  This has not been done because it is a bit of an inconsistent handling
  for documentation purposes.

- Add handling of `--` to permit options or `finish` to be made part of
  the feature branch name.

- Since `finish` is now a variable flag, simplify the name-building
  logic to always use `concatargs "${argv[@]}"`. This means that
  `git feature finish ...` and `git feature ...` behave the same in
  terms of feature branch name building.

- Basically rewrote the man page to include better descriptions of the
  options as well as adding a GIT CONFIG section and additional EXAMPLES
  for the new features/behaviour.
2023-09-19 11:10:59 +08:00
Dan Goodliffe 76cd9bdf45
git-psykorebase: determine branch names with bash pattern substitution (#1073)
Co-authored-by: Dan Goodliffe <dan.goodliffe@octal.co.uk>
2023-09-17 22:07:32 +08:00
罗泽轩 f95e339c03
git-cp: continue to merge when merge.ff is only (#1070)
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2023-09-13 13:02:06 +08:00
Austin Ziegler db71be578e
git archive-file: change base ARCHIVE_NAME (#1071)
Closes #1067

This makes the base ARCHIVE_NAME for `git-archive-file` to be the name
of the repo root directory instead of the name of the current directory.
2023-09-13 13:01:37 +08:00
Edwin Kofler cb29c62c65
readme: Add secondary location of screencast (#1065)
And, fix other typos.
2023-08-29 19:37:54 +08:00
Edwin Kofler 47e940272a
fix: Improve Bash hygiene (#1056)
* fix: Improve Bash hygiene

* Update quoting to account for new bugs

* quotes

* fix: Remove extra shellcheck comment
2023-08-18 16:15:40 +08:00
Edwin Kofler 8af43892f6
feat: Implement git-get command (#1045)
* feat: Add `git-get` command

* Update Commands.md

Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com>

* fix: Use Git config to store value

* improve `git-get` description

* fix: Add `--global` to git invocation

* Enable passing options to `git clone`

* fix: Ordering of checks

---------

Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com>
2023-08-12 13:49:33 +08:00
SchleimKeim 34609bd800
Corrected grammar in bin/git-cp (#1061)
Corrected grammar in bin/git-cp:41
2023-08-10 15:35:01 +08:00
Edwin Kofler 0b5d2cce55
Improve git-ignore command (#1047)
* Improve `git-ignore` command

* fix: Improve warnings/errors and modify `not_need_git_repo`
2023-07-13 14:29:39 +08:00
Jobin Kurian d7fb4495f1
I have made two improvements to the git-bulk: (#1054)
* I have made two improvements to the git-bulk:
1. Previously, if the "repository.txt" file did not end with a blank line, only three out of four repositories were cloned. This limitation has been fixed.
2. Now, there is support for cloning repositories into custom folder names when using the "repository.txt" file.

* Corrected the code indentation.

* removed the extra condition to check is the line was empty or not.

* Updated the comment for the new changes in the code.

---------

Co-authored-by: Jobin Kurian <jobin.kurian@netcorecloud.com>
2023-07-07 11:08:23 +08:00
Hemanth HM 5e543cb620
Merge pull request #1052 from spacewander/gsm
Add typo checker
2023-06-19 08:24:24 -07:00
kang 3a258cbaad
Type properly __gitex_command_successful calls and capitalizate github to GitHub (#1049)
* Type properly `__gitex_command_successful` calls

* Capitalize github to GitHub
2023-06-19 22:05:30 +08:00
spacewander ba9e6f0c5c Add typo checker 2023-06-19 21:51:53 +08:00
Nate Eagleson 5d7ed18c50
Fix comment typo in git-authors (#1050) 2023-05-29 10:25:08 +08:00
Edwin Kofler 2d221c1b67
tweak: Fix typos (#1046) 2023-05-18 10:19:24 +08:00
Igor Ostapenko f5bcbe5b0d
Makefile: init SYSCONFDIR before using it (#1044) 2023-05-08 09:57:55 +08:00
Igor Ostapenko 6d73f74f83
Makefile: tune MANPREFIX configuration for FreeBSD (#1043)
We could think of the following use cases:

1. User installs git-extras from official port/pkg. man/man* structure
is quite common among different systems, but location of man/ itself can
be different. FreeBSD Ports framework setups MANPREFIX to PREFIX by
default, i.e. it's /usr/local. The idea is that software may provide
different man/man* sections, with final path being /usr/local/man/man*.
In contrast, git-extras' Makefile process simplifies the things due to
it works with section 1 only, and expects MANPREFIX to be set down to
man/man1 path. For this reason, installation via FreeBSD Ports requires
unconditional man path configuration like this:

    ifeq ($(OS), FreeBSD)
        MANPREFIX = "$(PREFIX)/man/man1"

Otherwise, the path will be incorrect. And this is how official FreeBSD
port of git-extras has been fixing this difference for many years.

2. End user installs git-extras manually. There are two sub-cases:

  2.a. User explicitly defines custom MANPREFIX. Okay, it's user's
  decision -- nothing to do here, just follow as is.

  2.b. User does not define custom paths, i.e. it's expected to be
  installed according to FreeBSD defaults. And default values in
  git-extras' Makefile forms the expected correct path.

This change supports all the cases above, with the goal to avoid wrong
man path for the most of the situations. As long as official git-extras
make process tries to support FreeBSD, this change allows to omit extra
patching for FreeBSD Ports framework.
2023-05-05 09:46:38 +08:00
Igor Ostapenko bbf90c4623
Makefile: set bash COMPL_DIR to FreeBSD expected defaults (#1042)
See official conventions: https://docs.freebsd.org/en/books/porters-handbook/special/#shell-completion
2023-05-04 11:19:25 +08:00
罗泽轩 2fca5edb7d
Bump version to 7.1.0-dev (#1041) 2023-04-26 10:29:51 +08:00
罗泽轩 d7f0fcc546
Version 7.0.0 (#1040) 2023-04-24 22:03:26 +08:00
罗泽轩 df825715ba
Need bash 4.0+ (#1038) 2023-04-19 16:57:04 +08:00
hemanth.hm 2d5b12c01f
Merge pull request #1036 from spacewander/del
git-delete-tag: should not be quoted
2023-03-06 18:02:52 -08:00
spacewander 753555744b git-delete-tag: should not be quoted
Otherwise the whole result will be considered one tag/ref.
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2023-03-07 09:11:37 +08:00
Günther Grill d5eaa1895b
Add the current branch git summary (#1034)
Co-authored-by: guenthgr <guenther.grill@frauscher.com>
2023-02-27 11:26:53 +08:00
Edwin Kofler b372bab52a
chore: Improve Bash variously (#1032) 2023-02-21 10:03:56 +08:00
Günther Grill a008308267
Change the oneline option to a tebular version in the git summary (#1031)
Co-authored-by: guenthgr <guenther.grill@frauscher.com>
2023-02-12 20:14:16 +08:00
Edwin Kofler 5d0fe32617
chore: Remove X-style equality comparisons (#1028) 2023-02-03 09:41:52 +08:00
Edwin Kofler 75d0dbff6d
chore: Various Bash improvements (#1029) 2023-02-03 09:41:36 +08:00
hemanth.hm 9c3343d1ee
Merge pull request #1030 from hyperupcall/fix-reset-env
fix: No longer pollute env with `GREP_OPTIONS`
2023-02-01 16:29:54 -08:00
Edwin Kofler 82daeb7c94
fix: No longer pollute env with GREP_OPTIONS 2023-01-30 21:00:39 -08:00
罗泽轩 73fbf8e08d
tweak editor config (#1027) 2023-01-28 17:15:20 +08:00
罗泽轩 36e6de10c8
revert the completion dir change on MacOS (#1026)
Fix https://github.com/tj/git-extras/issues/1018
2023-01-27 19:38:56 +08:00
Wei Lee e1138b8190
Update gitignore.io urls to Toptal urls (#1025) 2023-01-27 17:49:38 +08:00
Edwin Kofler 589ef33e35
Add EditorConfig file (#1022) 2023-01-27 17:47:06 +08:00
Günther Grill e4e7310c49
Make the bulk output slimmer (#1020)
Co-authored-by: guenthgr <guenther.grill@frauscher.com>
2023-01-20 18:18:10 +08:00
Edwin Kofler 4611b24320
chore: Fix more Bash inconsistencies (#1021) 2023-01-20 18:17:49 +08:00
Günther Grill 04eb5c04e6
Repo status overview (#1017)
Co-authored-by: guenthgr <guenther.grill@frauscher.com>
2023-01-13 13:43:03 +08:00
Piotr Górski e29dcbb023
Replace egrep with grep -E (#1019) 2023-01-12 09:51:22 +08:00
Edwin Kofler d34fa527a0
chore: More Bash improvements and removing deprecations (#1016) 2023-01-10 08:59:43 +08:00
Edwin Kofler 35100b4e6f
chore: Improve quoting and builtin usage (#1015) 2023-01-04 08:58:31 +08:00
Günther Grill cd348dad21
Add summary fields (#1013)
Co-authored-by: guenthgr <guenther.grill@frauscher.com>
2023-01-04 08:58:16 +08:00
Günther Grill aea25d2d4b
Fix quiet mode of the bulk command (#1014)
Co-authored-by: guenthgr <guenther.grill@frauscher.com>
2022-12-28 09:11:53 +08:00
Kurban Mallachiev 4fff03f429
git-summary: remove stray \ in grep call (#1010) 2022-12-19 18:02:29 +08:00
Günther Grill 7c4152ce83
Allow to run git-extras within git-bulk (#1008)
Co-authored-by: guenthgr <guenther.grill@frauscher.com>
Fix https://github.com/tj/git-extras/issues/1007
2022-12-07 13:58:18 +08:00
Áron Hoffmann 960ec8d2fd
Make git-summary accept multiple paths for the --lines argument (#1005) 2022-11-22 13:40:37 +08:00
罗泽轩 1426002037
Makefile: ensure bash is used instead of /bin/sh (#1004) 2022-11-12 12:19:35 +08:00
zhiyanfoo 193a6dd2a8
fix base branch in the pull-request command (#1003) 2022-11-12 12:10:57 +08:00
Joshua Li 2d1aa6daea
Update docs for git abort change (#1000) 2022-10-17 15:45:52 +08:00
罗泽轩 71af8cec42
Bump version to 6.6.0-dev (#997) 2022-10-10 10:32:37 +08:00
spacewander 3ac3c54b06 Version 6.5.0
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2022-10-06 20:33:44 +08:00
罗泽轩 584e0283f5
git root: show '.' when using with -r in the root dir (#995) 2022-10-06 13:56:07 +08:00
罗泽轩 62aa91f221
git standup: work as expected when no configured (#996) 2022-10-06 13:55:45 +08:00
Sascha Andres 25b5d734c7
rephrase question depending on switch (#984) 2022-10-06 13:43:35 +08:00
Joshua Li 06aa5dbb1f
Include revert conflicts in git abort (#992) 2022-10-06 13:26:20 +08:00
Lone 8058b1718f
fix(git-effort):fork: retry: Resource temporarily unavailable #979 (#990) 2022-09-25 20:04:51 +08:00
timfeirg 956781ac1b
git-sync --force will sync without interaction (#989) 2022-08-24 14:28:05 +08:00
罗泽轩 3b9ea70e09
Merge pull request #988 from vanpipy/feature/fix-git-cp-did-not-keep-history
fix(git-cp): keep the history of the files
2022-08-05 18:17:54 +08:00
vanpipy 6e6f59b264 fix(git-cp): keep the history of the files 2022-08-01 09:12:47 +08:00
罗泽轩 01e2494294
Merge pull request #987 from spacewander/fgz
git-bulk: previous refactor redirected $PWD to stderr by mistake
2022-07-22 09:27:10 +08:00
spacewander e3c8359e4e git-bulk: previous refactor redirected $PWD to stderr by mistake
Fix #986
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2022-07-19 14:37:33 +08:00
罗泽轩 9accb6bb04
Merge pull request #983 from SuperSandro2000/patch-1
Fix lazy loading bash_completion from XDG_DATA_DIRS
2022-07-12 09:04:38 +08:00
Sandro 66961ad491
Fix lazy loading bash_completion from XDG_DATA_DIRS
If a bash_completion file should get lazy loaded and is placed in a none standard directory which is listed in XDG_DATA_DIRS it must be placed under share/bash-completion/completions. This is for example used in home-manager.

See 730368b35a/bash_completion (L2531)
2022-07-08 11:49:58 +02:00
罗泽轩 81665422b2
Merge pull request #974 from tfendin/970-standup-author-date
#970 standup added -A flag for author date
2022-07-04 10:40:19 +08:00
Tobias Fendin 48ef95d104 Minor changes to man/git-standup.md 2022-07-01 16:39:27 +02:00
Tobias Fendin bf240df083 Replaced -g and -A with -F gpg|authordate 2022-06-26 12:49:43 +02:00
罗泽轩 d597f684b1
Merge pull request #981 from pbnj/feat/browse-file
feat(bin/git-browse): open website at file & line number or range
2022-06-13 09:02:53 +08:00
Peter Benjamin 7acc1de7fa
fix(bin/git-browse): fix commit hash
Retrieving commit hashes with the following command is not very reliable for
users who sign their commits with gpg/ssh keys:

```
git log -n1 --format=format:"%H" "${filename}" 2>/dev/null
```
2022-06-09 19:44:02 -07:00
Peter Benjamin 8a6e39d589
fix(bin/git-browse): fix bitbucket url 2022-06-09 19:32:47 -07:00
Peter Benjamin 006549d661
feat: open website at file & line number or range
Ported from
f544dadf88/git/bin/git-browse

Closes #980
2022-06-09 19:26:55 -07:00
Tobias Fendin 233b3f0dd4 #970 standup added -A flag for author date 2022-05-14 15:17:58 +02:00
罗泽轩 fdf89cd93f
Merge pull request #972 from wpwoodjr/master
update git-utimes.{1,html} to add --newer flag
2022-05-11 14:12:06 +08:00
Bill Wood 95c54f6cdb update docs due to formatting error in git-utimes man page 2022-05-10 20:03:55 -04:00
Bill Wood 34237934d4 update per shellcheck advice 2022-05-10 19:42:12 -04:00
Bill Wood 8c744569c4 update git-utimes.{1,html} to add --newer flag 2022-05-10 15:30:28 -04:00
罗泽轩 458cb9fdad
Merge pull request #966 from tfendin/git-standup-implicit-week
Added config git-extras.standup-implicit-week for git standup
2022-05-09 13:06:33 +08:00
罗泽轩 12b27415ea
Merge pull request #969 from wpwoodjr/master
`git-utimes`: add --newer flag and don't touch files in the working tree and index
2022-05-09 13:04:40 +08:00
Tobias Fendin 0701c9e4d7 Don't read git-extras.standup.implicit-week twice in git standup 2022-05-07 12:31:16 +02:00
Bill Wood 42fd8a9e72 quote mod_s and git_s integer vars 2022-05-06 08:33:30 -04:00
Bill Wood 311edadd55 Merge branch 'master' of https://github.com/wpwoodjr/git-extras 2022-05-05 11:21:41 -04:00
Bill Wood d8dd44d8eb
Merge branch 'tj:master' into master 2022-05-05 11:21:07 -04:00
Bill Wood 41df8dc7f3 updates per request of spacewander 2022-05-05 11:19:30 -04:00
Bill Wood 48beb58670
Update Commands.md
Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com>
2022-05-05 10:43:48 -04:00
罗泽轩 5e07a96c57
Merge pull request #965 from tfendin/document-git-standup-w
Document git standup -w option
2022-05-05 11:03:53 +08:00
Bill Wood 3ef09dac72
Merge branch 'tj:master' into master 2022-05-02 09:17:00 -04:00
Bill Wood 799aa6e19a allow modification date updates for symbolic links 2022-05-01 22:01:57 -04:00
Bill Wood aa6b547f55 add --newer flag and ignore files in the working tree or index 2022-05-01 16:48:57 -04:00
Tobias Fendin f334dff09c Documented config git-extras.standup.implicit-week 2022-04-29 14:01:10 +02:00
Tobias Fendin 055110dd35 Minor change to git standup -w documentation 2022-04-29 11:08:51 +02:00
Tobias Fendin 745c7ab500 Improved check if -w or -d was given to git standup 2022-04-29 11:04:04 +02:00
Tobias Fendin 7cb5c55e13 Renamed config git-extras.standup.implicit-week from
git-extras.standup-implicit-week
2022-04-29 09:56:26 +02:00
罗泽轩 0d7334a63f
Merge pull request #967 from SukkaW/replace-git-io
docs(instllation): replace `git.io`
2022-04-29 14:24:41 +08:00
Tobias Fendin 5754b81047 Documented weekend behaviour of git standup -w 2022-04-28 19:24:12 +02:00
SukkaW 2aadd3ed41 docs(instllation): replace git.io 2022-04-28 11:31:59 +08:00
spacewander d2a09c1753 Bump version to 6.5.0-dev
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2022-04-27 21:06:07 +08:00
Tobias Fendin c7756455cb Added config git-extras.standup-implicit-week for git standup 2022-04-26 15:45:59 +02:00
Tobias Fendin 4fcd233bf2 Document git standup -w option 2022-04-26 15:24:43 +02:00
spacewander a73c19c14b Version 6.4.0
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2022-04-22 16:32:01 +08:00
罗泽轩 6bf2b0bc4c
Merge pull request #964 from spacewander/bro
git-browse/browse-ci: use powershell.exe only when it is available
2022-04-22 16:05:26 +08:00
spacewander d17f02979e git-browse/browse-ci: use powershell.exe only when it is available
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2022-04-19 20:51:22 +08:00
罗泽轩 716459f79d
Merge pull request #944 from SimonTate/feature/sem-ver-prefix
git-release: Add prefix to semver
2022-04-18 15:06:34 +08:00
罗泽轩 02263ca949
Merge pull request #957 from jackwasey/quiet-or-stderr
quiet option, errors to stderr
2022-04-18 15:06:04 +08:00
Simon Tate 613b909765 git-release: Add prefix to semver
To allow different release names other than specifically mm.vv.pp, this
adds the ability to use a prefix too.

For example, you can use:

```
$ git release --semver minor --prefix "prefix-"
```

This would allow the previous tags to be in the form `prefix-0.1.0`. A
more obvious use case for this would be a single letter, such as `v` or
`r` to use tags like: `r0.1.0` or `v0.1.0`.
2022-04-15 13:13:02 +01:00
罗泽轩 f556430122
Merge pull request #959 from pbnj/feat/git-browse-ci
feat: implement git-browse-ci
2022-04-13 20:14:53 +08:00
Peter Benjamin 9390d450aa fix: fix stderr & stdout redirect 2022-04-11 08:51:21 -07:00
罗泽轩 3d6f9456a3
Merge pull request #962 from spacewander/gicc
tweak git-ignore/ignore-io
2022-04-11 19:38:56 +08:00
spacewander 27ee8f0132 git-ignore-io: make sure .gi_list is created before access 2022-04-08 21:08:52 +08:00
spacewander 678a0c2e71 git-ignore: the directory of target file may not exist
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
2022-04-08 21:08:29 +08:00
Peter Benjamin 11a2801231 fix(git-browse-ci): set pipefail 2022-03-20 18:53:05 -07:00
Peter Benjamin d7565ee844 docs: fix docs 2022-03-19 20:03:44 -07:00
Peter Benjamin 02bbdba792 chore: update docs & completions 2022-03-19 20:00:57 -07:00
Peter Benjamin f513f58d9e feat: implement git-browse-ci 2022-03-19 19:39:03 -07:00
Jack O. Wasey c036c9cc22 quiet option, errors to stderr 2022-03-17 13:25:02 -04:00
罗泽轩 42458d838b
Merge pull request #956 from tfendin/xargs-conflicting-args 2022-03-01 11:44:14 +08:00
Tobias Fendin b2794c18a1 Removed xargs from git-effort 2022-02-25 16:45:54 +01:00
罗泽轩 760428fa46
Merge pull request #955 from katrinleinweber/summary-highlight-path-opt 2022-02-03 15:01:25 +08:00
Katrin Leinweber 76a3c1f28d Highlight path option more 2022-01-30 11:45:03 +01:00
罗泽轩 2025ebfb96
Merge pull request #951 from tfendin/local-commits-all-remotes 2022-01-06 11:50:40 +08:00
Tobias Fendin 5467c2ff5a Updated manual for git local-commits, must track a branch 2022-01-04 17:55:24 +01:00
罗泽轩 843b958c9a
Merge pull request #946 from tfendin/default_branch_logic 2022-01-04 14:11:56 +08:00
Tobias Fendin 7aa178acc9 Made local-commits work with all remotes 2022-01-03 16:45:09 +01:00
Tobias Fendin 2d722c93b8 Updated Readme.md after review 2022-01-03 16:13:28 +01:00
罗泽轩 b772251074
Merge pull request #949 from tfendin/git-ignore-ensure-newline 2022-01-03 21:24:34 +08:00
Tobias Fendin 777fcade17 Added section about main branch to the Readme file 2022-01-01 12:34:22 +01:00
罗泽轩 e370b19190
Merge pull request #947 from tfendin/git-unlock-synopsis-fix 2021-12-31 14:44:36 +08:00
罗泽轩 82b33ac4c7
Merge pull request #948 from tfendin/git-ignore-core-excludesFile 2021-12-31 14:43:55 +08:00
tfendin fff5fde917
Clarified comment of when newline should be added
Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com>
2021-12-30 12:47:29 +01:00
Tobias Fendin edffba3c2a Updated git-extras manual after review. 2021-12-30 12:41:28 +01:00
Tobias Fendin 11640196b2 git-ignore: ensure new patterns comes after a newline 2021-12-29 15:07:19 +01:00
Tobias Fendin 6b0ef19caf Fixed bug in git-ignore which ignored config core.excludeFiles 2021-12-29 14:33:28 +01:00
Tobias Fendin d914ed3a89 Spell corr in git-unlock synopsis 2021-12-29 14:08:57 +01:00
Tobias Fendin b2743d785f Consider init.defaultBranch in git_extra_default_branch, changed default
branch to main.
2021-12-29 13:57:12 +01:00
罗泽轩 2370a4e27b
Merge pull request #885 from spacewander/add_pkg 2021-11-26 12:47:16 +08:00
spacewander 2fdd664594 doc: add packaging status 2021-11-23 09:40:20 +08:00
罗泽轩 b33ec65e5f
Merge pull request #940 from overengineer/feature/magic
git-magic: Automated commit messages
2021-10-10 12:09:51 +08:00
overengineer 3f2ad3fa8f Ignored error when git restore --staged fails when there is no commit 2021-10-08 07:36:29 +03:00
overengineer e0101470ca Merge branch 'master' into merge/magic 2021-10-08 07:15:16 +03:00
overengineer 86bb1a6764 Added git-magic command 2021-10-08 07:05:50 +03:00
spacewander 96890821b0 Bump version to 6.4.0-dev 2021-10-03 21:25:29 +08:00
spacewander 0c0be92445 Version 6.3.0 2021-10-02 13:25:45 +08:00
罗泽轩 cc9b972e25
Merge pull request #942 from spacewander/ffgd 2021-10-02 13:06:40 +08:00
spacewander 760e64b864 git-delete-squashed-branches: fail fast if can't checkout 2021-10-01 20:04:28 +08:00
罗泽轩 f6fa554077
Merge pull request #939 from SimonTate/feature/force-clear
git-clear: add force option
2021-09-16 15:16:20 +08:00
Simon Tate bc8876b063 git-clear: add force option
Add a new -f/--force option to git-clear to allow usage in scripts,
or without user input.

Add -h/--help/? for help

Also update the manual for git-clear for these changes.
2021-09-14 13:45:50 +01:00
罗泽轩 c30ac80d72
Merge pull request #936 from tfendin/multiline_synopsis_fix
Format fix of Synopsis in manuals
2021-08-19 11:27:18 +08:00
罗泽轩 4de7b34c9f
Merge pull request #937 from tfendin/dedup_email_case_insensitive
Compare email adresses case insensitive in --dedup-by-email
2021-08-17 08:59:46 +08:00
罗泽轩 0edcf8b7e6
Merge pull request #934 from tfendin/no-merge-summary
No merge summary
2021-08-17 08:59:30 +08:00
Tobias Fendin 5feb0db212 Adopted git --dedup-by-email to the project coding style 2021-08-14 14:44:21 +02:00
Tobias Fendin 703a8481c6 summary --dedup-by-email now compares email adresses case insensitive. 2021-08-12 22:05:22 +02:00
Tobias Fendin 3413eeb058 Updated lines under Synopsis in the manuals so they are commonly formatted 2021-08-11 17:33:01 +02:00
Tobias Fendin a7b7f05b5b Add incompability check for git summary --line --no-merges, split synopsis in its manual. 2021-08-11 16:38:21 +02:00
罗泽轩 a07c5ece5d
Merge pull request #935 from ax1036/git-un-lock
fix git (un)lock with spaced filenames
2021-08-11 21:22:48 +08:00
ax1036 296f2a7ad2 fix git (un)lock with spaced filenames 2021-08-10 14:17:33 +02:00
Tobias Fendin e9708ba9f4 Updated completion for git summary 2021-08-07 17:31:51 +02:00
Tobias Fendin 3c91de7a1e Updated documentation for git-summary --no-merges 2021-08-07 17:31:51 +02:00
Tobias Fendin 3c632d9e32 Added option --no-merges to git summary 2021-08-07 17:31:49 +02:00
罗泽轩 3821b2c537
Merge pull request #932 from spacewander/git-su
git-summary: support filtering `--line` with path
2021-07-22 10:51:02 +08:00
spacewander ffad35c8db git-summary: support filtering --line with path 2021-07-09 17:57:30 +08:00
罗泽轩 d28ad34bcb
Merge pull request #865 from pkitszel/pkitszel-git-abort 2021-07-08 15:00:15 +08:00
Przemek Kitszel b8e58ee9ba Add git-abort
New subcommand just calls git $op --abort, where $op is current git operation, like rebase
2021-07-05 23:43:39 +02:00
罗泽轩 b225ed4934
Merge pull request #928 from duckunix/master 2021-06-09 21:03:33 +08:00
Don Harper 30383f9551 Update point to github docs for forks from API 2021-06-07 20:12:22 -05:00
Don Harper 1f72512ef0 update working 2021-06-06 21:26:01 -05:00
Don Harper b8b02d8ef1 git-fork | Update man/md/html pages to talk about need of the github personal access token 2021-06-04 23:04:31 -05:00
Don Harper d8b1007f68 Updated documentation (AUTHORS) 2021-06-04 20:12:41 -05:00
Don Harper 64993dbc7f bin/git-fork | updating for feedback from PR #928
Added support for pulling personal-access-token, borrowing from git-pull-request
2021-06-04 20:08:07 -05:00
Don Harper 4a4c5296f3 git-fork | update to match REST-API auth standards as of 2021-06
- Removed prompt for OATH Token
- Change prompt from 'password' to 'GitHub Personal Access Token'
2021-06-03 20:19:56 -05:00
罗泽轩 206fe7ccfb
Merge pull request #926 from nicokosi/patch-1 2021-06-02 09:40:28 +08:00
Nicolas Kosinski f1eab6a8a7
Update install doc: Homebrew is not just for macOS
I can confirm it also works Linux. I just installed `git-extras` via `Homebrew` on my Ubuntu machine:
```sh
$ uname -srv
Linux 5.8.0-53-generic #60~20.04.1-Ubuntu SMP Thu May 6 09:52:46 UTC 2021

$ brew install git-extras
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/cask and homebrew/core).
==> New Formulae
code-minimap              principalmapper           pywhat
==> Updated Formulae
Updated 70 formulae.
==> Updated Casks
Updated 36 casks.

==> Downloading https://ghcr.io/v2/linuxbrew/core/git-extras/manifests/6.2.0
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/linuxbrew/core/git-extras/blobs/sha256:5a86
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/
######################################################################## 100.0%
==> Pouring git-extras--6.2.0.x86_64_linux.bottle.tar.gz
==> Caveats
To load Zsh completions, add the following to your .zshrc:
  source /home/linuxbrew/.linuxbrew/opt/git-extras/share/git-extras/git-extras-completion.zsh
==> Summary
🍺  /home/linuxbrew/.linuxbrew/Cellar/git-extras/6.2.0: 146 files, 388.4KB
```
2021-06-01 14:58:26 +02:00
罗泽轩 cd8fe4f6e2
Merge pull request #924 from allejo/feature/delete-squashed-branches 2021-05-27 20:13:41 +08:00
Vladimir Jimenez bebcbd11ca
delete-squashed-branches: Make side-effect clear in docs 2021-05-25 20:50:04 -07:00
Vladimir Jimenez fb1e5db6d7
delete-squashed-branches: Make branch checkout side effect clear 2021-05-24 13:55:32 -07:00
Vladimir Jimenez 910242c92b
Add delete-squashed-branches command 2021-05-24 00:13:57 -07:00
罗泽轩 48b2c8fd37
Merge pull request #923 from CleanMachine1/patch-1 2021-05-24 12:16:20 +08:00
CleanMachine1 d86abec6a2
Picture is broken 2021-05-22 19:57:11 +01:00
罗泽轩 1894f453f6
Merge pull request #920 from matan129/patch-1 2021-04-22 15:16:20 +08:00
Matan Rosenberg c35c84a279
Ignore checked-out branches on different worktrees 2021-04-20 09:49:23 +03:00
罗泽轩 a62a67ef5f
Merge pull request #917 from heirecka/remove-git-line-summary-man-page
doc: Remove man page for git-line-summary
2021-03-27 10:21:50 +08:00
spacewander d0c40851c0 Bump version to 6.3.0-dev 2021-03-27 10:11:31 +08:00
Heiko Becker 52ada403ef doc: Remove man page for git-line-summary
git line-summary was deprecated in favour of git summary --line and
removed with d4de567ca7.
2021-03-26 23:59:33 +01:00
255 changed files with 8992 additions and 2757 deletions

30
.editorconfig Normal file
View file

@ -0,0 +1,30 @@
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 Normal file
View file

@ -0,0 +1,2 @@
gool
Cant

12
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,12 @@
<!--
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 Normal file
View file

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View file

@ -1,28 +1,112 @@
name: ci name: ci
on: on:
push: push:
branches: [ master ] branches: [main]
pull_request: pull_request:
branches: [ master ] branches: [main]
jobs: 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: build:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: platform:
- ubuntu-18.04 - ubuntu-latest
- macos-latest - macos-latest
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- name: Check out code. - name: Check out code.
uses: actions/checkout@v2 uses: actions/checkout@v7
- name: Linux Install - name: Linux Install
if: matrix.platform == 'ubuntu-18.04' if: matrix.platform == 'ubuntu-latest'
run: | run: sudo apt-get install -y bsdmainutils
sudo apt-get install -y bsdmainutils
- name: Script - name: Script
run: | run: ./check_integrity.sh
./check_integrity.sh $(find bin | cut -b 5- | xargs)
- name: Brew release - name: Brew release
if: matrix.platform == 'macos-latest' if: matrix.platform == 'macos-latest'
run: | run: |

18
.github/workflows/stale.yaml vendored Normal file
View file

@ -0,0 +1,18 @@
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 Normal file
View file

@ -0,0 +1,3 @@
.venv/
__pycache__/
coverage/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "vendor/bats-all"]
path = vendor/bats-all
url = https://github.com/hyperupcall/bats-all

5
.pytest.ini Normal file
View file

@ -0,0 +1,5 @@
[pytest]
minversion = 7.4
addopts = -ra -q
testpaths = tests
faulthandler_timeout = 5

73
AUTHORS
View file

@ -1,24 +1,23 @@
git-extras is written and maintained by TJ Holowaychuk and git-extras is written and maintained by various contributors:
various contributors:
Development Lead
````````````````
- TJ Holowaychuk <tj@vision-media.ca>
Maintainers Maintainers
``````````` ```````````
- TJ Holowaychuk <tj@vision-media.ca>
- Hemanth.HM <hemanth.hm@gmail.com> - Hemanth.HM <hemanth.hm@gmail.com>
- Nimit Kalra <me@nimit.io> - Nimit Kalra <me@nimit.io>
- Nicolai Skogheim <nicolai.skogheim@gmail.com> - Nicolai Skogheim <nicolai.skogheim@gmail.com>
- spacewander <spacewanderlzx@gmail.com> - spacewander <spacewanderlzx@gmail.com>
- Edwin Kofler <edwin@kofler.dev>
- Leroy <vanpipy@gmail.com>
Patches and Suggestions Patches and Suggestions
``````````````````````` ```````````````````````
- Jonhnny Weslley - Jonhnny Weslley
- Tobias Fendin
- LeeW - LeeW
- timfeirg
- Niklas Schlimm - Niklas Schlimm
- nickl- - nickl-
- timfeirg
- CJ - CJ
- Paul Wise - Paul Wise
- Damien Tardy-Panis - Damien Tardy-Panis
@ -26,6 +25,7 @@ Patches and Suggestions
- Yuriy VG - Yuriy VG
- Leila Muhtasib - Leila Muhtasib
- Richard Fearn - Richard Fearn
- Bill Wood
- Jesús Espino - Jesús Espino
- Rémy HUBSCHER - Rémy HUBSCHER
- go2null - go2null
@ -34,13 +34,20 @@ Patches and Suggestions
- Jan Schulz - Jan Schulz
- Kenneth Reitz - Kenneth Reitz
- Mark Pitman - Mark Pitman
- Peter Benjamin
- Günther Grill
- Luke Childs - Luke Childs
- Sasha Khamkov - Sasha Khamkov
- equt - equt
- oikarinen
- vyas - vyas
- Don Harper
- Pierre Ayoub
- Robin Winslow - Robin Winslow
- Ross Smith II
- Yi EungJun - Yi EungJun
- grindhold - grindhold
- wyattscarpenter
- Aggelos Orfanakos - Aggelos Orfanakos
- Camille Reynders - Camille Reynders
- Carlos Prado - Carlos Prado
@ -52,29 +59,34 @@ Patches and Suggestions
- Rico Sta. Cruz - Rico Sta. Cruz
- Takuma Yamaguchi - Takuma Yamaguchi
- thomas menzel - thomas menzel
- vanpipy
- vr8ce - vr8ce
- Brian J Brennan - Brian J Brennan
- Dominik Gedon - Dominik Gedon
- Hogan Long - Hogan Long
- Ivan Malopinsky - Ivan Malopinsky
- John Bachir
- Jonathan "Duke" Leto - Jonathan "Duke" Leto
- Julio Napurí - Julio Napurí
- Justin Dugger - Justin Dugger
- Nils Winkler - Nils Winkler
- Philipp Klose - Philipp Klose
- Raphael Boidol
- Richard Russon - Richard Russon
- Sam Bostock - Sam Bostock
- Vladimir Jimenez
- gisphm - gisphm
- jacobherrington - jacobherrington
- overengineer
- phigoro - phigoro
- wooorm - wooorm
- Alexis GRIMALDI - Alexis GRIMALDI
- Allan Odgaard - Allan Odgaard
- Andre Cerqueira - Andre Cerqueira
- Austin Ziegler
- Beth Skurrie - Beth Skurrie
- Brice Dutheil - Brice Dutheil
- Curtis McEnroe - Curtis McEnroe
- Daniele Paolella
- David Rogers - David Rogers
- Devin Withers - Devin Withers
- Domenico Rotiroti - Domenico Rotiroti
@ -83,7 +95,9 @@ Patches and Suggestions
- Gert Van Gool - Gert Van Gool
- Guillaume Seren - Guillaume Seren
- Hozefa - Hozefa
- Igor Ostapenko
- Jean Jordaan - Jean Jordaan
- Joshua Li
- Justin Guenther - Justin Guenther
- Kylie McClain - Kylie McClain
- Marc Harter - Marc Harter
@ -117,6 +131,7 @@ Patches and Suggestions
- Andrew Griffiths - Andrew Griffiths
- Andrew Marcinkevičius - Andrew Marcinkevičius
- Andrew Starr-Bochicchio - Andrew Starr-Bochicchio
- Andrew Sullivan Cant
- Andrey Elizarov - Andrey Elizarov
- Angel Aguilera - Angel Aguilera
- Antoine Beaupré - Antoine Beaupré
@ -133,7 +148,10 @@ Patches and Suggestions
- Carlos Rodríguez - Carlos Rodríguez
- Christophe Badoit - Christophe Badoit
- Ciro Nunes - Ciro Nunes
- CleanMachine1
- CodeByZach
- Craig MacGregor - Craig MacGregor
- Dan Goodliffe
- Dan Jackson - Dan Jackson
- Dan Kilman - Dan Kilman
- Daniel Bruder - Daniel Bruder
@ -146,69 +164,97 @@ Patches and Suggestions
- Edward Betts - Edward Betts
- Eli Schwartz - Eli Schwartz
- Emil Kjelsrud - Emil Kjelsrud
- Erik Skultety
- François M - François M
- George Crabtree - George Crabtree
- Gerrit-K
- Greg Allen - Greg Allen
- Guilhem Saurel
- Guillermo Rauch - Guillermo Rauch
- Gunnlaugur Thor Briem - Gunnlaugur Thor Briem
- Hasse Ramlev Hansen - Hasse Ramlev Hansen
- Heiko Becker
- Hugo Ruiz-Mireles
- Isaac Mungai - Isaac Mungai
- J.C. Yamokoski - J.C. Yamokoski
- Jack O. Wasey
- James Manning - James Manning
- James Zhu - James Zhu
- Jan Krueger - Jan Krueger
- Jared Baur
- Jarod Stewart - Jarod Stewart
- Jason Young - Jason Young
- Jens K. Mueller - Jens K. Mueller
- Jeremy Lin - Jeremy Lin
- Jesse Sipprell - Jesse Sipprell
- Jianjin Fan - Jianjin Fan
- Jobin Kurian
- Johannes Ewald - Johannes Ewald
- John Evans - John Evans
- John Hoffmann - John Hoffmann
- Jon Ander Peñalba - Jon Ander Peñalba
- Josh McKinney
- Joshua Appelman - Joshua Appelman
- José María Gutiérrez @TheTechOddBug - José María Gutiérrez @TheTechOddBug
- Katrin Leinweber
- Kevin Woo - Kevin Woo
- Konstantin Schukraft - Konstantin Schukraft
- Kurban Mallachiev
- Leandro López - Leandro López
- Lone
- Luis Miguel Hernanz - Luis Miguel Hernanz
- Maarten Winter - Maarten Winter
- Marco Glasbergen - Marco Glasbergen
- Matan Rosenberg
- Mathieu D. (MatToufoutu) - Mathieu D. (MatToufoutu)
- Matt - Matt
- Matt Colyer - Matt Colyer
- Matt Headley
- Matthew Avant - Matthew Avant
- Mattias Andersson
- Michael Komitee - Michael Komitee
- Michael Matuzak - Michael Matuzak
- Michele Bologna - Michele Bologna
- Montak Oleg - Montak Oleg
- Moritz Grauel - Moritz Grauel
- Nate Eagleson
- Nate Fischer - Nate Fischer
- Nathan Rajlich - Nathan Rajlich
- Nick Campbell - Nick Campbell
- Nick Payne - Nick Payne
- Nicolas Kosinski
- Niklas Fiekas - Niklas Fiekas
- Oliver Kopp - Oliver Kopp
- Orestis - Orestis
- Peter Bittner - Peter Bittner
- Piotr Górski
- Prayag Verma - Prayag Verma
- Przemek Kitszel
- R. Martinho Fernandes - R. Martinho Fernandes
- Raphael Fleischlin - Raphael Fleischlin
- Rasmus Wriedt Larsen - Rasmus Wriedt Larsen
- René
- Revisor
- Ricardo Fernández Serrata
- Riceball LEE - Riceball LEE
- Rob Kennedy - Rob Kennedy
- Robin von Bülow - Robin von Bülow
- Russ
- Ryan Bohn - Ryan Bohn
- Sachin Gupta - Sachin Gupta
- Sam Thursfield - Sam Thursfield
- Sandro
- Sascha Andres
- SchleimKeim
- Sean Molenaar - Sean Molenaar
- Sebastian Gniazdowski - Sebastian Gniazdowski
- Sebastián Mancilla - Sebastián Mancilla
- Simon Tate
- Stefan VanBuren - Stefan VanBuren
- Stephen Mathieson - Stephen Mathieson
- Steve Mao - Steve Mao
- Stu Feldt - Stu Feldt
- SukkaW
- Tim Preston - Tim Preston
- Timothy Hwang - Timothy Hwang
- Tin Lai - Tin Lai
@ -219,21 +265,32 @@ Patches and Suggestions
- TweeKane - TweeKane
- Valérian Galliat - Valérian Galliat
- Vitaly Chikunov - Vitaly Chikunov
- Wei Lee
- Wei Wu
- Wen Sun
- Wiktor Żurawik
- Xavier Krantz - Xavier Krantz
- Xiaopei Li - Xiaopei Li
- Zeeshan Ahmed - Zeeshan Ahmed
- ax1036
- creynders - creynders
- dead-horse - dead-horse
- eszabpt - eszabpt
- fengkx - fengkx
- johnpyp
- jykntr - jykntr
- kang
- meza - meza
- neydroid - neydroid
- nulltask - nulltask
- phanium
- sgleizes - sgleizes
- tfendin
- tiemonl - tiemonl
- zentarul - zentarul
- zeroDivisible - zeroDivisible
- zhiyanfoo
- Áron Hoffmann
- Étienne BERSAC - Étienne BERSAC
- ☃ pitr - ☃ pitr
- 单元源 - 单元源

View file

@ -1,15 +1,22 @@
## Your new git-extra command should support # Contributing
* OSX, Linux, BSD (You may need to browse their man page)<sup>*</sup> Thanks for contributing! Please read this document before you make a PR.
* Bash 3.2+ (If you aren't sure, see [the Bash changelog](https://www.tldp.org/LDP/abs/html/bash2.html))
* Git 2.1+
<sup>*</sup>If you aren't able to test your new command on a platform, ## Supported Platforms
make that clear in your PR and someone else may be able to test it on their system.
## To submit a new command, you should Any changes must support the following platforms:
Let's assume your new command is named `foo`. - 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`. 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`. 2. Read `./man/Readme.md` and write documentation for `git-foo`.
@ -17,6 +24,11 @@ Let's assume your new command is named `foo`.
4. Update `./etc/git-extras-completion.zsh`. Just follow existing code. 4. Update `./etc/git-extras-completion.zsh`. Just follow existing code.
5. (Optional) Update `./etc/bash_completion.sh`. 5. (Optional) Update `./etc/bash_completion.sh`.
6. (Optional) Update `./etc/git-extras.fish`. 6. (Optional) Update `./etc/git-extras.fish`.
7. Run `./check_integrity.sh foo` to check if all done. 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. 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`.

View file

@ -1,8 +1,10 @@
- [`git abort`](#git-abort)
- [`git alias`](#git-alias) - [`git alias`](#git-alias)
- [`git archive-file`](#git-archive-file) - [`git archive-file`](#git-archive-file)
- [`git authors`](#git-authors) - [`git authors`](#git-authors)
- [`git browse`](#git-browse) - [`git browse`](#git-browse)
- [`git browse-ci`](#git-browse-ci)
- [`git bulk`](#git-bulk) - [`git bulk`](#git-bulk)
- [`git brv`](#git-brv) - [`git brv`](#git-brv)
- [`git changelog`](#git-changelog) - [`git changelog`](#git-changelog)
@ -10,12 +12,15 @@
- [`git clear-soft`](#git-clear-soft) - [`git clear-soft`](#git-clear-soft)
- [`git coauthor`](#git-coauthor) - [`git coauthor`](#git-coauthor)
- [`git commits-since`](#git-commits-since) - [`git commits-since`](#git-commits-since)
- [`git continue`](#git-continue)
- [`git contrib`](#git-contrib) - [`git contrib`](#git-contrib)
- [`git count`](#git-count) - [`git count`](#git-count)
- [`git cp`](#git-cp) - [`git cp`](#git-cp)
- [`git create-branch`](#git-create-branch) - [`git create-branch`](#git-create-branch)
- [`git delete-branch`](#git-delete-branch) - [`git delete-branch`](#git-delete-branch)
- [`git delete-gone-branches`](#git-delete-gone-branches)
- [`git delete-merged-branches`](#git-delete-merged-branches) - [`git delete-merged-branches`](#git-delete-merged-branches)
- [`git delete-squashed-branches`](#git-delete-squashed-branches)
- [`git delete-submodule`](#git-delete-submodule) - [`git delete-submodule`](#git-delete-submodule)
- [`git delete-tag`](#git-delete-tag) - [`git delete-tag`](#git-delete-tag)
- [`git delta`](#git-delta) - [`git delta`](#git-delta)
@ -25,6 +30,7 @@
- [`git force-clone`](#git-force-clone) - [`git force-clone`](#git-force-clone)
- [`git fork`](#git-fork) - [`git fork`](#git-fork)
- [`git fresh-branch`](#git-fresh-branch) - [`git fresh-branch`](#git-fresh-branch)
- [`git get`](#git-get)
- [`git gh-pages`](#git-gh-pages) - [`git gh-pages`](#git-gh-pages)
- [`git graft`](#git-graft) - [`git graft`](#git-graft)
- [`git guilt`](#git-guilt) - [`git guilt`](#git-guilt)
@ -34,6 +40,7 @@
- [`git local-commits`](#git-local-commits) - [`git local-commits`](#git-local-commits)
- [`git lock`](#git-lock) - [`git lock`](#git-lock)
- [`git locked`](#git-locked) - [`git locked`](#git-locked)
- [`git magic`](#git-magic)
- [`git merge-into`](#git-merge-into) - [`git merge-into`](#git-merge-into)
- [`git merge-repo`](#git-merge-repo) - [`git merge-repo`](#git-merge-repo)
- [`git missing`](#git-missing) - [`git missing`](#git-missing)
@ -47,6 +54,7 @@
- [`git rebase-patch`](#git-rebase-patch) - [`git rebase-patch`](#git-rebase-patch)
- [`git release`](#git-release) - [`git release`](#git-release)
- [`git rename-branch`](#git-rename-branch) - [`git rename-branch`](#git-rename-branch)
- [`git rename-file`](#git-rename-file)
- [`git rename-tag`](#git-rename-tag) - [`git rename-tag`](#git-rename-tag)
- [`git rename-remote`](#git-rename-remote) - [`git rename-remote`](#git-rename-remote)
- [`git repl`](#git-repl) - [`git repl`](#git-repl)
@ -68,6 +76,8 @@
- [`git undo`](#git-undo) - [`git undo`](#git-undo)
- [`git unlock`](#git-unlock) - [`git unlock`](#git-unlock)
- [`git utimes`](#git-utimes) - [`git utimes`](#git-utimes)
- [`git unwip`](#git-unwip)
- [`git wip`](#git-wip)
## git extras ## git extras
@ -142,17 +152,20 @@ visionmedia (18):
## git summary ## git summary
Outputs a repo summary: Outputs a repo or path summary:
```bash ```bash
$ git summary $ git summary
project : git-extras project : git-extras
repo age : 10 months ago repo age : 10 months ago
commits : 163 branch : master
active : 60 days last active : 3 weeks ago
files : 93 active on : 93 days
authors : commits : 163
files : 93
uncommitted : 3
authors :
97 Tj Holowaychuk 59.5% 97 Tj Holowaychuk 59.5%
37 Jonhnny Weslley 22.7% 37 Jonhnny Weslley 22.7%
8 Kenneth Reitz 4.9% 8 Kenneth Reitz 4.9%
@ -169,8 +182,8 @@ authors :
1 Guillermo Rauch 0.6% 1 Guillermo Rauch 0.6%
``` ```
This command can also take a *commitish*, and will print a summary for commits in This command can also take a *committish*, and will print a summary for commits in
the commitish range: the committish range:
```bash ```bash
$ git summary v42.. $ git summary v42..
@ -213,6 +226,20 @@ project : git-extras
1 Daniel Schildt 0.0% 1 Daniel Schildt 0.0%
``` ```
The `--line` option can also take a path, which will print a filtered summary for that folder or file.
The option `--output-style` tries to put as much summary information of the repo into defined styled way as possible.
This is how the `tabular` output style and `oneline` output style look like
```bash
$ git summary --output-style tabular
# Repo | Age | Last active | Active on | Commits | Uncommitted | Branch
git-extras | 13 years | 7 hours ago | 807 days | 1703 | 3 | master
$ git summary --output-style oneline
git-extras / age: 13 years / last active: 7 hours ago / active on 807 days / commits: 1703 / uncommitted: 3 / branch: master
```
## git effort ## git effort
Displays "effort" statistics, currently just the number of commits per file, showing highlighting where the most activity is. The "active days" column is the total number of days which contributed modifications to this file. Displays "effort" statistics, currently just the number of commits per file, showing highlighting where the most activity is. The "active days" column is the total number of days which contributed modifications to this file.
@ -221,8 +248,6 @@ project : git-extras
node (master): git effort --above 15 {src,lib}/* node (master): git effort --above 15 {src,lib}/*
``` ```
![git effort](http://f.cl.ly/items/0b0w0S2K1d100e2T1a0D/Screen%20Shot%202012-02-08%20at%206.43.34%20PM.png)
If you wish to ignore files with commits `<=` a value you may use `--above`: If you wish to ignore files with commits `<=` a value you may use `--above`:
``` ```
@ -258,11 +283,20 @@ usage: git bulk [-g] ([-a]|[-w <ws-name>]) <git command>
git bulk --listall git bulk --listall
``` ```
Register a workspace so that `git bulk` knows about it (notice that <ws-root-directory> must be absolute path): Register a workspace so that `git bulk` knows about it (it will be registered in your `.gitconfig`):
```bash ```bash
$ git bulk --addworkspace personal ~/workspaces/personal $ git bulk --addworkspace personal ~/workspaces/personal
``` ```
Notice that `<ws-root-directory>` must be an absolute path (or an environment variable pointing to an absolute path).
In the case of a **single quoted environment variable**, it will be dereferenced at `git-bulk` runtime, suitable for dynamic workspaces (*e.g.*, defined in your `.bashrc`).
As an illustration:
```bash
$ git bulk --addworkspace personal '$PERSONAL_WORKSPACE'
```
With option `--from` the URL to a single repository or a file containing multiple URLs can be added and they will be cloned directly into the workspace. Suitable for the initial setup of a multi-repo project. With option `--from` the URL to a single repository or a file containing multiple URLs can be added and they will be cloned directly into the workspace. Suitable for the initial setup of a multi-repo project.
```bash ```bash
@ -339,6 +373,13 @@ $ git brv
2020-01-08 master origin/master 265b03e Merge pull request #816 from spacewander/git-sed-pathspec 2020-01-08 master origin/master 265b03e Merge pull request #816 from spacewander/git-sed-pathspec
``` ```
When your repo has many branches, it can be more convenient to see this list in reverse. This can be set as the default by setting the git-extras.brv.reverse git option to true.
```bash
$ git brv --reverse
2020-01-08 master origin/master 265b03e Merge pull request #816 from spacewander/git-sed-pathspec
2020-01-14 adds-git-brv fork/adds-git-brv 1ca0d76 Fixes #700: Adds git-brv
```
## git repl ## git repl
@ -347,14 +388,53 @@ Git read-eval-print-loop. Lets you run `git` commands without typing 'git'.
Commands can be prefixed with an exclamation mark (!) to be interpreted as Commands can be prefixed with an exclamation mark (!) to be interpreted as
a regular command. a regular command.
Type `exit` or `quit` to end the repl session. Type `exit`, `quit`, or `q` to end the repl session.
Any arguments to git repl will be taken as the first command to execute in
the repl.
### CONFIGURATION
Commands entered in a repl session will be saved to a history file and be available in
future sessions, similar to a shell or programming language repl. By default,
there is one global history file, ~/.git_repl_history. You can specify that your projects
each have their own independent history file. This file will be saved in .git_repl_history
at the top level of the repo, and will need to be added to the repo or global .gitignore.
```bash
# remove the --global flag to configure only an individual project to have its own history file
git config --global git-extras.repl.use-local-history "true"
```
You can specify a default command to run when hitting enter:
```bash
git config --global git-extras.repl.on-enter-command "git status -sb"
```
You can configure which character is used at the end of the prompt: (default `>`):
```bash
git config --global git-extras.repl.prompt-character "±"
```
You can specify the prefix for the prompt, or remove it (default `git`):
```bash
git config --global git-extras.repl.prefix ""
```
You can have the name of the current git repo shown in the prompt (default `false`):
```bash
git config --global git-extras.repl.show-project-name "true"
```
```bash ```bash
$ git repl $ git repl
git version 2.9.2 git version 2.34.1
git-extras version 3.0.0 git-extras version 7.3.0
type 'ls' to ls files below current directory, 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.
'!command' to execute any command or just 'subcommand' to execute any git subcommand
git (master)> ls-files git (master)> ls-files
History.md History.md
@ -385,9 +465,9 @@ $ git coauthor user user@email.com
2 files changed, 145 insertions(+), 0 deletions(-) 2 files changed, 145 insertions(+), 0 deletions(-)
create mode 100644 README.md create mode 100644 README.md
create mode 100644 CONTRIBUTING.md create mode 100644 CONTRIBUTING.md
$ git log -1 $ git log -1
commit b62ceae2685e6ece071f3c3754e9b77fd0a35c88 (HEAD -> master) commit b62ceae2685e6ece071f3c3754e9b77fd0a35c88 (HEAD -> master)
Author: user person <userperson@email.com> Author: user person <userperson@email.com>
Date: Sat Aug 17 17:33:53 2019 -0500 Date: Sat Aug 17 17:33:53 2019 -0500
@ -532,6 +612,20 @@ $ git rename-branch old-name new-name
$ git rename-branch new-name $ git rename-branch new-name
``` ```
## git rename-file
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.
```bash
# Rename a file
git rename-file old_filename new_filename
# Rename a directory
git rename-file old_directory new_directory
```
## git rename-tag ## git rename-tag
Rename a tag (locally and remotely). Rename a tag (locally and remotely).
@ -680,14 +774,14 @@ build
## git ignore-io ## git ignore-io
Generate sample gitignore file from [gitignore.io](https://www.gitignore.io) Generate sample gitignore file from [gitignore.io](https://www.toptal.com/developers/gitignore)
Without option, `git ignore-io <type>` shows the sample gitignore of specified types on screen. Without option, `git ignore-io <type>` shows the sample gitignore of specified types on screen.
```bash ```bash
$ git ignore-io vim $ git ignore-io vim
# Created by https://www.gitignore.io/api/vim # Created by https://www.toptal.com/developers/gitignore/api/vim
### Vim ### ### Vim ###
[._]*.s[a-w][a-z] [._]*.s[a-w][a-z]
@ -839,6 +933,21 @@ Delete local and remote tag `name`:
$ git delete-tag 0.0.1 $ git delete-tag 0.0.1
``` ```
## git delete-gone-branches
Deletes all local branches whose remote-tracking branch has been deleted (i.e. branches shown as `[gone]` in `git branch -vv`). Use `--dry-run` to preview what would be deleted.
```bash
$ git delete-gone-branches
Deleted branch feature-123 (was abc1234).
Deleted branch feature-456 (was def5678).
$ git delete-gone-branches --dry-run
Would delete the following branches:
feature-123
feature-456
```
## git delete-merged-branches ## git delete-merged-branches
Deletes branches that are listed in `git branch --merged`. Deletes branches that are listed in `git branch --merged`.
@ -851,14 +960,34 @@ Deleted feature/dashboard (was 923befa).
... ...
``` ```
## git delete-squashed-branches
Deletes branches that have been "squashed-merged" into a specified branch; this branch will be checked out as a side-effect. If no branch is specified, then it will default to the current checked out branch.
```bash
$ (feature-branch) git delete-squashed-branches main
Deleted branch dependabot/bundler/kramdown-2.3.1 (was 1d3fb00).
Deleted branch dependabot/bundler/rexml-3.2.5 (was a7e4052).
$ (main) git ...
```
## git fresh-branch ## git fresh-branch
Create empty local branch `name`: Create empty local branch `docs`:
```bash ```bash
$ git fresh-branch docs $ git fresh-branch docs
``` ```
## git get
Clone repository into a subdirectory of the configured path, `"$HOME/some-dir"`:
```bash
$ git config --global --add git-extras.get.clone-path "$HOME/some-dir"
$ git get 'https://github.com/hyperupcall/bake'
```
## git guilt ## git guilt
Calculate the change in blame between two revisions Calculate the change in blame between two revisions
@ -913,8 +1042,6 @@ $ git squash HEAD~3 "Work on a feature"
Populates the file matching `authors|contributors -i` with the authors of commits, according to the number of commits per author. Populates the file matching `authors|contributors -i` with the authors of commits, according to the number of commits per author.
Opens the file in `$EDITOR` when set.
See the ["MAPPING AUTHORS" section](https://git-scm.com/docs/git-shortlog#_mapping_authors) of **git-shortlog**(1) to coalesce together commits by the same person. See the ["MAPPING AUTHORS" section](https://git-scm.com/docs/git-shortlog#_mapping_authors) of **git-shortlog**(1) to coalesce together commits by the same person.
Updating AUTHORS file: Updating AUTHORS file:
@ -1076,6 +1203,14 @@ Internally this script uses `rsync` and not `scp` as the name suggests.
`git-rscp` - The reverse of `git-scp`. Copies specific files from the working directory of a remote repository to the current working directory. `git-rscp` - The reverse of `git-scp`. Copies specific files from the working directory of a remote repository to the current working directory.
The following options are available (must precede `<remote>`):
```bash
-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)
```
### Examples ### Examples
Copy unstaged files to remote. Useful when you want to make quick test without making any commits Copy unstaged files to remote. Useful when you want to make quick test without making any commits
@ -1236,7 +1371,7 @@ Creates a zip archive of the current git repository. The name of the archive wil
## git missing ## git missing
Print out which commits are on one branch or the other but not both. Print out which commits are on one branch or the other but not both. Optionally, you can specify a path to limit the comparison to a specific directory or file.
```bash ```bash
$ git missing master $ git missing master
@ -1244,6 +1379,12 @@ $ git missing master
> 97ef387 only on master > 97ef387 only on master
``` ```
```bash
$ git missing master -- src/
< ed52989 only on current branch, in src/ directory
> 7988c4b only on master, in src/ directory
```
## git lock ## git lock
Lock a local file `filename`: Lock a local file `filename`:
@ -1287,8 +1428,8 @@ $ git reset-file .htaccess dc82b19
## git mr ## git mr
Checks out a merge request from GitLab. Usage: `git mr <ID|URL> [REMOTE]`. Checks out a merge request from GitLab, or a pull request from Forgejo/Codeberg.
Default remote is `origin`. Usage: `git mr <ID|URL> [REMOTE]`. Default remote is `origin`.
``` bash ``` bash
$ git mr 51 $ git mr 51
@ -1300,12 +1441,21 @@ Switched to branch 'mr/51'
With full URL, the head is fetched from a temporary remote pointing to the base URL. With full URL, the head is fetched from a temporary remote pointing to the base URL.
``` bash ``` bash
$ git mr https://gitlab.com/owner/repository/merge_requests/51 $ git mr https://gitlab.com/owner/repository/merge_requests/51
From gitlab.com:owner/repository From gitlab.com:owner/repository
* [new ref] refs/merge-requests/51/head -> mr/51 * [new ref] refs/merge-requests/51/head -> mr/51
Switched to branch 'mr/51' Switched to branch 'mr/51'
``` ```
A Forgejo/Codeberg pull request URL is also supported:
``` bash
$ git mr https://codeberg.org/owner/repository/pulls/51
From codeberg.org:owner/repository
* [new ref] refs/pull/51/head -> mr/51
Switched to branch 'mr/51'
```
Just like [git pr](#git-pr), `git mr` accepts a `clean` argument to trash all Just like [git pr](#git-pr), `git mr` accepts a `clean` argument to trash all
`mr/` branches. Ensure current branch is not one. `mr/` branches. Ensure current branch is not one.
@ -1488,16 +1638,31 @@ Opens the current git repository website in your default web browser.
```bash ```bash
$ git browse $ git browse
$ git browse upstream
```
## git browse-ci
Opens the current git repository CI website (e.g. GitHub Actions, GitLab CI,
Bitbucket Pipelines) in your default web browser.
```bash
$ git browse-ci
$ git browse-ci upstream
``` ```
## git utimes ## git utimes
Change files modification time to their last commit date. Change files modification time to their last commit date. Does not touch files that are in the working tree or index.
The `--newer` flag preserves the original modification time of files that were committed from the local repo, by only touching files that are newer than their last commit date.
```bash ```bash
git-extras$ ls -l bin | head git-extras$ ls -l bin | head
total 308 total 308
-rwxr-xr-x 1 vt vt 489 Nov 8 13:56 git-alias -rwxr-xr-x 1 vt vt 489 Jul 28 2015 git-alias
-rwxr-xr-x 1 vt vt 1043 Nov 8 13:56 git-archive-file -rwxr-xr-x 1 vt vt 1043 Nov 8 13:56 git-archive-file
-rwxr-xr-x 1 vt vt 970 Nov 8 13:56 git-authors -rwxr-xr-x 1 vt vt 970 Nov 8 13:56 git-authors
-rwxr-xr-x 1 vt vt 267 Nov 8 13:56 git-back -rwxr-xr-x 1 vt vt 267 Nov 8 13:56 git-back
@ -1506,8 +1671,8 @@ total 308
-rwxr-xr-x 1 vt vt 6282 Nov 8 13:56 git-bulk -rwxr-xr-x 1 vt vt 6282 Nov 8 13:56 git-bulk
-rwxr-xr-x 1 vt vt 18561 Nov 8 13:56 git-changelog -rwxr-xr-x 1 vt vt 18561 Nov 8 13:56 git-changelog
-rwxr-xr-x 1 vt vt 215 Nov 8 13:56 git-clear -rwxr-xr-x 1 vt vt 215 Nov 8 13:56 git-clear
git-extras$ git utimes
+ touch -d 2015-08-09T19:27:49+08:00 bin/git-alias git-extras$ git utimes --newer
+ touch -d 2020-05-22T10:40:29+08:00 bin/git-archive-file + touch -d 2020-05-22T10:40:29+08:00 bin/git-archive-file
+ touch -d 2017-05-05T16:02:09+08:00 bin/git-authors + touch -d 2017-05-05T16:02:09+08:00 bin/git-authors
+ touch -d 2020-02-23T11:41:54+08:00 bin/git-back + touch -d 2020-02-23T11:41:54+08:00 bin/git-back
@ -1517,9 +1682,10 @@ git-extras$ git utimes
+ touch -d 2019-09-05T12:41:38+08:00 bin/git-changelog + touch -d 2019-09-05T12:41:38+08:00 bin/git-changelog
+ touch -d 2016-11-19T16:41:19+00:00 bin/git-clear + touch -d 2016-11-19T16:41:19+00:00 bin/git-clear
[...] [...]
git-extras$ ls -l bin | head git-extras$ ls -l bin | head
total 308 total 308
-rwxr-xr-x 1 vt vt 489 Aug 9 2015 git-alias -rwxr-xr-x 1 vt vt 489 Jul 28 2015 git-alias
-rwxr-xr-x 1 vt vt 1043 May 22 05:40 git-archive-file -rwxr-xr-x 1 vt vt 1043 May 22 05:40 git-archive-file
-rwxr-xr-x 1 vt vt 970 May 5 2017 git-authors -rwxr-xr-x 1 vt vt 970 May 5 2017 git-authors
-rwxr-xr-x 1 vt vt 267 Feb 23 2020 git-back -rwxr-xr-x 1 vt vt 267 Feb 23 2020 git-back
@ -1529,3 +1695,35 @@ total 308
-rwxr-xr-x 1 vt vt 18561 Sep 5 2019 git-changelog -rwxr-xr-x 1 vt vt 18561 Sep 5 2019 git-changelog
-rwxr-xr-x 1 vt vt 215 Nov 19 2016 git-clear -rwxr-xr-x 1 vt vt 215 Nov 19 2016 git-clear
``` ```
Note above, that because of the `--newer` flag, the file `git-alias` was not touched since its modified date is earlier than the commit date.
## git abort
Abort current revert, rebase, merge or cherry-pick, without the need to find exact command in history.
## git magic
Commits changes with a generated message.
## git continue
Continue current revert, rebase, merge or cherry-pick, without the need to find exact command in history.
## git wip
Create a Work In Progress(WIP) commit, which will include all changes in the
working directory. (i.e., changes to existing files, new files, removed files)
```bash
$ git wip
```
## git unwip
Undo a Work In Progress(WIP) commit and put all of those changes back into the
working directory.
```bash
$ git unwip
```

View file

@ -1,4 +1,298 @@
7.5.0 / 2026-05-09
==================
* Use standard prefix for home dir installs (#1248)
* refactor(abort): common `gitdir` (#1244)
* Fix man page usage docs (#1241)
* Fix `git-changelog` to handle whitespace in command arguments (#1232)
* chore(deps): bump bats-core/bats-action from 3.0.1 to 4.0.0 (#1230)
* add details when multiple active operations found (#1229)
* Add persistent history to `git-repl` (#1226)
* Support passing ref to `git-commits-since` (#1228)
* ability to specify command when hitting enter (#1223)
* Improve `git-repl` prompt (#1224)
* feat: `delete-branch` multiple unique branch names completions (#1221)
* chore(deps): bump actions/checkout from 5 to 6 (#1219)
* Made Git Magic use `--force-with-lease` (#1218)
* chore(deps): bump actions/stale from 9 to 10 (#1214)
* chore(deps): bump actions/setup-go from 5 to 6 (#1215)
* chore(deps): bump actions/setup-python from 5 to 6 (#1216)
* Fix minor typo in `git rename-branch` man page (#1211)
* Improvements to Bash completion (#1210)
* Fix use of `git-whatchanged` to `git-log` (#1212)
* chore(deps): bump actions/checkout from 4 to 5 (#1209)
* Bump version to 7.5.0-dev (#1207)
7.4.0 / 2025-06-19
==================
* Format comparisons, functions, and redirections to be consistent (#1201)
* add git-wip and git-unwip (#669)
* chore(deps): bump bats-core/bats-action from 3.0.0 to 3.0.1
* Mostly finish pytest to Bats conversion (#1200)
* Implement half of tests in Bats (#1187)
* Add stale bot for old PRs (#1186)
* feat(git-bulk): add new option to not follow hidden directories (#1195)
* Feat: allow git-summary showing full path of repository (#1193)
* feat(git-bulk): add new option to no follow symlinks (#1194)
* docs(git-bulk): Add zsh completion (#1190)
* fix(git-bulk): fix workspace selection when cd fails (#1197)
* fix(git-bulk): quiet find errors by default (#1196)
* fix(git-bulk): fix a bad integer expression (#1198)
* chore(deps): bump astral-sh/ruff-action from 2 to 3 (#1189)
* Fix all ShellCheck errors and add to CI (#1179)
* chore(deps): bump astral-sh/ruff-action from 1 to 2 (#1188)
* fix(ci): use poetry (#1183)
* Delete etc/test.fish (#1185)
* feat: add git-continue (#1176)
* feat: add ruff linter with ci check (#1182)
* fix(ci): missing dollar sign (#1184)
* fix(github-actions): changed files output for editorcondig-checker (#1180)
* Revert "feat: add ruff linter with ci check (#1178)" (#1181)
* feat: add ruff linter with ci check (#1178)
* Support `GITHUB_TOKEN` var for `git-fork` and `git-pull-request` (#1177)
* Bump version to 7.4.0-dev (#1175)
7.3.0 / 2024-10-20
==================
* Fix stripping trailing forward slash for git-get (#1172)
* Change git-cp to use cleaner branch approach (#1169)
* Improve warning for `git clear` (#1168)
* Enhance `git-repl` (#1160)
* Update some documentation that was out of sync (#1164)
* Use filetimes in check_integrity (#1162)
* Update git-alias.md: add brs to prevent incorrect line behavior (#1161)
* Update git-bulk.md: use correct stylization in synopsis (#1163)
* Update git-repl.md: typo: "let's" for "lets" (#1158)
* Update instructions for the OpenSUSE installation (#1157)
* Add pathspec support in `git-missing` (#1156)
* feat: add rename-file command (#1149)
* Update Commands.md (#1148)
* fix: git-summary commit count (#1147)
* tests: update dependencies (#1142)
* chore(deps): bump masesgroup/retrieve-changed-files from 2 to 3 (#1144)
* chore(deps): bump actions/setup-go from 4 to 5 (#1143)
* ci: add dependabot update for GitHub actions (#1141)
* ci: update actions (#1140)
* Bump version to 7.3.0-dev (#1139)
7.2.0 / 2024-04-21
==================
* refactor: test utils and simplify the case code (#1136)
* Add --proceed flag to git-delete-squashed-branches (#1134) (#1135)
* test(browse-ci): add unit tests (#1130)
* test(git-browse): add unit tests (#1127)
* feat: add reverse option to git-brv (#1123)
* fix(utimes): change /dev/stdin to -
* fix(utimes): Support filenames with backspaces, etc. (#1122)
* chore: add poetry to handle the tests of the git extras (#1121)
* Fix typo in ci.yml (#1120)
* Fix: harden utimes, use single quotes (#1109)
* Fix: patch git-utimes (fixes #1118) (#1119)
* add pull request manner (#1113)
* maintenance: Add my name as maintainer in AUTHORS (#1117)
* test(git-authors): add unit test (#1098)
* Add more comprensive dependencies (#1111)
* fix: git-standup find error (tj#1106) (#1110)
* Improve defaults for testing suite (#1104)
* feat: Speed up utimes (#1108)
* Overhaul CONTRIBUTING document (#1103)
* Bump version to 7.2.0-dev (#1099)
7.1.0 / 2023-10-28
==================
* git-cp: don't stop execution when merge.ff is not set (#1096)
* Allow `git cp` to create destination folder automatically (#1091)
* fix: Use `-iname` and expressions instead of `-iregex` (#1094)
* change installation source from master to main (#1092)
* test(git-archive-file): add unit test (#1084)
* ci: Use Go v1.20 for running `editorconfig-checker` (#1086)
* test(git-alias): add its unit test (#1077)
* makefile: Allow bypassing conflict check (#1080)
* fix: Restore zsh completions to working completion (#1079)
* fix: add missing quote in git-extras-completion.zsh (#1078)
* feat: Implement completion for `git coauthor` (#1074)
* fix: Move improvements via ShellCheck (#1075)
* feat(auto-test): CI with pytest (#1066)
* git-feature: add configurable branch separator (#1072)
* git-psykorebase: determine branch names with bash pattern substitution (#1073)
* git-cp: continue to merge when merge.ff is only (#1070)
* git archive-file: change base ARCHIVE_NAME (#1071)
* readme: Add secondary location of screencast (#1065)
* fix: Improve Bash hygiene (#1056)
* feat: Implement `git-get` command (#1045)
* Corrected grammar in bin/git-cp (#1061)
* Improve `git-ignore` command (#1047)
* I have made two improvements to the git-bulk: (#1054)
* Type properly `__gitex_command_successful` calls and capitalizate github to GitHub (#1049)
* Add typo checker
* Fix comment typo in git-authors (#1050)
* tweak: Fix typos (#1046)
* Makefile: init SYSCONFDIR before using it (#1044)
* Makefile: tune MANPREFIX configuration for FreeBSD (#1043)
* Makefile: set bash COMPL_DIR to FreeBSD expected defaults (#1042)
* Bump version to 7.1.0-dev (#1041)
7.0.0 / 2023-04-20
==================
* git-delete-tag: should not be quoted
* Add the current branch git summary (#1034)
* chore: Improve Bash variously (#1032)
* Change the oneline option to a tebular version in the git summary (#1031)
* chore: Remove `X`-style equality comparisons (#1028)
* chore: Various Bash improvements (#1029)
* fix: No longer pollute env with `GREP_OPTIONS`
* tweak editor config (#1027)
* revert the completion dir change on MacOS (#1026)
* Update gitignore.io urls to Toptal urls (#1025)
* Add EditorConfig file (#1022)
* Make the bulk output slimmer (#1020)
* chore: Fix more Bash inconsistencies (#1021)
* Repo status overview (#1017)
* Replace egrep with grep -E (#1019)
* chore: More Bash improvements and removing deprecations (#1016)
* chore: Improve quoting and builtin usage (#1015)
* Add summary fields (#1013)
* Fix quiet mode of the bulk command (#1014)
* git-summary: remove stray \ in grep call (#1010)
* Allow to run git-extras within git-bulk (#1008)
* Make git-summary accept multiple paths for the --lines argument (#1005)
* Makefile: ensure bash is used instead of /bin/sh (#1004)
* fix base branch in the pull-request command (#1003)
* Update docs for git abort change (#1000)
* Bump version to 6.6.0-dev (#997)
6.5.0 / 2022-10-06
==================
* git root: show '.' when using with -r in the root dir (#995)
* git standup: work as expected when no configured (#996)
* rephrase question depending on switch (#984)
* Include revert conflicts in git abort (#992)
* fix(git-effort):fork: retry: Resource temporarily unavailable #979 (#990)
* git-sync --force will sync without interaction (#989)
* fix(git-cp): keep the history of the files
* git-bulk: previous refactor redirected $PWD to stderr by mistake
* Fix lazy loading bash_completion from XDG_DATA_DIRS
* Minor changes to man/git-standup.md
* Replaced -g and -A with -F gpg|authordate
* fix(bin/git-browse): fix commit hash
* fix(bin/git-browse): fix bitbucket url
* feat: open website at file & line number or range
* #970 standup added -A flag for author date
* update docs due to formatting error in git-utimes man page
* update per shellcheck advice
* update git-utimes.{1,html} to add --newer flag
* Don't read git-extras.standup.implicit-week twice in git standup
* quote mod_s and git_s integer vars
* updates per request of spacewander
* Update Commands.md
* allow modification date updates for symbolic links
* add --newer flag and ignore files in the working tree or index
* Documented config git-extras.standup.implicit-week
* Minor change to git standup -w documentation
* Improved check if -w or -d was given to git standup
* Renamed config git-extras.standup.implicit-week from git-extras.standup-implicit-week
* Documented weekend behaviour of git standup -w
* docs(instllation): replace `git.io`
* Bump version to 6.5.0-dev
* Added config git-extras.standup-implicit-week for git standup
* Document git standup -w option
6.4.0 / 2022-04-22
==================
* Merge pull request #964 from spacewander/bro
* git-browse/browse-ci: use powershell.exe only when it is available
* Merge pull request #944 from SimonTate/feature/sem-ver-prefix
* Merge pull request #957 from jackwasey/quiet-or-stderr
* git-release: Add prefix to semver
* Merge pull request #959 from pbnj/feat/git-browse-ci
* fix: fix stderr & stdout redirect
* Merge pull request #962 from spacewander/gicc
* git-ignore-io: make sure .gi_list is created before access
* git-ignore: the directory of target file may not exist
* fix(git-browse-ci): set pipefail
* docs: fix docs
* chore: update docs & completions
* feat: implement git-browse-ci
* quiet option, errors to stderr
* Merge pull request #956 from tfendin/xargs-conflicting-args
* Removed xargs from git-effort
* Merge pull request #955 from katrinleinweber/summary-highlight-path-opt
* Highlight path option more
* Merge pull request #951 from tfendin/local-commits-all-remotes
* Updated manual for git local-commits, must track a branch
* Merge pull request #946 from tfendin/default_branch_logic
* Made local-commits work with all remotes
* Updated Readme.md after review
* Merge pull request #949 from tfendin/git-ignore-ensure-newline
* Added section about main branch to the Readme file
* Merge pull request #947 from tfendin/git-unlock-synopsis-fix
* Merge pull request #948 from tfendin/git-ignore-core-excludesFile
* Clarified comment of when newline should be added
* Updated git-extras manual after review.
* git-ignore: ensure new patterns comes after a newline
* Fixed bug in git-ignore which ignored config core.excludeFiles
* Spell corr in git-unlock synopsis
* Consider init.defaultBranch in git_extra_default_branch, changed default branch to main.
* Merge pull request #885 from spacewander/add_pkg
* doc: add packaging status
* Merge pull request #940 from overengineer/feature/magic
* Ignored error when git restore --staged fails when there is no commit
* Merge branch 'master' into merge/magic
* Added git-magic command
* Bump version to 6.4.0-dev
6.3.0 / 2021-10-02
==================
* Merge pull request #942 from spacewander/ffgd
* git-delete-squashed-branches: fail fast if can't checkout
* Merge pull request #939 from SimonTate/feature/force-clear
* git-clear: add force option
* Merge pull request #936 from tfendin/multiline_synopsis_fix
* Merge pull request #937 from tfendin/dedup_email_case_insensitive
* Merge pull request #934 from tfendin/no-merge-summary
* Adopted git --dedup-by-email to the project coding style
* summary --dedup-by-email now compares email adresses case insensitive.
* Updated lines under Synopsis in the manuals so they are commonly formatted
* Add incompability check for git summary --line --no-merges, split synopsis in its manual.
* Merge pull request #935 from ax1036/git-un-lock
* fix git (un)lock with spaced filenames
* Updated completion for git summary
* Updated documentation for git-summary --no-merges
* Added option --no-merges to git summary
* Merge pull request #932 from spacewander/git-su
* git-summary: support filtering `--line` with path
* Merge pull request #865 from pkitszel/pkitszel-git-abort
* Add git-abort
* Merge pull request #928 from duckunix/master
* Update point to github docs for forks from API
* update working
* git-fork | Update man/md/html pages to talk about need of the github personal access token
* Updated documentation (AUTHORS)
* bin/git-fork | updating for feedback from PR #928
* git-fork | update to match REST-API auth standards as of 2021-06
* Merge pull request #926 from nicokosi/patch-1
* Update install doc: Homebrew is not just for macOS
* Merge pull request #924 from allejo/feature/delete-squashed-branches
* delete-squashed-branches: Make side-effect clear in docs
* delete-squashed-branches: Make branch checkout side effect clear
* Add delete-squashed-branches command
* Merge pull request #923 from CleanMachine1/patch-1
* Picture is broken
* Merge pull request #920 from matan129/patch-1
* Ignore checked-out branches on different worktrees
* Merge pull request #917 from heirecka/remove-git-line-summary-man-page
* Bump version to 6.3.0-dev
* doc: Remove man page for git-line-summary
6.2.0 / 2021-03-26 6.2.0 / 2021-03-26
================== ==================

View file

@ -1,19 +1,31 @@
# Installing git-extras # Installation
## Dependencies ## Dependencies
Some commands require extra dependencies which are unavailable in some platforms. Some commands require extra dependencies which are unavailable in some platforms.
You may need to install them manually. You may need to install them manually. They are:
Those dependencies are listed below: * 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`)
* column 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 ## Installing with a package manager
Note that only the Homebrew package is maintained by the git-extras developers directly. 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. Other packages are maintained by the distribution's packagers or third-party volunteers.
[![Packaging status](https://repology.org/badge/vertical-allrepos/git-extras.svg)](https://repology.org/project/git-extras/versions)
### Debian ### Debian
```bash ```bash
@ -29,9 +41,10 @@ $ sudo dnf install git-extras
### openSUSE ### openSUSE
Substitute your openSUSE version in the command below (in this case we are considering openSUSE Leap 15.2): Substitute your openSUSE version in the command below (in this case we are considering openSUSE Leap 15.6):
```bash ```bash
$ sudo zypper ar https://download.opensuse.org/repositories/devel:/tools:/scm/openSUSE_Leap_15.2/devel:tools:scm.repo $ sudo zypper ar https://download.opensuse.org/repositories/devel:/tools:/scm/15.6/devel:tools:scm.repo
``` ```
and install it: and install it:
@ -62,8 +75,7 @@ $ nix-env -i git-extras
[Abdullah](https://github.com/AWAN) has written a [Pkgfile](https://abdullah.today/ports/git-extras/Pkgfile) for his beloved [distro](https://crux.nu). [Abdullah](https://github.com/AWAN) has written a [Pkgfile](https://abdullah.today/ports/git-extras/Pkgfile) for his beloved [distro](https://crux.nu).
### Homebrew
### macOS with Homebrew
```bash ```bash
$ brew install git-extras $ brew install git-extras
@ -71,7 +83,6 @@ $ 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. 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 ### Arch Linux
* [git-extras](https://aur.archlinux.org/packages/git-extras/) * [git-extras](https://aur.archlinux.org/packages/git-extras/)
@ -81,6 +92,7 @@ Installing from Homebrew will not give you the option omit certain `git-extras`
First, please install `Git for Windows 2.x` from 'https://github.com/git-for-windows/git/releases'. 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. Second, clone the `git-extras` repo into any folder you like.
```bash ```bash
git clone https://github.com/tj/git-extras.git git clone https://github.com/tj/git-extras.git
# checkout the latest tag (optional) # checkout the latest tag (optional)
@ -132,7 +144,7 @@ alternate location, specify a `PREFIX` when calling `make`.
```bash ```bash
# Non-root users can install under their home directory # Non-root users can install under their home directory
make install PREFIX=$HOME/software make install PREFIX=$HOME/.local
# For third-party software kept under /opt # For third-party software kept under /opt
make install PREFIX=/opt make install PREFIX=/opt
@ -142,7 +154,7 @@ If you want to relocate the bulk of the installation but still have configuratio
files installed to the system `/etc` dir or other alternate location, you can files installed to the system `/etc` dir or other alternate location, you can
specify `SYSCONFDIR` in addition to `PREFIX`. specify `SYSCONFDIR` in addition to `PREFIX`.
``` ```sh
$ sudo make install PREFIX=/usr/local SYSCONFDIR=/etc $ sudo make install PREFIX=/usr/local SYSCONFDIR=/etc
``` ```
@ -153,12 +165,13 @@ See the Makefile for details on advanced configuration options.
One-liner: One-liner:
```bash ```bash
curl -sSL https://git.io/git-extras-setup | sudo bash /dev/stdin curl -sSL https://raw.githubusercontent.com/tj/git-extras/main/install.sh | sudo bash /dev/stdin
``` ```
## Installing as Zsh plugin ## Installing as Zsh plugin
[ZInit](https://github.com/zdharma/zinit) can install git-extras by using: [ZInit](https://github.com/zdharma/zinit) can install git-extras by using:
```zsh ```zsh
zinit ice as"program" pick"$ZPFX/bin/git-*" src"etc/git-extras-completion.zsh" make"PREFIX=$ZPFX" zinit ice as"program" pick"$ZPFX/bin/git-*" src"etc/git-extras-completion.zsh" make"PREFIX=$ZPFX"
zinit light tj/git-extras zinit light tj/git-extras

View file

@ -1,14 +1,25 @@
PREFIX ?= /usr/local PREFIX ?= /usr/local
BINPREFIX ?= "$(PREFIX)/bin" BINPREFIX ?= "$(PREFIX)/bin"
SYSCONFDIR ?= $(PREFIX)/etc
SHELL := bash
OS = $(shell uname) OS = $(shell uname)
ifeq ($(OS), FreeBSD) ifeq ($(OS), FreeBSD)
MANPREFIX ?= "$(PREFIX)/man/man1" MANPREFIX ?= "$(PREFIX)/man/man1"
ifeq ($(MANPREFIX), /usr/local)
MANPREFIX = "/usr/local/man/man1"
endif
else else
MANPREFIX ?= "$(PREFIX)/share/man/man1" MANPREFIX ?= "$(PREFIX)/share/man/man1"
endif 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
SYSCONFDIR ?= $(PREFIX)/etc
BINS = $(wildcard bin/git-*) BINS = $(wildcard bin/git-*)
MANS = $(wildcard man/git-*.md) MANS = $(wildcard man/git-*.md)
MAN_BINS = $(filter-out man/git-extras.md, $(MANS)) MAN_BINS = $(filter-out man/git-extras.md, $(MANS))
@ -34,22 +45,24 @@ install: check
@if [ "$(INSTALL_VIA)" = brew ]; then \ @if [ "$(INSTALL_VIA)" = brew ]; then \
git apply brew-release.patch || { echo "Can't apply brew release patch"; exit 1; } \ git apply brew-release.patch || { echo "Can't apply brew release patch"; exit 1; } \
fi fi
@mkdir -p $(DESTDIR)$(MANPREFIX) mkdir -p $(DESTDIR)$(MANPREFIX)
@mkdir -p $(DESTDIR)$(BINPREFIX) mkdir -p $(DESTDIR)$(BINPREFIX)
@echo "... installing bins to $(DESTDIR)$(BINPREFIX)" @echo '... installing bins to '$(DESTDIR)$(BINPREFIX)
@echo "... installing man pages to $(DESTDIR)$(MANPREFIX)" @echo '... installing man pages to '$(DESTDIR)$(MANPREFIX)
$(eval TEMPFILE := $(shell mktemp -q $${TMPDIR:-/tmp}/git-extras.XXXXXX 2>/dev/null || mktemp -q)) $(eval TEMPFILE := $(shell mktemp -q $${TMPDIR:-/tmp}/git-extras.XXXXXX 2>/dev/null || mktemp -q))
@# chmod from rw-------(default) to rwxrwxr-x, so that users can exec the scripts @# chmod from rw-------(default) to rwxrwxr-x, so that users can exec the scripts
@chmod 775 $(TEMPFILE) @chmod 775 $(TEMPFILE)
$(eval EXISTED_ALIASES := $(shell \ $(eval EXISTED_ALIASES := $(shell \
git config --get-regexp 'alias.*' | awk '{print "git-" substr($$1, 7)}')) git config --get-regexp 'alias\..*' | awk '{print "git-" substr($$1, 7)}'))
@$(foreach COMMAND, $(COMMANDS), \ @$(foreach COMMAND, $(COMMANDS), \
disable=''; \ should_install='yes'; \
if test ! -z "$(filter $(COMMAND), $(EXISTED_ALIASES))"; then \ if [ ! -z "$(filter $(COMMAND), $(EXISTED_ALIASES))" ] && [ "$$SKIP_CONFLICT_CHECK" != yes ]; then \
read -p "$(COMMAND) conflicts with an alias, still install it and disable the alias? [y/n]" answer; \ read -p "$(COMMAND) conflicts with an alias, still install it? [y/n]: " answer; \
test "$$answer" = 'n' -o "$$answer" = 'N' && disable="true"; \ if [ "$$answer" = 'n' ] || [ "$$answer" = 'N' ]; then \
should_install="no"; \
fi; \
fi; \ fi; \
if test -z "$$disable"; then \ if [ "$$should_install" = 'yes' ]; then \
echo "... installing $(COMMAND)"; \ echo "... installing $(COMMAND)"; \
head -1 bin/$(COMMAND) > $(TEMPFILE); \ head -1 bin/$(COMMAND) > $(TEMPFILE); \
cat $(LIB) >> $(TEMPFILE); \ cat $(LIB) >> $(TEMPFILE); \
@ -69,8 +82,8 @@ install: check
cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX); \ cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX); \
echo "cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX)"; \ echo "cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX)"; \
fi fi
@mkdir -p $(DESTDIR)$(SYSCONFDIR)/bash_completion.d @mkdir -p $(COMPL_DIR)
cp -f etc/bash_completion.sh $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/git-extras cp -f etc/bash_completion.sh $(COMPL_DIR)/git-extras
@echo "" @echo ""
@echo "If you are a zsh user, you may want to 'source $(CODE_DIR)etc/git-extras-completion.zsh'" \ @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" "and put this line into ~/.zshrc to enable zsh completion"
@ -119,7 +132,7 @@ uninstall:
echo "... uninstalling $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN))"; \ echo "... uninstalling $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN))"; \
rm -f $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN)); \ rm -f $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN)); \
) )
rm -f $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/git-extras rm -f $(COMPL_DIR)/git-extras
clean: docclean clean: docclean
@ -127,4 +140,7 @@ docclean:
rm -f man/*.1 rm -f man/*.1
rm -f man/*.html rm -f man/*.html
.PHONY: default docs clean docclean check install uninstall test:
bats ./tests
.PHONY: default docs check install uninstall clean docclean test

View file

@ -6,15 +6,15 @@ Little git extras.
Just getting started? Check out these screencasts: Just getting started? Check out these screencasts:
* [introduction](https://vimeo.com/45506445) -- covering git-ignore, git-setup, git-changelog, git-release, git-effort and more * [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
## Installation ## Installation
See [Installation](Installation.md) page. See the [Installation](Installation.md) page.
## Commands ## Commands
Go to [Commands](Commands.md) page for basic usage and examples. Go to the [Commands](Commands.md) page for basic usage and examples.
__GIT utilities__ -- repo summary, repl, changelog population, author commit percentages and more __GIT utilities__ -- repo summary, repl, changelog population, author commit percentages and more
@ -23,3 +23,12 @@ __GIT utilities__ -- repo summary, repl, changelog population, author commit per
Interested in contributing? Awesome! Interested in contributing? Awesome!
Please read [Contributing](CONTRIBUTING.md) before you make a PR, thanks! 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`.

45
bin/git-abort Executable file
View file

@ -0,0 +1,45 @@
#!/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"

View file

@ -1,16 +1,45 @@
#!/usr/bin/env bash #!/usr/bin/env bash
options=""
usage() { usage() {
cat <<HERE cat <<HERE
usage: git alias # list all aliases usage: git alias [options] # list all aliases
or: git alias <search-pattern> # show aliases matching pattern or: git alias [options] <search-pattern> # show aliases matching pattern
or: git alias <alias-name> <command> # alias a command 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 HERE
} }
if [[ "$1" == "--local" || "$1" == "--global" ]]; then
options=$1
shift
fi
case $# in case $# in
0) git config --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort ;; 0)
1) git alias | grep -e "$1" ;; if [[ -z "$options" ]]; then
2) git config --global alias."$1" "$2" ;; 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 ;; *) >&2 echo "error: too many arguments." && usage && exit 1 ;;
esac esac

View file

@ -1,23 +1,23 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# extract current branch name # extract current branch name
BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null) BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
# get name of the most top folder of current directory, used for the # get name of the most top folder of current directory, used for the
# output filename # output filename
ARCHIVE_NAME=$(basename $(pwd)) ARCHIVE_NAME=$(basename "$(git rev-parse --show-toplevel)")
if [[ $BRANCH = tags* ]]; then if [[ $BRANCH = tags* ]]; then
BRANCH=$(git describe) BRANCH=$(git describe)
echo Building for tag \"$BRANCH\" echo Building for tag "\"$BRANCH\""
FILENAME=$ARCHIVE_NAME.$BRANCH.zip FILENAME=$ARCHIVE_NAME.$BRANCH.zip
else else
echo Building archive on branch \"$BRANCH\" echo Building archive on branch "\"$BRANCH\""
# get a version string, so archives will not be overwritten when creating # get a version string, so archives will not be overwritten when creating
# many of them # many of them
VERSION=$(git describe --always --long) VERSION=$(git describe --always --long)
# if not on master append branch name into the filename # if not on master append branch name into the filename
if [ "$BRANCH" = $(git_extra_default_branch) ]; then if [ "$BRANCH" = "$(git_extra_default_branch)" ]; then
FILENAME=$ARCHIVE_NAME.$VERSION.zip FILENAME=$ARCHIVE_NAME.$VERSION.zip
else else
FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip
@ -28,10 +28,10 @@ fi
FILENAME=${FILENAME//\//-} FILENAME=${FILENAME//\//-}
FILENAME=${FILENAME//\\/-} FILENAME=${FILENAME//\\/-}
# combine path and filename # combine path and filename
OUTPUT=$(pwd)/$FILENAME OUTPUT=$PWD/$FILENAME
# building archive # building archive
git archive --format zip --output $OUTPUT $BRANCH git archive --format zip --output "$OUTPUT" "$BRANCH"
# also display size of the resulting file # 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)"\)

View file

@ -3,7 +3,6 @@
LIST=false LIST=false
NO_EMAIL=false NO_EMAIL=false
FILE="" FILE=""
EDITOR=$(git var GIT_EDITOR)
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
@ -22,9 +21,9 @@ done
if ! $LIST; then if ! $LIST; then
FILE=$1 FILE=$1
if test "$FILE" = ""; then if [ -z "$FILE" ]; then
FILE=$(ls | egrep 'authors|contributors' -i|head -n1) FILE=$(find . -mindepth 1 -maxdepth 1 \( -iname '*authors*' -o -iname '*contributors*' \) | head -n1)
if test "$FILE" = ""; then if [ -z "$FILE" ]; then
FILE='AUTHORS' FILE='AUTHORS'
fi fi
fi fi
@ -36,11 +35,11 @@ fi
authors() { authors() {
if $NO_EMAIL; then if $NO_EMAIL; then
# eamil will be used to uniq authors. # email will be used to uniq authors.
git shortlog -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' \ git shortlog HEAD -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' \
| awk -F'<' '{gsub(/ +$/, "", $1); print $1}' | awk -F'<' '{gsub(/ +$/, "", $1); print $1}'
else else
git shortlog -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' git shortlog HEAD -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++'
fi fi
} }
@ -51,6 +50,5 @@ authors() {
if $LIST; then if $LIST; then
authors authors
else else
authors >> $FILE authors >> "$FILE"
test -n "$EDITOR" && $EDITOR $FILE
fi fi

View file

@ -1,54 +1,92 @@
#!/usr/bin/env bash #!/usr/bin/env bash
if [[ $1 == "" ]] remote=${1:-""}
then branch=""
branch=$(git rev-parse --abbrev-ref HEAD 2&> /dev/null) 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") remote=$(git config branch."${branch}".remote || echo "origin")
else
remote=$1
fi fi
if [[ $remote == "" ]] if [[ $remote == "" ]]; then
then
echo "Remote not found" echo "Remote not found"
exit 1 exit 1
fi fi
remote_url=$(git remote get-url $remote) remote_url=$(git remote get-url "$remote") || exit $?
if [[ $? -ne 0 ]] if [[ $remote_url = git@* ]]; then
then url=$(echo "$remote_url" | sed -E -e 's/:/\//' -e 's/\.git$//' -e 's/.*@(.*)/http:\/\/\1/')
exit $? elif [[ $remote_url = http* ]]; then
fi
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} url=${remote_url%.git}
fi fi
case "$OSTYPE" in # construct urls
darwin*) commit_hash=$(git rev-parse HEAD 2>/dev/null)
# MacOS commit_or_branch=${commit_hash:-${branch}}
open $url
;; if [[ $remote_url =~ gitlab ]]; then
msys) # construct gitlab urls
# Git-Bash on Windows # https://gitlab.com/<user_or_group>/<repo>/-/blob/<commit_or_branch>/<filename>#L<line1>-<line2>
start $url if [[ -n ${filename} ]]; then
;; url="${url}/-/blob/${commit_or_branch}/${filename}"
linux*) if [[ -n "${line1}" ]]; then
# Handle WSL on Windows url="${url}#L${line1}"
if uname -a | grep -i -q Microsoft if [[ -n "${line2}" ]]; then
then url="${url}-${line2}"
powershell.exe -NoProfile start $url fi
else
xdg-open $url
fi fi
;; fi
*) elif [[ $remote_url =~ github ]]; then
# fall back to xdg-open for BSDs, etc. # construct github urls
xdg-open $url # 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 esac

60
bin/git-browse-ci Executable file
View file

@ -0,0 +1,60 @@
#!/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

View file

@ -7,6 +7,20 @@ if ! (( BASH_VERSINFO[0] > 4 ||
exit 1 exit 1
fi 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 if [[ -t 1 ]]; then
shopt -s checkwinsize shopt -s checkwinsize
COLUMNS=$(tput cols) COLUMNS=$(tput cols)
@ -36,9 +50,14 @@ for b in "${!upstream[@]}"; do
done done
mapfile -t ordered < <( 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 for b in "${!date[@]}"; do
printf '%d\t%s\n' "${date[$b]}" "$b" printf '%d\t%s\n' "${date[$b]}" "$b"
done | sort -rn | cut -f2- done | sort -n $reverse_opt | cut -f2-
) )
current=$(git symbolic-ref -q --short HEAD) current=$(git symbolic-ref -q --short HEAD)
@ -59,4 +78,4 @@ for b in "${ordered[@]}"; do
"$color_branch_upstream" "-$uwidth" "${upstream[$b]}" "$reset" \ "$color_branch_upstream" "-$uwidth" "${upstream[$b]}" "$reset" \
"$color_diff_commit" "-$hwidth" "${hash[$b]}" "$reset" \ "$color_diff_commit" "-$hwidth" "${hash[$b]}" "$reset" \
"$msg" "$msg"
done done

View file

@ -8,12 +8,15 @@ bldred=${txtbld}$(tput setaf 1)
guardedmode=false guardedmode=false
singlemode=false singlemode=false
allwsmode=false allwsmode=false
quiet=false
no_follow_symlinks=false
no_follow_hidden=false
# #
# print usage message # print usage message
# #
usage() { usage() {
echo 1>&2 "usage: git bulk [-g] ([-a]|[-w <ws-name>]) <git command>" 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 --addworkspace <ws-name> <ws-root-directory> (--from <URL or file>)"
echo 1>&2 " git bulk --removeworkspace <ws-name>" echo 1>&2 " git bulk --removeworkspace <ws-name>"
echo 1>&2 " git bulk --addcurrent <ws-name>" echo 1>&2 " git bulk --addcurrent <ws-name>"
@ -21,22 +24,33 @@ usage() {
echo 1>&2 " git bulk --listall" echo 1>&2 " git bulk --listall"
} }
cdfail() {
echo 1>&2 "failed to change directory: $1"
exit 1
}
# add another workspace to global git config # add another workspace to global git config
function addworkspace { addworkspace() {
git config --global bulkworkspaces."$wsname" "$wsdir"; git config --global bulkworkspaces."$wsname" "$wsdir";
if [ ! -z "$source" ]; then if [ -n "$source" ]; then
if [ ! -d "$wsdir" ]; then echo "Path of workspace doesn't exist, make it first."; exit 1; fi if [ ! -d "$wsdir" ]; then echo 1>&2 "Path of workspace doesn't exist, make it first."; exit 1; fi
regex='http(s)?://|ssh://|(git@)?.*:.*/.*' regex='http(s)?://|ssh://|(git@)?.*:.*/.*'
if [[ "$source" =~ $regex ]]; then if [[ "$source" =~ $regex ]]; then
pushd "$wsdir" > /dev/null pushd "$wsdir" > /dev/null || cdfail "$wsdir"
git clone "$source" git clone "$source"
popd > /dev/null popd > /dev/null || cdfail "$OLDPWD"
else else
source=$(realpath "$source" 2>/dev/null) source=$(realpath "$source" 2>/dev/null)
if [ -f "$source" ]; then if [ -f "$source" ]; then
pushd "$wsdir" > /dev/null pushd "$wsdir" > /dev/null || cdfail "$wsdir"
while read -r line; do git clone "$line"; done < "$source"; while IFS= read -r line; do
popd > /dev/null 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 else
echo 1>&2 "format of URL or file unknown" echo 1>&2 "format of URL or file unknown"
fi fi
@ -45,101 +59,125 @@ function addworkspace {
} }
# add current directory # add current directory
function addcurrent { git config --global bulkworkspaces."$wsname" "$PWD"; } addcurrent() { git config --global bulkworkspaces."$wsname" "$PWD"; }
# remove workspace from global git config # remove workspace from global git config
function removeworkspace { checkWSName && git config --global --unset bulkworkspaces."$wsname"; } removeworkspace() { checkWSName && git config --global --unset bulkworkspaces."$wsname"; }
# remove workspace from global git config # remove workspace from global git config
function purge { git config --global --remove-section bulkworkspaces; } purge() { git config --global --remove-section bulkworkspaces; }
# list all current workspace locations defined # list all current workspace locations defined
function listall { git config --global --get-regexp bulkworkspaces; } listall() { git config --global --get-regexp bulkworkspaces; }
# guarded execution of a git command in one specific repository # guarded execution of a git command in one specific repository
function guardedExecution () { 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 if $guardedmode; then
echo -n "${inverse}git $gitcommand${reset} -> execute here (y/n)? " echo 1>&2 -n " Execute command here (y/n)? "
read -n 1 -r </dev/tty; echo read -n 1 -r </dev/tty; echo 1>&2
if [[ $REPLY =~ ^[Yy]$ ]]; then atomicExecution "$@"; fi if [[ $REPLY =~ ^[Yy]$ ]]; then
git "$@"
fi
else else
atomicExecution "$@" git "$@"
fi fi
} }
# atomic git command execution with log
function atomicExecution () {
echo "${bldred}->${reset} executing ${inverse}git $gitcommand${reset}" && git "$@"
}
# check if the passed command is known as a core git command # check if the passed command is known as a core git command
function checkGitCommand () { checkGitCommand () {
if git help -a | grep -o -q "\b${corecommand}\b"; then if git help -a | grep -o -q "\b${corecommand}\b"; then
echo "Core command \"$corecommand\" accepted." echo 1>&2 "Core command \"$corecommand\" accepted."
else else
if git config --get-regexp alias | grep -o -q "\.${corecommand} "; then if git config --get-regexp alias | grep -o -q "\.${corecommand} "; then
echo "Alias ${corecommand} accepted." echo 1>&2 "Alias ${corecommand} accepted."
else else
usage && echo "error: unknown GIT command: $corecommand" && exit 1 usage && echo 1>&2 "error: unknown GIT command: $corecommand" && exit 1
fi fi
fi fi
} }
# check if workspace name is registered # check if workspace name is registered
function checkWSName () { checkWSName () {
while read workspace; do while read -r workspace; do
parseWsName "$workspace" parseWsName "$workspace"
if [[ $rwsname == "$wsname" ]]; then return; fi if [[ $rwsname == "$wsname" ]]; then return; fi
done <<< "$(echo "$(listall)")" done <<< "$(listall)"
# when here the ws name was not found # when here the ws name was not found
usage && echo "error: unknown workspace name: $wsname" && exit 1 usage && echo 1>&2 "error: unknown workspace name: $wsname" && exit 1
} }
# parse out wsname from workspacespec # parse out wsname from workspacespec
function parseWsName () { parseWsName () {
local wsspec="$1" 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#* } 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%% *} rwsname=${wsspec#*.} && rwsname=${rwsname%% *}
} }
# detects the wsname of the current directory # detects the wsname of the current directory
function wsnameToCurrent () { wsnameToCurrent () {
while read workspace; do while read -r workspace; do
if [ -z "$workspace" ]; then continue; fi if [ -z "$workspace" ]; then continue; fi
parseWsName "$workspace" parseWsName "$workspace"
if echo "$PWD" | grep -o -q "$rwsdir"; then wsname="$rwsname" && return; fi if echo "$PWD" | grep -o -q "$rwsdir"; then wsname="$rwsname" && return; fi
done <<< "$(echo "$(listall)")" done <<< "$(listall)"
# when here then not in workspace dir # when here then not in workspace dir
echo "error: you are not in a workspace directory. your registered workspaces are:" && \ echo 1>&2 "error: you are not in a workspace directory. your registered workspaces are:" && \
wslist="$(echo "$(listall)")" && echo "${wslist:-'<no workspaces defined yet>'}" && exit 1 wslist="$(listall)" && echo 1>&2 "${wslist:-'<no workspaces defined yet>'}" && exit 1
} }
# helper to check number of arguments. # helper to check number of arguments.
function allowedargcount () { allowedargcount () {
if [ $paramcount -ne $1 ] && [ $paramcount -ne $2 ]; then if [ "$paramcount" -ne "${1:-0}" ] && [ "$paramcount" -ne "${2:-0}" ]; then
echo 1>&2 "error: wrong number of arguments" && usage; echo 1>&2 "error: wrong number of arguments" && usage;
exit 1; exit 1;
fi fi
} }
# execute the bulk operation # execute the bulk operation
function executBulkOp () { executBulkOp () {
checkGitCommand checkGitCommand
if ! $allwsmode && ! $singlemode; then wsnameToCurrent; fi # by default git bulk works within the 'current' workspace if ! $allwsmode && ! $singlemode; then wsnameToCurrent; fi # by default git bulk works within the 'current' workspace
listall | while read workspacespec; do listall | while read -r workspacespec; do
parseWsName "$workspacespec" parseWsName "$workspacespec"
if [[ -n $wsname ]] && [[ $rwsname != "$wsname" ]]; then continue; fi if [[ -n $wsname ]] && [[ $rwsname != "$wsname" ]]; then continue; fi
eval cd "\"$rwsdir\"" cd "$rwsdir" || exit 1
local actual=$(pwd) local actual=$PWD
echo "Executing bulk operation in workspace ${inverse}$actual${reset}" [ "${quiet?}" != "true" ] && echo 1>&2 "Executing bulk operation in workspace ${inverse}$actual${reset}"
eval find -L . -name ".git" | while read line; do
# 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 local gitrepodir=${line::${#line}-5} # cut the .git part of find results to have the root git directory of that repository
eval cd "\"$gitrepodir\"" # into git repo location cd "$gitrepodir" || exit 1 # into git repo location
local curdir=$(pwd) local curdir=$PWD
local leadingpath=${curdir#${actual}} local leadingpath=${curdir#"${actual}"}
echo "Current repository: ${leadingpath%/*}/${bldred}${curdir##*/}${reset}" # do not execute if we do not want to consider a ".git" directory under a hidden directory
guardedExecution "$@" if [ $no_follow_hidden = false ] || ! [[ "$leadingpath" =~ "/." ]]; then
eval cd "\"$rwsdir\"" # back to origin location of last find command guardedExecution "$@"
fi
cd "$rwsdir" || exit 1 # back to origin location of last find command
done done
done done
} }
@ -152,20 +190,25 @@ if [[ $paramcount -le 0 ]]; then usage; fi
# parse command parameters # parse command parameters
while [ "${#}" -ge 1 ] ; do while [ "${#}" -ge 1 ] ; do
case "$1" in case "$1" in
--quiet|-q) quiet='true' ;;
--listall|--purge) --listall|--purge)
butilcommand="${1:2}" && break ;; butilcommand="${1:2}" && break ;;
--removeworkspace|--addcurrent|--addworkspace) --removeworkspace|--addcurrent|--addworkspace)
butilcommand="${1:2}" && wsname="$2" && wsdir="$3" && if [ "$4" == "--from" ]; then source="$5"; fi && break ;; 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) -a)
allwsmode=true ;; allwsmode=true ;;
-g) -g)
guardedmode=true ;; guardedmode=true ;;
-w) -w)
singlemode=true && shift && wsname="$1" && checkWSName ;; 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 ;; usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
-*)
usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
*) # git core commands *) # git core commands
butilcommand="executBulkOp" && corecommand="$1" && gitcommand="$*" && break ;; butilcommand="executBulkOp" && corecommand="$1" && gitcommand="$*" && break ;;
esac && shift esac && shift
@ -175,7 +218,7 @@ done
if $allwsmode && $singlemode; then echo 1>&2 "error: options -w and -a are incompatible" && exit 1; fi 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 single mode check the supplied workspace name
if $singlemode; then echo "Selected single workspace mode in workspace: $wsname" && checkWSName; fi if $singlemode; then echo 1>&2 "Selected single workspace mode in workspace: $wsname" && checkWSName; fi
# check right number of arguments # check right number of arguments
case $butilcommand in case $butilcommand in

View file

@ -9,6 +9,15 @@ GIT_MERGELOG_FORMAT="$(git config changelog.mergeformat)"
GIT_EDITOR="$(git var GIT_EDITOR)" GIT_EDITOR="$(git var GIT_EDITOR)"
PROGNAME="git-changelog" PROGNAME="git-changelog"
git_editor() {
if test -z "${GIT_EDITOR:+set}"
then
GIT_EDITOR="$(git var GIT_EDITOR)" || return $?
fi
eval "$GIT_EDITOR" '"$@"'
}
_usage() { _usage() {
cat << EOF cat << EOF
usage: $PROGNAME options [file] usage: $PROGNAME options [file]
@ -146,6 +155,8 @@ _fetchCommitRange() {
local start_tag="$2" local start_tag="$2"
local final_tag="$3" local final_tag="$3"
# This shellcheck disable is applied to the whole if-body
# shellcheck disable=SC2086
if [[ "$list_all" == true ]]; then if [[ "$list_all" == true ]]; then
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}" git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}"
elif [[ -n "$final_tag" && "$start_tag" == "null" ]]; then elif [[ -n "$final_tag" && "$start_tag" == "null" ]]; then
@ -233,7 +244,7 @@ commitList() {
# fetch our tags # fetch our tags
local _ref _date _tag _tab='%x09' local _ref _date _tag _tab='%x09'
local _tag_regex='tag: *' local _tag_regex='tag: *'
while IFS=$'\t' read _ref _date _tag; do while IFS=$'\t' read -r _ref _date _tag; do
[[ -z "${_tag}" ]] && continue [[ -z "${_tag}" ]] && continue
# strip out tags form () # strip out tags form ()
# git v2.2.0+ supports '%D', like '%d' without the " (", ")" wrapping. One day we should use it instead. # git v2.2.0+ supports '%D', like '%d' without the " (", ")" wrapping. One day we should use it instead.
@ -280,7 +291,8 @@ commitList() {
for (( i=0; i<"${_tags_list_keys_length}"; i++ )); do for (( i=0; i<"${_tags_list_keys_length}"; i++ )); do
local __curr_tag="${tags_list_keys[$i]}" local __curr_tag="${tags_list_keys[$i]}"
local __prev_tag="${tags_list_keys[$i+1]:-null}" local __prev_tag="${tags_list_keys[$i+1]:-null}"
local __curr_date="$(_valueForKeyFakeAssocArray "${__curr_tag}" "${tags_list[*]}")" local __curr_date
__curr_date="$(_valueForKeyFakeAssocArray "${__curr_tag}" "${tags_list[*]}")"
__curr_date="${__curr_date##*=>}" __curr_date="${__curr_date##*=>}"
# output latest commits, up until the most-recent tag, these are all # output latest commits, up until the most-recent tag, these are all
@ -376,7 +388,7 @@ _exit() {
# so we need to calculate the total column number(COL_NUM) of header first. # so we need to calculate the total column number(COL_NUM) of header first.
# Why don't we just use the last column? # Why don't we just use the last column?
# Because the body of CMD column may contain space and be treated as multiple fields. # Because the body of CMD column may contain space and be treated as multiple fields.
pid_list=( $(ps -f | pid_list=( $(ps -f |
awk -v ppid=$$ 'NR == 1 { awk -v ppid=$$ 'NR == 1 {
COL_NUM = NF COL_NUM = NF
} }
@ -391,7 +403,7 @@ _exit() {
local _pid local _pid
for _pid in "${pid_list[@]}"; do for _pid in "${pid_list[@]}"; do
echo "killing: ${_pid}" echo "killing: ${_pid}"
kill -TERM ${_pid} kill -TERM "${_pid}"
done done
wait; stty sane; exit 1 wait; stty sane; exit 1
@ -474,10 +486,10 @@ main() {
esac esac
shift shift
done done
# The default log format unless already set # The default log format unless already set
[[ -z "$CUR_GIT_LOG_FORMAT" ]] && CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT" [[ -z "$CUR_GIT_LOG_FORMAT" ]] && CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
local _tag="$(_valueForKeyFakeAssocArray "start_tag" "${option[*]}")" local _tag="$(_valueForKeyFakeAssocArray "start_tag" "${option[*]}")"
local start_commit="$(_valueForKeyFakeAssocArray "start_commit" "${option[*]}")" local start_commit="$(_valueForKeyFakeAssocArray "start_commit" "${option[*]}")"
@ -487,7 +499,6 @@ main() {
return 1 return 1
fi fi
start_commit="$start_commit"
start_tag="$(git describe --tags --contains "$start_commit" 2>/dev/null || echo 'null')" start_tag="$(git describe --tags --contains "$start_commit" 2>/dev/null || echo 'null')"
if [[ -z "$start_tag" ]]; then if [[ -z "$start_tag" ]]; then
_error "Could find the associative tag for the start-commit!" _error "Could find the associative tag for the start-commit!"
@ -529,9 +540,10 @@ main() {
# #
# generate changelog # generate changelog
# #
local tmpfile="$(git_extra_mktemp)" local tmpfile changelog title_tag
local changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")" tmpfile="$(git_extra_mktemp)"
local title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")" changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
if [[ "$(_valueForKeyFakeAssocArray "list_style" "${option[*]}")" == true ]]; then if [[ "$(_valueForKeyFakeAssocArray "list_style" "${option[*]}")" == true ]]; then
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
@ -550,7 +562,7 @@ main() {
fi fi
if [[ -z "$changelog" ]]; then if [[ -z "$changelog" ]]; then
changelog="$(ls | egrep 'change|history' -i | head -n1)" changelog="$(find . -mindepth 1 -maxdepth 1 \( -iname '*change*' -o -iname '*history*' \) | head -n1)"
if [[ -z "$changelog" ]]; then if [[ -z "$changelog" ]]; then
changelog="History.md"; changelog="History.md";
fi fi
@ -569,12 +581,11 @@ main() {
else else
cp -f "$tmpfile" "$changelog" cp -f "$tmpfile" "$changelog"
rm -f "$tmpfile" rm -f "$tmpfile"
# Use `eval` to handle GIT_EDITOR which contains space and options,
# like ""C:\Program Files (x86)\Notepad++\notepad++.exe" -multiInst ". if [[ -n "$GIT_EDITOR" ]]; then
# Thanks @JanSchulz to inspire me this solution git_editor "$changelog" || _exit
[[ -n "$GIT_EDITOR" ]] && eval $GIT_EDITOR "$changelog" else
if [[ $? -ne 0 ]]; then less "$changelog" || _exit
_exit
fi fi
fi fi

View file

@ -1,7 +1,43 @@
#!/usr/bin/env bash #!/usr/bin/env bash
PROGNAME="git-clear"
FORCE=0
echo -n "Sure? - This command may delete files that cannot be recovered, including those in .gitignore [y/N]: " _usage() {
read ans cat << EOF
if [ "$ans" == "y" ] usage: $PROGNAME options
then git clean -d -f -x && git reset --hard 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 fi

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/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]: " echo -n "Sure? - This command may delete files that cannot be recovered. Files and directories in .gitignore will be preserved [y/N]: "
read ans read -r answer
if [ "$ans" == "y" ] if [ "$answer" = "y" ]
then git clean -d -f && git reset --hard then git clean -d -f && git reset --hard
fi fi

View file

@ -1,6 +1,36 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SINCE="last week" REF=""
test $# -ne 0 && SINCE=$@ SINCE=""
echo "... commits since $SINCE" >&2
git log --pretty='%an - %s' --after="@{$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

1
bin/git-continue Symbolic link
View file

@ -0,0 +1 @@
git-abort

View file

@ -5,4 +5,4 @@ if test "$1" = "--all"; then
echo echo
fi fi
echo total `git rev-list --count HEAD` echo total "$(git rev-list --count HEAD)"

View file

@ -32,9 +32,47 @@ elif [[ "$CURRENT_FILENAME" == "" ]]; then
usage usage
exit 10 # Missing arguments CURRENT_FILENAME exit 10 # Missing arguments CURRENT_FILENAME
else else
echo "Coping $CURRENT_FILENAME into $DESTINATION_FILENAME" if [ -e "$DESTINATION_FILENAME" ]; then
cp -r "${CURRENT_FILENAME}" "${DESTINATION_FILENAME}" echo 1>&2 "$DESTINATION_FILENAME already exists."
echo 1>&2 "Make sure to remove the destination first."
exit 40
fi
git add "$DESTINATION_FILENAME" if [[ "$DESTINATION_FILENAME" == */ ]]; then
git commit -m "Duplicate ${CURRENT_FILENAME} history in ${DESTINATION_FILENAME} history." -n 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 fi

View file

@ -3,7 +3,7 @@
test $# -eq 0 && echo "branch argument required." 1>&2 && exit 1 test $# -eq 0 && echo "branch argument required." 1>&2 && exit 1
# preference takes lowest priority; look for remote from prefs first # preference takes lowest priority; look for remote from prefs first
REMOTE_PREF=`git config git-extras.create-branch.remote` REMOTE_PREF=$(git config git-extras.create-branch.remote)
if [ -n "$REMOTE_PREF" ]; then if [ -n "$REMOTE_PREF" ]; then
REMOTE=$REMOTE_PREF REMOTE=$REMOTE_PREF
fi fi
@ -39,25 +39,25 @@ then
REMOTE=origin REMOTE=origin
fi fi
test -z $BRANCH && echo "branch argument required." 1>&2 && exit 1 test -z "$BRANCH" && echo "branch argument required." 1>&2 && exit 1
if [[ -n $REMOTE ]] if [[ -n $REMOTE ]]
then then
stderr=$(git_extra_mktemp) stderr=$(git_extra_mktemp)
git ls-remote --exit-code $REMOTE 1>/dev/null 2>"$stderr" git ls-remote --exit-code "$REMOTE" 1>/dev/null 2>"$stderr"
REMOTE_EXIT=$? REMOTE_EXIT=$?
REMOTE_ERROR=$(<"$stderr") REMOTE_ERROR=$(<"$stderr")
rm -f "$stderr" rm -f "$stderr"
if [ $REMOTE_EXIT -eq 0 ] if [ $REMOTE_EXIT -eq 0 ]
then then
if [[ -n $START_POINT ]]; then if [[ -n $START_POINT ]]; then
git fetch $REMOTE git fetch "$REMOTE"
git checkout --track -b $BRANCH $START_POINT git checkout --track -b "$BRANCH" "$START_POINT"
git push $REMOTE HEAD:refs/heads/$BRANCH git push "$REMOTE" "HEAD:refs/heads/$BRANCH"
else else
git push $REMOTE HEAD:refs/heads/$BRANCH git push "$REMOTE" "HEAD:refs/heads/$BRANCH"
git fetch $REMOTE git fetch "$REMOTE"
git checkout --track -b $BRANCH $REMOTE/$BRANCH git checkout --track -b "$BRANCH" "$REMOTE/$BRANCH"
fi fi
exit $? exit $?
else else
@ -70,7 +70,7 @@ fi
if [[ -n $START_POINT ]] if [[ -n $START_POINT ]]
then then
git checkout -b $BRANCH $START_POINT git checkout -b "$BRANCH" "$START_POINT"
else else
git checkout -b $BRANCH git checkout -b "$BRANCH"
fi fi

View file

@ -2,16 +2,16 @@
set -e set -e
# Assert there is at least one branch provided # 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 "$@" for branch in "$@"
do do
remote=$(git config branch.$branch.remote || echo "origin") remote=$(git config "branch.$branch.remote" || echo "origin")
ref=$(git config branch.$branch.merge || echo "refs/heads/$branch") ref=$(git config "branch.$branch.merge" || echo "refs/heads/$branch")
git branch -D $branch || true git branch -D "$branch" || true
# Avoid deleting local upstream # Avoid deleting local upstream
[ "$remote" == "." ] && continue [ "$remote" = "." ] && continue
git branch -d -r $remote/$branch || continue git branch -d -r "$remote/$branch" || continue
git push $remote :$ref git push "$remote" ":$ref"
done done

60
bin/git-delete-gone-branches Executable file
View file

@ -0,0 +1,60 @@
#!/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

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
branches=$(git branch --no-color --merged | grep -v "\*" | grep -v $(git_extra_default_branch) | grep -v svn) branches=$(git branch --no-color --merged | grep -vE "^(\*|\+)" | grep -v "$(git_extra_default_branch)" | grep -v svn)
if [ -n "$branches" ] if [ -n "$branches" ]
then then
echo "$branches" | xargs -n 1 git branch -d echo "$branches" | xargs git branch -d
fi fi

View file

@ -0,0 +1,28 @@
#!/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

View file

@ -15,7 +15,7 @@ test ! -f '.gitmodules' && abort 2 '.gitmodules file not found'
NAME="${1%/}" NAME="${1%/}"
test -z "$(git config --file='.gitmodules' "submodule.$NAME.url")" \ test -z "$(git config --file='.gitmodules' "submodule.$NAME.url")" \
&& abort 3 'Submodule not found' && abort 3 'Submodule not found'
# 1. Handle the .git directory # 1. Handle the .git directory
# 1.a. Delete the relevant section from .git/config # 1.a. Delete the relevant section from .git/config

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Assert there is at least one tag provided # Assert there is at least one tag provided
test -z $1 && echo "tag required." 1>&2 && exit 1 test -z "$1" && echo "tag required." 1>&2 && exit 1
# Detect the default remote exists or not # Detect the default remote exists or not
default_remote=$(git config git-extras.default-remote) default_remote=$(git config git-extras.default-remote)
@ -22,5 +22,7 @@ do
done done
# Delete all the tags # Delete all the tags
# shellcheck disable=SC2086
git tag -d $local_tags git tag -d $local_tags
git push $origin $origin_refs # shellcheck disable=SC2086
git push "$origin" $origin_refs

View file

@ -12,4 +12,4 @@ else
fi fi
fi fi
git diff --name-only --diff-filter=$filter $branch git diff --name-only --diff-filter="$filter" "$branch"

View file

@ -59,14 +59,14 @@ active_days() {
color_for() { color_for() {
if [ "$to_tty" = true ]; then if [ "$to_tty" = true ]; then
if [ $1 -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)" if [ "$1" -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)"
elif [ $1 -gt 150 ]; then color="$(tputq setaf 1)" # red elif [ "$1" -gt 150 ]; then color="$(tputq setaf 1)" # red
elif [ $1 -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)" elif [ "$1" -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)"
elif [ $1 -gt 100 ]; then color="$(tputq setaf 2)" # green elif [ "$1" -gt 100 ]; then color="$(tputq setaf 2)" # green
elif [ $1 -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)" elif [ "$1" -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)"
elif [ $1 -gt 50 ]; then color="$(tputq setaf 5)" # purplish elif [ "$1" -gt 50 ]; then color="$(tputq setaf 5)" # purplish
elif [ $1 -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)" elif [ "$1" -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)"
elif [ $1 -gt 10 ]; then color="$(tputq setaf 3)" # yellow elif [ "$1" -gt 10 ]; then color="$(tputq setaf 3)" # yellow
else color="$(tputq sgr0)" # default color else color="$(tputq sgr0)" # default color
fi fi
else else
@ -84,37 +84,38 @@ effort() {
local color reset_color commits len dot f_dot i msg active local color reset_color commits len dot f_dot i msg active
reset_color="" reset_color=""
test "$to_tty" = true && reset_color="$(tputq sgr0)" test "$to_tty" = true && reset_color="$(tputq sgr0)"
commit_dates=`dates "$path"` if ! commit_dates=$(dates "$path"); then
[ $? -gt 0 ] && exit 255 exit 255
fi
# Ensure it's not just an empty line # Ensure it's not just an empty line
if [ -z "`head -c 1 <<<$(echo $commit_dates)`" ] if [ -z "$(head -c 1 <<<"$commit_dates")" ]
then then
exit 0 exit 0
fi fi
commits=`wc -l <<<"$(echo "$commit_dates")"` commits=$(wc -l <<<"$commit_dates")
color='90' color='90'
# ignore <= --above # ignore <= --above
test $commits -le $above && exit 0 test "$commits" -le "$above" && exit 0
# commits # commits
color_for $(( $commits - $above )) color_for $(( commits - above ))
len=${#path} len=${#path}
dot="." dot="."
f_dot="$path" f_dot="$path"
i=0 ; while test $i -lt $(( $columns - $len )) ; do i=0 ; while test $i -lt $(( columns - len )) ; do
f_dot=$f_dot$dot f_dot=$f_dot$dot
i=$(($i+1)) i=$((i+1))
done done
msg=$(printf " ${color}%s %-10d" "$f_dot" $commits) msg=$(printf " ${color}%s %-10d" "$f_dot" "$commits")
# active days # active days
active=`active_days "$commit_dates"` active=$(active_days "$commit_dates")
color_for $(( $active - $above )) color_for $(( active - above ))
msg="$msg $(printf "${color} %d${reset_color}\n" $active)" msg="$msg $(printf "${color} %d${reset_color}\n" "$active")"
echo "$msg" echo "$msg"
} }
@ -136,7 +137,16 @@ sort_effort() {
clear clear
echo " " echo " "
heading heading
< $tmp sort -rn -k 2 < "$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=() declare -a paths=()
@ -181,8 +191,8 @@ export args_to_git_log
if test "${#paths}" -eq 0; then if test "${#paths}" -eq 0; then
save_ifs=$IFS save_ifs=$IFS
IFS=`echo -en "\n\b"` IFS=$(echo -en "\n\b")
paths=(`git ls-files`) paths=($(git ls-files))
IFS=$save_ifs IFS=$save_ifs
unset save_ifs unset save_ifs
fi fi
@ -203,31 +213,22 @@ export columns
hide_cursor hide_cursor
trap show_cursor_and_cleanup INT trap show_cursor_and_cleanup INT
# export functions so subshells can call them
export -f effort
export -f color_for
export -f active_days
export -f dates
export -f tputq
export to_tty
export above
export log_args
bash_params=
# If bash exits successfully with --import-functions,
# then we need to pass it (FreeBSD probably)
bash --import-functions -c ":" 1>/dev/null 2>&1
if [ $? -eq 0 ] ; then
bash_params="--import-functions"
fi
heading heading
# send paths to effort # send paths to effort
printf "%s\0" "${paths[@]}" | xargs -0 -n 1 -P 4 -I % bash $bash_params -c "effort \"%\"" | tee $tmp 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 # if more than one path, sort and print
test "$(wc -l $tmp | awk '{print $1}')" -gt 1 && sort_effort test "$(wc -l "$tmp" | awk '{print $1}')" -gt 1 && sort_effort
echo echo
show_cursor_and_cleanup show_cursor_and_cleanup

View file

@ -1,10 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
VERSION="6.2.0" VERSION="7.6.0-dev"
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/master/install.sh" INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
update() { update() {
local bin="$(which git-extras)" local bin="$(command -v git-extras)"
local prefix=${bin%/*/*} local prefix=${bin%/*/*}
local orig=$PWD local orig=$PWD
@ -14,10 +14,10 @@ update() {
} }
updateForWindows() { updateForWindows() {
local bin="$(which git-extras)" local bin="$(command -v git-extras)"
local prefix=${bin%/*/*} local prefix=${bin%/*/*}
local orig=$PWD local orig=$PWD
# we need to clean up /tmp manually on windows # we need to clean up /tmp manually on windows
cd /tmp \ cd /tmp \
&& rm -rf ./git-extras \ && rm -rf ./git-extras \

View file

@ -6,22 +6,30 @@ if [ -z "$branch_prefix" ]; then
branch_prefix="feature" branch_prefix="feature"
fi fi
branch_separator=$(git config --get git-extras.feature.separator)
if [ -z "$branch_separator" ]; then
branch_separator="/"
fi
merge_mode="--no-ff" merge_mode="--no-ff"
finish=false
declare -a argv declare -a argv
while test $# != 0 while test $# != 0
do do
case $1 in case $1 in
-a|--alias ) -a|--alias )
if [[ -n $2 ]] if [[ -n $2 ]] && [[ $2 != -- ]]
then then
shift # shift -a|-alias shift # shift -a|-alias
branch_prefix=$1 branch_prefix=$1
else else
argv+=($1) # treat tail '-a' as <name> echo >&2 "option $1 requires a value"
exit 1
fi fi
;; ;;
-r|--remote ) -r|--remote )
if [[ -n $2 ]] if [[ -n $2 ]] && [[ $2 != -- ]]
then then
remote=$2 remote=$2
shift shift
@ -29,6 +37,16 @@ do
remote="origin" remote="origin"
fi fi
;; ;;
-s|--separator )
if [[ -n $2 ]] && [[ $2 != -- ]]
then
branch_separator=$2
shift
else
echo >&2 "option $1 requires a value"
exit 1
fi
;;
--squash ) --squash )
merge_mode="--squash" merge_mode="--squash"
;; ;;
@ -36,8 +54,17 @@ do
start_point=$2 start_point=$2
shift shift
;; ;;
-- )
# terminate argument parsing
shift
argv+=("$@")
break
;;
finish )
finish=true
;;
* ) * )
argv+=($1) argv+=("$1")
;; ;;
esac esac
shift shift
@ -45,38 +72,34 @@ done
concatargs() { concatargs() {
str=$(IFS='-'; echo "$*") str=$(IFS='-'; echo "$*")
branch="$branch_prefix"/$str branch="$branch_prefix$branch_separator$str"
} }
if test "${argv[0]}" = "finish"; then test -z "${argv[0]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
test -z "${argv[1]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
branch="$branch_prefix"/"${argv[1]}" concatargs "${argv[@]}"
if "${finish}"
then
git merge ${merge_mode} "$branch" && git delete-branch "$branch" git merge ${merge_mode} "$branch" && git delete-branch "$branch"
else else
test -z "${argv[0]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
if test -n "${argv[1]}"; then
concatargs "${argv[@]}"
else
branch="$branch_prefix"/"${argv[0]}"
fi
if [[ -n $remote ]] && [[ -z $start_point ]] if [[ -n $remote ]] && [[ -z $start_point ]]
then then
git create-branch -r $remote $branch git create-branch -r "$remote" "$branch"
fi fi
if [[ -z $remote ]] && [[ -z $start_point ]] if [[ -z $remote ]] && [[ -z $start_point ]]
then then
git create-branch $branch git create-branch "$branch"
fi fi
if [[ -n $remote ]] && [[ -n $start_point ]] if [[ -n $remote ]] && [[ -n $start_point ]]
then then
git create-branch -r $remote --from $start_point $branch git create-branch -r "$remote" --from "$start_point" "$branch"
fi fi
if [[ -z $remote ]] && [[ -n $start_point ]] if [[ -z $remote ]] && [[ -n $start_point ]]
then then
git create-branch --from $start_point $branch git create-branch --from "$start_point" "$branch"
fi fi
fi fi

View file

@ -63,21 +63,21 @@ main() {
if [ -d "${destination_path}/.git" ]; then if [ -d "${destination_path}/.git" ]; then
( (
cd ${destination_path} cd "${destination_path}"
# Delete all remotes # Delete all remotes
for remote in `git remote`; do for remote in $(git remote); do
git remote rm ${remote} git remote rm "${remote}"
done done
# Add origin # Add origin
git remote add origin ${remote_url} git remote add origin "${remote_url}"
git fetch origin git fetch origin
# Set default branch # Set default branch
if [ -z "${branch:-}" ]; then if [ -z "${branch:-}" ]; then
branch=`LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$'` branch=$(LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$')
git remote set-head origin ${branch} git remote set-head origin "${branch}"
else else
git remote set-head origin -a git remote set-head origin -a
fi fi
@ -87,19 +87,20 @@ main() {
git reset --hard HEAD git reset --hard HEAD
# Get on the desired branch # Get on the desired branch
git checkout ${branch} git checkout "${branch}"
git reset --hard origin/${branch} git reset --hard "origin/${branch}"
# Delete all other branches # Delete all other branches
branches=`git branch | grep -v \* | xargs` # shellcheck disable=SC2063
branches=$(git branch | grep -v '*' | xargs)
if [ -n "${branches}" ]; then if [ -n "${branches}" ]; then
git branch -D ${branches} git branch -D "${branches}"
fi fi
) )
elif [ -n "${branch:-}" ]; then elif [ -n "${branch:-}" ]; then
git clone -b ${branch} ${remote_url} ${destination_path} git clone -b "${branch}" "${remote_url}" "${destination_path}"
else else
git clone ${remote_url} ${destination_path} git clone "${remote_url}" "${destination_path}"
fi fi
} }

View file

@ -12,16 +12,18 @@ test -z "$url" && abort "github repo needs to be specified as an argument"
# validate user # validate user
echo "Enter your github username" echo "Enter your github username"
read user read -r user
[ -n "$user" ] || abort "git username required" [ -n "$user" ] || abort "git username required"
echo "Enter github password for user \"$user\"" # personal access token
read -s passwd # config name is github-personal-access-token '_' is not allowed in git config
echo "Enter github two-factor authentication code (leave blank if not set up)"
read MFA_CODE 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"
# extract owner + project from repo url # extract owner + project from repo url
project=${url##*/} project=${url##*/}
owner=${url%/$project} owner=${url%/"$project"}
project=${project%.git} project=${project%.git}
if [[ $owner == git@* ]]; then if [[ $owner == git@* ]]; then
owner=${owner##*:} owner=${owner##*:}
@ -30,19 +32,20 @@ else
fi fi
# validate # validate
[ -z "$project" -o -z "$owner" ] && abort "github repo needs to be specified as an argument" [[ -z "$project" || -z "$owner" ]] && abort "github repo needs to be specified as an argument"
# create fork # create fork
curl -qsf \ if ! curl -qsf \
-X POST \ -X POST \
-u "$user:$passwd" \ -u "$user:$github_personal_access_token" \
-H "X-GitHub-OTP: $MFA_CODE" \ -H "X-GitHub-OTP: $MFA_CODE" \
"https://api.github.com/repos/$owner/$project/forks" "https://api.github.com/repos/$owner/$project/forks"
then
[ $? = 0 ] || abort "fork failed" abort "fork failed"
fi
echo "Add GitHub remote branch via SSH (you will be prompted to verify the server's credentials)? (y/n)" echo "Add GitHub remote branch via SSH (you will be prompted to verify the server's credentials)? (y/n)"
read use_ssh read -r use_ssh
# Check if user has ssh configured with GitHub # Check if user has ssh configured with GitHub
if [ -n "$use_ssh" ] && ssh -T git@github.com 2>&1 | grep -qi 'success'; then if [ -n "$use_ssh" ] && ssh -T git@github.com 2>&1 | grep -qi 'success'; then
remote_prefix="git@github.com:" remote_prefix="git@github.com:"
@ -58,7 +61,7 @@ else
# clone forked repo into current dir # clone forked repo into current dir
git clone "${remote_prefix}${user}/${project}.git" "$project" git clone "${remote_prefix}${user}/${project}.git" "$project"
# add reference to origin fork so can merge in upstream changes # add reference to origin fork so can merge in upstream changes
cd "$project" cd "$project" || exit
git remote add upstream "${remote_prefix}${owner}/${project}.git" git remote add upstream "${remote_prefix}${owner}/${project}.git"
git fetch upstream git fetch upstream
fi fi

View file

@ -2,19 +2,19 @@
branch=$1 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() clean()
{ {
git symbolic-ref HEAD refs/heads/$branch git symbolic-ref HEAD "refs/heads/$branch"
rm .git/index rm .git/index
git clean -fdx git clean -fdx
} }
if [ ! -z "$changes" ]; then if [ -n "$changes" ]; then
read -p "All untracked changes will be lost. Continue [y/N]? " res read -rp "All untracked changes will be lost. Continue [y/N]? " res
case $res in case $res in
[Yy]* ) ;; [Yy]* ) ;;
* ) exit 0;; * ) exit 0;;

40
bin/git-get Executable file
View file

@ -0,0 +1,40 @@
#!/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" "$@"

View file

@ -4,10 +4,10 @@ echo 'setting up gh-pages'
echo '-------------------' echo '-------------------'
echo 'Tell me your github account username: ' echo 'Tell me your github account username: '
read username read -r username
echo 'Now, tell me your repository name: ' echo 'Now, tell me your repository name: '
read repository read -r repository
git stash \ git stash \
&& git checkout -b 'gh-pages' \ && git checkout -b 'gh-pages' \

View file

@ -3,9 +3,9 @@
src=$1 src=$1
dst=$2 dst=$2
test -z $src && echo "source branch required." 1>&2 && exit 1 test -z "$src" && echo "source branch required." 1>&2 && exit 1
test -z $dst && echo "destination branch required." 1>&2 && exit 1 test -z "$dst" && echo "destination branch required." 1>&2 && exit 1
git checkout $dst \ git checkout "$dst" \
&& git merge --no-ff $src \ && git merge --no-ff "$src" \
&& git branch -d $src && git branch -d "$src"

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
for param in $* for param in "$@"
do do
case $param in case $param in
-h) -h)
@ -31,7 +31,7 @@ do
esac esac
done done
cd "$(git-root)" # cd for git blame cd "$(git-root)" || exit # cd for git blame
MERGED_LOG=$(git_extra_mktemp) MERGED_LOG=$(git_extra_mktemp)
if [[ $EMAIL == '-e' ]] if [[ $EMAIL == '-e' ]]
then then
@ -44,11 +44,13 @@ for file in $(git diff --name-only "$@")
do do
test -n "$DEBUG" && echo "git blame $file" test -n "$DEBUG" && echo "git blame $file"
# $1 - since $2 - until # $1 - since $2 - until
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null | # shellcheck disable=SC2086
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 |
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/- \1/' >> "$MERGED_LOG"
# if $2 not given, use current commit as "until" # if $2 not given, use current commit as "until"
# shellcheck disable=SC2086
git blame $NOT_WHITESPACE --line-porcelain "${2-@}" -- "$file" 2> /dev/null | 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 LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/+ \1/' >> "$MERGED_LOG"
done done
DEBUG="$DEBUG" awk ' DEBUG="$DEBUG" awk '
@ -71,8 +73,8 @@ END {
printf("%d %s\n", contributors[people], people) printf("%d %s\n", contributors[people], people)
} }
} }
}' $MERGED_LOG | sort -nr | # only gawk supports built-in sort function }' "$MERGED_LOG" | sort -nr | # only gawk supports built-in sort function
while read line while read -r line
do do
people=${line#* } people=${line#* }
num=${line%% *} num=${line%% *}
@ -87,7 +89,7 @@ do
do do
printf "+" printf "+"
done done
printf "(%s)" $num printf "(%s)" "$num"
else else
for (( i = 0; i < num; i++ )) for (( i = 0; i < num; i++ ))
do do
@ -103,7 +105,7 @@ do
do do
printf "-" printf "-"
done done
printf "(%s)" $num printf "(%s)" "$num"
else else
for (( i = 0; i > num; i-- )) for (( i = 0; i > num; i-- ))
do do

View file

@ -1,8 +1,28 @@
#!/usr/bin/env bash #!/usr/bin/env bash
: ${GIT_DIR:=$(git rev-parse --git-dir)} GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
function show_contents { # 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() {
local file="${2/#~/$HOME}" local file="${2/#~/$HOME}"
if [ -f "$file" ]; then if [ -f "$file" ]; then
echo "$1 gitignore: $2" && cat "$file" echo "$1 gitignore: $2" && cat "$file"
@ -11,18 +31,41 @@ function show_contents {
fi fi
} }
function global_ignore() { cd_to_git_root() {
git config --global core.excludesile || \ local error_level="$1"
([ -n "$XDG_CONFIG_HOME" ] && echo "$XDG_CONFIG_HOME/git/ignore") || \
echo "$HOME/.config/git/ignore" 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 { 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
}
show_global() {
show_contents Global "$(global_ignore)" show_contents Global "$(global_ignore)"
} }
function add_global { add_global() {
local global_gitignore="$(global_ignore)" local global_gitignore
global_gitignore="$(global_ignore)"
if [ -z "$global_gitignore" ]; then if [ -z "$global_gitignore" ]; then
echo "Can't find global .gitignore." echo "Can't find global .gitignore."
echo "" echo ""
@ -33,36 +76,104 @@ function add_global {
fi fi
} }
function show_local { show_local() {
cd "$(git root)" cd_to_git_root --warn
show_contents Local .gitignore show_contents Local .gitignore
} }
function add_local { add_local() {
cd "$(git root)" cd_to_git_root --warn
add_patterns .gitignore "$@" add_patterns .gitignore "$@"
} }
function show_private { show_private() {
cd "$(git root)" cd_to_git_root --error
show_contents Private "${GIT_DIR}/info/exclude" show_contents Private "${GIT_DIR}/info/exclude"
} }
function add_private { add_private() {
cd "$(git root)" cd_to_git_root --error
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info" test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
add_patterns "${GIT_DIR}/info/exclude" "$@" add_patterns "${GIT_DIR}/info/exclude" "$@"
} }
function add_patterns { add_patterns() {
echo "Adding pattern(s) to: $1" echo "Adding pattern(s) to: $1"
local file="${1/#~/$HOME}" 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 for pattern in "${@:2}"; do
echo "... adding '$pattern'" echo "... adding '$pattern'"
(test -f "$file" && test "$pattern" && grep -q -F -x -- "$pattern" "$file") || echo "$pattern" >> "$file" (test -f "$file" && test "$pattern" && grep -q -F -x -- "$pattern" "$file") || echo "$pattern" >> "$file"
done 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 if test $# -eq 0; then
show_global show_global
echo "---------------------------------" echo "---------------------------------"
@ -70,22 +181,105 @@ if test $# -eq 0; then
echo "---------------------------------" echo "---------------------------------"
show_private show_private
else else
case "$1" in # parse flags and args
-l|--local) level="none"
test $# -gt 1 && add_local "${@:2}" && echo action="none"
show_local declare -a args
;; while [ $# -gt 0 ]; do
-g|--global) case "$1" in
test $# -gt 1 && add_global "${@:2}" && echo -h|--help)
show_global _usage
;; exit 0
-p|--private) ;;
test $# -gt 1 && add_private "${@:2}" && echo -l|--local)
show_private level="local"
;; ;;
*) -g|--global)
add_local "$@" level="global"
;; ;;
esac -p|--private)
fi 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
fi

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
gitignore_io_url="https://www.gitignore.io/api/" gitignore_io_url="https://www.toptal.com/developers/gitignore/api/"
default_path="$HOME/.gi_list" default_path="$HOME/.gi_list"
if [[ -n "$XDG_CACHE_HOME" ]]; then if [[ -n "$XDG_CACHE_HOME" ]]; then
default_path="$XDG_CACHE_HOME/git-extras/gi_list" default_path="$XDG_CACHE_HOME/git-extras/gi_list"
@ -17,7 +17,7 @@ print_in_alphabetical_order() {
for ignorable in $(echo "$gi_list" | sort); for ignorable in $(echo "$gi_list" | sort);
do do
first_character=${ignorable:0:1} first_character=${ignorable:0:1}
if [[ $first_character = $previous_first_character ]]; then if [[ $first_character = "$previous_first_character" ]]; then
printf " %s" "$ignorable" printf " %s" "$ignorable"
elif [[ $first = true ]]; then elif [[ $first = true ]]; then
previous_first_character=$first_character previous_first_character=$first_character
@ -46,12 +46,11 @@ search() {
} }
print_last_modified_time() { print_last_modified_time() {
gi_list_date=$(stat -c "%y" "$default_path" 2> /dev/null) if ! gi_list_date=$(stat -c "%y" "$default_path" 2> /dev/null); then
if [ "$?" -ne 0 ]; then if ! gi_list_date=$(stat -f "%t%Sm" "$default_path" 2> /dev/null); then
gi_list_date=$(stat -f "%t%Sm" "$default_path" 2> /dev/null) if ! gi_list_date=$(date -r "$default_path" +%s 2> /dev/null); then
if [ "$?" -ne 0 ]; then gi_list_date=0
gi_list_date=$(date -r "$default_path" +%s 2> /dev/null) fi
[ "$?" -ne 0 ] && gi_list_date=0
fi fi
fi fi
echo "Last update time: $gi_list_date" echo "Last update time: $gi_list_date"
@ -85,7 +84,7 @@ show_usage() {
check_list_exist() { check_list_exist() {
if ! [ -f "$default_path" ]; then if ! [ -f "$default_path" ]; then
echo "-----Initial gitignore.io list----" echo "-----Initial gitignore.io list----"
update_gi_list & update_gi_list
echo "-----Save to $default_path-----" echo "-----Save to $default_path-----"
echo echo
fi fi

View file

@ -65,7 +65,7 @@ echon "${COLOR_TITLE}## Local Branches:${NORMAL}"
echon "$(local_branches)" echon "$(local_branches)"
SUBMODULES_LOG=$(submodules) SUBMODULES_LOG=$(submodules)
if [ ! -z "$SUBMODULES_LOG" ]; then if [ -n "$SUBMODULES_LOG" ]; then
echon "${COLOR_TITLE}## Submodule(s):${NORMAL}" echon "${COLOR_TITLE}## Submodule(s):${NORMAL}"
echon "$SUBMODULES_LOG" echon "$SUBMODULES_LOG"
fi fi
@ -73,7 +73,7 @@ fi
echon "${COLOR_TITLE}## Most Recent Commit:${NORMAL}" echon "${COLOR_TITLE}## Most Recent Commit:${NORMAL}"
echon "$(most_recent_commit)" echon "$(most_recent_commit)"
if [ ! -z "$HIDE_CONFIG" ]; then if [ -n "$HIDE_CONFIG" ]; then
echon "${COLOR_TITLE}## Configuration (.git/config):${NORMAL}" echon "${COLOR_TITLE}## Configuration (.git/config):${NORMAL}"
echon "$(get_config)" echon "$(get_config)"
fi fi

View file

@ -1,6 +1,3 @@
#!/usr/bin/env bash #!/usr/bin/env bash
ref=$(git symbolic-ref HEAD) git log "@{upstream}..@" "$@"
branch=${ref#refs/heads/}
git log origin/${branch}..${branch} $*

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
filename=$1 filename="$1"
test -z $filename && echo "filename required." 1>&2 && exit 1 test -z "$filename" && echo "filename required." 1>&2 && exit 1
git update-index --skip-worktree $filename git update-index --skip-worktree "$filename"

87
bin/git-magic Executable file
View file

@ -0,0 +1,87 @@
#!/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

View file

@ -18,7 +18,7 @@ then
git stash git stash
fi fi
if [ "${!#}" == '--ff-only' ]; then if [ "${!#}" = '--ff-only' ]; then
case $# in case $# in
2 ) # dest --ff 2 ) # dest --ff
git push "$(git rev-parse --show-toplevel)" "$cur_branch":"$1";; git push "$(git rev-parse --show-toplevel)" "$cur_branch":"$1";;

View file

@ -5,7 +5,7 @@ branch=$2
prefix=$3 prefix=$3
flat=0 flat=0
if test $prefix = "."; then if test "$prefix" = "."; then
prefix=$(mktemp -u 'git-merge-repo.XXXXXXX') prefix=$(mktemp -u 'git-merge-repo.XXXXXXX')
flat=1 flat=1
fi fi
@ -16,10 +16,10 @@ message=$(git log -1 --pretty=%B)
if test $flat -eq 1; then if test $flat -eq 1; then
git stash -u git stash -u
mv -i $prefix/* ./ mv -i "$prefix"/* ./
git undo git undo
git add . git add .
git commit -am "$message" git commit -am "$message"
git stash apply git stash apply
rm -drf $prefix rm -drf "$prefix"
fi fi

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
usage() { usage() {
echo 1>&2 "usage: git missing [<first branch>] <second branch> [<git log options>]" echo 1>&2 "usage: git missing [<first branch>] <second branch> [<git log options>] [[--] <path>...]"
} }
if [ "${#}" -lt 1 ] if [ "${#}" -lt 1 ]
@ -12,9 +12,20 @@ fi
declare -a git_log_args=() declare -a git_log_args=()
declare -a branches=() declare -a branches=()
declare -a pathspec=()
declare parse_path=false
for arg in "$@" ; do for arg in "$@" ; do
if [[ $parse_path == true ]]; then
pathspec+=("$@")
break
fi
case "$arg" in case "$arg" in
--)
parse_path=true
;;
--*) --*)
git_log_args+=( "$arg" ) git_log_args+=( "$arg" )
;; ;;
@ -38,4 +49,4 @@ else
exit 1 exit 1
fi fi
git log "${git_log_args[@]}" "$firstbranch"..."$secondbranch" --format="%m %h %s" --left-right git log "${git_log_args[@]}" "$firstbranch"..."$secondbranch" --format="%m %h %s" --left-right -- "${pathspec[@]}"

View file

@ -7,22 +7,30 @@ if [ -z "${1-}" ] ; then
exit 1 exit 1
fi fi
remote_ref_kind=merge-requests
if test "$1" = "clean"; then if test "$1" = "clean"; then
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read ref; do git for-each-ref refs/heads/mr/* --format='%(refname)' | while read -r ref; do
git branch -D ${ref#refs/heads/} git branch -D "${ref#refs/heads/}"
done done
exit 0 exit 0
elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then
remote=${BASH_REMATCH[1]}.git remote=${BASH_REMATCH[1]}.git
id=${BASH_REMATCH[3]} 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 else
id=$1 id=$1
remote=${2:-origin} remote=${2:-origin}
fi fi
branch=mr/$id branch=mr/$id
remote_ref=refs/merge-requests/$id/head remote_ref=refs/$remote_ref_kind/$id/head
git fetch -fu $remote $remote_ref:$branch git fetch -fu "$remote" "$remote_ref:$branch"
git checkout $branch git checkout "$branch"
git config --local --replace branch.$branch.merge $remote_ref git config --local --replace "branch.$branch.merge" "$remote_ref"
git config --local --replace branch.$branch.remote $remote git config --local --replace "branch.$branch.remote" "$remote"

View file

@ -9,15 +9,15 @@ do
file="$file"' '"$i" file="$file"' '"$i"
shift shift
done done
test -n "$*" && range="$*" test -n "$*" && range=("$@")
test -z "$file" && echo "file required." 1>&2 && exit 1 test -z "$file" && echo "file required." 1>&2 && exit 1
if [ -z "$range" ] if [ -z "${range[*]}" ]
then then
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \ git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
--prune-empty --tag-name-filter cat -- --all --prune-empty --tag-name-filter cat -- --all
else else
# don't quote $range so that we can forward multiple rev-list arguments # $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" \ git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
--prune-empty --tag-name-filter cat -- $range --prune-empty --tag-name-filter cat -- "${range[@]}"
fi fi

View file

@ -2,7 +2,7 @@
set -e set -e
set -o pipefail set -o pipefail
if ! command -v pastebinit >/dev/null 2>&1; then if ! command -v pastebinit &>/dev/null; then
echo >&2 "To run 'git paste', you need to install pastebinit in your system" echo >&2 "To run 'git paste', you need to install pastebinit in your system"
exit 1 exit 1
fi fi

View file

@ -12,10 +12,10 @@ pull() {
ref="refs/pull/$id/merge" ref="refs/pull/$id/merge"
fi fi
git fetch -fu $remote $ref:$branch && \ git fetch -fu "$remote" "$ref:$branch" && \
git checkout $branch && \ git checkout "$branch" && \
git config --local --replace branch.$branch.merge $ref && \ git config --local --replace "branch.$branch.merge" "$ref" && \
git config --local --replace branch.$branch.remote $remote; git config --local --replace "branch.$branch.remote" "$remote"
} }
pull_pr_if_matched() { pull_pr_if_matched() {
@ -54,8 +54,8 @@ done
test -z "$1" && echo "pr number required." 1>&2 && exit 1 test -z "$1" && echo "pr number required." 1>&2 && exit 1
if test "$1" = "clean"; then if test "$1" = "clean"; then
git for-each-ref refs/heads/pr/* --format='%(refname)' | while read ref; do git for-each-ref refs/heads/pr/* --format='%(refname)' | while read -r ref; do
git branch -D ${ref#refs/heads/} git branch -D "${ref#refs/heads/}"
done done
elif [[ "$1" =~ ^[0-9]+$ ]]; then elif [[ "$1" =~ ^[0-9]+$ ]]; then

View file

@ -5,7 +5,7 @@ SECONDARY_BRANCH=""
FF="--ff" FF="--ff"
CONTINUE="no" CONTINUE="no"
function current_branch() { current_branch() {
git rev-parse --abbrev-ref HEAD git rev-parse --abbrev-ref HEAD
} }
@ -19,10 +19,10 @@ function usage()
echo " -c|--continue: Continue after the user updates conflicts." echo " -c|--continue: Continue after the user updates conflicts."
} }
while [[ $# > 0 ]] while [[ $# -gt 0 ]]
do do
key="$1" key="$1"
case $key in case $key in
--no-ff) --no-ff)
FF="--no-ff" FF="--no-ff"
@ -50,19 +50,17 @@ fi
if [[ "$CONTINUE" == "yes" ]]; then if [[ "$CONTINUE" == "yes" ]]; then
TARGET_BRANCH=$(current_branch) TARGET_BRANCH=$(current_branch)
set $(echo "$TARGET_BRANCH" | sed -e "s/-rebased-on-top-of-/\n/g") SECONDARY_BRANCH=${TARGET_BRANCH%"-rebased-on-top-of-"*}
if [[ $# != 2 ]]; then 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}" echo "Couldn't continue rebasing on ${TARGET_BRANCH}"
exit 30 # Impossible to detect PRIMARY_BRANCH AND SECONDARY_BRANCH exit 30 # Impossible to detect PRIMARY_BRANCH AND SECONDARY_BRANCH
fi fi
SECONDARY_BRANCH=$1
PRIMARY_BRANCH=$2
echo "Continuing rebasing of $SECONDARY_BRANCH on top of $PRIMARY_BRANCH" echo "Continuing rebasing of $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
git commit || exit 51 git commit || exit 51
git branch -d ${SECONDARY_BRANCH} || exit 52 git branch -d "${SECONDARY_BRANCH}" || exit 52
git branch -m ${TARGET_BRANCH} ${SECONDARY_BRANCH} || exit 53 git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 53
elif [[ "$PRIMARY_BRANCH" == "" ]]; then elif [[ "$PRIMARY_BRANCH" == "" ]]; then
usage usage
@ -71,14 +69,13 @@ else
echo "Rebasing $SECONDARY_BRANCH on top of $PRIMARY_BRANCH" echo "Rebasing $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
TARGET_BRANCH="${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}" TARGET_BRANCH="${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}"
git checkout ${PRIMARY_BRANCH} || exit 41 git checkout "${PRIMARY_BRANCH}" || exit 41
git checkout -b ${TARGET_BRANCH} || exit 42 git checkout -b "${TARGET_BRANCH}" || exit 42
git merge ${SECONDARY_BRANCH} ${FF} \
-m "Psycho-rebased branch ${SECONDARY_BRANCH} on top of ${PRIMARY_BRANCH}"
if [[ $? == 0 ]]; then if git merge "${SECONDARY_BRANCH}" ${FF} \
git branch -d ${SECONDARY_BRANCH} || exit 43 -m "Psycho-rebased branch ${SECONDARY_BRANCH} on top of ${PRIMARY_BRANCH}"; then
git branch -m ${TARGET_BRANCH} ${SECONDARY_BRANCH} || exit 44 git branch -d "${SECONDARY_BRANCH}" || exit 43
git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 44
else else
echo "Resolve the conflict and run ``${PROGRAM} --continue``." echo "Resolve the conflict and run ``${PROGRAM} --continue``."
exit 1 exit 1

View file

@ -31,9 +31,9 @@ EOF
# personal access token # personal access token
# config name is github-personal-access-token '_' is not allowed in git config # config name is github-personal-access-token '_' is not allowed in git config
github_personal_access_token=$(git config git-extras.github-personal-access-token) github_personal_access_token=$(git config --default "$GITHUB_TOKEN" git-extras.github-personal-access-token)
test -z "$github_personal_access_token" && abort "git config git-extras.github-personal-access-token required" test -z "$github_personal_access_token" && abort "GITHUB_TOKEN or git config git-extras.github-personal-access-token required"
# branch # branch
@ -43,7 +43,7 @@ if [ -z "$remote" ]; then
echo 'no upstream found, push to origin as default' echo 'no upstream found, push to origin as default'
remote="origin" remote="origin"
fi fi
[ "$remote" == "." ] && abort "the upstream should be a remote branch." [ "$remote" = "." ] && abort "the upstream should be a remote branch."
# make sure it's pushed # make sure it's pushed
@ -72,7 +72,7 @@ echo
if [ -z "$base" ] if [ -z "$base" ]
then then
base="master" base="$(git_extra_default_branch)"
fi fi
body=$(json "$title" "$body" "$branch" "$base") body=$(json "$title" "$body" "$branch" "$base")
@ -82,4 +82,4 @@ curl \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token $github_personal_access_token" \ -H "Authorization: token $github_personal_access_token" \
-H "X-GitHub-OTP: $mfa_code" \ -H "X-GitHub-OTP: $mfa_code" \
"https://api.github.com/repos/$project/pulls" -d "$body" "https://api.github.com/repos/$project/pulls" -d "$body"

View file

@ -17,26 +17,26 @@ fi
# Use a temporary index. # Use a temporary index.
index=$(git_extra_mktemp) index=$(git_extra_mktemp)
cleanup() { cleanup() {
rm $index rm "$index"
exit 2 exit 2
} }
trap cleanup 2 trap cleanup INT
# Go back in history while parent commits are available. # Go back in history while parent commits are available.
echo "Trying to find a commit the patch applies to..." echo "Trying to find a commit the patch applies to..."
rev=$(git rev-parse HEAD) rev=$(git rev-parse HEAD)
while [ $? = 0 ] while [ $? = 0 ]
do do
GIT_INDEX_FILE=$index git read-tree $rev GIT_INDEX_FILE=$index git read-tree "$rev"
# Try to apply the patch. # Try to apply the patch.
GIT_INDEX_FILE=$index git apply --cached $1 >/dev/null 2>&1 GIT_INDEX_FILE=$index git apply --cached "$1" &>/dev/null
patch_failed=$? patch_failed=$?
# Do it again, but show the error, if the problem is the patch itself. # Do it again, but show the error, if the problem is the patch itself.
if [ $patch_failed = 128 ] if [ $patch_failed = 128 ]
then then
GIT_INDEX_FILE=$index git apply --index --check $1 GIT_INDEX_FILE=$index git apply --index --check "$1"
exit $patch_failed exit $patch_failed
fi fi
@ -45,19 +45,19 @@ do
then then
# Manufacture a commit. # Manufacture a commit.
tree=$(GIT_INDEX_FILE=$index git write-tree) tree=$(GIT_INDEX_FILE=$index git write-tree)
commit=$(git commit-tree $tree -p $rev -m "$1") commit=$(git commit-tree "$tree" -p "$rev" -m "$1")
rm $index 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 $? exit $?
fi fi
rev=$(git rev-parse --verify -q $rev^) rev=$(git rev-parse --verify -q "$rev^")
done done
# No compatible commit found. Restore. # No compatible commit found. Restore.
echo "Failed to find a commit the patch applies to." echo "Failed to find a commit the patch applies to."
rm $index rm "$index"
exit 1 exit 1

View file

@ -4,17 +4,17 @@ set -e
hook() { hook() {
local hook=.git/hooks/$1.sh local hook=.git/hooks/$1.sh
# compat without extname # compat without extname
if test ! -f $hook; then if test ! -f "$hook"; then
hook=.git/hooks/$1 hook=.git/hooks/$1
fi fi
if test -f $hook; then if test -f "$hook"; then
echo "... $1" echo "... $1"
shift shift
if test -x $hook; then if test -x "$hook"; then
$hook "$@" $hook "$@"
else else
. $hook "$@" . "$hook" "$@"
fi fi
fi fi
} }
@ -51,6 +51,12 @@ if test $# -gt 0; then
semver=$2 semver=$2
shift shift
;; ;;
--prefix)
test -z "$2" &&
exit_with_msg "prefix string required for --prefix option"
prefix="$2"
shift
;;
--no-empty-commit) no_empty_commit=true;; --no-empty-commit) no_empty_commit=true;;
--) shift; hook_args="$hook_args $*"; break;; --) shift; hook_args="$hook_args $*"; break;;
*) test -z "$version" && version=$1 ;; *) test -z "$version" && version=$1 ;;
@ -69,7 +75,7 @@ if test $# -gt 0; then
latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)") latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
if [[ ! "$latest_tag" =~ \ if [[ ! "$latest_tag" =~ \
^([^0-9]*)([1-9][0-9]+|[0-9])\.([1-9][0-9]+|[0-9])\.([1-9][0-9]+|[0-9])(.*) ]]; then ^$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 echo "the latest tag doesn't match semver format requirement" 1>&2
exit 1 exit 1
fi fi
@ -86,9 +92,9 @@ if test $# -gt 0; then
(( ++version )) (( ++version ))
case "$semver" in case "$semver" in
major ) version="${BASH_REMATCH[1]}$version.0.0${BASH_REMATCH[5]}" ;; major ) version="$prefix${BASH_REMATCH[1]}$version.0.0${BASH_REMATCH[5]}" ;;
minor ) version="${BASH_REMATCH[1]}${BASH_REMATCH[2]}.$version.0${BASH_REMATCH[5]}" ;; minor ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.$version.0${BASH_REMATCH[5]}" ;;
patch ) version="${BASH_REMATCH[1]}${BASH_REMATCH[2]}.${BASH_REMATCH[3]}.$version${BASH_REMATCH[5]}" ;; patch ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.${BASH_REMATCH[3]}.$version${BASH_REMATCH[5]}" ;;
esac esac
fi fi
@ -113,7 +119,7 @@ if test $# -gt 0; then
fi fi
declare -a sign_args declare -a sign_args
if [ "$sign" == true ]; then if [ "$sign" = true ]; then
sign_args=("-s") sign_args=("-s")
fi fi

View file

@ -2,9 +2,9 @@
set -e set -e
# Assert there is at least one branch provided # Assert there is at least one branch provided
test -z $1 && echo "new branch name required." 1>&2 && exit 1 test -z "$1" && echo "new branch name required." 1>&2 && exit 1
if [ -z $2 ]; then if [ -z "$2" ]; then
new_branch="$1" new_branch="$1"
old_branch="$(git symbolic-ref --short -q HEAD)" old_branch="$(git symbolic-ref --short -q HEAD)"
else else

101
bin/git-rename-file Executable file
View file

@ -0,0 +1,101 @@
#!/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'."

View file

@ -5,8 +5,8 @@ set -euo pipefail
old=$1 old=$1
new=$2 new=$2
test -z $old && echo "old remote name required." 1>&2 && exit 1 test -z "$old" && echo "old remote name required." 1>&2 && exit 1
test -z $new && echo "new 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 if ! git config --get "remote.$old.fetch" > /dev/null; then
echo "remote $old doesn't exist" echo "remote $old doesn't exist"
@ -14,7 +14,7 @@ if ! git config --get "remote.$old.fetch" > /dev/null; then
fi fi
if git config --get "remote.$new.fetch" > /dev/null; then if git config --get "remote.$new.fetch" > /dev/null; then
git remote remove $new git remote remove "$new"
fi fi
git remote rename $old $new git remote rename "$old $new"
git remote -v git remote -v

View file

@ -3,10 +3,10 @@
old=$1 old=$1
new=$2 new=$2
test -z $old && echo "old 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 test -z "$new" && echo "new tag name required." 1>&2 && exit 1
git tag $new $old git tag "$new" "$old"
git tag -d $old git tag -d "$old"
git push origin $new git push origin "$new"
git push origin :refs/tags/$old git push origin ":refs/tags/$old"

View file

@ -1,43 +1,99 @@
#!/usr/bin/env bash #!/usr/bin/env bash
shopt -s extglob
git version git version
echo "git-extras version ""$(git-extras -v)" 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" 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
while true; do while true; do
# Current branch # 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 # Prompt
if test -n "$cur"; then if test -n "$cur"; then
prompt="git ($cur)> " cur_string=" ($cur)"
else else
prompt="git> " 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=""
fi fi
# Readline prompt_base="$prefix$project_name$cur_string$es_string$prompt_character"
read -e -r -p "$prompt" cmd prompt_base_stripped=$(echo "$prompt_base" | awk '{$1=$1};1')
prompt="$prompt_base_stripped "
# EOF # Use arguments as a command if any are provided.
test $? -ne 0 && break 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
# History # Add command to history if it is not all whitespace
history -s "$cmd" if [[ ! "$cmd" =~ ^[[:space:]]*$ ]]; then
history -s "$cmd"
fi
# Built-in commands # Built-in commands
case $cmd in case $cmd in
ls) cmd=ls-files;; ls) cmd=ls-files;;
"") continue;; "")
quit|exit) break;; 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;;
esac esac
history -a
if [[ $cmd == !* ]]; then if [[ $cmd == !* ]]; then
eval ${cmd:1} # shellcheck disable=SC2086
eval ${cmd:1}
elif [[ $cmd == git* ]]; then elif [[ $cmd == git* ]]; then
# shellcheck disable=SC2086
eval $cmd eval $cmd
else else
eval git "$cmd" eval git "$cmd"
fi fi
exit_status=$?
done done
echo echo

View file

@ -4,14 +4,14 @@ file="$1"
commit="$2" commit="$2"
if [[ -f $file ]]; then if [[ -f $file ]]; then
git rm --cached -q -f -- $file git rm --cached -q -f -- "$file"
if [[ -z $commit ]]; then if [[ -z $commit ]]; then
git checkout HEAD -- $file git checkout HEAD -- "$file"
echo "Reset '$1' to HEAD" echo "Reset '$1' to HEAD"
else else
git checkout $commit -- $file git checkout "$commit" -- "$file"
echo "Reset '$1' to $commit" echo "Reset '$1' to $commit"
fi fi
else else
echo "File '$1' not found in `pwd`" echo "File '$1' not found in $PWD"
fi fi

View file

@ -6,7 +6,13 @@ git_root() {
# get the relative path of current path according to root of repo # get the relative path of current path according to root of repo
git_root_relative() { git_root_relative() {
git rev-parse --show-prefix 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
} }
if test $# -eq 0; then if test $# -eq 0; then

View file

@ -18,7 +18,7 @@ function _test_git_scp()
function set_remote() function set_remote()
{ {
remote=$1 remote=$1
if [ $(git remote | grep -c -- ^$remote$) -eq 0 ] if [ "$(git remote | grep -c -- "^$remote$")" -eq 0 ]
then then
COLOR_RED COLOR_RED
echo "Remote $remote does not exist in your git config" echo "Remote $remote does not exist in your git config"
@ -39,8 +39,9 @@ function php_lint()
test ! -f "$i" && continue test ! -f "$i" && continue
case "$i" in case "$i" in
*\.php|*\.phtml) *\.php|*\.phtml)
php -l "$i" > /dev/null if ! php -l "$i" > /dev/null; then
[ $? -gt 0 ] && error_count[${#error_count[@]}]="$i" error_count[${#error_count[@]}]="$i"
fi
;; ;;
esac esac
done done
@ -58,17 +59,17 @@ function php_lint()
function _dos2unix() function _dos2unix()
{ {
command -v dos2unix > /dev/null && dos2unix $@ command -v dos2unix > /dev/null && dos2unix "$@"
return 0 return 0
} }
function _sanitize() function _sanitize()
{ {
git config --get-all extras.scp.sanitize | while read i git config --get-all extras.scp.sanitize | while read -r i
do do
case $i in case $i in
php_lint) php_lint $@;; # git config --global --add extras.scp.sanitize php_lint php_lint) php_lint "$@";; # git config --global --add extras.scp.sanitize php_lint
dos2unix) _dos2unix $@;; # git config --global --add extras.scp.sanitize dos2unix dos2unix) _dos2unix "$@";; # git config --global --add extras.scp.sanitize dos2unix
esac esac
done done
return $? return $?
@ -76,14 +77,26 @@ function _sanitize()
function scp_and_stage function scp_and_stage
{ {
set_remote $1 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"
shift shift
local refhead="$(git rev-parse --quiet --verify $1)" local refhead
refhead="$(git rev-parse --quiet --verify "$1")"
if [ -n "$refhead" ] if [ -n "$refhead" ]
then then
shift 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" _error "refhead provided is not part of current branch"
fi fi
@ -92,46 +105,117 @@ function scp_and_stage
list=$(git ls-files "$@")" "$(git ls-files -o "$@") list=$(git ls-files "$@")" "$(git ls-files -o "$@")
elif [ -n "$refhead" ] elif [ -n "$refhead" ]
then then
git diff --stat $refhead [ "$verbose" -eq 1 ] && git --no-pager diff --stat "$refhead"
list=$(git diff $refhead --name-only) list=$(git diff "$refhead" --name-only)
else else
git diff [ "$verbose" -eq 1 ] && git --no-pager diff
list=$(git diff --name-only) list=$(git diff --name-only)
fi fi
deleted=$(for i in $list; do [ -f $i ] || echo $i; done) deleted=$(for i in $list; do [ -f "$i" ] || echo "$i"; done)
list=$(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
if [ -n "$list" ] if [ -n "$list" ]
then then
local _TMP=${0///} local _TMP=${0///}
echo "$list" > $_TMP && # shellcheck disable=SC2086
_sanitize $list && echo "$list" > "$_TMP"
_info Pushing to $remote \($(git config remote.$remote.url)\) && if [ "$dry_run" -eq 1 ] || _sanitize $list
rsync -rlDv --files-from=$_TMP ./ "$(git config remote.$remote.url)/" && then
git add --force $list && _info "Pushing to $remote ($(git config "remote.$remote.url"))"
rm $_TMP 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"
fi 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)
[ -n "$deleted" ] && if [ -n "$deleted" ]
COLOR_RED && then
echo Deleted remote files && COLOR_RED
ssh $(git config remote.$remote.url | cut -d: -f1) -t "rm $deleted" && echo Deleted remote files
echo "$deleted" if [ "$dry_run" -eq 1 ]
COLOR_RESET 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"
} }
function reverse_scp() function reverse_scp()
{ {
set_remote $1 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"
shift shift
local _TMP=${0///} local _TMP=${0///}
echo $@ > $_TMP && echo "$@" > "$_TMP"
rsync -rlDv --files-from=$_TMP "$(git config remote.$remote.url)/" ./ &&
rm $_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"
} }
function _info() function _info()
@ -145,13 +229,18 @@ function _usage()
{ {
echo "Usage: echo "Usage:
git scp -h|help|? git scp -h|help|?
git scp <remote> [ref|file..] # scp and stage your files to specified remote git scp [options] <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 scp [options] <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 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)
" "
case $1 in case $1 in
-v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' $0;; -v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' "$0" ;;
esac esac
exit exit
} }
@ -161,18 +250,18 @@ function _error()
[ $# -eq 0 ] && _usage && exit 0 [ $# -eq 0 ] && _usage && exit 0
echo echo
echo ERROR: "$@" echo "ERROR: $*"
echo echo
exit 1 exit 1
} }
### OPTIONS ### ### OPTIONS ###
case $(basename $0) in case $(basename "$0") in
git-scp) git-scp)
case $1 in case $1 in
''|-h|'?'|help|--help) shift; _test_git_scp; _usage $@;; ''|-h|'?'|help|--help) shift; _test_git_scp; _usage "$@";;
*) scp_and_stage $@;; *) scp_and_stage "$@";;
esac esac
;; ;;
git-rscp) reverse_scp $@;; git-rscp) reverse_scp "$@";;
esac esac

View file

@ -17,7 +17,7 @@ do_commit() {
} }
pathspec= pathspec=
while [ "X$1" != "X" ]; do while [ "$1" != "" ]; do
case "$1" in case "$1" in
-c|--commit) -c|--commit)
if git status --porcelain | grep .; then if git status --porcelain | grep .; then
@ -33,7 +33,7 @@ actual command:
} }
;; ;;
-f|--flags) -f|--flags)
if [ "X$2" = "X" ]; then if [ "$2" = "" ]; then
usage usage
echo "missing argument for $1" echo "missing argument for $1"
exit 1 exit 1
@ -55,11 +55,11 @@ actual command:
exit 1 exit 1
;; ;;
*) *)
if [ "X$search" = "X" ]; then if [ "$search" = "" ]; then
search="$1" search="$1"
elif [ "X$replacement" = "X" ]; then elif [ "$replacement" = "" ]; then
replacement="$1" replacement="$1"
elif [ "X$flags" = "X" ]; then elif [ "$flags" = "" ]; then
flags="$1" flags="$1"
else else
usage usage
@ -79,7 +79,7 @@ case "$all" in
escaped="${escaped//[/\\[}" escaped="${escaped//[/\\[}"
sep="$(printf '%s' "$ascii" | tr -d "$escaped")" sep="$(printf '%s' "$ascii" | tr -d "$escaped")"
sep="$(printf %.1s "$sep")" sep="$(printf %.1s "$sep")"
if [ "X$sep" = "X" ] ; then if [ "$sep" = "" ] ; then
echo 'could not find an unused character for sed separator character' echo 'could not find an unused character for sed separator character'
exit 1 exit 1
fi fi

View file

@ -2,7 +2,7 @@
COMMIT_MESSAGE='Initial commit' COMMIT_MESSAGE='Initial commit'
if [ "$1" == "-m" ]; then if [ "$1" = "-m" ]; then
COMMIT_MESSAGE=$2 COMMIT_MESSAGE=$2
shift; shift shift; shift
fi fi

View file

@ -1,3 +1,3 @@
#!/usr/bin/env bash #!/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 "$(git_extra_default_branch)" | tr -d ' '

View file

@ -1,3 +1,3 @@
#!/usr/bin/env bash #!/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 "$(git_extra_default_branch)" | tr -d ' '

View file

@ -22,8 +22,7 @@ EOF
error() { error() {
if [[ -n "$1" ]]; then if [[ -n "$1" ]]; then
local msg=$( echo "error: $1" ) echo "error: $1" >&2
echo -e "${msg}" >&2
fi fi
usage usage
exit 1 exit 1
@ -34,7 +33,7 @@ stamp() {
local commit_msg=$( git log -1 --pretty=%B ) local commit_msg=$( git log -1 --pretty=%B )
local stamp_msg local stamp_msg
[[ -n "${MSG}" ]] && stamp_msg="${ID} ${MSG}" || stamp_msg="${ID}" [[ -n "${MSG}" ]] && stamp_msg="${ID} ${MSG}" || stamp_msg="${ID}"
if ${REPLACE}; then if ${REPLACE}; then
# remove previous stamps with same ID from the commit message # remove previous stamps with same ID from the commit message
commit_msg=$( commit_msg=$(
@ -71,9 +70,9 @@ parse_options() {
;; ;;
esac esac
done done
ID="$1" ID="$1"
MSG="${@:2}" MSG="${*:2}"
} }

View file

@ -5,7 +5,7 @@
usage() { usage() {
cat <<EOS cat <<EOS
Usage: Usage:
git standup [-a <author name>] [-w <weekstart-weekend>] [-d <days-ago>] [-m <max-dir-depth>] [-g] [-h] [-f] [-B] [-n <number-of-commits] 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. -a - Specify author to restrict search to, default to current git user.
Use "-a all" if you don't want the restriction. Use "-a all" if you don't want the restriction.
@ -15,10 +15,11 @@ usage() {
-d - Specify the number of days back to include -d - Specify the number of days back to include
-D - Specify the date format for "git log" (default: relative) -D - Specify the date format for "git log" (default: relative)
-h - Display this help screen -h - Display this help screen
-g - Show if commit is GPG signed (G) or not (N)
-f - Fetch the latest commits beforehand -f - Fetch the latest commits beforehand
-B - Display the commits in branch groups -B - Display the commits in branch groups
-n - Limit the number of commits displayed per group -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: Examples:
git standup -a "John Doe" -w "MON-FRI" -m 3 git standup -a "John Doe" -w "MON-FRI" -m 3
@ -34,7 +35,7 @@ in_git_repo=$?
# Use colors, but only if connected to a terminal, and that terminal # Use colors, but only if connected to a terminal, and that terminal
# supports them. # supports them.
if which tput >/dev/null 2>&1; then if command -v tput &>/dev/null; then
ncolors=$(tput colors) ncolors=$(tput colors)
fi fi
if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
@ -63,7 +64,11 @@ fi
# which may fail on systems lacking tput or terminfo # which may fail on systems lacking tput or terminfo
set -e set -e
while getopts "hgfBd:a:w:m:D:n:L" opt; do 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 case $opt in
h) h)
usage usage
@ -77,7 +82,8 @@ while getopts "hgfBd:a:w:m:D:n:L" opt; do
fi fi
;; ;;
d) d)
test -n "$SINCE" && warn "-d option is conflict with -w" test -n "$RANGE_SPECIFIED" && warn "-d option is conflict with -w"
RANGE_SPECIFIED=yes
if [ "$OPTARG" -lt 1 ]; then if [ "$OPTARG" -lt 1 ]; then
>&2 echo "Specify days less than one is invalid" >&2 echo "Specify days less than one is invalid"
exit 1 exit 1
@ -85,10 +91,11 @@ while getopts "hgfBd:a:w:m:D:n:L" opt; do
SINCE="$OPTARG days ago" SINCE="$OPTARG days ago"
;; ;;
w) w)
if [ -n "$SINCE" ]; then if [ -n "$RANGE_SPECIFIED" ]; then
warn "-w option is conflict with -d" warn "-w option is conflict with -d"
continue continue
fi fi
RANGE_SPECIFIED=yes
week_range=${OPTARG} week_range=${OPTARG}
week_start="${week_range%%-*}" week_start="${week_range%%-*}"
@ -120,7 +127,8 @@ while getopts "hgfBd:a:w:m:D:n:L" opt; do
GIT_DATE_FORMAT=${OPTARG} GIT_DATE_FORMAT=${OPTARG}
;; ;;
g) g)
GIT_PRETTY_FORMAT="%C(yellow)gpg: %G?%Creset" warn "-g option is deprecated, use '-F gpg' instead"
USE_GPG_FORMAT=yes
;; ;;
B) B)
GROUP_BY_BRANCHES=true GROUP_BY_BRANCHES=true
@ -128,6 +136,21 @@ while getopts "hgfBd:a:w:m:D:n:L" opt; do
n) n)
MAX_COMMIT_NUM=${OPTARG} 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 usage
exit 1 exit 1
@ -148,13 +171,34 @@ if [[ $# -gt 0 ]]; then
UNTIL=$3 UNTIL=$3
fi fi
AUTHOR=${AUTHOR:="$(git config user.name)"} AUTHOR=${AUTHOR:="$(git config user.name || echo '')"}
SINCE=${SINCE:=yesterday} if [ -z "${AUTHOR}" ]; then
UNTIL=${UNTIL:=today} 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} FETCH_LAST_COMMIT=${FETCH_LAST_COMMIT:=false}
MAXDEPTH=${MAXDEPTH:=2} MAXDEPTH=${MAXDEPTH:=2}
GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen(%cd) %C(bold blue)<%an>%Creset $GIT_PRETTY_FORMAT" GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen(${COMMIT_DATE_FORMAT}) %C(bold blue)<%an>%Creset"
GIT_DATE_FORMAT=${GIT_DATE_FORMAT:=relative} 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 \ GIT_LOG_COMMAND="git --no-pager log \
--no-merges --no-merges
@ -166,7 +210,7 @@ GIT_LOG_COMMAND="git --no-pager log \
--color=$COLOR --color=$COLOR
--pretty=format:'$GIT_PRETTY_FORMAT' --pretty=format:'$GIT_PRETTY_FORMAT'
--date='$GIT_DATE_FORMAT'" --date='$GIT_DATE_FORMAT'"
if [[ ! -z "$MAX_COMMIT_NUM" ]]; then if [[ -n "$MAX_COMMIT_NUM" ]]; then
GIT_LOG_COMMAND="$GIT_LOG_COMMAND --max-count=$MAX_COMMIT_NUM" GIT_LOG_COMMAND="$GIT_LOG_COMMAND --max-count=$MAX_COMMIT_NUM"
fi fi
@ -177,13 +221,13 @@ git_output() {
for branch in $branches; do for branch in $branches; do
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if output=$(eval $GIT_LOG_COMMAND "$branch"); then if output=$(eval $GIT_LOG_COMMAND "$branch"); then
if [[ ! -z "$output" ]] ; then if [[ -n "$output" ]] ; then
echo "${GREEN}${branch}${NORMAL}" echo "${GREEN}${branch}${NORMAL}"
echo "$output" echo "$output"
echo "" echo ""
fi fi
fi fi
# TODO optimize:return if the latest commit of a branch is eariler than the 'since' day # TODO optimize:return if the latest commit of a branch is earlier than the 'since' day
done done
else else
# shellcheck disable=SC2086 # shellcheck disable=SC2086
@ -196,6 +240,7 @@ if [[ $in_git_repo != 0 ]]; then
## Set delimiter to newline for the loop ## Set delimiter to newline for the loop
IFS=$'\n' IFS=$'\n'
## Recursively search for git repositories ## Recursively search for git repositories
# shellcheck disable=SC2086
PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git) PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git)
# Fetch the latest commits, if required # Fetch the latest commits, if required
@ -225,7 +270,7 @@ if [[ $in_git_repo != 0 ]]; then
if [[ -d ".git" || -f ".git" ]] ; then if [[ -d ".git" || -f ".git" ]] ; then
if GITOUT=$(git_output); then if GITOUT=$(git_output); then
## Only output if there is some activities ## Only output if there is some activities
if [[ ! -z "$GITOUT" ]] ; then if [[ -n "$GITOUT" ]] ; then
echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}" echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}"
echo "$GITOUT" echo "$GITOUT"
echo "" echo ""
@ -243,7 +288,7 @@ else
fi fi
if GITOUT=$(git_output); then if GITOUT=$(git_output); then
if [[ ! -z "$GITOUT" ]] ; then if [[ -n "$GITOUT" ]] ; then
echo "$GITOUT" echo "$GITOUT"
else else
if [[ $AUTHOR = '.*' ]] ; then if [[ $AUTHOR = '.*' ]] ; then

View file

@ -3,16 +3,29 @@
cd "$(git root)" || { echo "Can't cd to top level directory";exit 1; } cd "$(git root)" || { echo "Can't cd to top level directory";exit 1; }
PROJECT_FULL_PATH=
SUMMARY_BY_LINE= SUMMARY_BY_LINE=
DEDUP_BY_EMAIL= DEDUP_BY_EMAIL=
MERGES_ARG=
OUTPUT_STYLE=
for arg in "$@"; do for arg in "$@"; do
case "$arg" in case "$arg" in
--full-path)
PROJECT_FULL_PATH=1
;;
--line) --line)
SUMMARY_BY_LINE=1 SUMMARY_BY_LINE=1
;; ;;
--dedup-by-email) --dedup-by-email)
DEDUP_BY_EMAIL=1 DEDUP_BY_EMAIL=1
;; ;;
--no-merges)
MERGES_ARG="--no-merges"
;;
--output-style)
OUTPUT_STYLE="$2"
shift
;;
-*) -*)
>&2 echo "unknown argument $arg found" >&2 echo "unknown argument $arg found"
exit 1 exit 1
@ -31,27 +44,38 @@ if [ -n "$DEDUP_BY_EMAIL" ] && [ -n "$SUMMARY_BY_LINE" ]; then
exit 1 exit 1
fi fi
commit="" if [ -n "$MERGES_ARG" ] && [ -n "$SUMMARY_BY_LINE" ]; then
test $# -ne 0 && commit=$* >&2 echo "--no-merges used with --line is not supported"
project=${PWD##*/} 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
# #
# get date for the given <commit> # get date for the given <commit>
# #
commit_date() {
date() {
# the $1 can be empty # the $1 can be empty
# shellcheck disable=SC2086 # shellcheck disable=SC2086
git log --pretty='format: %ai' $1 | cut -d ' ' -f 2 git log $MERGES_ARG --pretty='format: %ai' "$1" | cut -d ' ' -f 2
} }
# #
# get active days for the given <commit> # get active days for the given <commit>
# #
active_days() { active_days() {
# shellcheck disable=SC2086 commit_date "$1" | sort -r | uniq | awk '
date $1 | sort -r | uniq | awk '
{ sum += 1 } { sum += 1 }
END { print sum } END { print sum }
' '
@ -60,16 +84,14 @@ active_days() {
# #
# get the commit total # get the commit total
# #
commit_count() { commit_count() {
# shellcheck disable=SC2086 # shellcheck disable=SC2086
git log --oneline $commit | wc -l | tr -d ' ' git rev-list $MERGES_ARG --count "$commit"
} }
# #
# total file count # total file count
# #
file_count() { file_count() {
git ls-files | wc -l | tr -d ' ' git ls-files | wc -l | tr -d ' '
} }
@ -77,20 +99,20 @@ file_count() {
# #
# remove duplicate authors who belong to the same email address # remove duplicate authors who belong to the same email address
# #
dedup_by_email() { dedup_by_email() {
# in: # in:
# 27 luo zexuan <luozexuan@xxx.com> # 27 luo zexuan <LuoZexuan@xxx.com>
# 7 罗泽轩 <luozexuan@xxx.com> # 7 罗泽轩 <luozexuan@xxx.com>
# out: # out:
# 34 luo zexuan # 34 luo zexuan
LC_ALL=C awk ' LC_ALL=C awk '
{ {
sum += $1 sum += $1
if ($NF in emails) { last_field = tolower($NF)
emails[$NF] += $1 if (last_field in emails) {
emails[last_field] += $1
} else { } else {
email = $NF email = last_field
emails[email] = $1 emails[email] = $1
# set commits/email to empty # set commits/email to empty
$1=$NF="" $1=$NF=""
@ -117,7 +139,6 @@ dedup_by_email() {
# #
# list authors # list authors
# #
format_authors() { format_authors() {
# a rare unicode character is used as separator to avoid conflicting with # a rare unicode character is used as separator to avoid conflicting with
# author name. However, Linux column utility will escape tab if separator # author name. However, Linux column utility will escape tab if separator
@ -135,11 +156,17 @@ format_authors() {
# #
# fetch repository age from oldest commit # fetch repository age from oldest commit
# #
repository_age() { 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 | LC_ALL=C sed 's/ago//'
} }
#
# fetch repository age of the latest commit
#
last_active() {
git log --pretty=oneline --format="%ar" -n 1
}
# #
# list the last modified author for each line # list the last modified author for each line
# #
@ -147,51 +174,86 @@ single_file() {
while read -r data while read -r data
do do
if [[ $(file "$data") = *text* ]]; then if [[ $(file "$data") = *text* ]]; then
git blame --line-porcelain "$data" 2>/dev/null | grep "^author\ " | LC_ALL=C sed -n 's/^author //p'; git blame --line-porcelain "$data" 2>/dev/null | grep "^author " | LC_ALL=C sed -n 's/^author //p';
fi fi
done done
} }
current_branch_name() {
git rev-parse --abbrev-ref HEAD
}
# #
# list the author for all file # list the author for all file
# #
lines() { lines() {
git ls-files | single_file git ls-files -- "$@" | single_file
} }
# #
# get the number of the lines # get the number of the lines
# #
line_count() { line_count() {
lines | wc -l lines "$@" | wc -l
} }
# summary uncommitted_changes_count() {
git status --porcelain | wc -l
}
echo
echo " project : $project" 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 if [ -n "$SUMMARY_BY_LINE" ]; then
echo " lines : $(line_count)" print_summary_by_line
echo " authors :"
lines | sort | uniq -c | sort -rn | format_authors
else else
print_summary
# shellcheck disable=SC2046
echo " repo age :" $(repository_age)
# shellcheck disable=SC2086
echo " active :" "$(active_days $commit)" days
echo " commits :" "$(commit_count)"
if test "$commit" = ""; then
echo " files :" "$(file_count)"
fi
echo " authors : "
if [ -n "$DEDUP_BY_EMAIL" ]; then
# the $commit can be empty
# shellcheck disable=SC2086
git shortlog -n -s -e $commit | dedup_by_email | format_authors
else
# shellcheck disable=SC2086
git shortlog -n -s $commit | format_authors
fi
fi fi

View file

@ -8,6 +8,7 @@ Usage:
${command} [<remote> <branch>] ${command} [<remote> <branch>]
${command} -h | --help ${command} -h | --help
${command} -s | --soft ${command} -s | --soft
${command} -f | --force
Sync local branch with <remote>/<branch>. 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. When <remote> and <branch> are not specified on the command line, upstream of local branch will be used by default.
@ -22,6 +23,9 @@ Examples:
Sync without cleaning untracked files: Sync without cleaning untracked files:
${command} -s ${command} -s
Sync without interaction:
${command} -f
EOS EOS
} }
@ -36,6 +40,9 @@ function main()
-s | --soft) -s | --soft)
local soft="true" local soft="true"
;; ;;
-f | --force)
local force="YES"
;;
* ) * )
if [ "${remote}" = "" ]; then if [ "${remote}" = "" ]; then
local remote="$1" local remote="$1"
@ -52,14 +59,14 @@ function main()
done done
local remote_branch local remote_branch
if [ "${remote}" = "" ]; then if [ -z "${remote}" ]; then
if ! remote_branch="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)"; 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." echo "There is no upstream information of local branch."
exit 1 exit 1
fi fi
local branch="$(git rev-parse --abbrev-ref --symbolic-full-name @)" local branch="$(git rev-parse --abbrev-ref --symbolic-full-name @)"
local remote=$(git config "branch.${branch}.remote") local remote=$(git config "branch.${branch}.remote")
elif [ "${branch}" = "" ]; then elif [ -z "${branch}" ]; then
echo -e "Error: too few arguments.\n" echo -e "Error: too few arguments.\n"
_usage _usage
exit 1 exit 1
@ -67,10 +74,16 @@ function main()
remote_branch="${remote}/${branch}" remote_branch="${remote}/${branch}"
fi fi
echo -n "Are you sure you want to clean all changes & sync with '${remote_branch}'? [y/N]: " if [ "${force}" != "YES" ]; then
local res if [ "${soft}" = "true" ]; then
read res echo -n "Are you sure you want to sync with '${remote_branch}'? [y/N]: "
case "${res}" in 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" ) "Y" | "y" | "yes" | "Yes" | "YES" )
if [ "${soft}" = "true" ]; then if [ "${soft}" = "true" ]; then
git fetch "${remote}" "${branch}" && git reset --hard "${remote_branch}" git fetch "${remote}" "${branch}" && git reset --hard "${remote_branch}"

View file

@ -7,7 +7,7 @@ USAGE(){
test $# -lt 1 && USAGE test $# -lt 1 && USAGE
for filename in $@; do for filename in "$@"; do
touch "$filename" \ touch "$filename" \
&& git add "$filename" && git add "$filename"
done done

View file

@ -2,7 +2,7 @@
cstr="commits" cstr="commits"
ccnt=$(git rev-list --count HEAD) ccnt=$(git rev-list --count HEAD)
if [ $ccnt -eq 1 ]; then cstr="commit"; fi if [ "$ccnt" -eq 1 ]; then cstr="commit"; fi
parm3="" parm3=""
function _undo() function _undo()
@ -11,14 +11,14 @@ function _undo()
undo_cnt=${2:-1} undo_cnt=${2:-1}
reset=${3:-""} reset=${3:-""}
if [ $undo_cnt -gt $ccnt ]; then if [ "$undo_cnt" -gt "$ccnt" ]; then
echo "Only $ccnt $cstr, cannot undo $undo_cnt" echo "Only $ccnt $cstr, cannot undo $undo_cnt"
elif [ "$type" = "hard" ] && [ $ccnt -eq $undo_cnt ]; then elif [ "$type" = "hard" ] && [ "$ccnt" -eq "$undo_cnt" ]; then
echo "Cannot hard undo all commits" echo "Cannot hard undo all commits"
elif [ "$type" = "soft" ] && [ $ccnt -eq 1 ]; then elif [ "$type" = "soft" ] && [ "$ccnt" -eq 1 ]; then
git update-ref -d HEAD git update-ref -d HEAD
else else
git reset --$type HEAD~$undo_cnt git reset "--$type" "HEAD~$undo_cnt"
fi fi
if [ "$reset" != "" ]; then git reset; fi if [ "$reset" != "" ]; then git reset; fi
} }
@ -69,4 +69,4 @@ if [[ ! $parm2 =~ ^[1-9][0-9]*$ ]]; then
exit 1 exit 1
fi fi
_undo $parm1 $parm2 $parm3 _undo "$parm1" "$parm2" "$parm3"

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
filename=$1 filename="$1"
test -z $filename && echo "filename required." 1>&2 && exit 1 test -z "$filename" && echo "filename required." 1>&2 && exit 1
git update-index --no-skip-worktree $filename git update-index --no-skip-worktree "$filename"

14
bin/git-unwip Executable file
View file

@ -0,0 +1,14 @@
#!/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

View file

@ -2,29 +2,130 @@
# #
# Change files modification time to their last commit date # Change files modification time to their last commit date
# #
if [ "$1" = --touch ]; then
# Internal use option only just to parallelize things.
shift
for f; do
iso_t=$(git --no-pager log --no-renames --pretty=format:%cI -1 @ -- "$f" 2>/dev/null)
if [ -n "$iso_t" ]; then
bsd=$(date -j > /dev/null 2>&1 && echo 'true')
if [ "$bsd" == "true" ]; then
t=$(date -j -f %Y-%m-%dT%H:%M:%S "$iso_t" +%Y%m%d%H%M.%S)
else
t=$(date -d"$iso_t" +%Y%m%d%H%M.%S)
fi
echo "+ touch -t $t $f" >&2
touch -t "$t" "$f"
fi
done
else
opt_r=$(xargs -r false < /dev/null > /dev/null 2>&1 && echo '-r')
# `-n` should be limited or parallelization will not give effect, # Bash unofficial strict mode
# because all args will go into single worker. set -euo pipefail
NPROC=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null) IFS=$'\n\t'
# shellcheck disable=SC2086
git ls-tree -z -r -t --name-only @ \ if [[ "${1:-}" == "--newer" ]]; then
| xargs -0 -P${NPROC:-1} -n${NPROC:-1} $opt_r git utimes --touch op=le
shift
else
op=eq
fi 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[@]}" -

8
bin/git-wip Executable file
View file

@ -0,0 +1,8 @@
#!/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"

View file

@ -3,10 +3,10 @@ index e49cd24..4ae28b5 100755
--- a/bin/git-extras --- a/bin/git-extras
+++ b/bin/git-extras +++ b/bin/git-extras
@@ -4,13 +4,12 @@ VERSION="4.3.0" @@ -4,13 +4,12 @@ VERSION="4.3.0"
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/master/install.sh" INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
update() { update() {
- local bin="$(which git-extras)" - local bin="$(command -v git-extras)"
- local prefix=${bin%/*/*} - local prefix=${bin%/*/*}
- local orig=$PWD - local orig=$PWD
- -

View file

@ -6,5 +6,5 @@ err() {
} }
if ! command -v column >/dev/null 2>&1; then if ! command -v column >/dev/null 2>&1; then
err "Need to install dependency 'column' before installation" err "Need to install dependency 'column' before installation (can be found in bsdmainutils)"
fi fi

View file

@ -6,7 +6,7 @@ err() {
} }
make_doc() { make_doc() {
echo "'touch man/git-$1.md && make man/git-$1.{1,html}'" echo "touch man/git-$1.md && make man/git-$1.{1,html}"
} }
check_bash_script() { check_bash_script() {
@ -25,7 +25,7 @@ check_bash_script() {
check_git_extras_cmd_list() { check_git_extras_cmd_list() {
local whitelist=('extras') local whitelist=('extras')
for cmd in ${whitelist[*]}; do for cmd in "${whitelist[@]}"; do
test "$1" == "$cmd" && return test "$1" == "$cmd" && return
done done
@ -47,6 +47,11 @@ check_documentation() {
err "Create man/$cmd.1 and man/$cmd.html via $(make_doc "$1")" err "Create man/$cmd.1 and man/$cmd.html via $(make_doc "$1")"
fi fi
if [ "man/$cmd.md" -nt "man/$cmd.1" ] || [ "man/$cmd.md" -nt "man/$cmd.html" ]
then
err "man/$cmd.md, man/$cmd.1, and man/$cmd.html all exist, but man/$cmd.md is newer. You should rm man/$cmd.{1,html} && $(make_doc "$1")"
fi
check_git_extras_cmd_list "$@" check_git_extras_cmd_list "$@"
check_man_page_index "$@" check_man_page_index "$@"
} }
@ -54,7 +59,7 @@ check_documentation() {
check_Commands_page() { check_Commands_page() {
# These are special cases. All listed together, so we ignore them # These are special cases. All listed together, so we ignore them
local whitelist=('bug' 'chore' 'feature' 'refactor') local whitelist=('bug' 'chore' 'feature' 'refactor')
for cmd in ${whitelist[*]}; do for cmd in "${whitelist[@]}"; do
test "$1" == "$cmd" && return test "$1" == "$cmd" && return
done done
@ -77,12 +82,9 @@ check() {
check_completion "$1" check_completion "$1"
} }
usage() { test $# == 0 && set -- $(find bin | cut -b 5- | xargs)
echo >&2 "Usage: ./check_integrity.sh <command-name> [<command-name2> ...]"
exit 0
}
test $# == 0 && usage ./bin/git-utimes --newer
for name in "$@"; do for name in "$@"; do
name=${name#git-} name=${name#git-}

View file

@ -1,5 +1,29 @@
# shellcheck shell=bash
# bash completion support for git-extras. # bash completion support for git-extras.
__gitex_heads_unique() {
local branch specified=("${COMP_WORDS[@]:2}")
for branch in $(__git_heads); do
[[ " ${specified[*]} " == *" $branch "* ]] || printf '%s\n' "$branch"
done
}
_git_authors(){
__gitcomp "-l --list --no-email"
}
_git_browse(){
__git_complete_remote_or_refspec
}
_git_browse_ci(){
__git_complete_remote_or_refspec
}
_git_brv(){
__gitcomp "-r --reverse"
}
_git_changelog(){ _git_changelog(){
local s_opts=( '-a' '-l' '-t' '-f' '-s' '-n' '-p' '-x' '-h' '?' ) local s_opts=( '-a' '-l' '-t' '-f' '-s' '-n' '-p' '-x' '-h' '?' )
local l_opts=( local l_opts=(
@ -21,8 +45,29 @@ _git_changelog(){
__gitcomp "$merged_opts_str" __gitcomp "$merged_opts_str"
} }
_git_authors(){ _git_coauthor(){
__gitcomp "-l --list --no-email" local oldIfs=$IFS
IFS=$'\n'
local cur="${COMP_WORDS[COMP_CWORD]}"
local selection=
if ((COMP_CWORD == 2)); then
for line in $(git authors --list); do
selection+="${line% *}"$'\n'
done
elif ((COMP_CWORD == 3)); then
local chosen_name="${COMP_WORDS[COMP_CWORD-1]}"
for line in $(git authors --list); do
if [ "$chosen_name" = "${line% *}" ]; then
local email=${line#*<}
email=${email%>*}
selection+="$email"$'\n'
fi
done
fi
compopt +o default
compopt -o filenames
COMPREPLY=($(compgen -W "$selection" -- "$cur"))
IFS=$oldIfs
} }
_git_contrib(){ _git_contrib(){
@ -37,6 +82,10 @@ _git_contrib(){
COMPREPLY=($(compgen -W "$all" -- "$cur")) COMPREPLY=($(compgen -W "$all" -- "$cur"))
} }
_git_commits_since(){
__gitcomp "-r --ref"
}
_git_count(){ _git_count(){
__gitcomp "--all" __gitcomp "--all"
} }
@ -46,6 +95,10 @@ __git_cp(){
} }
_git_delete_branch(){ _git_delete_branch(){
__gitcomp "$(__gitex_heads_unique)"
}
_git_delete_squashed_branches(){
__gitcomp "$(__git_heads)" __gitcomp "$(__git_heads)"
} }
@ -62,6 +115,7 @@ _git_effort(){
case "$cur" in case "$cur" in
--*) --*)
# shellcheck disable=SC2154
__gitcomp " __gitcomp "
--above --above
$__git_log_common_options $__git_log_common_options
@ -77,7 +131,7 @@ _git_extras(){
} }
__git_extras_workflow(){ __git_extras_workflow(){
__gitcomp "$(__git_heads | grep -- ^$1/ | sed s/^$1\\///g) finish" __gitcomp "$(__git_heads | grep -- "^$1/" | sed s/^"$1"\\///g) finish"
} }
_git_feature(){ _git_feature(){
@ -91,16 +145,20 @@ _git_graft(){
_git_ignore(){ _git_ignore(){
case "$cur" in case "$cur" in
--*) --*)
__gitcomp "--global --local --private" __gitcomp "--global --local --private --edit --remove"
return return
;; ;;
-*) -*)
__gitcomp "--global --local --private -g -l -p" __gitcomp "--global --local --private --edit --remove -g -l -p -e -r"
return return
;; ;;
esac esac
} }
_git_info(){
__gitcomp "--color -c --no-config"
}
_git_missing(){ _git_missing(){
# Suggest all known refs # Suggest all known refs
__gitcomp "$(git for-each-ref --format='%(refname:short)')" __gitcomp "$(git for-each-ref --format='%(refname:short)')"
@ -123,30 +181,40 @@ _git_reauthor(){
__gitcomp "${comp}" __gitcomp "${comp}"
} }
_git_scp(){ __git_extras_rename(){
__git_complete_remote_or_refspec if ((COMP_CWORD == 2 || COMP_CWORD == 3)); then
__gitcomp "$1"
fi
} }
_git_stamp(){ _git_rename_branch(){
__gitcomp '--replace -r' __git_extras_rename "$(__git_heads)"
}
_git_rename_remote(){
__git_extras_rename "$(__git_remotes)"
}
_git_rename_tag(){
__git_extras_rename "$(__git_tags)"
} }
_git_rscp(){ _git_rscp(){
__git_complete_remote_or_refspec __git_complete_remote_or_refspec
} }
_git_scp(){
__git_complete_remote_or_refspec
}
_git_squash(){ _git_squash(){
__gitcomp "$(__git_heads)" __gitcomp "$(__git_heads)"
} }
_git_stamp(){
__gitcomp '--replace -r'
}
_git_undo(){ _git_undo(){
__gitcomp "--hard --soft -h -s" __gitcomp "--hard --soft -h -s"
} }
_git_info(){
__gitcomp "--color -c --no-config"
}
_git_browse(){
__git_complete_remote_or_refspec
}

View file

@ -51,7 +51,7 @@ __gitex_remote_names() {
local expl local expl
declare -a remote_names declare -a remote_names
remote_names=(${(f)"$(_call_program remotes git remote 2>/dev/null)"}) remote_names=(${(f)"$(_call_program remotes git remote 2>/dev/null)"})
__git_command_successful || return __gitex_command_successful || return
_wanted remote-names expl remote-name compadd $* - $remote_names _wanted remote-names expl remote-name compadd $* - $remote_names
} }
@ -59,7 +59,7 @@ __gitex_tag_names() {
local expl local expl
declare -a tag_names declare -a tag_names
tag_names=(${${(f)"$(_call_program tags git for-each-ref --format='"%(refname)"' refs/tags 2>/dev/null)"}#refs/tags/}) tag_names=(${${(f)"$(_call_program tags git for-each-ref --format='"%(refname)"' refs/tags 2>/dev/null)"}#refs/tags/})
__git_command_successful || return __gitex_command_successful || return
_wanted tag-names expl tag-name compadd $* - $tag_names _wanted tag-names expl tag-name compadd $* - $tag_names
} }
@ -68,15 +68,24 @@ __gitex_branch_names() {
local expl local expl
declare -a branch_names declare -a branch_names
branch_names=(${${(f)"$(_call_program branchrefs git for-each-ref --format='"%(refname)"' refs/heads 2>/dev/null)"}#refs/heads/}) branch_names=(${${(f)"$(_call_program branchrefs git for-each-ref --format='"%(refname)"' refs/heads 2>/dev/null)"}#refs/heads/})
__git_command_successful || return __gitex_command_successful || return
_wanted branch-names expl branch-name compadd $* - $branch_names _wanted branch-names expl branch-name compadd $* - $branch_names
} }
__gitex_branch_names_unique() {
local expl
declare -a branch_names already_specified
branch_names=(${${(f)"$(_call_program branchrefs git for-each-ref --format='"%(refname)"' refs/heads 2>/dev/null)"}#refs/heads/})
__gitex_command_successful || return
already_specified=(${words[2,-1]})
_wanted branch-names expl branch-name compadd -F already_specified $* - $branch_names
}
__gitex_specific_branch_names() { __gitex_specific_branch_names() {
local expl local expl
declare -a branch_names declare -a branch_names
branch_names=(${${(f)"$(_call_program branchrefs git for-each-ref --format='"%(refname)"' refs/heads/"$1" 2>/dev/null)"}#refs/heads/$1/}) branch_names=(${${(f)"$(_call_program branchrefs git for-each-ref --format='"%(refname)"' refs/heads/"$1" 2>/dev/null)"}#refs/heads/$1/})
__git_command_successful || return __gitex_command_successful || return
_wanted branch-names expl branch-name compadd - $branch_names _wanted branch-names expl branch-name compadd - $branch_names
} }
@ -88,16 +97,31 @@ __gitex_submodule_names() {
local expl local expl
declare -a submodule_names declare -a submodule_names
submodule_names=(${(f)"$(_call_program branchrefs git submodule status | awk '{print $2}')"}) # ' submodule_names=(${(f)"$(_call_program branchrefs git submodule status | awk '{print $2}')"}) # '
__git_command_successful || return __gitex_command_successful || return
_wanted submodule-names expl submodule-name compadd $* - $submodule_names _wanted submodule-names expl submodule-name compadd $* - $submodule_names
} }
__gitex_workspace_names() {
local expl
declare -a workspace_names
workspace_names=($(git bulk --listall | awk '{print $1}' | cut -d "." -f 2))
__gitex_command_successful || return
_wanted workspace-names expl workspace-names compadd $* - $workspace_names
}
__gitex_author_names() { __gitex_author_names() {
local expl local expl
declare -a author_names declare -a author_names
author_names=(${(f)"$(_call_program branchrefs git log --format='%aN' | sort -u)"}) author_names=(${(f)"$(_call_program branchrefs git log --format='%aN' | sort -u)"})
__git_command_successful || return __gitex_command_successful || return
_wanted author-names expl author-name compadd $* - $author_names
}
__gitex_author_emails() {
local expl
declare -a author_names
author_names=(${(f)"$(_call_program branchrefs git log --format='%aE' | sort -u)"})
__gitex_command_successful || return
_wanted author-names expl author-name compadd $* - $author_names _wanted author-names expl author-name compadd $* - $author_names
} }
@ -109,15 +133,47 @@ _git-authors() {
'--no-email[without email]' \ '--no-email[without email]' \
} }
_git-brv() {
_arguments \
'(-r --reverse)'{-r,--reverse}'[reverse order]'
}
_git-bulk() {
_arguments \
'-a[Run a git command on all workspaces and their repositories.]' \
'-g[Ask the user for confirmation on every execution (guarded mode).]' \
'-w[Run the git command on the specified workspace.]:workspace-name:__gitex_workspace_names' \
'-q[Suppress bulk output about current execution (quiet mode).]' \
'--no-follow-symlinks[Do not traverse symbolic links when searching for git repositories.]' \
'--no-follow-hidden[Do not traverse hidden directories when searching for git repositories.]' \
'--addworkspace[Register a workspace for bulk operations.]' \
'--removeworkspace[Remove the specified workspace.]:workspace-name:__gitex_workspace_names' \
'--addcurrent[Adds the current directory as workspace to git bulk operations]' \
'--purge[Removes all defined repository locations.]' \
'--listall[List all registered repositories.]' \
'--help[Show the help.]'
}
_git-changelog() { _git-changelog() {
_arguments \ _arguments \
'(-l --list)'{-l,--list}'[list commits]' \ '(-l --list)'{-l,--list}'[list commits]' \
} }
_git-clear() {
_arguments \
'(-f --force)'{-f,--force}'[force clear]' \
'(-h --help)'{-h,--help}'[help message]' \
}
_git-coauthor() { _git-coauthor() {
_arguments \ _arguments \
':co-author[co-author to add]' \ ':co-author[co-author to add]:__gitex_author_names' \
':co-author-email[email address of co-author to add]' ':co-author-email[email address of co-author to add]:__gitex_author_emails'
}
_git-commits-since() {
_arguments \
'(-r --ref)'{-r,--ref}'[show commits since ref]'
} }
_git-contrib() { _git-contrib() {
@ -154,6 +210,15 @@ _git-create-branch() {
} }
_git-delete-branch() { _git-delete-branch() {
__gitex_branch_names_unique
}
_git-delete-gone-branches() {
_arguments \
'(-n --dry-run)'{-n,--dry-run}'[show branches that would be deleted without deleting]'
}
_git-delete-squashed-branches() {
_arguments \ _arguments \
':branch-name:__gitex_branch_names' ':branch-name:__gitex_branch_names'
} }
@ -252,9 +317,12 @@ _git-guilt() {
_git-ignore() { _git-ignore() {
_arguments -C \ _arguments -C \
'(--local -l)'{--local,-l}'[show local gitignore]' \ '(--local -l)'{--local,-l}'[set context to local gitignore]' \
'(--global -g)'{--global,-g}'[show global gitignore]' \ '(--global -g)'{--global,-g}'[set context to global gitignore]' \
'(--private -p)'{--private,-p}'[show repo gitignore]' '(--private -p)'{--private,-p}'[set context to private gitignore]' \
'(--edit -e)'{--edit,-e}'[set action to edit]' \
'(--remove -r)'{--remove,-r}'[set action to remove]' \
'*:filename:_files'
} }
@ -286,10 +354,19 @@ _git-release() {
'-s[Create a signed and annotated tag.]' \ '-s[Create a signed and annotated tag.]' \
'-u[Create a tag, annotated and signed with the given key.]' \ '-u[Create a tag, annotated and signed with the given key.]' \
'--semver[If the latest tag in your repo matches the semver format requirement, you could increase part of it as the new release tag.]' \ '--semver[If the latest tag in your repo matches the semver format requirement, you could increase part of it as the new release tag.]' \
'--prefix[Add a prefix string to semver to allow more complex tags.]' \
'--no-empty-commit[Avoid creating empty commit if nothing could be committed.]' \ '--no-empty-commit[Avoid creating empty commit if nothing could be committed.]' \
'--[The arguments listed after "--" separator will be passed to pre/post-release hook.]' '--[The arguments listed after "--" separator will be passed to pre/post-release hook.]'
} }
_git-rename-file() {
_arguments -C \
'-h[show usage information]' \
'--help[show usage information]' \
'1:source:__git_files' \
'2:destination:__git_files'
}
_git-squash() { _git-squash() {
_arguments '--squash-msg[commit with the squashed commit messages]' _arguments '--squash-msg[commit with the squashed commit messages]'
_arguments \ _arguments \
@ -315,8 +392,10 @@ _git-standup() {
} }
_git-summary() { _git-summary() {
_arguments '--full-path[show repository full path]'
_arguments '--line[summarize with lines rather than commits]' _arguments '--line[summarize with lines rather than commits]'
_arguments '--dedup-by-email[remove duplicate users by the email address]' _arguments '--dedup-by-email[remove duplicate users by the email address]'
_arguments '--no-merges[exclude merge commits]'
__gitex_commits __gitex_commits
} }
@ -330,9 +409,11 @@ zstyle -g existing_user_commands ':completion:*:*:git:*' user-commands
zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
alias:'define, search and show aliases' \ alias:'define, search and show aliases' \
abort:'abort current revert, merge, rebase, or cherry-pick process' \
archive-file:'export the current head of the git repository to an archive' \ archive-file:'export the current head of the git repository to an archive' \
authors:'generate authors report' \ authors:'generate authors report' \
browse:'open repo website in browser' \ browse:'open repo website in browser' \
browse-ci:'open repo CI page in browser' \
bug:'create bug branch' \ bug:'create bug branch' \
bulk:'run bulk commands' \ bulk:'run bulk commands' \
brv:'list branches sorted by their last commit date'\ brv:'list branches sorted by their last commit date'\
@ -342,11 +423,14 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
clear:'rigorously clean up a repository' \ clear:'rigorously clean up a repository' \
coauthor:'add a co-author to the last commit' \ coauthor:'add a co-author to the last commit' \
commits-since:'show commit logs since some date' \ commits-since:'show commit logs since some date' \
continue:'continue current revert, merge, rebase, or cherry-pick process' \
contrib:'show user contributions' \ contrib:'show user contributions' \
count:'show commit count' \ count:'show commit count' \
create-branch:'create branches' \ create-branch:'create branches' \
delete-branch:'delete branches' \ delete-branch:'delete branches' \
delete-gone-branches:'delete branches whose remote is gone' \
delete-merged-branches:'delete merged branches' \ delete-merged-branches:'delete merged branches' \
delete-squashed-branches:'delete squashed branches' \
delete-submodule:'delete submodules' \ delete-submodule:'delete submodules' \
delete-tag:'delete tags' \ delete-tag:'delete tags' \
delta:'lists changed files' \ delta:'lists changed files' \
@ -354,17 +438,19 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
extras:'awesome git utilities' \ extras:'awesome git utilities' \
feature:'create/merge feature branch' \ feature:'create/merge feature branch' \
force-clone:'overwrite local repositories with clone' \ force-clone:'overwrite local repositories with clone' \
fork:'fork a repo on github' \ fork:'fork a repo on GitHub' \
fresh-branch:'create fresh branches' \ fresh-branch:'create fresh branches' \
gh-pages:'create the github pages branch' \ get:'clone a repository in a directory' \
gh-pages:'create the GitHub pages branch' \
graft:'merge and destroy a given branch' \ graft:'merge and destroy a given branch' \
guilt:'calculate change between two revisions' \ guilt:'calculate change between two revisions' \
ignore-io:'get sample gitignore file' \ ignore-io:'get sample gitignore file' \
ignore:'add .gitignore patterns' \ ignore:'Modify or display .gitignore files' \
info:'returns information on current repository' \ info:'returns information on current repository' \
local-commits:'list local commits' \ local-commits:'list local commits' \
lock:'lock a file excluded from version control' \ lock:'lock a file excluded from version control' \
locked:'ls files that have been locked' \ locked:'ls files that have been locked' \
magic:'commits everything with a generated message' \
merge-into:'merge one branch into another' \ merge-into:'merge one branch into another' \
merge-repo:'merge two repo histories' \ merge-repo:'merge two repo histories' \
missing:'show commits missing from another branch' \ missing:'show commits missing from another branch' \
@ -379,6 +465,7 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
refactor:'create refactor branch' \ refactor:'create refactor branch' \
release:'commit, tag and push changes to the repository' \ release:'commit, tag and push changes to the repository' \
rename-branch:'rename a branch' \ rename-branch:'rename a branch' \
rename-file:'rename a file or directory and ensure Git recognizes the change, regardless of filesystem case-sensitivity' \
rename-tag:'rename a tag' \ rename-tag:'rename a tag' \
rename-remote:'rename a remote' \ rename-remote:'rename a remote' \
repl:'git read-eval-print-loop' \ repl:'git read-eval-print-loop' \
@ -398,4 +485,6 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
touch:'touch and add file to the index' \ touch:'touch and add file to the index' \
undo:'remove latest commits' \ undo:'remove latest commits' \
unlock:'unlock a file excluded from version control' \ unlock:'unlock a file excluded from version control' \
utimes:'change files modification time to their last commit date' utimes:'change files modification time to their last commit date' \
unwip:'undo a WIP commit' \
wip:'create a WIP commit'

View file

@ -4,6 +4,7 @@ set __fish_git_extras_commands \
"archive-file:Export the current HEAD of the git repository to an archive" \ "archive-file:Export the current HEAD of the git repository to an archive" \
"authors:Generate authors report" \ "authors:Generate authors report" \
"browse:View the web page for the current repository" \ "browse:View the web page for the current repository" \
"browse-ci:View the CI page for the current repository" \
"brv:List branches sorted by their last commit date" \ "brv:List branches sorted by their last commit date" \
"bulk:Run git commands on multiple repositories" \ "bulk:Run git commands on multiple repositories" \
"changelog:Generate a changelog report" \ "changelog:Generate a changelog report" \
@ -17,6 +18,7 @@ set __fish_git_extras_commands \
"create-branch:Create branches" \ "create-branch:Create branches" \
"delete-branch:Delete branches" \ "delete-branch:Delete branches" \
"delete-merged-branches:Delete merged branches" \ "delete-merged-branches:Delete merged branches" \
"delete-squashed-branches:Delete squashed branches" \
"delete-submodule:Delete submodules" \ "delete-submodule:Delete submodules" \
"delete-tag:Delete tags" \ "delete-tag:Delete tags" \
"delta:Lists changed files" \ "delta:Lists changed files" \
@ -25,11 +27,12 @@ set __fish_git_extras_commands \
"force-clone:overwrite local repositories with clone" \ "force-clone:overwrite local repositories with clone" \
"fork:Fork a repo on github" \ "fork:Fork a repo on github" \
"fresh-branch:Create fresh branches" \ "fresh-branch:Create fresh branches" \
"get:Clone a repository in a directory" \
"gh-pages:Create the GitHub Pages branch" \ "gh-pages:Create the GitHub Pages branch" \
"graft:Merge and destroy a given branch" \ "graft:Merge and destroy a given branch" \
"guilt:calculate change between two revisions" \ "guilt:calculate change between two revisions" \
"ignore-io:Get sample gitignore file" \ "ignore-io:Get sample gitignore file" \
"ignore:Add .gitignore patterns" \ "ignore:Modify or display .gitignore files" \
"info:Returns information on current repository" \ "info:Returns information on current repository" \
"local-commits:List local commits" \ "local-commits:List local commits" \
"lock:Lock a file excluded from version control" \ "lock:Lock a file excluded from version control" \
@ -47,6 +50,7 @@ set __fish_git_extras_commands \
"rebase-patch:Rebases a patch" \ "rebase-patch:Rebases a patch" \
"release:Commit, tag and push changes to the repository" \ "release:Commit, tag and push changes to the repository" \
"rename-branch:rename local branch and push to remote" \ "rename-branch:rename local branch and push to remote" \
"rename-file:rename a file or directory and ensure Git recognizes the change, regardless of filesystem case-sensitivity" \
"rename-remote:Rename a remote" \ "rename-remote:Rename a remote" \
"rename-tag:Rename a tag" \ "rename-tag:Rename a tag" \
"repl:git read-eval-print-loop" \ "repl:git read-eval-print-loop" \
@ -68,20 +72,22 @@ set __fish_git_extras_commands \
"unlock:Unlock a file excluded from version control" "unlock:Unlock a file excluded from version control"
# completion for git-extras itself # completion for git-extras itself
complete -c git -f -n '__fish_git_needs_command' -a 'extras' -d 'GIT utilities: repo summary, repl, changelog population, and more' complete -c git -f -n __fish_git_needs_command -a extras -d 'GIT utilities: repo summary, repl, changelog population, and more'
complete -c git -f -n '__fish_git_using_command extras' -s h -l help -d 'Show the help message, can be used for any git-extras commands' complete -c git -f -n '__fish_git_using_command extras' -s h -l help -d 'Show the help message, can be used for any git-extras commands'
complete -c git -f -n '__fish_git_using_command extras' -s v -l version -d 'Show git-extras version number' complete -c git -f -n '__fish_git_using_command extras' -s v -l version -d 'Show git-extras version number'
complete -c git -f -n '__fish_git_using_command extras; and not contains -- update (commandline -opc)' -a "update" -d 'Self update' complete -c git -f -n '__fish_git_using_command extras; and not contains -- update (commandline -opc)' -a update -d 'Self update'
# completion for git-extras provided commands # completion for git-extras provided commands
set __fish_git_extras_commands (printf -- '%s\n' $__fish_git_extras_commands | sed 's/:/\textras:/' | string collect | string escape) set __fish_git_extras_commands (printf -- '%s\n' $__fish_git_extras_commands | sed 's/:/\textras:/' | string collect | string escape)
complete -c git -n '__fish_git_needs_command' -a "$__fish_git_extras_commands" complete -c git -n __fish_git_needs_command -a "$__fish_git_extras_commands"
# authors # authors
complete -c git -f -n '__fish_git_using_command authors' -s l -l list -d 'show authors' complete -c git -f -n '__fish_git_using_command authors' -s l -l list -d 'show authors'
complete -c git -f -n '__fish_git_using_command authors' -l no-email -d 'without email' complete -c git -f -n '__fish_git_using_command authors' -l no-email -d 'without email'
# brv
complete -c git -f -n '__fish_git_using_command brv' -s r -l reverse -d 'reverse the sort order'
# bulk # bulk
complete -c git -n '__fish_git_using_command bulk' -s a -d 'Run a git command on all workspaces and their repositories' complete -c git -n '__fish_git_using_command bulk' -s a -d 'Run a git command on all workspaces and their repositories'
complete -c git -n '__fish_git_using_command bulk' -s g -d 'Ask the user for confirmation on every execution' complete -c git -n '__fish_git_using_command bulk' -s g -d 'Ask the user for confirmation on every execution'
complete -c git -x -n '__fish_git_using_command bulk' -s w -d 'Run on specified workspace' complete -c git -x -n '__fish_git_using_command bulk' -s w -d 'Run on specified workspace'
complete -c git -x -n '__fish_git_using_command bulk' -l addworkspace -d 'Register a workspace for builk operations' complete -c git -x -n '__fish_git_using_command bulk' -l addworkspace -d 'Register a workspace for builk operations'
complete -c git -x -n '__fish_git_using_command bulk; and contains addworkspace (commandline -opc)' -l addworkspace -d 'the URL or file with URLs to be added' complete -c git -x -n '__fish_git_using_command bulk; and contains addworkspace (commandline -opc)' -l addworkspace -d 'the URL or file with URLs to be added'
@ -100,12 +106,41 @@ complete -c git -f -n '__fish_git_using_command changelog' -s n -l no-merges -d
complete -c git -f -n '__fish_git_using_command changelog' -s m -l merges-only -d 'Uses only merge commits (commits with more than 1 parent) for generated changelog' complete -c git -f -n '__fish_git_using_command changelog' -s m -l merges-only -d 'Uses only merge commits (commits with more than 1 parent) for generated changelog'
complete -c git -f -n '__fish_git_using_command changelog' -s p -l prune-old -d 'Replace existing changelog entirely with newly generated content' complete -c git -f -n '__fish_git_using_command changelog' -s p -l prune-old -d 'Replace existing changelog entirely with newly generated content'
complete -c git -f -n '__fish_git_using_command changelog' -s x -l stdout -d 'Write output to stdout instead of to a new changelog file' complete -c git -f -n '__fish_git_using_command changelog' -s x -l stdout -d 'Write output to stdout instead of to a new changelog file'
# coauthor
function __fish_git_arg_number -a number
set -l cmd (commandline -opc)
test (count $cmd) -eq $number
end
function __fish_git_extra_coauthor_name
for line in (git authors --list)
printf '%s\n' $line | string replace --regex ' <.*' ''
end
end
function __fish_git_extra_coauthor_email
set -l cmd (commandline -opc)
# name provided in the previous positional argument
set -l name $cmd[3]
for line in (git authors --list)
set -l loop_name (printf '%s\n' $line | string replace --regex ' <.*' '')
if test "$name" = "$loop_name"
printf '%s\n' $line | string replace --regex '.*?<' '' | string replace --regex '>.*?' ''
end
end
end
complete -c git -f -n '__fish_git_using_command coauthor; and __fish_git_arg_number 2' -a '(__fish_git_extra_coauthor_name)'
complete -c git -f -n '__fish_git_using_command coauthor; and __fish_git_arg_number 3' -a '(__fish_git_extra_coauthor_email)'
# commits-since
complete -c git -f -n '__fish_git_using_command commits-since' -s r -l ref -d 'show commits since ref'
# count # count
complete -c git -f -n '__fish_git_using_command count' -l all -d 'detailed commit count' complete -c git -f -n '__fish_git_using_command count' -l all -d 'detailed commit count'
# create-branch # create-branch
complete -c git -x -n '__fish_git_using_command create-branch' -s r -l remote -a '(__fish_git_unique_remote_branches)' -d 'setup remote tracking branch' complete -c git -x -n '__fish_git_using_command create-branch' -s r -l remote -a '(__fish_git_unique_remote_branches)' -d 'setup remote tracking branch'
# delete-branch # delete-branch
complete -c git -x -n '__fish_git_using_command delete-branch' -a '(__fish_git_branches)' -d 'branch to delete' complete -c git -x -n '__fish_git_using_command delete-branch' -a '(__fish_git_branches)' -d 'branch to delete'
# delete-squashed-branches
complete -c git -x -n '__fish_git_using_command delete-squashed-branches' -a '(__fish_git_branches)' -d 'branch to target for squashed merges'
# delete-submodule # delete-submodule
complete -c git -x -n "__fish_git_using_command delete-submodule" -a "(__fish_git submodule status 2>/dev/null | string trim | cut -d ' ' -f 2)" -d 'submodule to delete' complete -c git -x -n "__fish_git_using_command delete-submodule" -a "(__fish_git submodule status 2>/dev/null | string trim | cut -d ' ' -f 2)" -d 'submodule to delete'
# delete-tag # delete-tag
@ -114,22 +149,24 @@ complete -c git -x -n "__fish_git_using_command delete-tag" -a '(__fish_git for-
complete -c git -f -n '__fish_git_using_command effort' -l above -d 'ignore file with less than x commits' complete -c git -f -n '__fish_git_using_command effort' -l above -d 'ignore file with less than x commits'
# feature # feature
complete -c git -x -n '__fish_git_using_command feature' -s a -l alias -d 'use branch_prefix instead of feature' complete -c git -x -n '__fish_git_using_command feature' -s a -l alias -d 'use branch_prefix instead of feature'
complete -c git -f -n '__fish_git_using_command feature; and not contains -- finish (commandline -opc)' -a "finish" -d 'merge and delete the feature branch' complete -c git -f -n '__fish_git_using_command feature; and not contains -- finish (commandline -opc)' -a finish -d 'merge and delete the feature branch'
complete -c git -f -n '__fish_git_using_command feature; and contains -- finish (commandline -opc)' -l squash -d 'Run a squash merge' complete -c git -f -n '__fish_git_using_command feature; and contains -- finish (commandline -opc)' -l squash -d 'Run a squash merge'
complete -c git -x -n '__fish_git_using_command feature; and contains -- finish (commandline -opc)' -a '(__fish_git for-each-ref --format="%(refname)" 2>/dev/null | grep "refs/heads/feature")' -d 'name of feature branch' complete -c git -x -n '__fish_git_using_command feature; and contains -- finish (commandline -opc)' -a '(__fish_git for-each-ref --format="%(refname)" 2>/dev/null | grep "refs/heads/feature")' -d 'name of feature branch'
complete -c git -x -n '__fish_git_using_command feature; and not contains -- finish (commandline -opc)' -s r -l remote -a '(__fish_git_unique_remote_branches)' -d 'Setup a remote tracking branch' complete -c git -x -n '__fish_git_using_command feature; and not contains -- finish (commandline -opc)' -s r -l remote -a '(__fish_git_unique_remote_branches)' -d 'Setup a remote tracking branch'
# graft # graft
complete -c git -x -n '__fish_git_using_command graft' -s r -l remote -a '(__fish_git_branches)' -d 'src-branch-name' complete -c git -x -n '__fish_git_using_command graft' -s r -l remote -a '(__fish_git_branches)' -d src-branch-name
complete -c git -x -n '__fish_git_using_command graft' -s r -l remote -a '(__fish_git_branches)' -d 'dest-branch-name' complete -c git -x -n '__fish_git_using_command graft' -s r -l remote -a '(__fish_git_branches)' -d dest-branch-name
# guilt # guilt
complete -c git -f -n '__fish_git_using_command guilt' -s w -l ignore-whitespace -d 'ignore whitespace only changes' complete -c git -f -n '__fish_git_using_command guilt' -s w -l ignore-whitespace -d 'ignore whitespace only changes'
complete -c git -f -n '__fish_git_using_command guilt' -s e -l email -d 'display author emails instead of names' complete -c git -f -n '__fish_git_using_command guilt' -s e -l email -d 'display author emails instead of names'
complete -c git -f -n '__fish_git_using_command guilt' -s d -l debug -d 'output debug information' complete -c git -f -n '__fish_git_using_command guilt' -s d -l debug -d 'output debug information'
complete -c git -f -n '__fish_git_using_command guilt' -s h -d 'output usage information' complete -c git -f -n '__fish_git_using_command guilt' -s h -d 'output usage information'
# ignore # ignore
complete -c git -f -n '__fish_git_using_command ignore' -s l -l local -d 'show local gitignore' complete -c git -f -n '__fish_git_using_command ignore' -s l -l local -d 'set context to local gitignore'
complete -c git -f -n '__fish_git_using_command ignore' -s g -l global -d 'show global gitignore' complete -c git -f -n '__fish_git_using_command ignore' -s g -l global -d 'set context to global gitignore'
complete -c git -f -n '__fish_git_using_command ignore' -s p -l private -d 'show repo gitignore' complete -c git -f -n '__fish_git_using_command ignore' -s p -l private -d 'set context to private gitignore'
complete -c git -f -n '__fish_git_using_command ignore' -s e -l edit -d 'set action to edit'
complete -c git -f -n '__fish_git_using_command ignore' -s r -l remove -d 'set action to remove'
# ignore-io # ignore-io
function __fish_git_extra_get_ignore_io_types function __fish_git_extra_get_ignore_io_types
# we will first remove every tab spaces, and then append `\t` at the end to remove the default description # we will first remove every tab spaces, and then append `\t` at the end to remove the default description
@ -144,32 +181,35 @@ complete -c git -x -n '__fish_git_using_command ignore-io' -s s -l search -d 'se
complete -c git -x -n '__fish_git_using_command ignore-io' -s t -l show-update-time -d 'Show the last modified time of ~/.gi_list' complete -c git -x -n '__fish_git_using_command ignore-io' -s t -l show-update-time -d 'Show the last modified time of ~/.gi_list'
complete -c git -x -n '__fish_git_using_command ignore-io' -s u -l update -d 'Update ~/.gi_list' complete -c git -x -n '__fish_git_using_command ignore-io' -s u -l update -d 'Update ~/.gi_list'
# merge-into # merge-into
complete -c git -n '__fish_git_using_command merge-into' -l ff-only -d 'merge only fast-forward' complete -c git -n '__fish_git_using_command merge-into' -l ff-only -d 'merge only fast-forward'
complete -c git -x -n '__fish_git_using_command merge-into' -a '(__fish_git_branches)' complete -c git -x -n '__fish_git_using_command merge-into' -a '(__fish_git_branches)'
# missing # missing
complete -c git -x -n '__fish_git_using_command missing' -a '(__fish_git_branches)' complete -c git -x -n '__fish_git_using_command missing' -a '(__fish_git_branches)'
# rename-file
complete -c git -f -n '__fish_git_using_command rename-file' -s h -l help -d 'Show usage information'
# squash # squash
complete -c git -x -n '__fish_git_using_command squash' -a '(__fish_git_branches)' complete -c git -x -n '__fish_git_using_command squash' -a '(__fish_git_branches)'
complete -c git -x -n '__fish_git_using_command squash' -l squash-msg -d 'commit with the squashed commit messages' complete -c git -x -n '__fish_git_using_command squash' -l squash-msg -d 'commit with the squashed commit messages'
# stamp # stamp
complete -c git -x -n '__fish_git_using_command stamp' -s r -l replace -d 'replace stamps with same id' complete -c git -x -n '__fish_git_using_command stamp' -s r -l replace -d 'replace stamps with same id'
# standup # standup
complete -c git -x -n '__fish_git_using_command standup' -s a -d 'Specify the author of commits. Use "all" to specify all authors' complete -c git -x -n '__fish_git_using_command standup' -s a -d 'Specify the author of commits. Use all to specify all authors'
complete -c git -x -n '__fish_git_using_command standup' -s m -d 'The depth of recursive directory search' complete -c git -x -n '__fish_git_using_command standup' -s m -d 'The depth of recursive directory search'
complete -c git -x -n '__fish_git_using_command standup' -s d -d 'Show history since N days ago' complete -c git -x -n '__fish_git_using_command standup' -s d -d 'Show history since N days ago'
complete -c git -x -n '__fish_git_using_command standup' -s D -d 'Specify the date format displayed in commit history' complete -c git -x -n '__fish_git_using_command standup' -s D -d 'Specify the date format displayed in commit history'
complete -c git -n '__fish_git_using_command standup' -s f -d 'Fetch commits before showing history' complete -c git -n '__fish_git_using_command standup' -s f -d 'Fetch commits before showing history'
complete -c git -n '__fish_git_using_command standup' -s g -d 'Display GPG signed info' complete -c git -n '__fish_git_using_command standup' -s g -d 'Display GPG signed info'
complete -c git -n '__fish_git_using_command standup' -s h -l help -d 'Display help message' complete -c git -n '__fish_git_using_command standup' -s h -l help -d 'Display help message'
complete -c git -n '__fish_git_using_command standup' -s L -d 'Enable the inclusion of symbolic links' complete -c git -n '__fish_git_using_command standup' -s L -d 'Enable the inclusion of symbolic links'
complete -c git -n '__fish_git_using_command standup' -s B -d 'Display the commits in branch group' complete -c git -n '__fish_git_using_command standup' -s B -d 'Display the commits in branch group'
complete -c git -x -n '__fish_git_using_command standup' -s n -d 'Limit the number of commits displayed per group' complete -c git -x -n '__fish_git_using_command standup' -s n -d 'Limit the number of commits displayed per group'
# summary # summary
complete -c git -n '__fish_git_using_command summary' -l line -d 'summarize with lines rather than commits' complete -c git -n '__fish_git_using_command summary' -l line -d 'summarize with lines rather than commits'
complete -c git -n '__fish_git_using_command summary' -l dedup-by-email -d 'remove duplicate users by the email address' complete -c git -n '__fish_git_using_command summary' -l dedup-by-email -d 'remove duplicate users by the email address'
complete -c git -n '__fish_git_using_command summary' -l no-merges -d 'exclude merge commits'
# release # release
complete -c git -x -n '__fish_git_using_command release' -s c -d 'Generates/populates the changelog with all commit message since the last tag' complete -c git -x -n '__fish_git_using_command release' -s c -d 'Generates/populates the changelog with all commit message since the last tag'
complete -c git -x -n '__fish_git_using_command release' -s r -d 'The "remote" repository that is destination of a push operation' complete -c git -x -n '__fish_git_using_command release' -s r -d 'The remote repository that is destination of a push operation'
complete -c git -x -n '__fish_git_using_command release' -s m -d 'use the custom commit information instead of the default message' complete -c git -x -n '__fish_git_using_command release' -s m -d 'use the custom commit information instead of the default message'
complete -c git -x -n '__fish_git_using_command release' -s s -d 'Create a signed and annotated tag' complete -c git -x -n '__fish_git_using_command release' -s s -d 'Create a signed and annotated tag'
complete -c git -x -n '__fish_git_using_command release' -s u -d 'Create a tag, annotated and signed with the given key' complete -c git -x -n '__fish_git_using_command release' -s u -d 'Create a tag, annotated and signed with the given key'
@ -178,5 +218,3 @@ complete -c git -x -n '__fish_git_using_command release' -l no-empty-commit -d '
# undo # undo
complete -c git -x -n '__fish_git_using_command undo' -s s -l soft -d 'only rolls back the commit but changes remain un-staged' complete -c git -x -n '__fish_git_using_command undo' -s s -l soft -d 'only rolls back the commit but changes remain un-staged'
complete -c git -x -n '__fish_git_using_command undo' -s h -l hard -d 'wipes your commit(s)' complete -c git -x -n '__fish_git_using_command undo' -s h -l hard -d 'wipes your commit(s)'

View file

@ -6,11 +6,14 @@ git_extra_mktemp() {
} }
git_extra_default_branch() { git_extra_default_branch() {
local default_branch local extras_default_branch init_default_branch
default_branch=$(git config --get git-extras.default-branch) extras_default_branch=$(git config --get git-extras.default-branch)
if [ -z "$default_branch" ]; then init_default_branch=$(git config --get init.defaultBranch)
echo "master" if [ -n "$extras_default_branch" ]; then
echo "$extras_default_branch"
elif [ -n "$init_default_branch" ]; then
echo "$init_default_branch"
else else
echo "$default_branch" echo "main"
fi fi
} }

View file

@ -3,7 +3,9 @@
# #
is_git_repo() { is_git_repo() {
git rev-parse --show-toplevel > /dev/null 2>&1 # --git-dir succeeds for both normal and bare repositories, whereas
# --show-toplevel fails inside a bare repository (it has no working tree).
git rev-parse --git-dir > /dev/null 2>&1
result=$? result=$?
if test $result != 0; then if test $result != 0; then
>&2 echo 'Not a git repo!' >&2 echo 'Not a git repo!'

View file

@ -1,2 +1,2 @@
# reset environment variables that could interfere with normal usage # reset environment variables that could interfere with normal usage
export GREP_OPTIONS= unset -v GREP_OPTIONS

View file

@ -39,13 +39,34 @@ goto :defaultpath
rem remove the last slash rem remove the last slash
SET bindir=%bindir:~0,-1% SET bindir=%bindir:~0,-1%
for %%G in ("%bindir%") do set installdir=%%~dpG for %%G in ("%bindir%") do set installdir=%%~dpG
set PREFIX=%installdir%mingw64 rem strip a trailing slash so we can inspect the leaf directory name
if "%installdir:~-1%"=="\" set installdir=%installdir:~0,-1%
rem git.exe found under <GitRoot>\mingw64\bin or <GitRoot>\clangarm64\bin
rem means installdir already is the architecture directory itself
for %%G in ("%installdir%") do set archdir=%%~nxG
if /I "%archdir%"=="mingw64" (
set PREFIX=%installdir%
) else if /I "%archdir%"=="clangarm64" (
set PREFIX=%installdir%
) else if exist "%installdir%\mingw64" (
set PREFIX=%installdir%\mingw64
) else if exist "%installdir%\clangarm64" (
set PREFIX=%installdir%\clangarm64
) else (
set PREFIX=%installdir%\mingw64
)
goto :foundprefix goto :foundprefix
:defaultpath :defaultpath
:: default for Git for Windows 2.x :: default for Git for Windows 2.x
if exist "%ProgramFiles%\Git" ( if exist "%ProgramFiles%\Git" (
set PREFIX=%ProgramFiles%\Git\mingw64 if exist "%ProgramFiles%\Git\mingw64" (
set PREFIX=%ProgramFiles%\Git\mingw64
) else if exist "%ProgramFiles%\Git\clangarm64" (
set PREFIX=%ProgramFiles%\Git\clangarm64
) else (
set PREFIX=%ProgramFiles%\Git\mingw64
)
) )
:foundprefix :foundprefix
@ -55,9 +76,11 @@ IF NOT "%~1"=="" (
REM just supplying the git dir is enough... REM just supplying the git dir is enough...
if exist "%~1\mingw64" ( if exist "%~1\mingw64" (
set PREFIX=%~1\mingw64 set PREFIX=%~1\mingw64
) else if exist "%~1\clangarm64" (
set PREFIX=%~1\clangarm64
) else ( ) else (
echo Using git install path "%~1" as PREFIX, please make sure it's really a echo Using git install path "%~1" as PREFIX, please make sure it's really a
echo path to the mingw64 directory... echo path to the mingw64 or clangarm64 directory...
echo. echo.
SET PREFIX=%~1 SET PREFIX=%~1
) )
@ -72,12 +95,14 @@ set GIT_INSTALL_DIR=!GIT_INSTALL_DIR:"=!
IF %GIT_INSTALL_DIR:~-1%==\ SET GIT_INSTALL_DIR=%GIT_INSTALL_DIR:~0,-1% IF %GIT_INSTALL_DIR:~-1%==\ SET GIT_INSTALL_DIR=%GIT_INSTALL_DIR:~0,-1%
if not exist "%GIT_INSTALL_DIR%\mingw64" ( if not exist "%GIT_INSTALL_DIR%\mingw64" (
echo No mingw64 folder found in %GIT_INSTALL_DIR%. if not exist "%GIT_INSTALL_DIR%\clangarm64" (
echo. echo No mingw64 or clangarm64 folder found in %GIT_INSTALL_DIR%.
echo Please supply a proper "Git for Windows 2.x" install path: echo.
echo "install.cmd c:\[git-install-path]" echo Please supply a proper "Git for Windows 2.x" install path:
set ERROR=1 echo "install.cmd c:\[git-install-path]"
goto :exit set ERROR=1
goto :exit
)
) )
echo Installing to %PREFIX% echo Installing to %PREFIX%
@ -116,7 +141,7 @@ FOR /R "%GITEXTRAS%\bin" %%i in (*.*) DO (
TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%~ni" TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%~ni"
TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%~ni" TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%~ni"
TYPE "%GITEXTRAS%\helper\is-git-repo" >> "%PREFIX%\bin\%%~ni" TYPE "%GITEXTRAS%\helper\is-git-repo" >> "%PREFIX%\bin\%%~ni"
REM Added /E option for installation fix on Windows 10.0.17134 and higher REM Added /E option for installation fix on Windows 10.0.17134 and higher
MORE /E +2 "%GITEXTRAS%\bin\%%~ni" >> "%PREFIX%\bin\%%~ni" MORE /E +2 "%GITEXTRAS%\bin\%%~ni" >> "%PREFIX%\bin\%%~ni"
) )
@ -127,7 +152,7 @@ FOR %%i in (%COMMANDS_WITHOUT_REPO%) DO (
ECHO #^^!/usr/bin/env bash > "%PREFIX%\bin\%%i" ECHO #^^!/usr/bin/env bash > "%PREFIX%\bin\%%i"
TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%i" TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%i"
TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%i" TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%i"
REM Added /E option for installation fix on Windows 10.0.17134 and higher REM Added /E option for installation fix on Windows 10.0.17134 and higher
MORE /E +2 "%GITEXTRAS%\bin\%%i" >> "%PREFIX%\bin\%%i" MORE /E +2 "%GITEXTRAS%\bin\%%i" >> "%PREFIX%\bin\%%i"
) )

19
man/git-abort.1 Normal file
View file

@ -0,0 +1,19 @@
.\" generated with Ronn-NG/v0.8.0
.\" http://github.com/apjanke/ronn-ng/tree/0.8.0
.TH "GIT\-ABORT" "1" "August 2020" "" "Git Extras"
.SH "NAME"
\fBgit\-abort\fR \- Abort current git operation
.SH "SYNOPSIS"
\fBgit\-abort\fR
.SH "DESCRIPTION"
Abort current git revert, rebase, merge or cherry\-pick process\.
.SH "OPTIONS"
There are no options, it just aborts current operation\.
.SH "EXAMPLES"
\fBgit\-abort\fR
.SH "AUTHOR"
Written by Przemek Kitszel <\fI\%mailto:pkitszel@gmail\.com\fR>
.SH "REPORTING BUGS"
<\fI\%https://github\.com/tj/git\-extras/issues\fR>
.SH "SEE ALSO"
<\fI\%https://github\.com/tj/git\-extras\fR>

114
man/git-abort.html Normal file
View file

@ -0,0 +1,114 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf8'>
<meta name='generator' content='Ronn-NG/v0.8.0 (http://github.com/apjanke/ronn-ng/tree/0.8.0)'>
<title>git-abort(1) - Abort current git operation</title>
<style type='text/css' media='all'>
/* style: man */
body#manpage {margin:0}
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
.mp h2 {margin:10px 0 0 0}
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
.mp h3 {margin:0 0 0 4ex}
.mp dt {margin:0;clear:left}
.mp dt.flush {float:left;width:8ex}
.mp dd {margin:0 0 0 9ex}
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
.mp pre {margin-bottom:20px}
.mp pre+h2,.mp pre+h3 {margin-top:22px}
.mp h2+pre,.mp h3+pre {margin-top:5px}
.mp img {display:block;margin:auto}
.mp h1.man-title {display:none}
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
.mp h2 {font-size:16px;line-height:1.25}
.mp h1 {font-size:20px;line-height:2}
.mp {text-align:justify;background:#fff}
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
.mp u {text-decoration:underline}
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
.mp b.man-ref {font-weight:normal;color:#434241}
.mp pre {padding:0 4ex}
.mp pre code {font-weight:normal;color:#434241}
.mp h2+pre,h3+pre {padding-left:0}
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
ol.man-decor {width:100%}
ol.man-decor li.tl {text-align:left}
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
ol.man-decor li.tr {text-align:right;float:right}
</style>
</head>
<!--
The following styles are deprecated and will be removed at some point:
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
.man-navigation should be used instead.
-->
<body id='manpage'>
<div class='mp' id='man'>
<div class='man-navigation' style='display:none'>
<a href="#NAME">NAME</a>
<a href="#SYNOPSIS">SYNOPSIS</a>
<a href="#DESCRIPTION">DESCRIPTION</a>
<a href="#OPTIONS">OPTIONS</a>
<a href="#EXAMPLES">EXAMPLES</a>
<a href="#AUTHOR">AUTHOR</a>
<a href="#REPORTING-BUGS">REPORTING BUGS</a>
<a href="#SEE-ALSO">SEE ALSO</a>
</div>
<ol class='man-decor man-head man head'>
<li class='tl'>git-abort(1)</li>
<li class='tc'>Git Extras</li>
<li class='tr'>git-abort(1)</li>
</ol>
<h2 id="NAME">NAME</h2>
<p class="man-name">
<code>git-abort</code> - <span class="man-whatis">Abort current git operation</span>
</p>
<h2 id="SYNOPSIS">SYNOPSIS</h2>
<p><code>git-abort</code></p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<p> Abort current git revert, rebase, merge or cherry-pick process.</p>
<h2 id="OPTIONS">OPTIONS</h2>
<p> There are no options, it just aborts current operation.</p>
<h2 id="EXAMPLES">EXAMPLES</h2>
<p> <code>git-abort</code></p>
<h2 id="AUTHOR">AUTHOR</h2>
<p>Written by Przemek Kitszel &lt;<a href="mailto:pkitszel@gmail.com" data-bare-link="true">pkitszel@gmail.com</a>&gt;</p>
<h2 id="REPORTING-BUGS">REPORTING BUGS</h2>
<p>&lt;<a href="https://github.com/tj/git-extras/issues" data-bare-link="true">https://github.com/tj/git-extras/issues</a>&gt;</p>
<h2 id="SEE-ALSO">SEE ALSO</h2>
<p>&lt;<a href="https://github.com/tj/git-extras" data-bare-link="true">https://github.com/tj/git-extras</a>&gt;</p>
<ol class='man-decor man-foot man foot'>
<li class='tl'></li>
<li class='tc'>August 2020</li>
<li class='tr'>git-abort(1)</li>
</ol>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more