From f0b9e18e54e6502ae6fe623d937fb0f4b40d4368 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wiktor=20=C5=BBurawik?=
Date: Mon, 26 Aug 2024 04:38:15 +0200
Subject: [PATCH 01/62] Add pathspec support in `git-missing` (#1156)
* 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
---
Commands.md | 8 +++++++-
bin/git-missing | 15 +++++++++++++--
man/git-missing.1 | 31 ++++++++++++++++++++++++++-----
man/git-missing.html | 29 ++++++++++++++++++++++-------
man/git-missing.md | 20 +++++++++++++++++---
5 files changed, 85 insertions(+), 18 deletions(-)
diff --git a/Commands.md b/Commands.md
index af72af0..4ea5520 100644
--- a/Commands.md
+++ b/Commands.md
@@ -1296,7 +1296,7 @@ Creates a zip archive of the current git repository. The name of the archive wil
## 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
$ git missing master
@@ -1304,6 +1304,12 @@ $ git missing 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
Lock a local file `filename`:
diff --git a/bin/git-missing b/bin/git-missing
index 9569468..32fbdab 100755
--- a/bin/git-missing
+++ b/bin/git-missing
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
usage() {
- echo 1>&2 "usage: git missing [] []"
+ echo 1>&2 "usage: git missing [] [] [[--] ...]"
}
if [ "${#}" -lt 1 ]
@@ -12,9 +12,20 @@ fi
declare -a git_log_args=()
declare -a branches=()
+declare -a pathspec=()
+declare parse_path=false
+
for arg in "$@" ; do
+ if [[ $parse_path == true ]]; then
+ pathspec+=("$@")
+ break
+ fi
+
case "$arg" in
+ --)
+ parse_path=true
+ ;;
--*)
git_log_args+=( "$arg" )
;;
@@ -38,4 +49,4 @@ else
exit 1
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[@]}"
diff --git a/man/git-missing.1 b/man/git-missing.1
index 2e82e7a..413880d 100644
--- a/man/git-missing.1
+++ b/man/git-missing.1
@@ -1,16 +1,16 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "GIT\-MISSING" "1" "April 2018" "" "Git Extras"
+.TH "GIT\-MISSING" "1" "August 2024" "" "Git Extras"
.
.SH "NAME"
\fBgit\-missing\fR \- Show commits missing from another branch
.
.SH "SYNOPSIS"
-\fBgit\-missing\fR [] []
+\fBgit\-missing\fR [] [] [[\-\-] \.\.\.]
.
.SH "DESCRIPTION"
-Shows commits that are in either of two branches but not both\. Useful for seeing what would come across in a merge or push\.
+Shows commits that are in either of two branches but not both\. Useful for seeing what would come across in a merge or push\. Optionally, the comparison can be limited to specific paths\.
.
.SH "OPTIONS"
[]
@@ -30,6 +30,12 @@ Second branch to compare\.
.P
Any flags that should be passed to \'git log\', such as \-\-no\-merges\.
.
+.P
+[[\-\-] \.\.\.]
+.
+.P
+Optional path specifications (pathspec) to limit the comparison to specific files or directories\. For more details about the pathspec syntax, see the pathspec entry in gitglossary[7] \fIhttps://git\-scm\.com/docs/gitglossary#Documentation/gitglossary\.txt\-aiddefpathspecapathspec\fR\.
+.
.SH "EXAMPLES"
Show commits on either my current branch or master but not both:
.
@@ -60,11 +66,26 @@ $ git missing foo bar
.
.IP "" 0
.
+.P
+Show commits on either my current branch or master but not both, limited to the src/ directory:
+.
+.IP "" 4
+.
+.nf
+
+$ git missing master \-\- src/
+< ed52989 only on current checked out branch, in src/ directory
+> 7988c4b only on master, in src/ directory
+.
+.fi
+.
+.IP "" 0
+.
.SH "AUTHOR"
Written by Nate Jones <\fInate@endot\.org\fR>
.
.SH "REPORTING BUGS"
-<\fIhttp://github\.com/tj/git\-extras/issues\fR>
+<\fIhttps://github\.com/tj/git\-extras/issues\fR>
.
.SH "SEE ALSO"
-<\fIhttp://github\.com/tj/git\-extras\fR>
+<\fIhttps://github\.com/tj/git\-extras\fR>
diff --git a/man/git-missing.html b/man/git-missing.html
index 427d924..d82a07c 100644
--- a/man/git-missing.html
+++ b/man/git-missing.html
@@ -76,12 +76,13 @@
SYNOPSIS
-git-missing [<first branch>] <second branch> [<git log options>]
+git-missing [<first branch>] <second branch> [<git log options>] [[--] <path>...]
DESCRIPTION
- Shows commits that are in either of two branches but not both. Useful for
- seeing what would come across in a merge or push.
+ Shows commits that are in either of two branches but not both. Useful for
+ seeing what would come across in a merge or push. Optionally, the comparison
+ can be limited to specific paths.
OPTIONS
@@ -97,6 +98,12 @@
Any flags that should be passed to 'git log', such as --no-merges.
+ [[--] <path>...]
+
+ Optional path specifications (pathspec) to limit the comparison to specific
+ files or directories. For more details about the pathspec syntax, see the
+ pathspec entry in gitglossary[7].
+
EXAMPLES
Show commits on either my current branch or master but not both:
@@ -113,22 +120,30 @@
> f38797e only on bar
+ Show commits on either my current branch or master but not both, limited to the
+ src/ directory:
+
+$ git missing master -- src/
+< ed52989 only on current checked out branch, in src/ directory
+> 7988c4b only on master, in src/ directory
+
+
AUTHOR
-Written by Nate Jones <nate@endot.org>
+Written by Nate Jones <nate@endot.org>
REPORTING BUGS
-<http://github.com/tj/git-extras/issues>
+<https://github.com/tj/git-extras/issues>
SEE ALSO
-<http://github.com/tj/git-extras>
+<https://github.com/tj/git-extras>
diff --git a/man/git-missing.md b/man/git-missing.md
index 6f4ff65..8b49430 100644
--- a/man/git-missing.md
+++ b/man/git-missing.md
@@ -3,12 +3,13 @@ git-missing(1) -- Show commits missing from another branch
## SYNOPSIS
-`git-missing` [<first branch>] <second branch> [<git log options>]
+`git-missing` [<first branch>] <second branch> [<git log options>] [[--] <path>...]
## DESCRIPTION
- Shows commits that are in either of two branches but not both. Useful for
- seeing what would come across in a merge or push.
+ Shows commits that are in either of two branches but not both. Useful for
+ seeing what would come across in a merge or push. Optionally, the comparison
+ can be limited to specific paths.
## OPTIONS
@@ -24,6 +25,12 @@ git-missing(1) -- Show commits missing from another branch
Any flags that should be passed to 'git log', such as --no-merges.
+ [[--] <path>...]
+
+ Optional path specifications (pathspec) to limit the comparison to specific
+ files or directories. For more details about the pathspec syntax, see the
+ pathspec entry in [gitglossary[7]](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec).
+
## EXAMPLES
Show commits on either my current branch or master but not both:
@@ -38,6 +45,13 @@ git-missing(1) -- Show commits missing from another branch
< b8f0d14 only on foo
> f38797e only on bar
+ Show commits on either my current branch or master but not both, limited to the
+ src/ directory:
+
+ $ git missing master -- src/
+ < ed52989 only on current checked out branch, in src/ directory
+ > 7988c4b only on master, in src/ directory
+
## AUTHOR
Written by Nate Jones <>
From 2c161aae024560d36931414e35c7ea3a25ae252f Mon Sep 17 00:00:00 2001
From: Revisor
Date: Wed, 4 Sep 2024 07:59:35 +0200
Subject: [PATCH 02/62] Update instructions for the OpenSUSE installation
(#1157)
---
Installation.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Installation.md b/Installation.md
index 2e82a69..174e8e0 100644
--- a/Installation.md
+++ b/Installation.md
@@ -41,10 +41,10 @@ $ sudo dnf install git-extras
### 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
-$ 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:
From f0671e27336258456f0ec2d2dac0f7b5ad646fe0 Mon Sep 17 00:00:00 2001
From: wyattscarpenter
Date: Mon, 16 Sep 2024 12:19:03 -0700
Subject: [PATCH 03/62] Update git-repl.md: typo: "let's" for "lets" (#1158)
---
man/git-repl.1 | 2 +-
man/git-repl.html | 2 +-
man/git-repl.md | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/man/git-repl.1 b/man/git-repl.1
index c7ab7a8..b3040f2 100644
--- a/man/git-repl.1
+++ b/man/git-repl.1
@@ -10,7 +10,7 @@
\fBgit\-repl\fR
.
.SH "DESCRIPTION"
-Git read\-eval\-print\-loop\. Let\'s you run \fBgit\fR commands without typing \'git\'\.
+Git read\-eval\-print\-loop\. Lets you run \fBgit\fR commands without typing \'git\'\.
.
.P
Commands can be prefixed with an exclamation mark (!) to be interpreted as a regular command\.
diff --git a/man/git-repl.html b/man/git-repl.html
index e669a1b..f24f7ce 100644
--- a/man/git-repl.html
+++ b/man/git-repl.html
@@ -80,7 +80,7 @@
DESCRIPTION
- Git read-eval-print-loop. Let's you run git commands without typing 'git'.
+ 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
a regular command.
diff --git a/man/git-repl.md b/man/git-repl.md
index cfbfeb0..132533d 100644
--- a/man/git-repl.md
+++ b/man/git-repl.md
@@ -7,7 +7,7 @@ git-repl(1) -- git read-eval-print-loop
## DESCRIPTION
- Git read-eval-print-loop. Let's you run `git` commands without typing 'git'.
+ 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
a regular command.
From da03b7b133f44a8f1bcf68c54dd6487a0c3957ae Mon Sep 17 00:00:00 2001
From: wyattscarpenter
Date: Wed, 18 Sep 2024 20:17:00 -0700
Subject: [PATCH 04/62] 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
---
man/git-bulk.1 | 67 +++++++++----------------------------------
man/git-bulk.html | 72 +++++++++++++++++++++++------------------------
man/git-bulk.md | 12 ++++----
3 files changed, 56 insertions(+), 95 deletions(-)
diff --git a/man/git-bulk.1 b/man/git-bulk.1
index b1dc523..1266608 100644
--- a/man/git-bulk.1
+++ b/man/git-bulk.1
@@ -1,104 +1,69 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "GIT\-BULK" "1" "August 2020" "" "Git Extras"
-.
+.\" generated with Ronn-NG/v0.9.1
+.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
+.TH "GIT\-BULK" "1" "September 2024" "" "Git Extras"
.SH "NAME"
\fBgit\-bulk\fR \- Run git commands on multiple repositories
-.
.SH "SYNOPSIS"
-git bulk [\-g] ([\-a]|[\-w ])
-.
+\fBgit\-bulk\fR [\-g] ([\-a]|[\-w
.br
-git bulk \-\-addworkspace (\-\-from )
-.
+\fBgit\-bulk\fR \-\-addworkspace
.br
-git bulk \-\-removeworkspace
-.
+\fBgit\-bulk\fR \-\-removeworkspace
.br
-git bulk \-\-addcurrent
-.
+\fBgit\-bulk\fR \-\-addcurrent
.br
-git bulk \-\-purge
-.
+\fBgit\-bulk\fR \-\-purge
.br
-git bulk \-\-listall
-.
+\fBgit\-bulk\fR \-\-listall
.SH "DESCRIPTION"
git bulk adds convenient support for operations that you want to execute on multiple git repositories\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
simply register workspaces that contain multiple git repos in their directory structure
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
run any git command on the repositories of the registered workspaces in one command to \fBgit bulk\fR
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
use the "guarded mode" to check on each execution
-.
.IP "" 0
-.
.SH "OPTIONS"
\-a
-.
.P
Run a git command on all workspaces and their repositories\.
-.
.P
\-g
-.
.P
Ask the user for confirmation on every execution\.
-.
.P
\-w
-.
.P
Run the git command on the specified workspace\. The workspace must be registered\.
-.
.P
-.
.P
Any git Command you wish to execute on the repositories\.
-.
.P
-\-\-addworkspace (\-\-from get registered under this workspace with the name \. must be absolute path\.
-.
.P
-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\.
.P
\-\-removeworkspace
-.
.P
Remove the workspace with the logical name \.
-.
.P
\-\-addcurrent
-.
.P
Adds the current directory as workspace to git bulk operations\. The workspace is referenced with its logical name \.
-.
.P
git bulk \-\-purge
-.
.P
Removes all defined repository locations\.
-.
.P
git bulk \-\-listall
-.
.P
List all registered repositories\.
-.
.SH "EXAMPLES"
-.
.nf
-
Register a workspace so that git bulk knows about it:
$ git bulk \-\-addworkspace personal ~/workspaces/personal
@@ -142,14 +107,10 @@ $ git bulk \-\-removeworkspace personal
Remove all registered workspaces:
$ git bulk \-\-purge
-.
.fi
-.
.SH "AUTHOR"
Written by Niklas Schlimm <\fIns103@hotmail\.de\fR>
-.
.SH "REPORTING BUGS"
-.
.SH "SEE ALSO"
<\fIhttps://github\.com/tj/git\-extras\fR>
diff --git a/man/git-bulk.html b/man/git-bulk.html
index c805fff..de121b1 100644
--- a/man/git-bulk.html
+++ b/man/git-bulk.html
@@ -1,8 +1,8 @@
-
-
+
+
git-bulk(1) - Run git commands on multiple repositories
+
+
+
+
+
+
+
+
+ - git-continue(1)
+ - Git Extras
+ - git-continue(1)
+
+
+
+
+
NAME
+
+ git-continue - Continue current git operation
+
+
SYNOPSIS
+
+
git-continue
+
+
DESCRIPTION
+
+
Continue current git revert, rebase, merge or cherry-pick process.
+
+
OPTIONS
+
+
There are no options, it just continues current operation.
+
+
EXAMPLES
+
+
git-continue
+
+
AUTHOR
+
+
Written by oikarinen
+
+
REPORTING BUGS
+
+
<https://github.com/tj/git-extras/issues>
+
+
SEE ALSO
+
+
<https://github.com/tj/git-extras>
+
+
+
+
+
+
diff --git a/man/git-continue.md b/man/git-continue.md
new file mode 100644
index 0000000..439a95c
--- /dev/null
+++ b/man/git-continue.md
@@ -0,0 +1,30 @@
+git-continue(1) -- Continue current git operation
+================================
+
+## SYNOPSIS
+
+`git-continue`
+
+## DESCRIPTION
+
+ Continue current git revert, rebase, merge or cherry-pick process.
+
+## OPTIONS
+
+ There are no options, it just continues current operation.
+
+## EXAMPLES
+
+ `git-continue`
+
+## AUTHOR
+
+Written by oikarinen
+
+## REPORTING BUGS
+
+<>
+
+## SEE ALSO
+
+<>
diff --git a/man/git-extras.1 b/man/git-extras.1
index c7d4af0..8f09cbf 100644
--- a/man/git-extras.1
+++ b/man/git-extras.1
@@ -49,6 +49,8 @@ Change the default branch to \fB$BRANCH\fR\. If \fBgit\-extras\.default\-branch\
.IP "\[ci]" 4
\fBgit\-commits\-since(1)\fR Show commit logs since some date
.IP "\[ci]" 4
+\fBgit\-continue(1)\fR Continue current git operation
+.IP "\[ci]" 4
\fBgit\-contrib(1)\fR Show user's contributions
.IP "\[ci]" 4
\fBgit\-count(1)\fR Show commit count
diff --git a/man/git-extras.html b/man/git-extras.html
index 00ed5d0..36448ef 100644
--- a/man/git-extras.html
+++ b/man/git-extras.html
@@ -69,7 +69,7 @@
git-extras(1)
-
+
NAME
@@ -131,6 +131,8 @@
git-commits-since(1) Show commit logs since some date
+git-continue(1) Continue current git operation
+
git-contrib(1) Show user's contributions
git-count(1) Show commit count
diff --git a/man/git-extras.md b/man/git-extras.md
index 8ea9d88..c53d708 100644
--- a/man/git-extras.md
+++ b/man/git-extras.md
@@ -40,6 +40,7 @@ git-extras(1) -- Awesome GIT utilities
- **git-clear(1)** Rigorously clean up a repository
- **git-coauthor(1)** Add a co-author to the last commit
- **git-commits-since(1)** Show commit logs since some date
+ - **git-continue(1)** Continue current git operation
- **git-contrib(1)** Show user's contributions
- **git-count(1)** Show commit count
- **git-cp(1)** Copy a file keeping its history
diff --git a/man/index.txt b/man/index.txt
index 21c6503..c4c7168 100644
--- a/man/index.txt
+++ b/man/index.txt
@@ -13,6 +13,7 @@ git-clear(1) git-clear
git-coauthor(1) git-coauthor
git-commits-since(1) git-commits-since
git-contrib(1) git-contrib
+git-continue(1) git-continue
git-count(1) git-count
git-cp(1) git-cp
git-create-branch(1) git-create-branch
diff --git a/tests/README.md b/tests/README.md
index d97c077..7c60a07 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -22,7 +22,8 @@ It is done or go without `poetry`,
1. Install python >= 3.11
2. Install pytest >= 8.1.2
3. Install gitpython >= 3.1.43
-4. Run `pytest`
+4. Install testpath >= 0.6.0
+5. Run `pytest`
The second way maybe blocked the some missing dependencies at someday, so the first one is recommended.
diff --git a/tests/conftest.py b/tests/conftest.py
index 4f9e0db..3b31908 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -7,8 +7,8 @@ from helper import TempRepository
def create_repo(dirname=None):
repo = TempRepository(dirname)
- repo.create_tmp_file()
- repo.create_tmp_file()
+ repo.create_tmp_file() # tmp_file_a
+ repo.create_tmp_file() # tmp_file_b
repo.switch_cwd_under_repo()
return repo
@@ -35,3 +35,11 @@ def named_temp_repo(request):
init_repo_git_status(repo)
yield repo
repo.teardown()
+
+
+@pytest.fixture(scope="function")
+def temp_repo_clean():
+ """Create a temporary repository that is reset for each function call."""
+ repo = create_repo()
+ init_repo_git_status(repo)
+ return repo
diff --git a/tests/test_git_continue.py b/tests/test_git_continue.py
new file mode 100644
index 0000000..8fd7d5e
--- /dev/null
+++ b/tests/test_git_continue.py
@@ -0,0 +1,84 @@
+from git import GitCommandError
+
+
+class TestGitContinue:
+
+ @classmethod
+ def _init_repo(cls, repo):
+ git = repo.get_repo_git()
+ tmp_file = repo.get_file(0)
+ git.branch("A")
+ git.branch("B")
+ git.branch("C")
+ git.checkout("A")
+ repo.writefile(tmp_file, "a")
+ git.add(".")
+ git.commit("-m", "A")
+ git.checkout("B")
+ repo.writefile(tmp_file, "b")
+ git.add(".")
+ git.commit("-m", "B")
+
+ def test_init(self, temp_repo_clean):
+ TestGitContinue._init_repo(temp_repo_clean)
+ git = temp_repo_clean.get_repo_git()
+ git.status()
+
+ def test_cherry_pick(self, temp_repo_clean):
+ TestGitContinue._init_repo(temp_repo_clean)
+ git = temp_repo_clean.get_repo_git()
+ try:
+ git.cherry_pick("A")
+ except GitCommandError as err:
+ print(err)
+ result = git.status()
+ assert "Unmerged path" in result
+ git.add(".")
+ temp_repo_clean.invoke_extras_command("continue")
+ result = git.status()
+ assert "nothing to commit, working tree clean" in result
+
+ def test_merge(self, temp_repo_clean):
+ TestGitContinue._init_repo(temp_repo_clean)
+ git = temp_repo_clean.get_repo_git()
+ try:
+ git.merge("A")
+ except GitCommandError as err:
+ print(err)
+ result = git.status()
+ assert "Unmerged path" in result
+ git.add(".")
+ git.commit("-m", "resolve conflict")
+ temp_repo_clean.invoke_extras_command("continue")
+ result = git.status()
+ assert "nothing to commit, working tree clean" in result
+
+ def test_rebase(self, temp_repo_clean):
+ TestGitContinue._init_repo(temp_repo_clean)
+ git = temp_repo_clean.get_repo_git()
+ try:
+ git.rebase("A")
+ except GitCommandError as err:
+ print(err)
+ result = git.status()
+ assert "Unmerged path" in result
+ git.add(".")
+ git.commit("-m", "resolve conflict")
+ temp_repo_clean.invoke_extras_command("continue")
+ result = git.status()
+ assert "nothing to commit, working tree clean" in result
+
+ def test_revert(self, temp_repo_clean):
+ TestGitContinue._init_repo(temp_repo_clean)
+ git = temp_repo_clean.get_repo_git()
+ try:
+ git.revert("A")
+ except GitCommandError as err:
+ print(err)
+ result = git.status()
+ assert "Unmerged path" in result
+ git.add(".")
+ git.commit("-m", "resolve conflict")
+ temp_repo_clean.invoke_extras_command("continue")
+ result = git.status()
+ assert "nothing to commit, working tree clean" in result
From 1345c5977d5cb78d7a6d609222edd489e8f049ff Mon Sep 17 00:00:00 2001
From: Edwin Kofler
Date: Thu, 28 Nov 2024 18:39:09 -0800
Subject: [PATCH 21/62] Delete etc/test.fish (#1185)
---
etc/test.fish | 20 --------------------
1 file changed, 20 deletions(-)
delete mode 100644 etc/test.fish
diff --git a/etc/test.fish b/etc/test.fish
deleted file mode 100644
index 159e97d..0000000
--- a/etc/test.fish
+++ /dev/null
@@ -1,20 +0,0 @@
-complete -e blah
-
-function __fish_git_arg_number -a number
- set -l cmd (commandline -opc)
- test (count $cmd) -eq $number
-end
-
-function __fish_git_extra_coauthor_name
- printf '%s\n' 'a' 'apple' 'ann'
-end
-
-function __fish_git_extra_coauthor_email
- set -l cmd (commandline -opc)
-
- set -l value $cmd[3]
- printf '%s\n' 'n' 'n1' 'n2' "$value"
-end
-
-complete -c blah -f -n '__fish_git_using_command coauthor; and __fish_git_arg_number 2' -a '(__fish_git_extra_coauthor_name)'
-complete -c blah -f -n '__fish_git_using_command coauthor; and __fish_git_arg_number 3' -a '(__fish_git_extra_coauthor_email)'
From 3db275cbe2426624b8a7c339dfe6b91ef36ab347 Mon Sep 17 00:00:00 2001
From: oikarinen <7252104+oikarinen@users.noreply.github.com>
Date: Fri, 29 Nov 2024 04:39:42 +0200
Subject: [PATCH 22/62] fix(ci): use poetry (#1183)
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
---
.github/workflows/ci.yml | 42 ++++++++++++++++++++++++++++------------
tests/poetry.lock | 29 ++++++++++++++++++++-------
tests/pyproject.toml | 7 ++++++-
3 files changed, 58 insertions(+), 20 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a4a0393..ba5557a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,7 @@ on:
branches: [main]
jobs:
lint:
- runs-on: 'ubuntu-latest'
+ runs-on: ubuntu-latest
steps:
- name: Check out code.
uses: actions/checkout@v4
@@ -44,26 +44,46 @@ jobs:
steps:
- name: Check out code.
uses: actions/checkout@v4
+ - name: Install poetry
+ run: pip install poetry
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ cache: 'poetry'
+ cache-dependency-path: "tests/pyproject.toml"
+ python-version-file: "tests/pyproject.toml"
+ - name: Install dependencies
+ run: |
+ cd tests || exit
+ poetry install --only dev
- name: spell check
run: |
- pip install codespell==2.2
- git grep --cached -l '' | grep -v -e 'History\.md' -e 'AUTHORS' -e 'man/.*\.1' -e 'man/.*\.html' | xargs codespell --ignore-words=.github/.ignore_words
+ cd tests
+ git grep --cached -l '' .. | \
+ grep -v -e 'History\.md' -e 'AUTHORS' -e 'man/.*\.1' -e 'man/.*\.html' | \
+ xargs poetry run codespell --ignore-words=../.github/.ignore_words
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Set up Python 3.12
+ - name: Install poetry
+ run: pip install poetry
+ - name: Set up Python
uses: actions/setup-python@v5
with:
- python-version: '3.12'
+ cache: 'poetry'
+ cache-dependency-path: "tests/pyproject.toml"
+ python-version-file: "tests/pyproject.toml"
- name: Install dependencies
run: |
- python -m pip install --upgrade pip
- pip install pytest==8.1.2 GitPython==3.1.43 testpath==0.6.0
+ cd tests || exit
+ poetry install --only test
- name: Unit test
- run: make test
+ run: |
+ cd tests
+ poetry run pytest
build:
strategy:
@@ -78,11 +98,9 @@ jobs:
uses: actions/checkout@v4
- name: Linux Install
if: matrix.platform == 'ubuntu-latest'
- run: |
- sudo apt-get install -y bsdmainutils
+ run: sudo apt-get install -y bsdmainutils
- name: Script
- run: |
- ./check_integrity.sh
+ run: ./check_integrity.sh
- name: Brew release
if: matrix.platform == 'macos-latest'
run: |
diff --git a/tests/poetry.lock b/tests/poetry.lock
index 5fa5e29..bb52a52 100644
--- a/tests/poetry.lock
+++ b/tests/poetry.lock
@@ -1,4 +1,19 @@
-# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
+
+[[package]]
+name = "codespell"
+version = "2.2.0"
+description = "Codespell"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "codespell-2.2.0-py3-none-any.whl", hash = "sha256:3cc3fcb484a8302683add19e7d11504c79c79b10d4ea0675409417a044b27374"},
+ {file = "codespell-2.2.0.tar.gz", hash = "sha256:3dce0cd1348d277f8d934d1d4dcbbf510f9ddfd1b9005e9b25fb983189962561"},
+]
+
+[package.extras]
+dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"]
+hard-encoding-detection = ["chardet"]
[[package]]
name = "colorama"
@@ -56,13 +71,13 @@ files = [
[[package]]
name = "packaging"
-version = "24.0"
+version = "24.2"
description = "Core utilities for Python packages"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
- {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
+ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
+ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
]
[[package]]
@@ -127,5 +142,5 @@ test = ["pytest"]
[metadata]
lock-version = "2.0"
-python-versions = "^3.11"
-content-hash = "e73f9840c5f034acbd9b1ebc082e7ce4600ac3235991e57cd825c261a5f2e14e"
+python-versions = "^3.12"
+content-hash = "e730a1e6e7fd2f51858e8c8cfa8b56d606eadd7a0a629ca8af078904721a5159"
diff --git a/tests/pyproject.toml b/tests/pyproject.toml
index f12d487..f04e61d 100644
--- a/tests/pyproject.toml
+++ b/tests/pyproject.toml
@@ -8,11 +8,16 @@ license = "MIT"
readme = "README.md"
[tool.poetry.dependencies]
-python = "^3.11"
+python = "^3.12"
+
+[tool.poetry.group.test.dependencies]
pytest = "8.1.2"
gitpython = "3.1.43"
testpath = "0.6.0"
+[tool.poetry.group.dev.dependencies]
+codespell = "2.2"
+
[tool.pytest.ini_options]
minversion = "7.4"
addopts = "-ra -q"
From fdaca2cd267f01d27d8db30441532233c4ca92f7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 2 Dec 2024 10:06:50 +0800
Subject: [PATCH 23/62] chore(deps): bump astral-sh/ruff-action from 1 to 2
(#1188)
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]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ba5557a..5689a17 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,7 +37,7 @@ jobs:
# NOTE: use env to pass the output in order to avoid possible injection attacks
FILES: "${{ steps.files.outputs.added_modified }}"
- name: Lint and format Python with Ruff
- uses: astral-sh/ruff-action@v1
+ uses: astral-sh/ruff-action@v2
typo:
runs-on: ubuntu-latest
From e691826b4b5fcb15dcee650e3948d00b9e58836c Mon Sep 17 00:00:00 2001
From: oikarinen <7252104+oikarinen@users.noreply.github.com>
Date: Sat, 21 Dec 2024 09:16:39 +0200
Subject: [PATCH 24/62] Fix all ShellCheck errors and add to CI (#1179)
---
.github/workflows/ci.yml | 2 ++
bin/git-create-branch | 2 +-
bin/git-fork | 2 +-
bin/git-guilt | 8 +++++---
bin/git-obliterate | 8 ++++----
bin/git-repl | 2 ++
bin/git-scp | 13 +++++++------
7 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5689a17..8eceaa3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,6 +36,8 @@ jobs:
env:
# NOTE: use env to pass the output in order to avoid possible injection attacks
FILES: "${{ steps.files.outputs.added_modified }}"
+ - name: Shellcheck
+ run: shellcheck --severity=error bin/* ./*.sh
- name: Lint and format Python with Ruff
uses: astral-sh/ruff-action@v2
diff --git a/bin/git-create-branch b/bin/git-create-branch
index c11c4ea..369a5f8 100755
--- a/bin/git-create-branch
+++ b/bin/git-create-branch
@@ -39,7 +39,7 @@ then
REMOTE=origin
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 ]]
then
diff --git a/bin/git-fork b/bin/git-fork
index 3d3742f..2242c4a 100755
--- a/bin/git-fork
+++ b/bin/git-fork
@@ -61,7 +61,7 @@ else
# clone forked repo into current dir
git clone "${remote_prefix}${user}/${project}.git" "$project"
# add reference to origin fork so can merge in upstream changes
- cd "$project"
+ cd "$project" || exit
git remote add upstream "${remote_prefix}${owner}/${project}.git"
git fetch upstream
fi
diff --git a/bin/git-guilt b/bin/git-guilt
index 3fe644a..30e2e0a 100755
--- a/bin/git-guilt
+++ b/bin/git-guilt
@@ -31,7 +31,7 @@ do
esac
done
-cd "$(git-root)" # cd for git blame
+cd "$(git-root)" || exit # cd for git blame
MERGED_LOG=$(git_extra_mktemp)
if [[ $EMAIL == '-e' ]]
then
@@ -44,9 +44,11 @@ for file in $(git diff --name-only "$@")
do
test -n "$DEBUG" && echo "git blame $file"
# $1 - since $2 - until
+ # shellcheck disable=SC2086
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/- \1/' >> "$MERGED_LOG"
# if $2 not given, use current commit as "until"
+ # shellcheck disable=SC2086
git blame $NOT_WHITESPACE --line-porcelain "${2-@}" -- "$file" 2> /dev/null |
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/+ \1/' >> "$MERGED_LOG"
done
@@ -71,7 +73,7 @@ END {
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 -r line
do
people=${line#* }
@@ -103,7 +105,7 @@ do
do
printf "-"
done
- printf "(%s)" $num
+ printf "(%s)" "$num"
else
for (( i = 0; i > num; i-- ))
do
diff --git a/bin/git-obliterate b/bin/git-obliterate
index 992e0eb..83e4ca1 100755
--- a/bin/git-obliterate
+++ b/bin/git-obliterate
@@ -9,15 +9,15 @@ do
file="$file"' '"$i"
shift
done
-test -n "$*" && range="$*"
+test -n "$*" && range=("$@")
test -z "$file" && echo "file required." 1>&2 && exit 1
-if [ -z "$range" ]
+if [ -z "${range[*]}" ]
then
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
--prune-empty --tag-name-filter cat -- --all
else
- # 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" \
- --prune-empty --tag-name-filter cat -- $range
+ --prune-empty --tag-name-filter cat -- "${range[@]}"
fi
diff --git a/bin/git-repl b/bin/git-repl
index a136879..a706de3 100755
--- a/bin/git-repl
+++ b/bin/git-repl
@@ -44,8 +44,10 @@ while true; do
esac
if [[ $cmd == !* ]]; then
+ # shellcheck disable=SC2086
eval ${cmd:1}
elif [[ $cmd == git* ]]; then
+ # shellcheck disable=SC2086
eval $cmd
else
eval git "$cmd"
diff --git a/bin/git-scp b/bin/git-scp
index 52d4d14..7a10a1f 100755
--- a/bin/git-scp
+++ b/bin/git-scp
@@ -59,7 +59,7 @@ function php_lint()
function _dos2unix()
{
- command -v dos2unix > /dev/null && dos2unix $@
+ command -v dos2unix > /dev/null && dos2unix "$@"
return 0
}
@@ -68,8 +68,8 @@ function _sanitize()
git config --get-all extras.scp.sanitize | while read -r i
do
case $i in
- php_lint) php_lint $@;; # git config --global --add extras.scp.sanitize php_lint
- dos2unix) _dos2unix $@;; # git config --global --add extras.scp.sanitize dos2unix
+ php_lint) php_lint "$@";; # git config --global --add extras.scp.sanitize php_lint
+ dos2unix) _dos2unix "$@";; # git config --global --add extras.scp.sanitize dos2unix
esac
done
return $?
@@ -107,6 +107,7 @@ function scp_and_stage
if [ -n "$list" ]
then
local _TMP=${0///}
+ # shellcheck disable=SC2086
echo "$list" > "$_TMP" &&
_sanitize $list &&
_info "Pushing to $remote ($(git config "remote.$remote.url"))" &&
@@ -131,7 +132,7 @@ function reverse_scp()
shift
local _TMP=${0///}
- echo $@ > "$_TMP" &&
+ echo "$@" > "$_TMP" &&
rsync -rlDv --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./ &&
rm "$_TMP"
}
@@ -173,8 +174,8 @@ case $(basename "$0") in
git-scp)
case $1 in
''|-h|'?'|help|--help) shift; _test_git_scp; _usage "$@";;
- *) scp_and_stage $@;;
+ *) scp_and_stage "$@";;
esac
;;
- git-rscp) reverse_scp $@;;
+ git-rscp) reverse_scp "$@";;
esac
From df53711fc75d4969a67fe4a024f462717d6a18d2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 29 Dec 2024 18:13:50 -0800
Subject: [PATCH 25/62] chore(deps): bump astral-sh/ruff-action from 2 to 3
(#1189)
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]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8eceaa3..7e963ef 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -39,7 +39,7 @@ jobs:
- name: Shellcheck
run: shellcheck --severity=error bin/* ./*.sh
- name: Lint and format Python with Ruff
- uses: astral-sh/ruff-action@v2
+ uses: astral-sh/ruff-action@v3
typo:
runs-on: ubuntu-latest
From 82cc37d0f5fcfc5aae8ba631a4054a4701e4a557 Mon Sep 17 00:00:00 2001
From: Pierre Ayoub
Date: Thu, 20 Feb 2025 08:41:42 +0100
Subject: [PATCH 26/62] 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.
---
bin/git-bulk | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/git-bulk b/bin/git-bulk
index e3bdd66..48c1954 100755
--- a/bin/git-bulk
+++ b/bin/git-bulk
@@ -129,7 +129,7 @@ function wsnameToCurrent () {
# helper to check number of arguments.
function 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;
exit 1;
fi
From 1a9b0c2ab46b6868f2e408c1b48f02c9fd499a30 Mon Sep 17 00:00:00 2001
From: Pierre Ayoub
Date: Thu, 20 Feb 2025 08:47:27 +0100
Subject: [PATCH 27/62] fix(git-bulk): quiet find errors by default (#1196)
---
bin/git-bulk | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/git-bulk b/bin/git-bulk
index 48c1954..1b54e20 100755
--- a/bin/git-bulk
+++ b/bin/git-bulk
@@ -146,7 +146,7 @@ function executBulkOp () {
local actual=$PWD
[ "${quiet?}" != "true" ] && echo 1>&2 "Executing bulk operation in workspace ${inverse}$actual${reset}"
- allGitFolders=( $(eval find -L . -name ".git") )
+ allGitFolders=( $(eval find -L . -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
From 8ce63003c03e423956a5d9be42f5c503d908b88d Mon Sep 17 00:00:00 2001
From: Pierre Ayoub
Date: Wed, 26 Feb 2025 05:33:16 +0100
Subject: [PATCH 28/62] 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
---
Commands.md | 11 ++++++++++-
bin/git-bulk | 16 +++++++++++++---
man/git-bulk.1 | 12 ++++++++++--
man/git-bulk.html | 16 ++++++++++++++--
man/git-bulk.md | 10 +++++++++-
man/index.txt | 2 +-
6 files changed, 57 insertions(+), 10 deletions(-)
diff --git a/Commands.md b/Commands.md
index de98cef..1085ba7 100644
--- a/Commands.md
+++ b/Commands.md
@@ -280,11 +280,20 @@ usage: git bulk [-g] ([-a]|[-w ])
git bulk --listall
```
- Register a workspace so that `git bulk` knows about it (notice that must be absolute path):
+ Register a workspace so that `git bulk` knows about it (it will be registered in your `.gitconfig`):
```bash
$ git bulk --addworkspace personal ~/workspaces/personal
```
+
+ Notice that `` 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.
```bash
diff --git a/bin/git-bulk b/bin/git-bulk
index 1b54e20..ab5898e 100755
--- a/bin/git-bulk
+++ b/bin/git-bulk
@@ -111,7 +111,17 @@ function checkWSName () {
# parse out wsname from workspacespec
function parseWsName () {
local wsspec="$1"
+ # Get the workspace value from its specification in the `.gitconfig`.
+ # May be an absolute path or a variable name of the form: `$VARNAME`
rwsdir=${wsspec#* }
+ if [[ ${rwsdir:0:1} == '$' ]]; then
+ # Dereference the `rwsdir` value which is a variable name.
+ rwsdir_varname=${rwsdir:1}
+ rwsdir=${!rwsdir_varname}
+ if [[ -z "${rwsdir}" ]]; then
+ echo 1>&2 "error: bad environment variable: $rwsdir_varname" && exit 1
+ fi
+ fi
rwsname=${wsspec#*.} && rwsname=${rwsname%% *}
}
@@ -142,7 +152,7 @@ function executBulkOp () {
listall | while read -r workspacespec; do
parseWsName "$workspacespec"
if [[ -n $wsname ]] && [[ $rwsname != "$wsname" ]]; then continue; fi
- eval cd "\"$rwsdir\""
+ cd "$rwsdir" || exit 1
local actual=$PWD
[ "${quiet?}" != "true" ] && echo 1>&2 "Executing bulk operation in workspace ${inverse}$actual${reset}"
@@ -150,11 +160,11 @@ function executBulkOp () {
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
- eval cd "\"$gitrepodir\"" # into git repo location
+ cd "$gitrepodir" || exit 1 # into git repo location
local curdir=$PWD
local leadingpath=${curdir#"${actual}"}
guardedExecution "$@"
- eval cd "\"$rwsdir\"" # back to origin location of last find command
+ cd "$rwsdir" || exit 1 # back to origin location of last find command
done
done
}
diff --git a/man/git-bulk.1 b/man/git-bulk.1
index 1266608..69a6a38 100644
--- a/man/git-bulk.1
+++ b/man/git-bulk.1
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
-.TH "GIT\-BULK" "1" "September 2024" "" "Git Extras"
+.TH "GIT\-BULK" "1" "February 2025" "" "Git Extras"
.SH "NAME"
\fBgit\-bulk\fR \- Run git commands on multiple repositories
.SH "SYNOPSIS"
@@ -64,10 +64,14 @@ git bulk \-\-listall
List all registered repositories\.
.SH "EXAMPLES"
.nf
-Register a workspace so that git bulk knows about it:
+Register a workspace so that git bulk knows about it using an absolute path:
$ git bulk \-\-addworkspace personal ~/workspaces/personal
+Or register a workspace using an environment variable pointing to an absolute path:
+
+$ git bulk \-\-addworkspace personal '$PERSONAL_WORKSPACE'
+
Use option \-\-from in order to directly clone a repository or multiple repositories
$ git bulk \-\-addworkspace personal ~/workspaces/personal \-\-from https://github\.com/tj/git\-extras\.git
@@ -108,6 +112,10 @@ Remove all registered workspaces:
$ git bulk \-\-purge
.fi
+.SH "FILES"
+.IP "\[ci]" 4
+\fB\.gitconfig\fR: Store the \fBgit\-bulk\fR registered workspaces under the \fBbulkworkspaces\fR key\.
+.IP "" 0
.SH "AUTHOR"
Written by Niklas Schlimm <\fIns103@hotmail\.de\fR>
.SH "REPORTING BUGS"
diff --git a/man/git-bulk.html b/man/git-bulk.html
index de121b1..7fe7689 100644
--- a/man/git-bulk.html
+++ b/man/git-bulk.html
@@ -58,6 +58,7 @@
DESCRIPTION
OPTIONS
EXAMPLES
+ FILES
AUTHOR
REPORTING BUGS
SEE ALSO
@@ -137,10 +138,14 @@
EXAMPLES
-Register a workspace so that git bulk knows about it:
+Register a workspace so that git bulk knows about it using an absolute path:
$ git bulk --addworkspace personal ~/workspaces/personal
+Or register a workspace using an environment variable pointing to an absolute path:
+
+$ git bulk --addworkspace personal '$PERSONAL_WORKSPACE'
+
Use option --from in order to directly clone a repository or multiple repositories
$ git bulk --addworkspace personal ~/workspaces/personal --from https://github.com/tj/git-extras.git
@@ -182,6 +187,13 @@ Remove all registered workspaces:
$ git bulk --purge
+FILES
+
+
+ -
+
.gitconfig: Store the git-bulk registered workspaces under the bulkworkspaces key.
+
+
AUTHOR
Written by Niklas Schlimm <ns103@hotmail.de>
@@ -196,7 +208,7 @@ $ git bulk --purge
diff --git a/man/git-bulk.md b/man/git-bulk.md
index 80edda4..4dca4d3 100644
--- a/man/git-bulk.md
+++ b/man/git-bulk.md
@@ -60,10 +60,14 @@ git bulk adds convenient support for operations that you want to execute on mult
## EXAMPLES
- Register a workspace so that git bulk knows about it:
+ Register a workspace so that git bulk knows about it using an absolute path:
$ git bulk --addworkspace personal ~/workspaces/personal
+ Or register a workspace using an environment variable pointing to an absolute path:
+
+ $ git bulk --addworkspace personal '$PERSONAL_WORKSPACE'
+
Use option --from in order to directly clone a repository or multiple repositories
$ git bulk --addworkspace personal ~/workspaces/personal --from https://github.com/tj/git-extras.git
@@ -104,6 +108,10 @@ git bulk adds convenient support for operations that you want to execute on mult
$ git bulk --purge
+## FILES
+
+- `.gitconfig`: Store the `git-bulk` registered workspaces under the `bulkworkspaces` key.
+
## AUTHOR
Written by Niklas Schlimm <>
diff --git a/man/index.txt b/man/index.txt
index c4c7168..5e98b17 100644
--- a/man/index.txt
+++ b/man/index.txt
@@ -12,8 +12,8 @@ git-clear-soft(1) git-clear-soft
git-clear(1) git-clear
git-coauthor(1) git-coauthor
git-commits-since(1) git-commits-since
-git-contrib(1) git-contrib
git-continue(1) git-continue
+git-contrib(1) git-contrib
git-count(1) git-count
git-cp(1) git-cp
git-create-branch(1) git-create-branch
From 64687599ae2de65ab937f1b80571c12e4f74015f Mon Sep 17 00:00:00 2001
From: Pierre Ayoub
Date: Wed, 26 Feb 2025 05:34:04 +0100
Subject: [PATCH 29/62] 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.
---
etc/git-extras-completion.zsh | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh
index 05f271b..9e1c18d 100644
--- a/etc/git-extras-completion.zsh
+++ b/etc/git-extras-completion.zsh
@@ -92,6 +92,13 @@ __gitex_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() {
local expl
@@ -122,6 +129,20 @@ _git-brv() {
'(-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).]' \
+ '--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() {
_arguments \
'(-l --list)'{-l,--list}'[list commits]' \
From 8eed5f49ad8e7fbed7c72b11cc27afadd6fee91d Mon Sep 17 00:00:00 2001
From: Pierre Ayoub
Date: Thu, 27 Feb 2025 07:18:16 +0100
Subject: [PATCH 30/62] 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`
---
bin/git-bulk | 15 +++++++++++++--
man/git-bulk.1 | 6 +++++-
man/git-bulk.html | 6 +++++-
man/git-bulk.md | 6 +++++-
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/bin/git-bulk b/bin/git-bulk
index ab5898e..fe58e48 100755
--- a/bin/git-bulk
+++ b/bin/git-bulk
@@ -9,12 +9,13 @@ guardedmode=false
singlemode=false
allwsmode=false
quiet=false
+no_follow_symlinks=false
#
# print usage message
#
usage() {
- echo 1>&2 "usage: git bulk [-q|--quiet] [-g] ([-a]|[-w ]) "
+ echo 1>&2 "usage: git bulk [--no-follow-symlinks] [-q|--quiet] [-g] ([-a]|[-w ]) "
echo 1>&2 " git bulk --addworkspace (--from )"
echo 1>&2 " git bulk --removeworkspace "
echo 1>&2 " git bulk --addcurrent "
@@ -156,7 +157,15 @@ function executBulkOp () {
local actual=$PWD
[ "${quiet?}" != "true" ] && echo 1>&2 "Executing bulk operation in workspace ${inverse}$actual${reset}"
- allGitFolders=( $(eval find -L . -name ".git" 2>/dev/null) )
+ # 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
@@ -182,6 +191,8 @@ while [ "${#}" -ge 1 ] ; do
butilcommand="${1:2}" && break ;;
--removeworkspace|--addcurrent|--addworkspace)
butilcommand="${1:2}" && wsname="$2" && wsdir="$3" && if [ "$4" == "--from" ]; then source="$5"; fi && break ;;
+ --no-follow-symlinks)
+ no_follow_symlinks=true ;;
-a)
allwsmode=true ;;
-g)
diff --git a/man/git-bulk.1 b/man/git-bulk.1
index 69a6a38..0ed0a14 100644
--- a/man/git-bulk.1
+++ b/man/git-bulk.1
@@ -4,7 +4,7 @@
.SH "NAME"
\fBgit\-bulk\fR \- Run git commands on multiple repositories
.SH "SYNOPSIS"
-\fBgit\-bulk\fR [\-g] ([\-a]|[\-w
+\fBgit\-bulk\fR [\-g] [\-\-no\-follow\-symlinks] ([\-a]|[\-w
.br
\fBgit\-bulk\fR \-\-addworkspace
.br
@@ -33,6 +33,10 @@ Run a git command on all workspaces and their repositories\.
.P
Ask the user for confirmation on every execution\.
.P
+\-\-no\-follow\-symlinks
+.P
+Do not traverse symbolic links under the workspace when searching for git repositories\.
+.P
\-w
.P
Run the git command on the specified workspace\. The workspace must be registered\.
diff --git a/man/git-bulk.html b/man/git-bulk.html
index 7fe7689..66b0e17 100644
--- a/man/git-bulk.html
+++ b/man/git-bulk.html
@@ -78,7 +78,7 @@
SYNOPSIS
-git-bulk [-g] ([-a]|[-w ])
+
git-bulk [-g] [--no-follow-symlinks] ([-a]|[-w ])
git-bulk --addworkspace (--from )
git-bulk --removeworkspace <ws-name>
git-bulk --addcurrent <ws-name>
@@ -106,6 +106,10 @@
Ask the user for confirmation on every execution.
+--no-follow-symlinks
+
+Do not traverse symbolic links under the workspace when searching for git repositories.
+
-w <ws-name>
Run the git command on the specified workspace. The workspace must be registered.
diff --git a/man/git-bulk.md b/man/git-bulk.md
index 4dca4d3..f5550d5 100644
--- a/man/git-bulk.md
+++ b/man/git-bulk.md
@@ -3,7 +3,7 @@ git-bulk(1) -- Run git commands on multiple repositories
## SYNOPSIS
-`git-bulk` [-g] ([-a]|[-w <ws-name>]) <git command>
+`git-bulk` [-g] [--no-follow-symlinks] ([-a]|[-w <ws-name>]) <git command>
`git-bulk` --addworkspace <ws-name> <ws-root-directory> (--from <URL or file>)
`git-bulk` --removeworkspace <ws-name>
`git-bulk` --addcurrent <ws-name>
@@ -28,6 +28,10 @@ git bulk adds convenient support for operations that you want to execute on mult
Ask the user for confirmation on every execution.
+ --no-follow-symlinks
+
+ Do not traverse symbolic links under the workspace when searching for git repositories.
+
-w <ws-name>
Run the git command on the specified workspace. The workspace must be registered.
From a841845d9a1465cc6d9b65b53baa2c71b8da1eeb Mon Sep 17 00:00:00 2001
From: Pierre Ayoub
Date: Thu, 27 Feb 2025 07:25:55 +0100
Subject: [PATCH 31/62] Feat: allow git-summary showing full path of repository
(#1193)
* 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
---
bin/git-summary | 11 ++++++++++-
etc/git-extras-completion.zsh | 1 +
man/git-summary.1 | 6 +++++-
man/git-summary.html | 6 +++++-
man/git-summary.md | 4 ++++
5 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/bin/git-summary b/bin/git-summary
index f90adf1..b69a9ae 100755
--- a/bin/git-summary
+++ b/bin/git-summary
@@ -3,12 +3,16 @@
cd "$(git root)" || { echo "Can't cd to top level directory";exit 1; }
+PROJECT_FULL_PATH=
SUMMARY_BY_LINE=
DEDUP_BY_EMAIL=
MERGES_ARG=
OUTPUT_STYLE=
for arg in "$@"; do
case "$arg" in
+ --full-path)
+ PROJECT_FULL_PATH=1
+ ;;
--line)
SUMMARY_BY_LINE=1
;;
@@ -51,7 +55,12 @@ if [ -n "$SUMMARY_BY_LINE" ]; then
else
[ $# -ne 0 ] && commit=$*
fi
-project=${PWD##*/}
+
+if [[ -n "$PROJECT_FULL_PATH" ]]; then
+ project=${PWD/${HOME}/\~}
+else
+ project=${PWD##*/}
+fi
#
# get date for the given
diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh
index 9e1c18d..63e77f5 100644
--- a/etc/git-extras-completion.zsh
+++ b/etc/git-extras-completion.zsh
@@ -370,6 +370,7 @@ _git-standup() {
}
_git-summary() {
+ _arguments '--full-path[show repository full path]'
_arguments '--line[summarize with lines rather than commits]'
_arguments '--dedup-by-email[remove duplicate users by the email address]'
_arguments '--no-merges[exclude merge commits]'
diff --git a/man/git-summary.1 b/man/git-summary.1
index 87a3085..a5594be 100644
--- a/man/git-summary.1
+++ b/man/git-summary.1
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
-.TH "GIT\-SUMMARY" "1" "June 2023" "" "Git Extras"
+.TH "GIT\-SUMMARY" "1" "February 2025" "" "Git Extras"
.SH "NAME"
\fBgit\-summary\fR \- Show repository summary
.SH "SYNOPSIS"
@@ -38,6 +38,10 @@ $ git summary \-\-dedup\-by\-email
.P
Exclude merge commits\.
.P
+\-\-full\-path
+.P
+Show the full path of the repository instead of its directory name\.
+.P
\-\-line
.P
Summarize with lines other than commits\. When \fB\-\-line\fR is specified, the last argument is treated as \.
diff --git a/man/git-summary.html b/man/git-summary.html
index ffcd497..8122b82 100644
--- a/man/git-summary.html
+++ b/man/git-summary.html
@@ -114,6 +114,10 @@ $ git summary --dedup-by-email
Exclude merge commits.
+--full-path
+
+Show the full path of the repository instead of its directory name.
+
--line
Summarize with lines other than commits.
@@ -221,7 +225,7 @@ git-extras / age: 13 years / last active: 7 hours ago / active on 807 days / com
diff --git a/man/git-summary.md b/man/git-summary.md
index 11ca4bb..708586d 100644
--- a/man/git-summary.md
+++ b/man/git-summary.md
@@ -39,6 +39,10 @@ Shows a summary of the repository or a path within it.
Exclude merge commits.
+ --full-path
+
+ Show the full path of the repository instead of its directory name.
+
--line
Summarize with lines other than commits.
From 18d101732552c0bfba53cccb572de4f95c4ae9ae Mon Sep 17 00:00:00 2001
From: Pierre Ayoub
Date: Wed, 5 Mar 2025 03:39:41 +0100
Subject: [PATCH 32/62] feat(git-bulk): add new option to not follow hidden
directories (#1195)
* 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
* 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
Co-authored-by: Edwin Kofler
---
bin/git-bulk | 10 ++++++++--
etc/git-extras-completion.zsh | 2 ++
man/git-bulk.1 | 6 +++++-
man/git-bulk.html | 6 +++++-
man/git-bulk.md | 6 +++++-
5 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/bin/git-bulk b/bin/git-bulk
index fe58e48..1f414ea 100755
--- a/bin/git-bulk
+++ b/bin/git-bulk
@@ -10,12 +10,13 @@ singlemode=false
allwsmode=false
quiet=false
no_follow_symlinks=false
+no_follow_hidden=false
#
# print usage message
#
usage() {
- echo 1>&2 "usage: git bulk [--no-follow-symlinks] [-q|--quiet] [-g] ([-a]|[-w ]) "
+ echo 1>&2 "usage: git bulk [--no-follow-symlinks] [--no-follow-hidden] [-q|--quiet] [-g] ([-a]|[-w ]) "
echo 1>&2 " git bulk --addworkspace (--from )"
echo 1>&2 " git bulk --removeworkspace "
echo 1>&2 " git bulk --addcurrent "
@@ -172,7 +173,10 @@ function executBulkOp () {
cd "$gitrepodir" || exit 1 # into git repo location
local curdir=$PWD
local leadingpath=${curdir#"${actual}"}
- guardedExecution "$@"
+ # do not execute if we do not want to consider a ".git" directory under a hidden directory
+ if [ $no_follow_hidden = false ] || ! [[ "$leadingpath" =~ "/." ]]; then
+ guardedExecution "$@"
+ fi
cd "$rwsdir" || exit 1 # back to origin location of last find command
done
done
@@ -193,6 +197,8 @@ while [ "${#}" -ge 1 ] ; do
butilcommand="${1:2}" && wsname="$2" && wsdir="$3" && if [ "$4" == "--from" ]; then source="$5"; fi && break ;;
--no-follow-symlinks)
no_follow_symlinks=true ;;
+ --no-follow-hidden)
+ no_follow_hidden=true ;;
-a)
allwsmode=true ;;
-g)
diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh
index 63e77f5..cbe72e1 100644
--- a/etc/git-extras-completion.zsh
+++ b/etc/git-extras-completion.zsh
@@ -135,6 +135,8 @@ _git-bulk() {
'-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]' \
diff --git a/man/git-bulk.1 b/man/git-bulk.1
index 0ed0a14..64a7d9b 100644
--- a/man/git-bulk.1
+++ b/man/git-bulk.1
@@ -4,7 +4,7 @@
.SH "NAME"
\fBgit\-bulk\fR \- Run git commands on multiple repositories
.SH "SYNOPSIS"
-\fBgit\-bulk\fR [\-g] [\-\-no\-follow\-symlinks] ([\-a]|[\-w
+\fBgit\-bulk\fR [\-g] [\-\-no\-follow\-symlinks] [\-\-no\-follow\-hidden] ([\-a]|[\-w
.br
\fBgit\-bulk\fR \-\-addworkspace
.br
@@ -37,6 +37,10 @@ Ask the user for confirmation on every execution\.
.P
Do not traverse symbolic links under the workspace when searching for git repositories\.
.P
+\-\-no\-follow\-hidden
+.P
+Do not traverse hidden (dotted) directories under the workspace when searching for git repositories\.
+.P
\-w
.P
Run the git command on the specified workspace\. The workspace must be registered\.
diff --git a/man/git-bulk.html b/man/git-bulk.html
index 66b0e17..b5165be 100644
--- a/man/git-bulk.html
+++ b/man/git-bulk.html
@@ -78,7 +78,7 @@
SYNOPSIS
-git-bulk [-g] [--no-follow-symlinks] ([-a]|[-w ])
+
git-bulk [-g] [--no-follow-symlinks] [--no-follow-hidden] ([-a]|[-w ])
git-bulk --addworkspace (--from )
git-bulk --removeworkspace <ws-name>
git-bulk --addcurrent <ws-name>
@@ -110,6 +110,10 @@
Do not traverse symbolic links under the workspace when searching for git repositories.
+--no-follow-hidden
+
+Do not traverse hidden (dotted) directories under the workspace when searching for git repositories.
+
-w <ws-name>
Run the git command on the specified workspace. The workspace must be registered.
diff --git a/man/git-bulk.md b/man/git-bulk.md
index f5550d5..68ac35c 100644
--- a/man/git-bulk.md
+++ b/man/git-bulk.md
@@ -3,7 +3,7 @@ git-bulk(1) -- Run git commands on multiple repositories
## SYNOPSIS
-`git-bulk` [-g] [--no-follow-symlinks] ([-a]|[-w <ws-name>]) <git command>
+`git-bulk` [-g] [--no-follow-symlinks] [--no-follow-hidden] ([-a]|[-w <ws-name>]) <git command>
`git-bulk` --addworkspace <ws-name> <ws-root-directory> (--from <URL or file>)
`git-bulk` --removeworkspace <ws-name>
`git-bulk` --addcurrent <ws-name>
@@ -32,6 +32,10 @@ git bulk adds convenient support for operations that you want to execute on mult
Do not traverse symbolic links under the workspace when searching for git repositories.
+ --no-follow-hidden
+
+ Do not traverse hidden (dotted) directories under the workspace when searching for git repositories.
+
-w <ws-name>
Run the git command on the specified workspace. The workspace must be registered.
From aadf5f874be3d1b77c0ec47c9fc3e52a4057ac7a Mon Sep 17 00:00:00 2001
From: Edwin Kofler
Date: Wed, 12 Mar 2025 03:14:53 -0700
Subject: [PATCH 33/62] Add stale bot for old PRs (#1186)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Add stale bot for old PRs
* Update message
* Update .github/workflows/stale.yaml
Co-authored-by: 罗泽轩
* Fix stale workflow
* Update .github/workflows/stale.yaml
Co-authored-by: 罗泽轩
---------
Co-authored-by: 罗泽轩
---
.github/workflows/stale.yaml | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
create mode 100644 .github/workflows/stale.yaml
diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml
new file mode 100644
index 0000000..3c6a1d2
--- /dev/null
+++ b/.github/workflows/stale.yaml
@@ -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@v9"
+ 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
From 18ecffef5e75277f5d8a07f21227dc39450256e0 Mon Sep 17 00:00:00 2001
From: Edwin Kofler
Date: Wed, 12 Mar 2025 03:15:37 -0700
Subject: [PATCH 34/62] Implement half of tests in Bats (#1187)
* 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
---
.editorconfig | 8 +++
.github/workflows/ci.yml | 15 ++++-
.gitmodules | 3 +
tests/git-abort.bats | 93 +++++++++++++++++++++++++++++++
tests/git-alias.bats | 106 ++++++++++++++++++++++++++++++++++++
tests/git-archive-file.bats | 66 ++++++++++++++++++++++
tests/git-authors.bats | 52 ++++++++++++++++++
tests/test_util.sh | 26 +++++++++
vendor/bats-all | 1 +
9 files changed, 368 insertions(+), 2 deletions(-)
create mode 100644 .gitmodules
create mode 100644 tests/git-abort.bats
create mode 100644 tests/git-alias.bats
create mode 100644 tests/git-archive-file.bats
create mode 100644 tests/git-authors.bats
create mode 100644 tests/test_util.sh
create mode 160000 vendor/bats-all
diff --git a/.editorconfig b/.editorconfig
index bc1a4e7..b715678 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -20,3 +20,11 @@ trim_trailing_whitespace = false
[*.py]
indent_size = 4
+
+[*.bats]
+indent_style = tab
+indent_size = 4
+
+[tests/*.sh]
+indent_style = tab
+indent_size = 4
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7e963ef..b0257c9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -70,22 +70,33 @@ jobs:
steps:
- uses: actions/checkout@v4
+ with:
+ submodules: recursive
- name: Install poetry
run: pip install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
+ python-version: '3.12'
cache: 'poetry'
cache-dependency-path: "tests/pyproject.toml"
python-version-file: "tests/pyproject.toml"
- - name: Install dependencies
+ - name: Install Python Dependencies
run: |
cd tests || exit
poetry install --only test
- - name: Unit test
+ - name: Setup Bats
+ id: setup-bats
+ uses: bats-core/bats-action@3.0.0
+ - name: Test with Pytest
run: |
cd tests
poetry run pytest
+ - name: Test with Bats
+ env:
+ BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }}
+ TERM: xterm
+ run: bats ./tests
build:
strategy:
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..33dec90
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/bats-all"]
+ path = vendor/bats-all
+ url = https://github.com/hyperupcall/bats-all
diff --git a/tests/git-abort.bats b/tests/git-abort.bats
new file mode 100644
index 0000000..94f9f8d
--- /dev/null
+++ b/tests/git-abort.bats
@@ -0,0 +1,93 @@
+# shellcheck shell=bash
+
+source "$BATS_TEST_DIRNAME/test_util.sh"
+
+setup_file() {
+ test_util.setup_file
+}
+
+setup() {
+ test_util.cd_test
+
+ git init
+ git commit --allow-empty -m "Initial commit"
+ git branch A
+ git branch B
+ git checkout A
+ printf '%s\n' 'a' > tmpfile
+ git add .
+ git commit -m A
+ git checkout B
+ printf '%s\n' 'b' > tmpfile
+ git add .
+ git commit -m B
+ git status
+}
+
+@test "cherry pick" {
+ run git cherry-pick A
+ assert_failure
+
+ run git status
+ assert_line -p 'You are currently cherry-picking commit'
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ run git abort
+ assert_success
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
+
+@test "merge" {
+ run git merge A
+ assert_failure
+
+ run git status
+ assert_line -p 'You have unmerged paths'
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ run git abort
+ assert_success
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
+
+@test "rebase" {
+ run git rebase A
+ assert_failure
+
+ run git status
+ assert_line -p 'You are currently rebasing branch'
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ run git abort
+ assert_success
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
+
+@test "revert" {
+ run git revert A
+ assert_failure
+
+ run git status
+ assert_line -p 'You are currently reverting commit'
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ run git abort
+ assert_success
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
diff --git a/tests/git-alias.bats b/tests/git-alias.bats
new file mode 100644
index 0000000..fa210a7
--- /dev/null
+++ b/tests/git-alias.bats
@@ -0,0 +1,106 @@
+# shellcheck shell=bash
+
+source "$BATS_TEST_DIRNAME/test_util.sh"
+
+setup_file() {
+ test_util.setup_file
+}
+
+setup() {
+ test_util.cd_test
+
+ git init
+ git config --global alias.globalalias status
+ git config --global alias.x status
+ git config --local alias.localalias status
+ git config --local alias.y status
+}
+
+@test "list all works" {
+ run git alias
+ assert_output - <<-'EOF'
+ globalalias = status
+ localalias = status
+ x = status
+ y = status
+EOF
+ assert_success
+}
+
+@test "list all globally works" {
+ run git alias --global
+ assert_output - <<-'EOF'
+ globalalias = status
+ x = status
+EOF
+ assert_success
+}
+
+@test "list all locally works" {
+ run git alias --local
+ assert_output - <<-'EOF'
+ localalias = status
+ y = status
+EOF
+ assert_success
+}
+
+@test "search globally works" {
+ run git alias --global global
+ assert_output - <<-'EOF'
+ globalalias = status
+EOF
+ assert_success
+
+ run git alias --global local
+ assert_output ''
+ assert_failure
+}
+
+@test "search locally works" {
+ run git alias --local local
+ assert_output - <<-'EOF'
+ localalias = status
+EOF
+ assert_success
+
+ run git alias --local global
+ assert_output ''
+ assert_failure
+}
+
+@test "get alias globally and defaultly" {
+ run git alias globalalias
+ assert_output - <<-'EOF'
+ globalalias = status
+EOF
+ assert_success
+}
+
+@test "set alias globally and defaultly" {
+ git alias globalalias diff
+ run git alias diff
+ assert_output - <<-'EOF'
+ globalalias = diff
+EOF
+ assert_success
+}
+
+@test "get alias locally" {
+ run git alias --local localalias
+ assert_output - <<-'EOF'
+ localalias = status
+EOF
+ assert_success
+}
+
+@test "set alias locally" {
+ git alias --local localalias diff
+ run git alias
+ assert_output - <<-'EOF'
+ globalalias = status
+ localalias = diff
+ x = status
+ y = status
+EOF
+}
diff --git a/tests/git-archive-file.bats b/tests/git-archive-file.bats
new file mode 100644
index 0000000..76cffd6
--- /dev/null
+++ b/tests/git-archive-file.bats
@@ -0,0 +1,66 @@
+# shellcheck shell=bash
+
+source "$BATS_TEST_DIRNAME/test_util.sh"
+
+setup_file() {
+ test_util.setup_file
+}
+
+setup() {
+ test_util.cd_test
+
+ git init
+ printf '%s\n' 'data' > tmpfile
+ git add .
+ git commit -m 'test: add data'
+ git tag 0.1.0 -m 'bump: 0.1.0'
+}
+
+@test "archive file on tags branch" {
+ git checkout -b tags0.1.0
+ run git archive-file
+ assert_success
+
+ local describe_output=
+ describe_output=$(git describe)
+ assert_file_exists "${PWD##*/}.$describe_output.zip"
+}
+
+@test "archive file on any not tags branch without default branch" {
+ git checkout -b not-tags-branch
+ run git archive-file
+ assert_success
+
+ local describe_output=
+ describe_output=$(git describe --always --long)
+ assert_file_exists "${PWD##*/}.$describe_output.not-tags-branch.zip"
+}
+
+@test "archive file on any not tags branch with default branch" {
+ skip "Not working as expected"
+
+ run git archive-file
+ assert_success
+
+ local describe_output=
+ describe_output=$(git describe --always --long)
+ assert_file_exists "${PWD##*/}.$describe_output.zip"
+}
+
+@test "archive file on branch name has slash" {
+ git checkout -b feature/slash
+ run git archive-file
+ assert_success
+
+ local describe_output=
+ describe_output=$(git describe --always --long)
+ assert_file_exists "${PWD##*/}.$describe_output.feature-slash.zip"
+}
+
+@test "archive file on dirname has backslash" {
+ skip
+}
+
+@test "archive file on tag name has slash" {
+ skip
+}
diff --git a/tests/git-authors.bats b/tests/git-authors.bats
new file mode 100644
index 0000000..203a882
--- /dev/null
+++ b/tests/git-authors.bats
@@ -0,0 +1,52 @@
+# shellcheck shell=bash
+
+source "$BATS_TEST_DIRNAME/test_util.sh"
+
+
+setup_file() {
+ test_util.setup_file
+}
+
+setup() {
+ test_util.cd_test
+
+ git init
+ GIT_CONFIG_VALUE_0='test@example.com'
+ GIT_CONFIG_VALUE_1='test'
+ printf '%s\n' 'A' > tmpfile
+ git add .
+ git commit -m 'test: add data A'
+ GIT_CONFIG_VALUE_0='testagain@example.com'
+ GIT_CONFIG_VALUE_1='testagain'
+ printf '%s\n' 'B' > tmpfile
+ git add .
+ git commit -m 'test: add data B'
+}
+
+@test "output authors has email without any parameter" {
+ run git authors
+ assert_success
+
+ local content=$(\ntestagain '
+}
+
+@test "list authors has email defaultly" {
+ run git authors --list
+ assert_output $'test \ntestagain '
+ assert_success
+
+ run git authors -l
+ assert_output $'test \ntestagain '
+ assert_success
+}
+
+@test "list authors has no email" {
+ run git authors --list --no-email
+ assert_output $'test\ntestagain'
+ assert_success
+
+ run git authors -l --no-email
+ assert_output $'test\ntestagain'
+ assert_success
+}
diff --git a/tests/test_util.sh b/tests/test_util.sh
new file mode 100644
index 0000000..73ca915
--- /dev/null
+++ b/tests/test_util.sh
@@ -0,0 +1,26 @@
+# shellcheck shell=bash
+
+source "$BATS_TEST_DIRNAME/../vendor/bats-all/load.bash"
+
+test_util.setup_file() {
+ cd "$BATS_FILE_TMPDIR"
+
+ export GIT_CONFIG_NOSYSTEM=1
+ export GIT_CONFIG_GLOBAL="$PWD/git_config"
+ export GIT_CONFIG_COUNT=3
+ export GIT_CONFIG_KEY_0="user.email"
+ export GIT_CONFIG_VALUE_0="name@example.com"
+ export GIT_CONFIG_KEY_1="user.name"
+ export GIT_CONFIG_VALUE_1="Name"
+ # This removes default warning about default "master" branch on some Git versions.
+ export GIT_CONFIG_KEY_2="init.defaultBranch"
+ export GIT_CONFIG_VALUE_2="main"
+
+ # Append to path so that we can access all commands included from git-extras
+ # TODO: This currently breaks with commands that are included in "not_needed_git_repo" etc.
+ PATH="$BATS_TEST_DIRNAME/../bin:$PATH"
+}
+
+test_util.cd_test() {
+ cd "$BATS_TEST_TMPDIR"
+}
diff --git a/vendor/bats-all b/vendor/bats-all
new file mode 160000
index 0000000..a16a4a2
--- /dev/null
+++ b/vendor/bats-all
@@ -0,0 +1 @@
+Subproject commit a16a4a2cfa744878992a5f5d3f206319dba54158
From c49ca70814f4169ee18f9a67ec15f9e3604fc063 Mon Sep 17 00:00:00 2001
From: Edwin Kofler
Date: Tue, 25 Mar 2025 20:19:30 -0700
Subject: [PATCH 35/62] 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
---
.github/workflows/ci.yml | 20 ++++--
.gitignore | 1 +
tests/README.md | 36 +++++++++--
tests/bin/open | 2 +
tests/bin/powershell.exe | 2 +
tests/bin/start | 2 +
tests/bin/xdg-open | 2 +
tests/git-abort.bats | 10 +--
tests/git-alias.bats | 2 +-
tests/git-archive-file.bats | 2 +-
tests/git-authors.bats | 15 +++--
tests/git-browse-ci.bats | 117 ++++++++++++++++++++++++++++++++++
tests/git-browse.bats | 123 ++++++++++++++++++++++++++++++++++++
tests/git-continue.bats | 92 +++++++++++++++++++++++++++
tests/test_util.sh | 14 ++--
15 files changed, 409 insertions(+), 31 deletions(-)
create mode 100755 tests/bin/open
create mode 100755 tests/bin/powershell.exe
create mode 100755 tests/bin/start
create mode 100755 tests/bin/xdg-open
create mode 100644 tests/git-browse-ci.bats
create mode 100644 tests/git-browse.bats
create mode 100755 tests/git-continue.bats
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b0257c9..1dc305a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,9 +65,9 @@ jobs:
grep -v -e 'History\.md' -e 'AUTHORS' -e 'man/.*\.1' -e 'man/.*\.html' | \
xargs poetry run codespell --ignore-words=../.github/.ignore_words
- test:
+ test-pytest:
+ name: 'Test with Pytest'
runs-on: ubuntu-latest
-
steps:
- uses: actions/checkout@v4
with:
@@ -85,13 +85,23 @@ jobs:
run: |
cd tests || exit
poetry install --only test
- - name: Setup Bats
- id: setup-bats
- uses: bats-core/bats-action@3.0.0
- name: Test with Pytest
run: |
cd tests
poetry run pytest
+
+ test-bats:
+ name: 'Test with Bats'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Setup Bats
+ id: setup-bats
+ uses: bats-core/bats-action@3.0.0
+ with:
+ bats-version: 'v1.8.1'
- name: Test with Bats
env:
BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }}
diff --git a/.gitignore b/.gitignore
index a230a78..294ece4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
.venv/
__pycache__/
+coverage/
diff --git a/tests/README.md b/tests/README.md
index 7c60a07..836021d 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,7 +1,28 @@
-# Test for git-extras
-The git-extras has its own testcases now, and the more is on the way! So let's introduce it.
+# Testing
-We choose python to help us to reach to other shore cause **python is life saver**.
+Originally, the tests were written in pytest. However, tests are in the process of being converted to Bats so coverage can be calculated.
+
+## Bats Testing
+
+We require a somewhat recent version of Bats. Version v1.8.1 is tested in CI. Once it is installed, the tests can be executed like so:
+
+```sh
+bats ./tests
+```
+
+We highly recommend adding tests for new features and fixes.
+
+### Code Coverage
+
+Coverage can be calculated with [bashcov](https://github.com/infertux/bashcov) like so:
+
+```sh
+bashcov -- bats ./tests
+```
+
+By default, the report will be generated in `./coverage/index.html`.
+
+## Python Testing
The test part depends on:
@@ -12,7 +33,8 @@ The test part depends on:
So the versions are higher than above is recommended.
-# How to run the tests
+### How to run the tests
+
1. Install `poetry`
2. Install the dependencies via `poetry install`
3. Run `poetry run pytest`
@@ -27,7 +49,8 @@ It is done or go without `poetry`,
The second way maybe blocked the some missing dependencies at someday, so the first one is recommended.
-# What and how to create a unit test
+### What and how to create a unit test
+
One command has a unit test, because one `git-*` command is just do one thing, so we can eat a piece of `git-*` command in one time.
For example,
@@ -39,7 +62,8 @@ For example,
* `named_temp_repo` is just same as `temp_repo` except the custom directory renaming.
4. Loop the third step until the 100% coverage of the function of the `git-alias`
-# References
+### References
+
* [poetry](https://github.com/python-poetry/poetry)
* [pytest](https://github.com/pytest-dev/pytest/)
* [git python](https://github.com/gitpython-developers/GitPython)
diff --git a/tests/bin/open b/tests/bin/open
new file mode 100755
index 0000000..2be3eaf
--- /dev/null
+++ b/tests/bin/open
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+printf '%s\n' "open $*"
diff --git a/tests/bin/powershell.exe b/tests/bin/powershell.exe
new file mode 100755
index 0000000..aee36a1
--- /dev/null
+++ b/tests/bin/powershell.exe
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+printf '%s\n' "powershell.exe $*"
diff --git a/tests/bin/start b/tests/bin/start
new file mode 100755
index 0000000..89eeebd
--- /dev/null
+++ b/tests/bin/start
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+printf '%s\n' "start $*"
diff --git a/tests/bin/xdg-open b/tests/bin/xdg-open
new file mode 100755
index 0000000..951b999
--- /dev/null
+++ b/tests/bin/xdg-open
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+printf '%s\n' "xdg-open $*"
diff --git a/tests/git-abort.bats b/tests/git-abort.bats
index 94f9f8d..73e8302 100644
--- a/tests/git-abort.bats
+++ b/tests/git-abort.bats
@@ -9,7 +9,7 @@ setup_file() {
setup() {
test_util.cd_test
- git init
+ test_util.git_init
git commit --allow-empty -m "Initial commit"
git branch A
git branch B
@@ -24,7 +24,7 @@ setup() {
git status
}
-@test "cherry pick" {
+@test "works with cherry pick" {
run git cherry-pick A
assert_failure
@@ -41,7 +41,7 @@ setup() {
assert_success
}
-@test "merge" {
+@test "works with merge" {
run git merge A
assert_failure
@@ -58,7 +58,7 @@ setup() {
assert_success
}
-@test "rebase" {
+@test "works with rebase" {
run git rebase A
assert_failure
@@ -75,7 +75,7 @@ setup() {
assert_success
}
-@test "revert" {
+@test "works with revert" {
run git revert A
assert_failure
diff --git a/tests/git-alias.bats b/tests/git-alias.bats
index fa210a7..bb15410 100644
--- a/tests/git-alias.bats
+++ b/tests/git-alias.bats
@@ -9,7 +9,7 @@ setup_file() {
setup() {
test_util.cd_test
- git init
+ test_util.git_init
git config --global alias.globalalias status
git config --global alias.x status
git config --local alias.localalias status
diff --git a/tests/git-archive-file.bats b/tests/git-archive-file.bats
index 76cffd6..b7f1849 100644
--- a/tests/git-archive-file.bats
+++ b/tests/git-archive-file.bats
@@ -9,7 +9,7 @@ setup_file() {
setup() {
test_util.cd_test
- git init
+ test_util.git_init
printf '%s\n' 'data' > tmpfile
git add .
git commit -m 'test: add data'
diff --git a/tests/git-authors.bats b/tests/git-authors.bats
index 203a882..2fafffa 100644
--- a/tests/git-authors.bats
+++ b/tests/git-authors.bats
@@ -10,17 +10,22 @@ setup_file() {
setup() {
test_util.cd_test
- git init
- GIT_CONFIG_VALUE_0='test@example.com'
- GIT_CONFIG_VALUE_1='test'
+ test_util.git_init
+
+ git config user.name 'test'
+ git config user.email 'test@example.com'
printf '%s\n' 'A' > tmpfile
git add .
git commit -m 'test: add data A'
- GIT_CONFIG_VALUE_0='testagain@example.com'
- GIT_CONFIG_VALUE_1='testagain'
+
+ git config user.name 'testagain'
+ git config user.email 'testagain@example.com'
printf '%s\n' 'B' > tmpfile
git add .
git commit -m 'test: add data B'
+
+ # git config unset user.name
+ # git config unset user.email
}
@test "output authors has email without any parameter" {
diff --git a/tests/git-browse-ci.bats b/tests/git-browse-ci.bats
new file mode 100644
index 0000000..fee89fc
--- /dev/null
+++ b/tests/git-browse-ci.bats
@@ -0,0 +1,117 @@
+# shellcheck shell=bash
+
+source "$BATS_TEST_DIRNAME/test_util.sh"
+
+setup_file() {
+ test_util.setup_file
+
+ PATH="$BATS_TEST_DIRNAME/bin:$PATH"
+}
+
+setup() {
+ test_util.cd_test
+
+ test_util.git_init
+}
+
+get_ci_uri() {
+ local mode=$1
+
+ if [ "$mode" = 'github' ]; then
+ REPLY="https://github.com/tj/git-extras/actions"
+ elif [ "$mode" = 'gitlab' ]; then
+ REPLY="https://gitlab.com/tj/git-extras/-/pipelines"
+ elif [ "$mode" = 'bitbucket' ]; then
+ REPLY="https://bitbucket.org/tj/git-extras/addon/pipelines/home"
+ fi
+}
+
+@test "works with mac and github" {
+ get_ci_uri 'github'
+ local expected_url=$REPLY
+
+ git remote add upstream https://github.com/tj/git-extras
+ OSTYPE=darwin run git browse-ci upstream
+ assert_output "open $expected_url"
+ assert_success
+}
+
+@test "works with mac and gitlab" {
+ get_ci_uri 'gitlab'
+ local expected_url=$REPLY
+
+ git remote add upstream https://gitlab.com/tj/git-extras
+ OSTYPE=darwin run git browse-ci upstream
+ assert_output "open $expected_url"
+ assert_success
+}
+
+@test "works with mac and bitbucket" {
+ get_ci_uri 'bitbucket'
+ local expected_url=$REPLY
+
+ git remote add upstream https://bitbucket.org/tj/git-extras
+ OSTYPE=darwin run git browse-ci upstream
+ assert_output "open $expected_url"
+ assert_success
+}
+
+@test "works with windows and github" {
+ get_ci_uri 'github'
+ local expected_url=$REPLY
+
+ git remote add upstream https://github.com/tj/git-extras
+ OSTYPE=msys run git browse-ci upstream
+ assert_output "start $expected_url"
+ assert_success
+}
+
+@test "works with windows and gitlab" {
+ get_ci_uri 'gitlab'
+ local expected_url=$REPLY
+
+ git remote add upstream https://gitlab.com/tj/git-extras
+ OSTYPE=msys run git browse-ci upstream
+ assert_output "start $expected_url"
+ assert_success
+}
+
+@test "works with windows and bitbucket" {
+ get_ci_uri 'bitbucket'
+ local expected_url=$REPLY
+
+ git remote add upstream https://bitbucket.org/tj/git-extras
+ OSTYPE=msys run git browse-ci upstream
+ assert_output "start $expected_url"
+ assert_success
+}
+
+@test "works with linux and github" {
+ get_ci_uri 'github'
+ local expected_url=$REPLY
+
+ git remote add upstream https://github.com/tj/git-extras
+ OSTYPE=linux-gnu run git browse-ci upstream
+ assert_output "xdg-open $expected_url"
+ assert_success
+}
+
+@test "works with linux and gitlab" {
+ get_ci_uri 'gitlab'
+ local expected_url=$REPLY
+
+ git remote add upstream https://gitlab.com/tj/git-extras
+ OSTYPE=linux-gnu run git browse-ci upstream
+ assert_output "xdg-open $expected_url"
+ assert_success
+}
+
+@test "works with linux and bitbucket" {
+ get_ci_uri 'bitbucket'
+ local expected_url=$REPLY
+
+ git remote add upstream https://bitbucket.org/tj/git-extras
+ OSTYPE=linux-gnu run git browse-ci upstream
+ assert_output "xdg-open $expected_url"
+ assert_success
+}
diff --git a/tests/git-browse.bats b/tests/git-browse.bats
new file mode 100644
index 0000000..190ef00
--- /dev/null
+++ b/tests/git-browse.bats
@@ -0,0 +1,123 @@
+# shellcheck shell=bash
+
+source "$BATS_TEST_DIRNAME/test_util.sh"
+
+setup_file() {
+ test_util.setup_file
+
+ PATH="$BATS_TEST_DIRNAME/bin:$PATH"
+}
+
+setup() {
+ test_util.cd_test
+
+ test_util.git_init
+ touch ./browse_this
+ git add ./browse_this
+ git commit -m 'Add test file'
+}
+
+get_file_uri() {
+ local mode=$1
+ local filename=$2
+
+ local commit_hash=
+ commit_hash=$(git rev-parse HEAD)
+ if [ "$mode" = 'github' ]; then
+ REPLY="https://github.com/tj/git-extras/blob/$commit_hash/${filename}"
+ elif [ "$mode" = 'gitlab' ]; then
+ REPLY="https://gitlab.com/tj/git-extras/-/blob/${commit_hash}/${filename}"
+ elif [ "$mode" = 'bitbucket' ]; then
+ REPLY="https://bitbucket.org/tj/git-extras/src/${commit_hash}/${filename}"
+ fi
+}
+
+@test "works with mac and github" {
+ get_file_uri github ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://github.com/tj/git-extras
+ OSTYPE=darwin run git browse upstream ./browse_this
+ assert_output "open $expected_url"
+ assert_success
+}
+
+@test "works with mac and gitlab" {
+ get_file_uri gitlab ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://gitlab.com/tj/git-extras
+ OSTYPE=darwin run git browse upstream ./browse_this
+ assert_output "open $expected_url"
+ assert_success
+}
+
+@test "works with mac and bitbucket" {
+ get_file_uri bitbucket ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://bitbucket.org/tj/git-extras
+ OSTYPE=darwin run git browse upstream ./browse_this
+ assert_output "open $expected_url"
+ assert_success
+}
+
+@test "works with windows and github" {
+ get_file_uri github ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://github.com/tj/git-extras
+ OSTYPE=msys run git browse upstream ./browse_this
+ assert_output "start $expected_url"
+ assert_success
+}
+
+@test "works with windows and gitlab" {
+ get_file_uri gitlab ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://gitlab.com/tj/git-extras
+ OSTYPE=msys run git browse upstream ./browse_this
+ assert_output "start $expected_url"
+ assert_success
+}
+
+@test "works with windows and bitbucket" {
+ get_file_uri bitbucket ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://bitbucket.org/tj/git-extras
+ OSTYPE=msys run git browse upstream ./browse_this
+ assert_output "start $expected_url"
+ assert_success
+}
+
+@test "works with linux and github" {
+ get_file_uri github ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://github.com/tj/git-extras
+ OSTYPE=linux-gnu run git browse upstream ./browse_this
+ assert_output "xdg-open $expected_url"
+ assert_success
+}
+
+@test "works with linux and gitlab" {
+ get_file_uri gitlab ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://gitlab.com/tj/git-extras
+ OSTYPE=linux-gnu run git browse upstream ./browse_this
+ assert_output "xdg-open $expected_url"
+ assert_success
+}
+
+@test "works with linux and bitbucket" {
+ get_file_uri bitbucket ./browse_this
+ local expected_url=$REPLY
+
+ git remote add upstream https://bitbucket.org/tj/git-extras
+ OSTYPE=linux-gnu run git browse upstream ./browse_this
+ assert_output "xdg-open $expected_url"
+ assert_success
+}
diff --git a/tests/git-continue.bats b/tests/git-continue.bats
new file mode 100755
index 0000000..da916a8
--- /dev/null
+++ b/tests/git-continue.bats
@@ -0,0 +1,92 @@
+#!/usr/bin/env bats
+
+source "$BATS_TEST_DIRNAME/test_util.sh"
+
+setup_file() {
+ test_util.setup_file
+}
+
+setup() {
+ test_util.cd_test
+
+ test_util.git_init
+ git commit -m 'Initial commit' --allow-empty
+
+ git switch -c A main
+ printf '%s\n' 'a' >> ./tmp_file
+ git add ./tmp_file
+ git commit -m 'A'
+
+ git switch -c B main
+ printf '%s\n' 'b' >> ./tmp_file
+ git add ./tmp_file
+ git commit -m 'B'
+}
+
+@test "works with cherry pick" {
+ run git cherry-pick A
+ assert_failure
+
+ run git status
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ git add .
+ GIT_EDITOR=cat run git continue
+ assert_success
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
+
+@test "works with merge" {
+ run git merge A
+ assert_failure
+
+ run git status
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ git add .
+ GIT_EDITOR=cat run git continue
+ assert_success
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
+
+@test "works with rebase" {
+ run git rebase A
+ assert_failure
+
+ run git status
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ git add .
+ GIT_EDITOR=cat run git continue
+ assert_success
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
+
+@test "works with revert" {
+ run git revert A
+ assert_failure
+
+ run git status
+ assert_line -p 'Unmerged paths:'
+ assert_success
+
+ git add .
+ GIT_EDITOR=cat run git continue
+ assert_failure # TODO: Git seems to do nothing and error out?
+
+ run git status
+ assert_line -p 'nothing to commit, working tree clean'
+ assert_success
+}
diff --git a/tests/test_util.sh b/tests/test_util.sh
index 73ca915..4c85773 100644
--- a/tests/test_util.sh
+++ b/tests/test_util.sh
@@ -7,14 +7,6 @@ test_util.setup_file() {
export GIT_CONFIG_NOSYSTEM=1
export GIT_CONFIG_GLOBAL="$PWD/git_config"
- export GIT_CONFIG_COUNT=3
- export GIT_CONFIG_KEY_0="user.email"
- export GIT_CONFIG_VALUE_0="name@example.com"
- export GIT_CONFIG_KEY_1="user.name"
- export GIT_CONFIG_VALUE_1="Name"
- # This removes default warning about default "master" branch on some Git versions.
- export GIT_CONFIG_KEY_2="init.defaultBranch"
- export GIT_CONFIG_VALUE_2="main"
# Append to path so that we can access all commands included from git-extras
# TODO: This currently breaks with commands that are included in "not_needed_git_repo" etc.
@@ -24,3 +16,9 @@ test_util.setup_file() {
test_util.cd_test() {
cd "$BATS_TEST_TMPDIR"
}
+
+test_util.git_init() {
+ git init --initial-branch main
+ git config user.name 'Name'
+ git config user.email 'name@example.com'
+}
From 783812b627eeeb06317e629a44539f2a8ca527a2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 31 Mar 2025 00:23:42 +0000
Subject: [PATCH 36/62] 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]
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1dc305a..348c4ed 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -99,7 +99,7 @@ jobs:
submodules: recursive
- name: Setup Bats
id: setup-bats
- uses: bats-core/bats-action@3.0.0
+ uses: bats-core/bats-action@3.0.1
with:
bats-version: 'v1.8.1'
- name: Test with Bats
From c63b46a33b41fafa15b0d0da67e4a6a1dd65608c Mon Sep 17 00:00:00 2001
From: Andrew Sullivan Cant
Date: Wed, 14 May 2025 05:05:41 -0400
Subject: [PATCH 37/62] 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}
```
---
.github/.ignore_words | 1 +
Commands.md | 20 +++++
bin/git-unwip | 14 +++
bin/git-wip | 8 ++
etc/git-extras-completion.zsh | 4 +-
man/git-extras.1 | 158 +++++++++++++++++-----------------
man/git-extras.html | 12 ++-
man/git-extras.md | 2 +
man/git-summary.1 | 7 +-
man/git-summary.html | 22 +++--
man/git-summary.md | 4 +-
man/git-unwip.1 | 31 +++++++
man/git-unwip.html | 122 ++++++++++++++++++++++++++
man/git-unwip.md | 36 ++++++++
man/git-wip.1 | 31 +++++++
man/git-wip.html | 122 ++++++++++++++++++++++++++
man/git-wip.md | 36 ++++++++
man/index.txt | 2 +
tests/poetry.lock | 18 ++--
tests/pyproject.toml | 2 +-
20 files changed, 549 insertions(+), 103 deletions(-)
create mode 100755 bin/git-unwip
create mode 100755 bin/git-wip
create mode 100644 man/git-unwip.1
create mode 100644 man/git-unwip.html
create mode 100644 man/git-unwip.md
create mode 100644 man/git-wip.1
create mode 100644 man/git-wip.html
create mode 100644 man/git-wip.md
diff --git a/.github/.ignore_words b/.github/.ignore_words
index 8aad33d..35d9297 100644
--- a/.github/.ignore_words
+++ b/.github/.ignore_words
@@ -1 +1,2 @@
gool
+Cant
diff --git a/Commands.md b/Commands.md
index 1085ba7..fa59614 100644
--- a/Commands.md
+++ b/Commands.md
@@ -75,6 +75,8 @@
- [`git undo`](#git-undo)
- [`git unlock`](#git-unlock)
- [`git utimes`](#git-utimes)
+ - [`git unwip`](#git-unwip)
+ - [`git wip`](#git-wip)
## git extras
@@ -1637,3 +1639,21 @@ 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
+```
diff --git a/bin/git-unwip b/bin/git-unwip
new file mode 100755
index 0000000..4e772b0
--- /dev/null
+++ b/bin/git-unwip
@@ -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
diff --git a/bin/git-wip b/bin/git-wip
new file mode 100755
index 0000000..a25d68f
--- /dev/null
+++ b/bin/git-wip
@@ -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"
diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh
index cbe72e1..13957f0 100644
--- a/etc/git-extras-completion.zsh
+++ b/etc/git-extras-completion.zsh
@@ -464,4 +464,6 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
touch:'touch and add file to the index' \
undo:'remove latest commits' \
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'
diff --git a/man/git-extras.1 b/man/git-extras.1
index 8f09cbf..cba16e0 100644
--- a/man/git-extras.1
+++ b/man/git-extras.1
@@ -1,6 +1,6 @@
-.\" generated with Ronn-NG/v0.9.1
-.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
-.TH "GIT\-EXTRAS" "1" "September 2024" "" "Git Extras"
+.\" generated with Ronn-NG/v0.10.1
+.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
+.TH "GIT\-EXTRAS" "1" "May 2025" "" "Git Extras"
.SH "NAME"
\fBgit\-extras\fR \- Awesome GIT utilities
.SH "SYNOPSIS"
@@ -22,154 +22,158 @@ Self update\.
.P
Change the default branch to \fB$BRANCH\fR\. If \fBgit\-extras\.default\-branch\fR isn't set, \fBinit\.defaultBranch\fR is used instead\. If none of them are set it defaults to \fBmain\fR\.
.SH "COMMANDS"
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-abort(1)\fR Abort current git operation
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-alias(1)\fR Define, search and show aliases
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-archive\-file(1)\fR Export the current HEAD of the git repository to an archive
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-authors(1)\fR Generate authors report
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-browse\-ci(1)\fR \fIView the web page for the current repository\fR
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-browse(1)\fR \fIView the web page for the current repository\fR
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-brv(1)\fR List branches sorted by their last commit date
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-bulk(1)\fR Run git commands on multiple repositories
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-changelog(1)\fR Generate a changelog report
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-clear\-soft(1)\fR Soft clean up a repository
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-clear(1)\fR Rigorously clean up a repository
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-coauthor(1)\fR Add a co\-author to the last commit
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-commits\-since(1)\fR Show commit logs since some date
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-continue(1)\fR Continue current git operation
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-contrib(1)\fR Show user's contributions
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-count(1)\fR Show commit count
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-cp(1)\fR Copy a file keeping its history
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-create\-branch(1)\fR Create branches
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-delete\-branch(1)\fR Delete branches
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-delete\-merged\-branches(1)\fR Delete merged branches
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-delete\-squashed\-branches(1)\fR Delete branches that were squashed
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-delete\-submodule(1)\fR Delete submodules
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-delete\-tag(1)\fR Delete tags
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-delta(1)\fR Lists changed files
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-effort(1)\fR Show effort statistics on file(s)
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-feature(1)\fR Create/Merge feature branch
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-force\-clone(1)\fR overwrite local repositories with clone
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-fork(1)\fR Fork a repo on github
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-fresh\-branch(1)\fR Create fresh branches
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-get(1)\fR Clone a Git repository under a configured directory
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-gh\-pages(1)\fR Create the GitHub Pages branch
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-graft(1)\fR Merge and destroy a given branch
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-guilt(1)\fR calculate change between two revisions
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-ignore\-io(1)\fR Get sample gitignore file
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-ignore(1)\fR Add \.gitignore patterns
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-info(1)\fR Returns information on current repository
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-local\-commits(1)\fR List local commits
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-lock(1)\fR Lock a file excluded from version control
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-locked(1)\fR ls files that have been locked
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-magic(1)\fR Automate add/commit/push routines
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-merge\-into(1)\fR Merge one branch into another
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-merge\-repo(1)\fR Merge two repo histories
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-missing(1)\fR Show commits missing from another branch
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-mr(1)\fR Checks out a merge request locally
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-obliterate(1)\fR rewrite past commits to remove some files
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-paste(1)\fR Send patches to pastebin for chat conversations
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-pr(1)\fR Checks out a pull request locally
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-psykorebase(1)\fR Rebase a branch with a merge commit
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-pull\-request(1)\fR Create pull request for GitHub project
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-reauthor(1)\fR Rewrite history to change author's identity
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-rebase\-patch(1)\fR Rebases a patch
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-release(1)\fR Commit, tag and push changes to the repository
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-rename\-branch(1)\fR rename local branch and push to remote
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-rename\-file(1)\fR Rename a file or directory and ensure Git recognizes the change, regardless of filesystem case\-sensitivity\.
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-rename\-remote(1)\fR Rename a remote
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-rename\-tag(1)\fR Rename a tag
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-repl(1)\fR git read\-eval\-print\-loop
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-reset\-file(1)\fR Reset one file
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-root(1)\fR show path of root
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-scp(1)\fR Copy files to SSH compatible \fBgit\-remote\fR
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-sed(1)\fR replace patterns in git\-controlled files
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-setup(1)\fR Set up a git repository
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-show\-merged\-branches(1)\fR Show merged branches
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-show\-tree(1)\fR show branch tree of commit history
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-show\-unmerged\-branches(1)\fR Show unmerged branches
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-squash(1)\fR squash N last changes up to a ref'ed commit
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-stamp(1)\fR Stamp the last commit message
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-standup(1)\fR Recall the commit history
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-summary(1)\fR Show repository summary
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-sync(1)\fR Sync local branch with remote branch
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-touch(1)\fR Touch and add file to the index
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-undo(1)\fR Remove latest commits
-.IP "\[ci]" 4
+.IP "\(bu" 4
\fBgit\-unlock(1)\fR Unlock a file excluded from version control
-.IP "\[ci]" 4
+.IP "\(bu" 4
+\fBgit\-unwip(1)\fR Undo a Work In Progress commit
+.IP "\(bu" 4
\fBgit\-utimes(1)\fR Change files modification time to their last commit date
+.IP "\(bu" 4
+\fBgit\-wip(1)\fR Create a Work In Progress commit
.IP "" 0
.SH "AUTHOR"
Written by Tj Holowaychuk <\fItj@vision\-media\.ca\fR>
diff --git a/man/git-extras.html b/man/git-extras.html
index 36448ef..1688eaf 100644
--- a/man/git-extras.html
+++ b/man/git-extras.html
@@ -1,8 +1,8 @@
-
-
+
+
git-extras(1) - Awesome GIT utilities
+
+
+
+
+
+
+
+
+ - git-unwip(1)
+ - Git Extras
+ - git-unwip(1)
+
+
+
+
+
NAME
+
+ git-unwip - Undo a Work In Progress commit
+
+
SYNOPSIS
+
+
git-unwip
+
+
DESCRIPTION
+
+
Undo a Work In Progress commit.
+
+
OPTIONS
+
+
None
+
+
EXAMPLES
+
+
Create a WIP commit which stores all changes in the working directory.
+
+
$ git wip
+
+
+
Later on, undo the commit and continue making changes.
+
+
$ git unwip
+
+
+
AUTHOR
+
+
Written by Andrew Sullivan Cant <mail@andrewsullivancant.ca>
+
+
REPORTING BUGS
+
+
<https://github.com/tj/git-extras/issues>
+
+
SEE ALSO
+
+
<https://github.com/tj/git-extras>
+
+
+
+
+
+
diff --git a/man/git-unwip.md b/man/git-unwip.md
new file mode 100644
index 0000000..6d4d961
--- /dev/null
+++ b/man/git-unwip.md
@@ -0,0 +1,36 @@
+git-unwip(1) -- Undo a Work In Progress commit
+================================
+
+## SYNOPSIS
+
+`git-unwip`
+
+## DESCRIPTION
+
+ Undo a Work In Progress commit.
+
+## OPTIONS
+
+ None
+
+## EXAMPLES
+
+ Create a WIP commit which stores all changes in the working directory.
+
+ $ git wip
+
+ Later on, undo the commit and continue making changes.
+
+ $ git unwip
+
+## AUTHOR
+
+Written by Andrew Sullivan Cant <>
+
+## REPORTING BUGS
+
+<>
+
+## SEE ALSO
+
+<>
diff --git a/man/git-wip.1 b/man/git-wip.1
new file mode 100644
index 0000000..3d1e326
--- /dev/null
+++ b/man/git-wip.1
@@ -0,0 +1,31 @@
+.\" generated with Ronn-NG/v0.10.1
+.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
+.TH "GIT\-WIP" "1" "May 2025" "" "Git Extras"
+.SH "NAME"
+\fBgit\-wip\fR \- Create a Work In Progress commit
+.SH "SYNOPSIS"
+\fBgit\-wip\fR
+.SH "DESCRIPTION"
+Create a Work In Progress commit, include all files in the working directory\.
+.SH "OPTIONS"
+None
+.SH "EXAMPLES"
+Create a WIP commit which stores all changes in the working directory\.
+.IP "" 4
+.nf
+$ git wip
+.fi
+.IP "" 0
+.P
+Later on, undo the commit and continue making changes\.
+.IP "" 4
+.nf
+$ git unwip
+.fi
+.IP "" 0
+.SH "AUTHOR"
+Written by Andrew Sullivan Cant <\fImail@andrewsullivancant\.ca\fR>
+.SH "REPORTING BUGS"
+<\fIhttps://github\.com/tj/git\-extras/issues\fR>
+.SH "SEE ALSO"
+<\fIhttps://github\.com/tj/git\-extras\fR>
diff --git a/man/git-wip.html b/man/git-wip.html
new file mode 100644
index 0000000..b640255
--- /dev/null
+++ b/man/git-wip.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+ git-wip(1) - Create a Work In Progress commit
+
+
+
+
+
+
+
+
+
+ - git-wip(1)
+ - Git Extras
+ - git-wip(1)
+
+
+
+
+
NAME
+
+ git-wip - Create a Work In Progress commit
+
+
SYNOPSIS
+
+
git-wip
+
+
DESCRIPTION
+
+
Create a Work In Progress commit, include all files in the working directory.
+
+
OPTIONS
+
+
None
+
+
EXAMPLES
+
+
Create a WIP commit which stores all changes in the working directory.
+
+
$ git wip
+
+
+
Later on, undo the commit and continue making changes.
+
+
$ git unwip
+
+
+
AUTHOR
+
+
Written by Andrew Sullivan Cant <mail@andrewsullivancant.ca>
+
+
REPORTING BUGS
+
+
<https://github.com/tj/git-extras/issues>
+
+
SEE ALSO
+
+
<https://github.com/tj/git-extras>
+
+
+
+
+
+
diff --git a/man/git-wip.md b/man/git-wip.md
new file mode 100644
index 0000000..58bc19b
--- /dev/null
+++ b/man/git-wip.md
@@ -0,0 +1,36 @@
+git-wip(1) -- Create a Work In Progress commit
+================================
+
+## SYNOPSIS
+
+`git-wip`
+
+## DESCRIPTION
+
+ Create a Work In Progress commit, include all files in the working directory.
+
+## OPTIONS
+
+ None
+
+## EXAMPLES
+
+ Create a WIP commit which stores all changes in the working directory.
+
+ $ git wip
+
+ Later on, undo the commit and continue making changes.
+
+ $ git unwip
+
+## AUTHOR
+
+Written by Andrew Sullivan Cant <>
+
+## REPORTING BUGS
+
+<>
+
+## SEE ALSO
+
+<>
diff --git a/man/index.txt b/man/index.txt
index 5e98b17..4f24ad0 100644
--- a/man/index.txt
+++ b/man/index.txt
@@ -73,4 +73,6 @@ git-sync(1) git-sync
git-touch(1) git-touch
git-undo(1) git-undo
git-unlock(1) git-unlock
+git-unwip(1) git-unwip
git-utimes(1) git-utimes
+git-wip(1) git-wip
diff --git a/tests/poetry.lock b/tests/poetry.lock
index bb52a52..cef29c8 100644
--- a/tests/poetry.lock
+++ b/tests/poetry.lock
@@ -1,19 +1,21 @@
-# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "codespell"
-version = "2.2.0"
-description = "Codespell"
+version = "2.4.0"
+description = "Fix common misspellings in text files"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "codespell-2.2.0-py3-none-any.whl", hash = "sha256:3cc3fcb484a8302683add19e7d11504c79c79b10d4ea0675409417a044b27374"},
- {file = "codespell-2.2.0.tar.gz", hash = "sha256:3dce0cd1348d277f8d934d1d4dcbbf510f9ddfd1b9005e9b25fb983189962561"},
+ {file = "codespell-2.4.0-py3-none-any.whl", hash = "sha256:b4c5b779f747dd481587aeecb5773301183f52b94b96ed51a28126d0482eec1d"},
+ {file = "codespell-2.4.0.tar.gz", hash = "sha256:587d45b14707fb8ce51339ba4cce50ae0e98ce228ef61f3c5e160e34f681be58"},
]
[package.extras]
-dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"]
+dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"]
hard-encoding-detection = ["chardet"]
+toml = ["tomli"]
+types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"]
[[package]]
name = "colorama"
@@ -143,4 +145,4 @@ test = ["pytest"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
-content-hash = "e730a1e6e7fd2f51858e8c8cfa8b56d606eadd7a0a629ca8af078904721a5159"
+content-hash = "fcf0ba3dd6655735da5c1e21ba32b94011d32bc5a30fbdaf31146d3820c1442a"
diff --git a/tests/pyproject.toml b/tests/pyproject.toml
index f04e61d..3d017cb 100644
--- a/tests/pyproject.toml
+++ b/tests/pyproject.toml
@@ -16,7 +16,7 @@ gitpython = "3.1.43"
testpath = "0.6.0"
[tool.poetry.group.dev.dependencies]
-codespell = "2.2"
+codespell = "2.4"
[tool.pytest.ini_options]
minversion = "7.4"
From 3ddc315a76a0612c2122f3c73c00729f9f8869f9 Mon Sep 17 00:00:00 2001
From: Edwin Kofler
Date: Thu, 5 Jun 2025 20:10:50 -0700
Subject: [PATCH 38/62] 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
---
.github/workflows/ci.yml | 2 +
bin/git-abort | 6 +-
bin/git-bulk | 26 ++--
bin/git-clear | 4 +-
bin/git-clear-soft | 2 +-
bin/git-ignore | 20 +--
bin/git-merge-into | 2 +-
bin/git-paste | 2 +-
bin/git-psykorebase | 2 +-
bin/git-pull-request | 2 +-
bin/git-rebase-patch | 2 +-
bin/git-release | 2 +-
bin/git-setup | 2 +-
bin/git-standup | 2 +-
bin/git-summary | 10 +-
scripts/checkstyle.py | 262 +++++++++++++++++++++++++++++++++++++++
16 files changed, 306 insertions(+), 42 deletions(-)
create mode 100755 scripts/checkstyle.py
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 348c4ed..0d5c32f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,6 +36,8 @@ jobs:
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
- name: Lint and format Python with Ruff
diff --git a/bin/git-abort b/bin/git-abort
index 201fa48..af1712b 100755
--- a/bin/git-abort
+++ b/bin/git-abort
@@ -2,7 +2,7 @@
set -euo pipefail
-function discover_op() {
+discover_op() {
local gitdir
# 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
@@ -14,7 +14,7 @@ function discover_op() {
done
}
-function validate_op() {
+validate_op() {
local op="$1"
if [ -z "$op" ]; then
echo "No active operation found" >&2
@@ -26,7 +26,7 @@ function validate_op() {
fi
}
-function discover_action() {
+discover_action() {
local action=${1/git-/}
if [ "$action" != "abort" ] && [ "$action" != "continue" ]; then
echo "Invalid action: $1" >&2
diff --git a/bin/git-bulk b/bin/git-bulk
index 1f414ea..06eecd2 100755
--- a/bin/git-bulk
+++ b/bin/git-bulk
@@ -30,7 +30,7 @@ cdfail() {
}
# add another workspace to global git config
-function addworkspace {
+addworkspace() {
git config --global bulkworkspaces."$wsname" "$wsdir";
if [ -n "$source" ]; then
if [ ! -d "$wsdir" ]; then echo 1>&2 "Path of workspace doesn't exist, make it first."; exit 1; fi
@@ -59,19 +59,19 @@ function addworkspace {
}
# add current directory
-function addcurrent { git config --global bulkworkspaces."$wsname" "$PWD"; }
+addcurrent() { git config --global bulkworkspaces."$wsname" "$PWD"; }
# 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
-function purge { git config --global --remove-section bulkworkspaces; }
+purge() { git config --global --remove-section bulkworkspaces; }
# 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
-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
@@ -88,7 +88,7 @@ function guardedExecution () {
}
# 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
echo 1>&2 "Core command \"$corecommand\" accepted."
else
@@ -101,7 +101,7 @@ function checkGitCommand () {
}
# check if workspace name is registered
-function checkWSName () {
+checkWSName () {
while read -r workspace; do
parseWsName "$workspace"
if [[ $rwsname == "$wsname" ]]; then return; fi
@@ -111,7 +111,7 @@ function checkWSName () {
}
# parse out wsname from workspacespec
-function parseWsName () {
+parseWsName () {
local wsspec="$1"
# Get the workspace value from its specification in the `.gitconfig`.
# May be an absolute path or a variable name of the form: `$VARNAME`
@@ -128,7 +128,7 @@ function parseWsName () {
}
# detects the wsname of the current directory
-function wsnameToCurrent () {
+wsnameToCurrent () {
while read -r workspace; do
if [ -z "$workspace" ]; then continue; fi
parseWsName "$workspace"
@@ -140,7 +140,7 @@ function wsnameToCurrent () {
}
# helper to check number of arguments.
-function allowedargcount () {
+allowedargcount () {
if [ "$paramcount" -ne "${1:-0}" ] && [ "$paramcount" -ne "${2:-0}" ]; then
echo 1>&2 "error: wrong number of arguments" && usage;
exit 1;
@@ -148,7 +148,7 @@ function allowedargcount () {
}
# execute the bulk operation
-function executBulkOp () {
+executBulkOp () {
checkGitCommand
if ! $allwsmode && ! $singlemode; then wsnameToCurrent; fi # by default git bulk works within the 'current' workspace
listall | while read -r workspacespec; do
@@ -194,7 +194,7 @@ while [ "${#}" -ge 1 ] ; do
--listall|--purge)
butilcommand="${1:2}" && break ;;
--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)
diff --git a/bin/git-clear b/bin/git-clear
index 0c8cbd1..6fd3ae8 100755
--- a/bin/git-clear
+++ b/bin/git-clear
@@ -2,7 +2,7 @@
PROGNAME="git-clear"
FORCE=0
-function _usage() {
+_usage() {
cat << EOF
usage: $PROGNAME options
usage: $PROGNAME -h|help|?
@@ -38,6 +38,6 @@ else
clean=y
fi
-if [ "$clean" == "y" ]; then
+if [ "$clean" = "y" ]; then
git clean -d -f -x && git reset --hard
fi
diff --git a/bin/git-clear-soft b/bin/git-clear-soft
index 0473b57..1274d21 100755
--- a/bin/git-clear-soft
+++ b/bin/git-clear-soft
@@ -2,6 +2,6 @@
echo -n "Sure? - This command may delete files that cannot be recovered. Files and directories in .gitignore will be preserved [y/N]: "
read -r answer
-if [ "$answer" == "y" ]
+if [ "$answer" = "y" ]
then git clean -d -f && git reset --hard
fi
diff --git a/bin/git-ignore b/bin/git-ignore
index e380a2e..34a1791 100755
--- a/bin/git-ignore
+++ b/bin/git-ignore
@@ -2,7 +2,7 @@
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
-function show_contents {
+show_contents() {
local file="${2/#~/$HOME}"
if [ -f "$file" ]; then
echo "$1 gitignore: $2" && cat "$file"
@@ -11,7 +11,7 @@ function show_contents {
fi
}
-function cd_to_git_root {
+cd_to_git_root() {
local error_level="$1"
if ! git rev-parse --git-dir &>/dev/null; then
@@ -29,7 +29,7 @@ function cd_to_git_root {
fi
}
-function global_ignore() {
+global_ignore() {
if ! git config --global core.excludesFile 2>/dev/null; then
if [ -f "$HOME/.gitignore" ]; then
echo "$HOME/.gitignore"
@@ -39,11 +39,11 @@ function global_ignore() {
fi
}
-function show_global {
+show_global() {
show_contents Global "$(global_ignore)"
}
-function add_global {
+add_global() {
local global_gitignore
global_gitignore="$(global_ignore)"
if [ -z "$global_gitignore" ]; then
@@ -56,28 +56,28 @@ function add_global {
fi
}
-function show_local {
+show_local() {
cd_to_git_root --warn
show_contents Local .gitignore
}
-function add_local {
+add_local() {
cd_to_git_root --warn
add_patterns .gitignore "$@"
}
-function show_private {
+show_private() {
cd_to_git_root --error
show_contents Private "${GIT_DIR}/info/exclude"
}
-function add_private {
+add_private() {
cd_to_git_root --error
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
add_patterns "${GIT_DIR}/info/exclude" "$@"
}
-function add_patterns {
+add_patterns() {
echo "Adding pattern(s) to: $1"
local file="${1/#~/$HOME}"
dir_name=$(dirname "$file")
diff --git a/bin/git-merge-into b/bin/git-merge-into
index 0561a8d..e547555 100755
--- a/bin/git-merge-into
+++ b/bin/git-merge-into
@@ -18,7 +18,7 @@ then
git stash
fi
-if [ "${!#}" == '--ff-only' ]; then
+if [ "${!#}" = '--ff-only' ]; then
case $# in
2 ) # dest --ff
git push "$(git rev-parse --show-toplevel)" "$cur_branch":"$1";;
diff --git a/bin/git-paste b/bin/git-paste
index 33643dd..173367c 100755
--- a/bin/git-paste
+++ b/bin/git-paste
@@ -2,7 +2,7 @@
set -e
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"
exit 1
fi
diff --git a/bin/git-psykorebase b/bin/git-psykorebase
index b3e1a7c..a5e7577 100755
--- a/bin/git-psykorebase
+++ b/bin/git-psykorebase
@@ -5,7 +5,7 @@ SECONDARY_BRANCH=""
FF="--ff"
CONTINUE="no"
-function current_branch() {
+current_branch() {
git rev-parse --abbrev-ref HEAD
}
diff --git a/bin/git-pull-request b/bin/git-pull-request
index 3bd2060..e31de4f 100755
--- a/bin/git-pull-request
+++ b/bin/git-pull-request
@@ -43,7 +43,7 @@ if [ -z "$remote" ]; then
echo 'no upstream found, push to origin as default'
remote="origin"
fi
-[ "$remote" == "." ] && abort "the upstream should be a remote branch."
+[ "$remote" = "." ] && abort "the upstream should be a remote branch."
# make sure it's pushed
diff --git a/bin/git-rebase-patch b/bin/git-rebase-patch
index 395304e..521d5fd 100755
--- a/bin/git-rebase-patch
+++ b/bin/git-rebase-patch
@@ -30,7 +30,7 @@ do
GIT_INDEX_FILE=$index git read-tree "$rev"
# 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=$?
# Do it again, but show the error, if the problem is the patch itself.
diff --git a/bin/git-release b/bin/git-release
index 676495d..8df102b 100755
--- a/bin/git-release
+++ b/bin/git-release
@@ -119,7 +119,7 @@ if test $# -gt 0; then
fi
declare -a sign_args
- if [ "$sign" == true ]; then
+ if [ "$sign" = true ]; then
sign_args=("-s")
fi
diff --git a/bin/git-setup b/bin/git-setup
index d1319ec..2a8c82d 100755
--- a/bin/git-setup
+++ b/bin/git-setup
@@ -2,7 +2,7 @@
COMMIT_MESSAGE='Initial commit'
-if [ "$1" == "-m" ]; then
+if [ "$1" = "-m" ]; then
COMMIT_MESSAGE=$2
shift; shift
fi
diff --git a/bin/git-standup b/bin/git-standup
index c0787f5..89f8afe 100755
--- a/bin/git-standup
+++ b/bin/git-standup
@@ -35,7 +35,7 @@ in_git_repo=$?
# Use colors, but only if connected to a terminal, and that terminal
# supports them.
-if command -v tput >/dev/null 2>&1; then
+if command -v tput &>/dev/null; then
ncolors=$(tput colors)
fi
if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
diff --git a/bin/git-summary b/bin/git-summary
index b69a9ae..3dbe1b6 100755
--- a/bin/git-summary
+++ b/bin/git-summary
@@ -206,10 +206,10 @@ COLUMN_CMD_DELIMTER="¬" # Hopefully, this symbol is not used in branch names...
SP="$COLUMN_CMD_DELIMTER|"
print_summary_by_line() {
- if [ "$OUTPUT_STYLE" == "tabular" ]; then
+ 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
+ elif [ "$OUTPUT_STYLE" = "oneline" ]; then
echo "$project / lines: $(line_count "${paths[@]}")"
elif [ -n "$SUMMARY_BY_LINE" ]; then
echo
@@ -221,10 +221,10 @@ print_summary_by_line() {
}
print_summary() {
- if [ "$OUTPUT_STYLE" == "tabular" ]; then
+ 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
+ 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
@@ -236,7 +236,7 @@ print_summary() {
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
+ if [ "$commit" = "HEAD" ]; then
echo " files : $(file_count)"
fi
echo " uncommitted : $(uncommitted_changes_count)"
diff --git a/scripts/checkstyle.py b/scripts/checkstyle.py
new file mode 100755
index 0000000..6e2d3dd
--- /dev/null
+++ b/scripts/checkstyle.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python3
+import re
+import os
+import argparse
+from pathlib import Path
+from typing import Callable, List, Dict, Any # compat
+
+# This file checks Bash and Shell scripts for violations not found with
+# shellcheck or existing methods. You can use it in several ways:
+#
+# Lint all .bash, .sh, .bats files along with 'bin/asdf' and print out violations:
+# $ ./scripts/checkstyle.py
+#
+# The former, but also fix all violations. This must be ran until there
+# are zero violations since any line can have more than one violation:
+# $ ./scripts/checkstyle.py --fix
+#
+# Lint a particular file:
+# $ ./scripts/checkstyle.py ./lib/functions/installs.bash
+#
+# Check to ensure all regular expressions are working as intended:
+# $ ./scripts/checkstyle.py --internal-test-regex
+
+Rule = Dict[str, Any]
+
+class c:
+ RED = '\033[91m'
+ GREEN = '\033[92m'
+ YELLOW = '\033[93m'
+ BLUE = '\033[94m'
+ MAGENTA = '\033[95m'
+ CYAN = '\033[96m'
+ RESET = '\033[0m'
+ BOLD = '\033[1m'
+ UNDERLINE = '\033[4m'
+ LINK: Callable[[str, str], str] = lambda href, text: f'\033]8;;{href}\a{text}\033]8;;\a'
+
+def utilGetStrs(line: Any, m: Any):
+ return (
+ line[0:m.start('match')],
+ line[m.start('match'):m.end('match')],
+ line[m.end('match'):]
+ )
+
+# Before: printf '%s\\n' '^w^'
+# After: printf '%s\n' '^w^'
+def noDoubleBackslashFixer(line: str, m: Any) -> str:
+ prestr, midstr, poststr = utilGetStrs(line, m)
+
+ return f'{prestr}{midstr[1:]}{poststr}'
+
+# Before: $(pwd)
+# After: $PWD
+def noPwdCaptureFixer(line: str, m: Any) -> str:
+ prestr, _, poststr = utilGetStrs(line, m)
+
+ return f'{prestr}$PWD{poststr}'
+
+# Before: [ a == b ]
+# After: [ a = b ]
+def noTestDoubleEqualsFixer(line: str, m: Any) -> str:
+ prestr, _, poststr = utilGetStrs(line, m)
+
+ return f'{prestr}={poststr}'
+
+# Before: function fn() { ...
+# After: fn() { ...
+# ---
+# Before: function fn { ...
+# After fn() { ...
+def noFunctionKeywordFixer(line: str, m: Any) -> str:
+ prestr, midstr, poststr = utilGetStrs(line, m)
+
+ midstr = midstr.strip()
+ midstr = midstr[len('function'):]
+ midstr = midstr.strip()
+
+ parenIdx = midstr.find('(')
+ if parenIdx != -1:
+ midstr = midstr[:parenIdx]
+
+ return f'{prestr}{midstr}() {poststr}'
+
+# Before: >/dev/null 2>&1
+# After: &>/dev/null
+# ---
+# Before: 2>/dev/null 1>&2
+# After: &>/dev/null
+def noVerboseRedirectionFixer(line: str, m: Any) -> str:
+ prestr, _, poststr = utilGetStrs(line, m)
+
+ return f'{prestr}&>/dev/null{poststr}'
+
+def lintfile(file: Path, rules: List[Rule], options: Dict[str, Any]):
+ content_arr = file.read_text().split('\n')
+
+ for line_i, line in enumerate(content_arr):
+ if 'checkstyle-ignore' in line:
+ continue
+
+ for rule in rules:
+ should_run = False
+ if 'sh' in rule['fileTypes']:
+ if file.name.endswith('.sh'):
+ should_run = True
+ if 'bash' in rule['fileTypes']:
+ if file.name.endswith('.bash') or file.name.endswith('.bats') or file.name.startswith('git-'):
+ should_run = True
+
+ if options['verbose']:
+ print(f'{str(file)}: {should_run}')
+
+ if not should_run:
+ continue
+
+ m = re.search(rule['regex'], line)
+ if m is not None and m.group('match') is not None:
+ dir = os.path.relpath(file.resolve(), Path.cwd())
+ prestr = line[0:m.start('match')]
+ midstr = line[m.start('match'):m.end('match')]
+ poststr = line[m.end('match'):]
+
+ print(f'{c.CYAN}{dir}{c.RESET}:{line_i + 1}')
+ print(f'{c.MAGENTA}{rule["name"]}{c.RESET}: {rule["reason"]}')
+ print(f'{prestr}{c.RED}{midstr}{c.RESET}{poststr}')
+ print()
+
+ if options['fix']:
+ content_arr[line_i] = rule['fixerFn'](line, m)
+
+ rule['found'] += 1
+
+ if options['fix']:
+ file.write_text('\n'.join(content_arr))
+
+def main():
+ rules: List[Rule] = [
+ {
+ 'name': 'no-pwd-capture',
+ 'regex': '(?P\\$\\(pwd\\))',
+ 'reason': '$PWD is essentially equivalent to $(pwd) without the overhead of a subshell',
+ 'fileTypes': ['bash', 'sh'],
+ 'fixerFn': noPwdCaptureFixer,
+ 'testPositiveMatches': [
+ '$(pwd)'
+ ],
+ 'testNegativeMatches': [
+ '$PWD'
+ ],
+ },
+ {
+ 'name': 'no-test-double-equals',
+ 'regex': '(?==).*?]',
+ 'reason': 'Disallow double equals in places where they are not necessary for consistency',
+ 'fileTypes': ['bash', 'sh'],
+ 'fixerFn': noTestDoubleEqualsFixer,
+ 'testPositiveMatches': [
+ '[ a == b ]',
+ '[ "${lines[0]}" == blah ]',
+ ],
+ 'testNegativeMatches': [
+ '[ a = b ]',
+ '[[ a = b ]]',
+ '[[ a == b ]]',
+ '[ a = b ] || [[ a == b ]]',
+ '[[ a = b ]] || [[ a == b ]]',
+ '[[ "${lines[0]}" == \'usage: \'* ]]',
+ '[ "${lines[0]}" = blah ]',
+ ],
+ },
+ {
+ 'name': 'no-function-keyword',
+ 'regex': '^[ \\t]*(?Pfunction .*?(?:\\([ \\t]*\\))?[ \\t]*){',
+ 'reason': 'Only allow functions declared like `fn_name() {{ :; }}` for consistency (see ' + c.LINK('https://www.shellcheck.net/wiki/SC2113', 'ShellCheck SC2113') + ')',
+ 'fileTypes': ['bash', 'sh'],
+ 'fixerFn': noFunctionKeywordFixer,
+ 'testPositiveMatches': [
+ 'function fn() { :; }',
+ 'function fn { :; }',
+ ],
+ 'testNegativeMatches': [
+ 'fn() { :; }',
+ ],
+ },
+ {
+ 'name': 'no-verbose-redirection',
+ 'regex': '(?P(>/dev/null 2>&1|2>/dev/null 1>&2))',
+ 'reason': 'Use `&>/dev/null` instead of `>/dev/null 2>&1` or `2>/dev/null 1>&2` for consistency',
+ 'fileTypes': ['bash'],
+ 'fixerFn': noVerboseRedirectionFixer,
+ 'testPositiveMatches': [
+ 'echo woof >/dev/null 2>&1',
+ 'echo woof 2>/dev/null 1>&2',
+ ],
+ 'testNegativeMatches': [
+ 'echo woof &>/dev/null',
+ 'echo woof >&/dev/null',
+ ],
+ },
+ ]
+ [rule.update({ 'found': 0 }) for rule in rules]
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('files', metavar='FILES', nargs='*')
+ parser.add_argument('--fix', action='store_true')
+ parser.add_argument('--verbose', action='store_true')
+ parser.add_argument('--internal-test-regex', action='store_true')
+ args = parser.parse_args()
+
+ if args.internal_test_regex:
+ for rule in rules:
+ for positiveMatch in rule['testPositiveMatches']:
+ m: Any = re.search(rule['regex'], positiveMatch)
+ if m is None or m.group('match') is None:
+ print(f'{c.MAGENTA}{rule["name"]}{c.RESET}: Failed {c.CYAN}positive{c.RESET} test:')
+ print(f'=> {positiveMatch}')
+ print()
+
+ for negativeMatch in rule['testNegativeMatches']:
+ m: Any = re.search(rule['regex'], negativeMatch)
+ if m is not None and m.group('match') is not None:
+ print(f'{c.MAGENTA}{rule["name"]}{c.RESET}: Failed {c.YELLOW}negative{c.RESET} test:')
+ print(f'=> {negativeMatch}')
+ print()
+ print('Done.')
+ return
+
+ options = {
+ 'fix': args.fix,
+ 'verbose': args.verbose,
+ }
+
+ # parse files and print matched lints
+ if len(args.files) > 0:
+ for file in args.files:
+ p = Path(file)
+ if p.is_file():
+ lintfile(p, rules, options)
+ else:
+ for file in Path.cwd().glob('**/*'):
+ if '.git' in str(file.absolute()):
+ continue
+
+ if file.is_file():
+ lintfile(file, rules, options)
+
+ # print final results
+ print(f'{c.UNDERLINE}TOTAL ISSUES{c.RESET}')
+ for rule in rules:
+ print(f'{c.MAGENTA}{rule["name"]}{c.RESET}: {rule["found"]}')
+
+ grand_total = sum([rule['found'] for rule in rules])
+ print(f'GRAND TOTAL: {grand_total}')
+ print(f'{c.BOLD}{c.YELLOW}NOTE:{c.RESET} Run "./scripts/checkstyle.py --fix" to automatically fix all issues (may need to run multiple times)')
+
+ # exit
+ if grand_total == 0:
+ exit(0)
+ else:
+ exit(2)
+
+main()
From 13cbdfc51e22e635939d07af86b33fca78c45411 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BD=97=E6=B3=BD=E8=BD=A9?=
Date: Fri, 20 Jun 2025 11:12:58 +0800
Subject: [PATCH 39/62] Version 7.4.0 (#1206)
Signed-off-by: spacewander
---
AUTHORS | 6 +++++-
History.md | 30 ++++++++++++++++++++++++++++++
bin/git-extras | 2 +-
3 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index a53a2c8..63915c5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -40,11 +40,14 @@ Patches and Suggestions
- Luke Childs
- Sasha Khamkov
- equt
+- oikarinen
- vyas
- Don Harper
+- Pierre Ayoub
- Robin Winslow
- Ross Smith II
- Yi EungJun
+- dependabot[bot]
- grindhold
- wyattscarpenter
- Aggelos Orfanakos
@@ -109,7 +112,6 @@ Patches and Suggestions
- Wil Moore III
- William Montgomery
- Ye Lin Aung
-- dependabot[bot]
- luozexuan
- roxchgt
- soffolk
@@ -129,6 +131,7 @@ Patches and Suggestions
- Andrew Griffiths
- Andrew Marcinkevičius
- Andrew Starr-Bochicchio
+- Andrew Sullivan Cant
- Andrey Elizarov
- Angel Aguilera
- Antoine Beaupré
@@ -165,6 +168,7 @@ Patches and Suggestions
- George Crabtree
- Gerrit-K
- Greg Allen
+- Guilhem Saurel
- Guillermo Rauch
- Gunnlaugur Thor Briem
- Hasse Ramlev Hansen
diff --git a/History.md b/History.md
index 8046bf2..39e0235 100644
--- a/History.md
+++ b/History.md
@@ -1,4 +1,34 @@
+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
==================
diff --git a/bin/git-extras b/bin/git-extras
index bda96a6..686ed81 100755
--- a/bin/git-extras
+++ b/bin/git-extras
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-VERSION="7.4.0-dev"
+VERSION="7.4.0"
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
update() {
From 215382b12d0ece199224300ec8513b658093aa8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BD=97=E6=B3=BD=E8=BD=A9?=
Date: Mon, 23 Jun 2025 11:08:27 +0800
Subject: [PATCH 40/62] Bump version to 7.5.0-dev (#1207)
Signed-off-by: spacewander
---
bin/git-extras | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/git-extras b/bin/git-extras
index 686ed81..2ada627 100755
--- a/bin/git-extras
+++ b/bin/git-extras
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-VERSION="7.4.0"
+VERSION="7.5.0-dev"
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
update() {
From 5c9a7a2533d138360609a7a47c5bb65675e2d2bd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 18 Aug 2025 11:15:38 +0800
Subject: [PATCH 41/62] 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]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0d5c32f..8f975f3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code.
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
fetch-depth: 0
- name: 'Get Changed Files'
@@ -47,7 +47,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code.
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install poetry
run: pip install poetry
- name: Set up Python
@@ -71,7 +71,7 @@ jobs:
name: 'Test with Pytest'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- name: Install poetry
@@ -96,7 +96,7 @@ jobs:
name: 'Test with Bats'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- name: Setup Bats
@@ -120,7 +120,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- name: Check out code.
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Linux Install
if: matrix.platform == 'ubuntu-latest'
run: sudo apt-get install -y bsdmainutils
From 14586c7da045db6b100022e54278e9a49657d5bf Mon Sep 17 00:00:00 2001
From: Edwin Kofler
Date: Fri, 29 Aug 2025 03:27:55 -0700
Subject: [PATCH 42/62] Fix use of `git-whatchanged` to `git-log` (#1212)
---
bin/git-utimes | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/bin/git-utimes b/bin/git-utimes
index 9955a9d..f0dc1a4 100755
--- a/bin/git-utimes
+++ b/bin/git-utimes
@@ -34,10 +34,10 @@ fi
status_opts=(--porcelain --short)
# %ct: committer date, UNIX timestamp / %at: author date, UNIX timestamp
-whatchanged_opts=(--format='%ct')
+log_opts=(--format='%ct')
if git status --help 2>&1 | grep -q -- "--no-renames"; then
status_opts+=(--no-renames)
- whatchanged_opts+=(--no-renames)
+ log_opts+=(--no-renames)
fi
if git status --help 2>&1 | grep -q -- "--untracked-files"; then
status_opts+=(--untracked-files=no)
@@ -126,6 +126,6 @@ git --no-pager status "${status_opts[@]}" . \
| cut -c 4- >"${tmpfile}"
# prefix is not stripped:
-git --no-pager whatchanged "${whatchanged_opts[@]}" . \
+git --no-pager log --raw --no-merges "${log_opts[@]}" . \
| awk "${awk_flags[@]}" "${awk_script}" "${tmpfile}" - \
| BASH_ENV='' bash "${bash_opts[@]}" -
From cf6d40d446e73c1047a26b907e2f82f9e545c6fd Mon Sep 17 00:00:00 2001
From: Daniele Paolella <53744340+danpaolella@users.noreply.github.com>
Date: Fri, 29 Aug 2025 12:29:31 +0200
Subject: [PATCH 43/62] 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
---
etc/bash_completion.sh | 70 +++++++++++++++++++++++++-----------------
1 file changed, 42 insertions(+), 28 deletions(-)
diff --git a/etc/bash_completion.sh b/etc/bash_completion.sh
index 7a6360a..3139c57 100644
--- a/etc/bash_completion.sh
+++ b/etc/bash_completion.sh
@@ -1,6 +1,22 @@
# shellcheck shell=bash
# bash completion support for git-extras.
+_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(){
local s_opts=( '-a' '-l' '-t' '-f' '-s' '-n' '-p' '-x' '-h' '?' )
local l_opts=(
@@ -22,14 +38,6 @@ _git_changelog(){
__gitcomp "$merged_opts_str"
}
-_git_authors(){
- __gitcomp "-l --list --no-email"
-}
-
-_git_brv(){
- __gitcomp "-r --reverse"
-}
-
_git_coauthor(){
local oldIfs=$IFS
IFS=$'\n'
@@ -136,6 +144,10 @@ _git_ignore(){
esac
}
+_git_info(){
+ __gitcomp "--color -c --no-config"
+}
+
_git_missing(){
# Suggest all known refs
__gitcomp "$(git for-each-ref --format='%(refname:short)')"
@@ -158,38 +170,40 @@ _git_reauthor(){
__gitcomp "${comp}"
}
-_git_scp(){
- __git_complete_remote_or_refspec
+__git_extras_rename(){
+ if ((COMP_CWORD == 2 || COMP_CWORD == 3)); then
+ __gitcomp "$1"
+ fi
}
-_git_stamp(){
- __gitcomp '--replace -r'
+_git_rename_branch(){
+ __git_extras_rename "$(__git_heads)"
+}
+
+_git_rename_remote(){
+ __git_extras_rename "$(__git_remotes)"
+}
+
+_git_rename_tag(){
+ __git_extras_rename "$(__git_tags)"
}
_git_rscp(){
__git_complete_remote_or_refspec
}
+_git_scp(){
+ __git_complete_remote_or_refspec
+}
+
_git_squash(){
__gitcomp "$(__git_heads)"
}
+_git_stamp(){
+ __gitcomp '--replace -r'
+}
+
_git_undo(){
__gitcomp "--hard --soft -h -s"
}
-
-_git_info(){
- __gitcomp "--color -c --no-config"
-}
-
-_git_browse(){
- __git_complete_remote_or_refspec
-}
-
-_git_browse_ci(){
- __git_complete_remote_or_refspec
-}
-
-_git_rename_file() {
- __gitcomp "-h --help"
-}
From b57eb8b92a5e58d25f8a2b700edec4e2bab517a4 Mon Sep 17 00:00:00 2001
From: Daniele Paolella <53744340+danpaolella@users.noreply.github.com>
Date: Thu, 4 Sep 2025 04:51:48 +0200
Subject: [PATCH 44/62] 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
---
man/git-rename-branch.1 | 23 ++++++++---------------
man/git-rename-branch.html | 16 +++++++---------
man/git-rename-branch.md | 10 +++++-----
3 files changed, 20 insertions(+), 29 deletions(-)
diff --git a/man/git-rename-branch.1 b/man/git-rename-branch.1
index 8ada6b5..6129b43 100644
--- a/man/git-rename-branch.1
+++ b/man/git-rename-branch.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "GIT\-RENAME\-BRANCH" "1" "July 2019" "" "Git Extras"
+.TH "GIT\-RENAME\-BRANCH" "1" "September 2025" "" "Git Extras"
.
.SH "NAME"
\fBgit\-rename\-branch\fR \- rename local branch and push to remote
@@ -10,26 +10,19 @@
\fBgit\-rename\-branch\fR
.
.SH "DESCRIPTION"
-.
-.nf
-
Rename local branch and push the new branch to remote
.
-.fi
-.
.SH "OPTIONS"
+
.
-.nf
-
-<old\-branch>
-
+.P
Old branch whose has to be renamed\. This is an optional parameter\. If no value is supplied then the current branch will be renamed\.
-
-<new\-branch>
-
-New branch name
.
-.fi
+.P
+
+.
+.P
+New branch name
.
.SH "EXAMPLES"
.
diff --git a/man/git-rename-branch.html b/man/git-rename-branch.html
index f757aa4..5e22b78 100644
--- a/man/git-rename-branch.html
+++ b/man/git-rename-branch.html
@@ -80,19 +80,17 @@
DESCRIPTION
-Rename local branch and push the new branch to remote
-
+ Rename local branch and push the new branch to remote
OPTIONS
-<old-branch>
+ <old-branch>
-Old branch whose has to be renamed. This is an optional parameter. If no value is supplied then the current branch will be renamed.
+ Old branch whose has to be renamed. This is an optional parameter. If no value is supplied then the current branch will be renamed.
-<new-branch>
+ <new-branch>
-New branch name
-
+ New branch name
EXAMPLES
@@ -103,7 +101,7 @@ $ git rename-branch new-name
AUTHOR
-Written by Hozefa Jodiawalla <hozefarules@gmail.com>
+Written by Hozefa Jodiawalla <hozefarules@gmail.com>
REPORTING BUGS
@@ -116,7 +114,7 @@ $ git rename-branch new-name
diff --git a/man/git-rename-branch.md b/man/git-rename-branch.md
index 69e2d76..a85dbc4 100644
--- a/man/git-rename-branch.md
+++ b/man/git-rename-branch.md
@@ -7,17 +7,17 @@ git-rename-branch(1) -- rename local branch and push to remote
## DESCRIPTION
- Rename local branch and push the new branch to remote
+ Rename local branch and push the new branch to remote
## OPTIONS
- <old-branch>
+ <old-branch>
- Old branch whose has to be renamed. This is an optional parameter. If no value is supplied then the current branch will be renamed.
+ Old branch whose has to be renamed. This is an optional parameter. If no value is supplied then the current branch will be renamed.
- <new-branch>
+ <new-branch>
- New branch name
+ New branch name
## EXAMPLES
From 4d05955bc583d433a71e808d889a5a29eff6e060 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 8 Sep 2025 10:28:51 +0800
Subject: [PATCH 45/62] 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]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8f975f3..7d8afa4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,7 +51,7 @@ jobs:
- name: Install poetry
run: pip install poetry
- name: Set up Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
cache: 'poetry'
cache-dependency-path: "tests/pyproject.toml"
@@ -77,7 +77,7 @@ jobs:
- name: Install poetry
run: pip install poetry
- name: Set up Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: 'poetry'
From 1f1320f4d6aa079fb5954cc7e4329f81a15b6d12 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 8 Sep 2025 10:29:28 +0800
Subject: [PATCH 46/62] 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]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7d8afa4..7a53c5b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,7 +26,7 @@ jobs:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BEFORE_SHA: "${{ github.event.before }}"
- - uses: 'actions/setup-go@v5'
+ - uses: 'actions/setup-go@v6'
with:
go-version: '1.20'
- name: 'Install EditorConfig Lint'
From eb34655aea91c4fcd282797d16aa46fc23cd63c1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 8 Sep 2025 10:29:50 +0800
Subject: [PATCH 47/62] 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]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/stale.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml
index 3c6a1d2..ff2fc42 100644
--- a/.github/workflows/stale.yaml
+++ b/.github/workflows/stale.yaml
@@ -11,7 +11,7 @@ jobs:
runs-on: "ubuntu-latest"
if: github.repository_owner == 'tj'
steps:
- - uses: "actions/stale@v9"
+ - uses: "actions/stale@v10"
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
From daf9d4148e8a97418a1fc1b9f22d2a500bdf254d Mon Sep 17 00:00:00 2001
From: Hugo Ruiz-Mireles
Date: Tue, 21 Oct 2025 20:49:27 -0700
Subject: [PATCH 48/62] 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.
---
bin/git-magic | 2 +-
man/git-magic.1 | 173 ++++++++++-------------
man/git-magic.html | 334 ++++++++++++++++++++++-----------------------
man/git-magic.md | 2 +-
4 files changed, 240 insertions(+), 271 deletions(-)
diff --git a/bin/git-magic b/bin/git-magic
index 8bc159a..de1dbaf 100755
--- a/bin/git-magic
+++ b/bin/git-magic
@@ -30,7 +30,7 @@ while getopts "m:eapfh" arg; do
PUSH=true
;;
f)
- FORCE='-f'
+ FORCE='--force-with-lease'
;;
h)
echo "$USAGE"
diff --git a/man/git-magic.1 b/man/git-magic.1
index f2b1b48..fae6717 100644
--- a/man/git-magic.1
+++ b/man/git-magic.1
@@ -1,102 +1,71 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "GIT\-MAGIC" "1" "May 2023" "" "Git Extras"
-.
-.SH "NAME"
-\fBgit\-magic\fR \- Automate add/commit/push routines
-.
-.SH "SYNOPSIS"
-\fBgit\-magic\fR [\-a] [\-m \fImsg\fR] [\-e] [\-p] [\-f]
-.
-.SH "DESCRIPTION"
-Produces summary of changes for commit message from \fBgit status \-\-porcelain\fR output\. Commits staged changes with the generated commit message and opens editor to modify generated commit message optionally\. Also staging and pushing can be automated optionally\.
-.
-.SH "OPTIONS"
-\-a
-.
-.P
-Adds everything including untracked files\.
-.
-.P
-\-m \fImsg\fR
-.
-.P
-Use the given \fImsg\fR as the commit message\. If multiple \-m options are given, their values are concatenated as separate paragraphs\. Passed to git commit command\. The generated is appended to user\-given messages\.
-.
-.P
-\-e
-.
-.P
-This option lets you further edit the generated message\. Passed to git commit command\.
-.
-.P
-\-p
-.
-.P
-Runs \fBgit push\fR after commit\.
-.
-.P
-\-f
-.
-.P
-Adds \fB\-f\fR option to \fBgit push\fR command\.
-.
-.P
-\-h
-.
-.P
-Prints synopsis\.
-.
-.SH "EXAMPLES"
-This example stages all changes then commits with automatic commit message\.
-.
-.IP "" 4
-.
-.nf
-
-$ git magic \-a
-[feature/magic dc2a11e] A man/git\-magic\.md
- 1 file changed, 37 insertions(+)
- create mode 100644 man/git\-auto\.md
-# git log
-Author: overengineer <54alpersaid@gmail\.com>
-Date: Thu Sep 30 20:14:22 2021 +0300
-
- M man/git\-magic\.md
-.
-.fi
-.
-.IP "" 0
-.
-.P
-\fB\-m\fR option PREPENDS generated message\.
-.
-.IP "" 4
-.
-.nf
-
-$ git magic \-am "Added documentation for git magic"
-[feature/magic dc2a11e] Added documentation for git magic
- 1 file changed, 42 insertions(+), 0 deletions(\-)
- create mode 100644 A man/git\-auto\.md
-$ git log
-Author: overengineer <54alpersaid@gmail\.com>
-Date: Thu Sep 30 20:14:22 2021 +0300
-
- Added documentation for git magic
-
- M man/git\-magic\.md
-.
-.fi
-.
-.IP "" 0
-.
-.SH "AUTHOR"
-Written by Alper S\. Soylu <54alpersaid@gmail\.com>
-.
-.SH "REPORTING BUGS"
-<\fIhttps://github\.com/tj/git\-extras/issues\fR>
-.
-.SH "SEE ALSO"
-<\fIhttps://github\.com/tj/git\-extras\fR>
+.\" generated with Ronn-NG/v0.10.1
+.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
+.TH "GIT\-MAGIC" "1" "September 2025" "" "Git Extras"
+.SH "NAME"
+\fBgit\-magic\fR \- Automate add/commit/push routines
+.SH "SYNOPSIS"
+\fBgit\-magic\fR [\-a] [\-m \fImsg\fR] [\-e] [\-p] [\-f]
+.SH "DESCRIPTION"
+Produces summary of changes for commit message from \fBgit status \-\-porcelain\fR output\. Commits staged changes with the generated commit message and opens editor to modify generated commit message optionally\. Also staging and pushing can be automated optionally\.
+.SH "OPTIONS"
+\-a
+.P
+Adds everything including untracked files\.
+.P
+\-m \fImsg\fR
+.P
+Use the given \fImsg\fR as the commit message\. If multiple \-m options are given, their values are concatenated as separate paragraphs\. Passed to git commit command\. The generated is appended to user\-given messages\.
+.P
+\-e
+.P
+This option lets you further edit the generated message\. Passed to git commit command\.
+.P
+\-p
+.P
+Runs \fBgit push\fR after commit\.
+.P
+\-f
+.P
+Adds \fB\-\-force\-with\-lease\fR option to \fBgit push\fR command for safer force pushing\.
+.P
+\-h
+.P
+Prints synopsis\.
+.SH "EXAMPLES"
+This example stages all changes then commits with automatic commit message\.
+.IP "" 4
+.nf
+$ git magic \-a
+[feature/magic dc2a11e] A man/git\-magic\.md
+ 1 file changed, 37 insertions(+)
+ create mode 100644 man/git\-auto\.md
+# git log
+Author: overengineer <54alpersaid@gmail\.com>
+Date: Thu Sep 30 20:14:22 2021 +0300
+
+ M man/git\-magic\.md
+.fi
+.IP "" 0
+.P
+\fB\-m\fR option PREPENDS generated message\.
+.IP "" 4
+.nf
+$ git magic \-am "Added documentation for git magic"
+[feature/magic dc2a11e] Added documentation for git magic
+ 1 file changed, 42 insertions(+), 0 deletions(\-)
+ create mode 100644 A man/git\-auto\.md
+$ git log
+Author: overengineer <54alpersaid@gmail\.com>
+Date: Thu Sep 30 20:14:22 2021 +0300
+
+ Added documentation for git magic
+
+ M man/git\-magic\.md
+.fi
+.IP "" 0
+.SH "AUTHOR"
+Written by Alper S\. Soylu \fI54alpersaid@gmail\.com\fR
+.SH "REPORTING BUGS"
+<\fIhttps://github\.com/tj/git\-extras/issues\fR>
+.SH "SEE ALSO"
+<\fIhttps://github\.com/tj/git\-extras\fR>
diff --git a/man/git-magic.html b/man/git-magic.html
index 90d668e..a9506e7 100644
--- a/man/git-magic.html
+++ b/man/git-magic.html
@@ -1,167 +1,167 @@
-
-
-
-
-
- git-magic(1) - Automate add/commit/push routines
-
-
-
-
-
-
-
-
-
- - git-magic(1)
- - Git Extras
- - git-magic(1)
-
-
-
NAME
-
- git-magic - Automate add/commit/push routines
-
-
-
SYNOPSIS
-
-
git-magic [-a] [-m msg] [-e] [-p] [-f]
-
-
DESCRIPTION
-
-
Produces summary of changes for commit message from git status --porcelain output.
-Commits staged changes with the generated commit message and
-opens editor to modify generated commit message optionally.
-Also staging and pushing can be automated optionally.
-
-
OPTIONS
-
-
-a
-
-
Adds everything including untracked files.
-
-
-m msg
-
-
Use the given msg as the commit message. If multiple -m options are given, their values are concatenated as separate paragraphs.
-Passed to git commit command. The generated is appended to user-given messages.
-
-
-e
-
-
This option lets you further edit the generated message.
-Passed to git commit command.
-
-
-p
-
-
Runs git push after commit.
-
-
-f
-
-
Adds -f option to git push command.
-
-
-h
-
-
Prints synopsis.
-
-
EXAMPLES
-
-
This example stages all changes then commits with automatic commit message.
-
-
$ git magic -a
-[feature/magic dc2a11e] A man/git-magic.md
- 1 file changed, 37 insertions(+)
- create mode 100644 man/git-auto.md
-# git log
-Author: overengineer <54alpersaid@gmail.com>
-Date: Thu Sep 30 20:14:22 2021 +0300
-
- M man/git-magic.md
-
-
-
-m option PREPENDS generated message.
-
-
$ git magic -am "Added documentation for git magic"
-[feature/magic dc2a11e] Added documentation for git magic
- 1 file changed, 42 insertions(+), 0 deletions(-)
- create mode 100644 A man/git-auto.md
-$ git log
-Author: overengineer <54alpersaid@gmail.com>
-Date: Thu Sep 30 20:14:22 2021 +0300
-
- Added documentation for git magic
-
- M man/git-magic.md
-
-
-
AUTHOR
-
-
Written by Alper S. Soylu <54alpersaid@gmail.com>
-
-
REPORTING BUGS
-
-
<https://github.com/tj/git-extras/issues>
-
-
SEE ALSO
-
-
<https://github.com/tj/git-extras>
-
-
-
-
-
-
-
+
+
+
+
+
+ git-magic(1) - Automate add/commit/push routines
+
+
+
+
+
+
+
+
+
+ - git-magic(1)
+ - Git Extras
+ - git-magic(1)
+
+
+
+
+
NAME
+
+ git-magic - Automate add/commit/push routines
+
+
SYNOPSIS
+
+
git-magic [-a] [-m msg] [-e] [-p] [-f]
+
+
DESCRIPTION
+
+
Produces summary of changes for commit message from git status --porcelain output.
+Commits staged changes with the generated commit message and
+opens editor to modify generated commit message optionally.
+Also staging and pushing can be automated optionally.
+
+
OPTIONS
+
+
-a
+
+
Adds everything including untracked files.
+
+
-m msg
+
+
Use the given msg as the commit message. If multiple -m options are given, their values are concatenated as separate paragraphs.
+Passed to git commit command. The generated is appended to user-given messages.
+
+
-e
+
+
This option lets you further edit the generated message.
+Passed to git commit command.
+
+
-p
+
+
Runs git push after commit.
+
+
-f
+
+
Adds --force-with-lease option to git push command for safer force pushing.
+
+
-h
+
+
Prints synopsis.
+
+
EXAMPLES
+
+
This example stages all changes then commits with automatic commit message.
+
+
$ git magic -a
+[feature/magic dc2a11e] A man/git-magic.md
+ 1 file changed, 37 insertions(+)
+ create mode 100644 man/git-auto.md
+# git log
+Author: overengineer <54alpersaid@gmail.com>
+Date: Thu Sep 30 20:14:22 2021 +0300
+
+ M man/git-magic.md
+
+
+
-m option PREPENDS generated message.
+
+
$ git magic -am "Added documentation for git magic"
+[feature/magic dc2a11e] Added documentation for git magic
+ 1 file changed, 42 insertions(+), 0 deletions(-)
+ create mode 100644 A man/git-auto.md
+$ git log
+Author: overengineer <54alpersaid@gmail.com>
+Date: Thu Sep 30 20:14:22 2021 +0300
+
+ Added documentation for git magic
+
+ M man/git-magic.md
+
+
+
AUTHOR
+
+
Written by Alper S. Soylu 54alpersaid@gmail.com
+
+
REPORTING BUGS
+
+
<https://github.com/tj/git-extras/issues>
+
+
SEE ALSO
+
+
<https://github.com/tj/git-extras>
+
+
+
+
+
+
diff --git a/man/git-magic.md b/man/git-magic.md
index 8da5878..d640d66 100644
--- a/man/git-magic.md
+++ b/man/git-magic.md
@@ -34,7 +34,7 @@ Runs `git push` after commit.
-f
-Adds `-f` option to `git push` command.
+Adds `--force-with-lease` option to `git push` command for safer force pushing.
-h
From f5c48e484281374f0276f82baef8eaaaae1e59ec Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 24 Nov 2025 10:42:06 +0800
Subject: [PATCH 49/62] 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]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7a53c5b..d6f419c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code.
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: 'Get Changed Files'
@@ -47,7 +47,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code.
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Install poetry
run: pip install poetry
- name: Set up Python
@@ -71,7 +71,7 @@ jobs:
name: 'Test with Pytest'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
with:
submodules: recursive
- name: Install poetry
@@ -96,7 +96,7 @@ jobs:
name: 'Test with Bats'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
with:
submodules: recursive
- name: Setup Bats
@@ -120,7 +120,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- name: Check out code.
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Linux Install
if: matrix.platform == 'ubuntu-latest'
run: sudo apt-get install -y bsdmainutils
From 6f4cf0c1bb23b39d58c9ff638e97269c31544fa8 Mon Sep 17 00:00:00 2001
From: johnpyp
Date: Thu, 18 Dec 2025 15:40:24 -0500
Subject: [PATCH 50/62] feat: `delete-branch` multiple unique branch names
completions (#1221)
---
etc/bash_completion.sh | 9 ++++++++-
etc/git-extras-completion.zsh | 12 ++++++++++--
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/etc/bash_completion.sh b/etc/bash_completion.sh
index 3139c57..fa561ef 100644
--- a/etc/bash_completion.sh
+++ b/etc/bash_completion.sh
@@ -1,6 +1,13 @@
# shellcheck shell=bash
# 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"
}
@@ -84,7 +91,7 @@ __git_cp(){
}
_git_delete_branch(){
- __gitcomp "$(__git_heads)"
+ __gitcomp "$(__gitex_heads_unique)"
}
_git_delete_squashed_branches(){
diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh
index 13957f0..0ab03d2 100644
--- a/etc/git-extras-completion.zsh
+++ b/etc/git-extras-completion.zsh
@@ -72,6 +72,15 @@ __gitex_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() {
local expl
declare -a branch_names
@@ -196,8 +205,7 @@ _git-create-branch() {
}
_git-delete-branch() {
- _arguments \
- ':branch-name:__gitex_branch_names'
+ __gitex_branch_names_unique
}
_git-delete-squashed-branches() {
From b9bd309a87ddacbc4baef3954255f30d20420f80 Mon Sep 17 00:00:00 2001
From: John Bachir
Date: Mon, 19 Jan 2026 16:15:42 -0800
Subject: [PATCH 51/62] Improve `git-repl` prompt (#1224)
* repl config ideas
* documentation
* Update bin/git-repl
Co-authored-by: Edwin Kofler
* separator
* man page and web docs
* always use dir for project name
---------
Co-authored-by: Edwin Kofler
---
Commands.md | 17 +++++++++++++++++
bin/git-repl | 15 ++++++++++++++-
man/git-repl.1 | 16 +++++++++++++---
man/git-repl.html | 22 +++++++++++++++++++---
man/git-repl.md | 15 +++++++++++++++
5 files changed, 78 insertions(+), 7 deletions(-)
diff --git a/Commands.md b/Commands.md
index fa59614..1b04250 100644
--- a/Commands.md
+++ b/Commands.md
@@ -392,6 +392,23 @@ 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.
+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 (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
$ git repl
git version 2.34.1
diff --git a/bin/git-repl b/bin/git-repl
index a706de3..b95d9d2 100755
--- a/bin/git-repl
+++ b/bin/git-repl
@@ -19,7 +19,20 @@ while true; do
else
es_string=""
fi
- prompt="git$cur_string$es_string> "
+ 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
+
+ prompt_base="$prefix$project_name$cur_string$es_string$prompt_character"
+ prompt_base_stripped=$(echo "$prompt_base" | awk '{$1=$1};1')
+ prompt="$prompt_base_stripped "
# Use arguments as a command if any are provided.
if [ $# -ne 0 ]; then
diff --git a/man/git-repl.1 b/man/git-repl.1
index b3721f4..162b0de 100644
--- a/man/git-repl.1
+++ b/man/git-repl.1
@@ -1,6 +1,6 @@
-.\" generated with Ronn-NG/v0.9.1
-.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
-.TH "GIT\-REPL" "1" "September 2024" "" "Git Extras"
+.\" generated with Ronn-NG/v0.10.1
+.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
+.TH "GIT\-REPL" "1" "January 2026" "" "Git Extras"
.SH "NAME"
\fBgit\-repl\fR \- git read\-eval\-print\-loop
.SH "SYNOPSIS"
@@ -31,6 +31,16 @@ Equivalent of 'git ls\-files'\.
exit|quit|q
.P
Ends the repl session\.
+.SH "CONFIGURATION"
+You can configure which character is used at the end of the prompt (default \fB>\fR):
+.P
+\fBgit config \-\-global git\-extras\.repl\.prompt\-character "±"\fR
+.P
+You can specify the prefix for the prompt (default \fBgit\fR): \fBgit config \-\-global git\-extras\.repl\.prefix ""\fR
+.P
+You can have the name of the current git repo shown in the prompt (default \fBfalse\fR):
+.P
+\fBgit config \-\-global git\-extras\.repl\.show\-project\-name "true"\fR
.SH "EXAMPLES"
.nf
$ git repl
diff --git a/man/git-repl.html b/man/git-repl.html
index 3f9750e..d67ba2c 100644
--- a/man/git-repl.html
+++ b/man/git-repl.html
@@ -1,8 +1,8 @@
-
-
+
+
git-repl(1) - git read-eval-print-loop