mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 07:26:17 -04:00
Compare commits
15 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cf28ec559 | ||
|
|
09b77e8ed6 | ||
|
|
4c38988ba9 | ||
|
|
dbf68bac59 | ||
|
|
03569856db | ||
|
|
d11e45f0b1 | ||
|
|
fd2e03cf55 | ||
|
|
5a14757944 | ||
|
|
b450b36036 | ||
|
|
7f36d37283 | ||
|
|
184d37e980 | ||
|
|
e92878b81b | ||
|
|
b9e1d88f2c | ||
|
|
99207eee8e | ||
|
|
9d6319becb |
75
.github/workflows/ci.yml
vendored
75
.github/workflows/ci.yml
vendored
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code.
|
- name: Check out code.
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: 'Get Changed Files'
|
- name: 'Get Changed Files'
|
||||||
|
|
@ -26,7 +26,26 @@ jobs:
|
||||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
BEFORE_SHA: "${{ github.event.before }}"
|
BEFORE_SHA: "${{ github.event.before }}"
|
||||||
- uses: 'actions/setup-go@v6'
|
- name: 'Check Generated Manuals'
|
||||||
|
run: |
|
||||||
|
failed=0
|
||||||
|
while IFS= read -r source; do
|
||||||
|
case "$source" in
|
||||||
|
man/git-*.md)
|
||||||
|
stem=${source%.md}
|
||||||
|
for generated in "$stem.1" "$stem.html"; do
|
||||||
|
if ! grep -Fqx -- "$generated" <<< "$CHANGED_FILES"; then
|
||||||
|
echo "::error file=$source::$generated must be updated with $source"
|
||||||
|
failed=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<< "$CHANGED_FILES"
|
||||||
|
exit "$failed"
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: "${{ steps.files.outputs.added_modified }}"
|
||||||
|
- uses: 'actions/setup-go@v7'
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.20'
|
||||||
- name: 'Install EditorConfig Lint'
|
- name: 'Install EditorConfig Lint'
|
||||||
|
|
@ -40,63 +59,25 @@ jobs:
|
||||||
run: ./scripts/checkstyle.py
|
run: ./scripts/checkstyle.py
|
||||||
- name: Shellcheck
|
- name: Shellcheck
|
||||||
run: shellcheck --severity=error bin/* ./*.sh
|
run: shellcheck --severity=error bin/* ./*.sh
|
||||||
- name: Lint and format Python with Ruff
|
|
||||||
uses: astral-sh/ruff-action@v3
|
|
||||||
|
|
||||||
typo:
|
typo:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code.
|
- name: Check out code.
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
- name: Install poetry
|
- name: Install codespell
|
||||||
run: pip install poetry
|
run: python3 -m pip install codespell==2.4
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
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
|
- name: spell check
|
||||||
run: |
|
run: |
|
||||||
cd tests
|
git grep --cached -l '' . | \
|
||||||
git grep --cached -l '' .. | \
|
|
||||||
grep -v -e 'History\.md' -e 'AUTHORS' -e 'man/.*\.1' -e 'man/.*\.html' | \
|
grep -v -e 'History\.md' -e 'AUTHORS' -e 'man/.*\.1' -e 'man/.*\.html' | \
|
||||||
xargs poetry run codespell --ignore-words=../.github/.ignore_words
|
xargs codespell --ignore-words=.github/.ignore_words
|
||||||
|
|
||||||
test-pytest:
|
|
||||||
name: 'Test with Pytest'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
- name: Install poetry
|
|
||||||
run: pip install poetry
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
cache: 'poetry'
|
|
||||||
cache-dependency-path: "tests/pyproject.toml"
|
|
||||||
python-version-file: "tests/pyproject.toml"
|
|
||||||
- name: Install Python Dependencies
|
|
||||||
run: |
|
|
||||||
cd tests || exit
|
|
||||||
poetry install --only test
|
|
||||||
- name: Test with Pytest
|
|
||||||
run: |
|
|
||||||
cd tests
|
|
||||||
poetry run pytest
|
|
||||||
|
|
||||||
test-bats:
|
test-bats:
|
||||||
name: 'Test with Bats'
|
name: 'Test with Bats'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Setup Bats
|
- name: Setup Bats
|
||||||
|
|
@ -120,7 +101,7 @@ jobs:
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code.
|
- name: Check out code.
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
- name: Linux Install
|
- name: Linux Install
|
||||||
if: matrix.platform == 'ubuntu-latest'
|
if: matrix.platform == 'ubuntu-latest'
|
||||||
run: sudo apt-get install -y bsdmainutils
|
run: sudo apt-get install -y bsdmainutils
|
||||||
|
|
|
||||||
2
.github/workflows/stale.yaml
vendored
2
.github/workflows/stale.yaml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
if: github.repository_owner == 'tj'
|
if: github.repository_owner == 'tj'
|
||||||
steps:
|
steps:
|
||||||
- uses: "actions/stale@v10"
|
- uses: "actions/stale@v11"
|
||||||
with:
|
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"
|
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-stale: 350
|
||||||
|
|
|
||||||
37
Commands.md
37
Commands.md
|
|
@ -18,6 +18,7 @@
|
||||||
- [`git cp`](#git-cp)
|
- [`git cp`](#git-cp)
|
||||||
- [`git create-branch`](#git-create-branch)
|
- [`git create-branch`](#git-create-branch)
|
||||||
- [`git delete-branch`](#git-delete-branch)
|
- [`git delete-branch`](#git-delete-branch)
|
||||||
|
- [`git delete-gone-branches`](#git-delete-gone-branches)
|
||||||
- [`git delete-merged-branches`](#git-delete-merged-branches)
|
- [`git delete-merged-branches`](#git-delete-merged-branches)
|
||||||
- [`git delete-squashed-branches`](#git-delete-squashed-branches)
|
- [`git delete-squashed-branches`](#git-delete-squashed-branches)
|
||||||
- [`git delete-submodule`](#git-delete-submodule)
|
- [`git delete-submodule`](#git-delete-submodule)
|
||||||
|
|
@ -932,6 +933,21 @@ Delete local and remote tag `name`:
|
||||||
$ git delete-tag 0.0.1
|
$ git delete-tag 0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git delete-gone-branches
|
||||||
|
|
||||||
|
Deletes all local branches whose remote-tracking branch has been deleted (i.e. branches shown as `[gone]` in `git branch -vv`). Use `--dry-run` to preview what would be deleted.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git delete-gone-branches
|
||||||
|
Deleted branch feature-123 (was abc1234).
|
||||||
|
Deleted branch feature-456 (was def5678).
|
||||||
|
|
||||||
|
$ git delete-gone-branches --dry-run
|
||||||
|
Would delete the following branches:
|
||||||
|
feature-123
|
||||||
|
feature-456
|
||||||
|
```
|
||||||
|
|
||||||
## git delete-merged-branches
|
## git delete-merged-branches
|
||||||
|
|
||||||
Deletes branches that are listed in `git branch --merged`.
|
Deletes branches that are listed in `git branch --merged`.
|
||||||
|
|
@ -1187,6 +1203,14 @@ Internally this script uses `rsync` and not `scp` as the name suggests.
|
||||||
|
|
||||||
`git-rscp` - The reverse of `git-scp`. Copies specific files from the working directory of a remote repository to the current working directory.
|
`git-rscp` - The reverse of `git-scp`. Copies specific files from the working directory of a remote repository to the current working directory.
|
||||||
|
|
||||||
|
The following options are available (must precede `<remote>`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
-v, --verbose Print what will be synced before syncing
|
||||||
|
-i, --interactive Prompt for confirmation before syncing (implies --verbose)
|
||||||
|
-n, --dry-run Show what would be synced, change nothing (implies --verbose)
|
||||||
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
Copy unstaged files to remote. Useful when you want to make quick test without making any commits
|
Copy unstaged files to remote. Useful when you want to make quick test without making any commits
|
||||||
|
|
@ -1404,8 +1428,8 @@ $ git reset-file .htaccess dc82b19
|
||||||
|
|
||||||
## git mr
|
## git mr
|
||||||
|
|
||||||
Checks out a merge request from GitLab. Usage: `git mr <ID|URL> [REMOTE]`.
|
Checks out a merge request from GitLab, or a pull request from Forgejo/Codeberg.
|
||||||
Default remote is `origin`.
|
Usage: `git mr <ID|URL> [REMOTE]`. Default remote is `origin`.
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
$ git mr 51
|
$ git mr 51
|
||||||
|
|
@ -1423,6 +1447,15 @@ From gitlab.com:owner/repository
|
||||||
Switched to branch 'mr/51'
|
Switched to branch 'mr/51'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A Forgejo/Codeberg pull request URL is also supported:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ git mr https://codeberg.org/owner/repository/pulls/51
|
||||||
|
From codeberg.org:owner/repository
|
||||||
|
* [new ref] refs/pull/51/head -> mr/51
|
||||||
|
Switched to branch 'mr/51'
|
||||||
|
```
|
||||||
|
|
||||||
Just like [git pr](#git-pr), `git mr` accepts a `clean` argument to trash all
|
Just like [git pr](#git-pr), `git mr` accepts a `clean` argument to trash all
|
||||||
`mr/` branches. Ensure current branch is not one.
|
`mr/` branches. Ensure current branch is not one.
|
||||||
|
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -141,6 +141,6 @@ docclean:
|
||||||
rm -f man/*.html
|
rm -f man/*.html
|
||||||
|
|
||||||
test:
|
test:
|
||||||
pytest
|
bats ./tests
|
||||||
|
|
||||||
.PHONY: default docs check install uninstall clean docclean test
|
.PHONY: default docs check install uninstall clean docclean test
|
||||||
|
|
|
||||||
60
bin/git-delete-gone-branches
Executable file
60
bin/git-delete-gone-branches
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
dry_run=false
|
||||||
|
force=false
|
||||||
|
prune=false
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-n|--dry-run)
|
||||||
|
dry_run=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-f|--force)
|
||||||
|
force=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-p|--prune)
|
||||||
|
prune=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
echo "Usage: git delete-gone-branches [-n|--dry-run] [-f|--force] [-p|--prune]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$prune" = true ]; then
|
||||||
|
git fetch --prune
|
||||||
|
fi
|
||||||
|
|
||||||
|
gone_branches=$(git for-each-ref --format \
|
||||||
|
'%(if:equals=gone)%(upstream:track,nobracket)%(then)%(refname:short)%(end)' refs/heads/ | sed '/^$/d')
|
||||||
|
|
||||||
|
if [ -z "$gone_branches" ]; then
|
||||||
|
echo "No branches with a gone remote found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$dry_run" = true ]; then
|
||||||
|
echo "Would delete the following branches:"
|
||||||
|
echo "$gone_branches"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "The following branches will be deleted:"
|
||||||
|
echo "$gone_branches"
|
||||||
|
|
||||||
|
if [ "$force" = false ]; then
|
||||||
|
read -r -p "Delete these branches? [y/N] " confirm
|
||||||
|
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$gone_branches" | xargs git branch -D
|
||||||
|
|
@ -3,5 +3,5 @@
|
||||||
branches=$(git branch --no-color --merged | grep -vE "^(\*|\+)" | grep -v "$(git_extra_default_branch)" | grep -v svn)
|
branches=$(git branch --no-color --merged | grep -vE "^(\*|\+)" | grep -v "$(git_extra_default_branch)" | grep -v svn)
|
||||||
if [ -n "$branches" ]
|
if [ -n "$branches" ]
|
||||||
then
|
then
|
||||||
echo "$branches" | xargs -n 1 git branch -d
|
echo "$branches" | xargs git branch -d
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="7.5.0"
|
VERSION="7.6.0-dev"
|
||||||
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
|
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|
|
||||||
199
bin/git-ignore
199
bin/git-ignore
|
|
@ -2,6 +2,26 @@
|
||||||
|
|
||||||
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
|
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
|
||||||
|
|
||||||
|
# awk script that takes 2 input files. The first one is newline deilmeted
|
||||||
|
# string of patterns to look for, second one is the file to remove them from.
|
||||||
|
# outfile has to be passed with -v upon call and is the file result is
|
||||||
|
# written to.
|
||||||
|
read -r -d '' AWK_SCRIPT <<'EOF'
|
||||||
|
# process the first "file" (newline split argument list)
|
||||||
|
NR==FNR { a[b++]=$0; next; }
|
||||||
|
# process file
|
||||||
|
{
|
||||||
|
found=0;
|
||||||
|
for (i=0; i != b; i++) {
|
||||||
|
if (a[i] == $0) { found=1; }
|
||||||
|
}
|
||||||
|
if (found == 1) { print "... removing '"$0"'"; }
|
||||||
|
else { print $0 > outfile }
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
show_contents() {
|
show_contents() {
|
||||||
local file="${2/#~/$HOME}"
|
local file="${2/#~/$HOME}"
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
|
|
@ -94,6 +114,66 @@ add_patterns() {
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
edit_file() {
|
||||||
|
local file="${2/#~/$HOME}"
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
echo "Editing $1 gitignore: ($2)" && eval "$(git var GIT_EDITOR) $(printf '%q' "$file")"
|
||||||
|
echo "Done."
|
||||||
|
else
|
||||||
|
echo "There is no $1 .gitignore yet." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: git ignore [OPTION]... [PATTERN]...
|
||||||
|
Modify or display local global or private gitignore files.
|
||||||
|
|
||||||
|
OPTIONs set context (local, global, private) and action (display, add,
|
||||||
|
remove, edit). In case of collision, last flag overwrites preceding ones.
|
||||||
|
Defaults are: local context, action is display (see man git ignore for
|
||||||
|
more details).
|
||||||
|
Context examples:
|
||||||
|
local: .../repo/.gitignore
|
||||||
|
private: .../repo/.git/info/exclude
|
||||||
|
global: \$HOME/.gitignore
|
||||||
|
|
||||||
|
PATTERNs are the gitignore patterns as specified in git documentation.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-l, --local Set context to local gitignore
|
||||||
|
-g, --global Set context to global gitignore
|
||||||
|
-p, --private Set context to private gitignore
|
||||||
|
-e, --edit Set action to edit
|
||||||
|
-r, --remove Set action to remove
|
||||||
|
-h, --help Print this message
|
||||||
|
-- End of options delimiter
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_patterns() {
|
||||||
|
declare -a args
|
||||||
|
local tmpfile
|
||||||
|
args=( "${@:3}" )
|
||||||
|
local file="${2/#~/$HOME}"
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
tmpfile="$(mktemp -q "${TMPDIR:-/tmp}"/git-extras-ignore.XXXXXX 2>/dev/null)"
|
||||||
|
echo "Removing patterns from $1 gitignore: $2"
|
||||||
|
awk -v outfile="$tmpfile" "${AWK_SCRIPT}"\
|
||||||
|
<(printf '%s\n' "${args[@]}") "$file"
|
||||||
|
cat "$tmpfile" > "$file"
|
||||||
|
rm "$tmpfile"
|
||||||
|
else
|
||||||
|
echo "There is no $1 .gitignore yet"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# can have only 1 action and 1 level, if conflicting flags are passed
|
||||||
|
# latter ones overwrite former
|
||||||
|
# sanity of args not tested (e.g. there should be no args if -e is supplied)
|
||||||
if test $# -eq 0; then
|
if test $# -eq 0; then
|
||||||
show_global
|
show_global
|
||||||
echo "---------------------------------"
|
echo "---------------------------------"
|
||||||
|
|
@ -101,22 +181,105 @@ if test $# -eq 0; then
|
||||||
echo "---------------------------------"
|
echo "---------------------------------"
|
||||||
show_private
|
show_private
|
||||||
else
|
else
|
||||||
case "$1" in
|
# parse flags and args
|
||||||
-l|--local)
|
level="none"
|
||||||
test $# -gt 1 && add_local "${@:2}" && echo
|
action="none"
|
||||||
show_local
|
declare -a args
|
||||||
;;
|
while [ $# -gt 0 ]; do
|
||||||
-g|--global)
|
case "$1" in
|
||||||
test $# -gt 1 && add_global "${@:2}" && echo
|
-h|--help)
|
||||||
show_global
|
_usage
|
||||||
;;
|
exit 0
|
||||||
-p|--private)
|
;;
|
||||||
test $# -gt 1 && add_private "${@:2}" && echo
|
-l|--local)
|
||||||
show_private
|
level="local"
|
||||||
;;
|
;;
|
||||||
*)
|
-g|--global)
|
||||||
add_local "$@"
|
level="global"
|
||||||
;;
|
;;
|
||||||
esac
|
-p|--private)
|
||||||
fi
|
level="private"
|
||||||
|
;;
|
||||||
|
-e|--edit)
|
||||||
|
action="edit"
|
||||||
|
;;
|
||||||
|
-r|--remove)
|
||||||
|
action="remove"
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
args+=("$@")
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
args+=("$1")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
# compatible with previous version
|
||||||
|
if [ "$action" = "none" ]; then
|
||||||
|
case "$level" in
|
||||||
|
local)
|
||||||
|
test ${#args[@]} -ne 0 && add_local "${args[@]}" && echo
|
||||||
|
show_local
|
||||||
|
;;
|
||||||
|
global)
|
||||||
|
test ${#args[@]} -ne 0 && add_global "${args[@]}" && echo
|
||||||
|
show_global
|
||||||
|
;;
|
||||||
|
private)
|
||||||
|
test ${#args[@]} -ne 0 && add_private "${args[@]}" && echo
|
||||||
|
show_private
|
||||||
|
;;
|
||||||
|
none)
|
||||||
|
add_local "${args[@]}"
|
||||||
|
# maybe show_local here too?
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
# open file in editor
|
||||||
|
elif [ "$action" = "edit" ]; then
|
||||||
|
case "$level" in
|
||||||
|
local|none)
|
||||||
|
cd_to_git_root --warn
|
||||||
|
edit_file 'local' .gitignore && echo
|
||||||
|
;;
|
||||||
|
global)
|
||||||
|
global_gitignore="$(global_ignore)"
|
||||||
|
edit_file global "$global_gitignore" && echo
|
||||||
|
;;
|
||||||
|
private)
|
||||||
|
cd_to_git_root --error
|
||||||
|
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
|
||||||
|
edit_file private "${GIT_DIR}/info/exclude" && echo
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
# remove entries
|
||||||
|
else
|
||||||
|
if [ ${#args[@]} -eq 0 ]; then
|
||||||
|
echo "Nothing to remove."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
case "$level" in
|
||||||
|
local|none)
|
||||||
|
cd_to_git_root --warn
|
||||||
|
remove_patterns 'local' .gitignore "${args[@]}" && echo
|
||||||
|
show_local
|
||||||
|
;;
|
||||||
|
global)
|
||||||
|
global_gitignore="$(global_ignore)"
|
||||||
|
remove_patterns global "$global_gitignore" "${args[@]}" && echo
|
||||||
|
show_global
|
||||||
|
;;
|
||||||
|
private)
|
||||||
|
cd_to_git_root --error
|
||||||
|
remove_patterns private "${GIT_DIR}/info/exclude" "${args[@]}" && echo
|
||||||
|
show_private
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
|
||||||
10
bin/git-mr
10
bin/git-mr
|
|
@ -7,6 +7,8 @@ if [ -z "${1-}" ] ; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
remote_ref_kind=merge-requests
|
||||||
|
|
||||||
if test "$1" = "clean"; then
|
if test "$1" = "clean"; then
|
||||||
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read -r ref; do
|
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read -r ref; do
|
||||||
git branch -D "${ref#refs/heads/}"
|
git branch -D "${ref#refs/heads/}"
|
||||||
|
|
@ -15,13 +17,19 @@ if test "$1" = "clean"; then
|
||||||
elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then
|
elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then
|
||||||
remote=${BASH_REMATCH[1]}.git
|
remote=${BASH_REMATCH[1]}.git
|
||||||
id=${BASH_REMATCH[3]}
|
id=${BASH_REMATCH[3]}
|
||||||
|
elif [[ $1 =~ ^(https?://[^/]+/(.+))/pulls/([0-9]+).*$ ]]; then
|
||||||
|
# Forgejo/Codeberg pull request URL, e.g.
|
||||||
|
# https://codeberg.org/owner/repository/pulls/453
|
||||||
|
remote=${BASH_REMATCH[1]}.git
|
||||||
|
id=${BASH_REMATCH[3]}
|
||||||
|
remote_ref_kind=pull
|
||||||
else
|
else
|
||||||
id=$1
|
id=$1
|
||||||
remote=${2:-origin}
|
remote=${2:-origin}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
branch=mr/$id
|
branch=mr/$id
|
||||||
remote_ref=refs/merge-requests/$id/head
|
remote_ref=refs/$remote_ref_kind/$id/head
|
||||||
git fetch -fu "$remote" "$remote_ref:$branch"
|
git fetch -fu "$remote" "$remote_ref:$branch"
|
||||||
git checkout "$branch"
|
git checkout "$branch"
|
||||||
git config --local --replace "branch.$branch.merge" "$remote_ref"
|
git config --local --replace "branch.$branch.merge" "$remote_ref"
|
||||||
|
|
|
||||||
122
bin/git-scp
122
bin/git-scp
|
|
@ -77,6 +77,17 @@ function _sanitize()
|
||||||
|
|
||||||
function scp_and_stage
|
function scp_and_stage
|
||||||
{
|
{
|
||||||
|
local verbose=0 interactive=0 dry_run=0
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
case "$1" in
|
||||||
|
-v|--verbose) verbose=1; shift;;
|
||||||
|
-i|--interactive) verbose=1; interactive=1; shift;;
|
||||||
|
-n|--dry-run) verbose=1; dry_run=1; shift;;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
set_remote "$1"
|
set_remote "$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
|
|
@ -94,46 +105,116 @@ function scp_and_stage
|
||||||
list=$(git ls-files "$@")" "$(git ls-files -o "$@")
|
list=$(git ls-files "$@")" "$(git ls-files -o "$@")
|
||||||
elif [ -n "$refhead" ]
|
elif [ -n "$refhead" ]
|
||||||
then
|
then
|
||||||
git diff --stat "$refhead"
|
[ "$verbose" -eq 1 ] && git --no-pager diff --stat "$refhead"
|
||||||
list=$(git diff "$refhead" --name-only)
|
list=$(git diff "$refhead" --name-only)
|
||||||
else
|
else
|
||||||
git diff
|
[ "$verbose" -eq 1 ] && git --no-pager diff
|
||||||
list=$(git diff --name-only)
|
list=$(git diff --name-only)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
deleted=$(for i in $list; do [ -f "$i" ] || echo "$i"; done)
|
deleted=$(for i in $list; do [ -f "$i" ] || echo "$i"; done)
|
||||||
list=$(for i in $list; do [ -f "$i" ] && echo "$i"; done)
|
list=$(for i in $list; do [ -f "$i" ] && echo "$i"; done)
|
||||||
|
|
||||||
|
if [ "$interactive" -eq 1 ] && [ "$dry_run" -eq 0 ] && { [ -n "$list" ] || [ -n "$deleted" ]; }
|
||||||
|
then
|
||||||
|
local reply
|
||||||
|
read -r -p "Proceed with sync to $remote? [y/N] " reply
|
||||||
|
case "$reply" in
|
||||||
|
y|Y|yes|YES) ;;
|
||||||
|
*) _info "Aborted, nothing synced."; exit 0;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
local status=0
|
||||||
|
|
||||||
if [ -n "$list" ]
|
if [ -n "$list" ]
|
||||||
then
|
then
|
||||||
local _TMP=${0///}
|
local _TMP=${0///}
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
echo "$list" > "$_TMP" &&
|
echo "$list" > "$_TMP"
|
||||||
_sanitize $list &&
|
if [ "$dry_run" -eq 1 ] || _sanitize $list
|
||||||
_info "Pushing to $remote ($(git config "remote.$remote.url"))" &&
|
then
|
||||||
rsync -rlDv --files-from="$_TMP" ./ "$(git config "remote.$remote.url")/" &&
|
_info "Pushing to $remote ($(git config "remote.$remote.url"))"
|
||||||
git add --force $list &&
|
if [ "$dry_run" -eq 1 ]
|
||||||
|
then
|
||||||
|
rsync -rlDvn --files-from="$_TMP" ./ "$(git config "remote.$remote.url")/"
|
||||||
|
else
|
||||||
|
rsync -rlDv --files-from="$_TMP" ./ "$(git config "remote.$remote.url")/" &&
|
||||||
|
git add --force $list
|
||||||
|
fi
|
||||||
|
status=$?
|
||||||
|
else
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
rm "$_TMP"
|
rm "$_TMP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
deleted=$(for i in $deleted; do echo "$(git config "remote.$remote.url" | cut -d: -f2)/$i"; done)
|
deleted=$(for i in $deleted; do echo "$(git config "remote.$remote.url" | cut -d: -f2)/$i"; done)
|
||||||
|
|
||||||
[ -n "$deleted" ] &&
|
if [ -n "$deleted" ]
|
||||||
COLOR_RED &&
|
then
|
||||||
echo Deleted remote files &&
|
COLOR_RED
|
||||||
ssh "$(git config "remote.$remote.url" | cut -d: -f1)" -t "rm $deleted" &&
|
echo Deleted remote files
|
||||||
echo "$deleted"
|
if [ "$dry_run" -eq 1 ]
|
||||||
COLOR_RESET
|
then
|
||||||
|
echo "$deleted"
|
||||||
|
else
|
||||||
|
if ssh "$(git config "remote.$remote.url" | cut -d: -f1)" -t "rm $deleted"
|
||||||
|
then
|
||||||
|
echo "$deleted"
|
||||||
|
else
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
COLOR_RESET
|
||||||
|
fi
|
||||||
|
|
||||||
|
return "$status"
|
||||||
}
|
}
|
||||||
|
|
||||||
function reverse_scp()
|
function reverse_scp()
|
||||||
{
|
{
|
||||||
|
local verbose=0 interactive=0 dry_run=0
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
case "$1" in
|
||||||
|
-v|--verbose) verbose=1; shift;;
|
||||||
|
-i|--interactive) verbose=1; interactive=1; shift;;
|
||||||
|
-n|--dry-run) verbose=1; dry_run=1; shift;;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
set_remote "$1"
|
set_remote "$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
local _TMP=${0///}
|
local _TMP=${0///}
|
||||||
echo "$@" > "$_TMP" &&
|
echo "$@" > "$_TMP"
|
||||||
rsync -rlDv --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./ &&
|
|
||||||
|
local status=0
|
||||||
|
if [ "$verbose" -eq 1 ]
|
||||||
|
then
|
||||||
|
rsync -rlDvn --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./
|
||||||
|
status=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$dry_run" -eq 1 ]
|
||||||
|
then
|
||||||
|
rm "$_TMP"
|
||||||
|
return "$status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$interactive" -eq 1 ]
|
||||||
|
then
|
||||||
|
local reply
|
||||||
|
read -r -p "Proceed with copy from $remote? [y/N] " reply
|
||||||
|
case "$reply" in
|
||||||
|
y|Y|yes|YES) ;;
|
||||||
|
*) _info "Aborted, nothing copied."; rm "$_TMP"; exit 0;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
rsync -rlDv --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./ &&
|
||||||
rm "$_TMP"
|
rm "$_TMP"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,9 +229,14 @@ function _usage()
|
||||||
{
|
{
|
||||||
echo "Usage:
|
echo "Usage:
|
||||||
git scp -h|help|?
|
git scp -h|help|?
|
||||||
git scp <remote> [ref|file..] # scp and stage your files to specified remote
|
git scp [options] <remote> [ref|file..] # scp and stage your files to specified remote
|
||||||
git scp <remote> [<ref>] # show diff relative to <ref> and upload unstaged files to <remote>
|
git scp [options] <remote> [<ref>] # show diff relative to <ref> and upload unstaged files to <remote>
|
||||||
git rscp <remote> [<file|directory>] # copy <remote> files to current working directory
|
git rscp [options] <remote> [<file|directory>] # copy <remote> files to current working directory
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-v, --verbose print what will be synced before syncing
|
||||||
|
-i, --interactive prompt for confirmation before syncing (implies --verbose)
|
||||||
|
-n, --dry-run show what would be synced, change nothing (implies --verbose; no staging, no remote writes/deletes)
|
||||||
"
|
"
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
|
|
|
||||||
|
|
@ -145,11 +145,11 @@ _git_graft(){
|
||||||
_git_ignore(){
|
_git_ignore(){
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
--*)
|
--*)
|
||||||
__gitcomp "--global --local --private"
|
__gitcomp "--global --local --private --edit --remove"
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
-*)
|
-*)
|
||||||
__gitcomp "--global --local --private -g -l -p"
|
__gitcomp "--global --local --private --edit --remove -g -l -p -e -r"
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -213,6 +213,11 @@ _git-delete-branch() {
|
||||||
__gitex_branch_names_unique
|
__gitex_branch_names_unique
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_git-delete-gone-branches() {
|
||||||
|
_arguments \
|
||||||
|
'(-n --dry-run)'{-n,--dry-run}'[show branches that would be deleted without deleting]'
|
||||||
|
}
|
||||||
|
|
||||||
_git-delete-squashed-branches() {
|
_git-delete-squashed-branches() {
|
||||||
_arguments \
|
_arguments \
|
||||||
':branch-name:__gitex_branch_names'
|
':branch-name:__gitex_branch_names'
|
||||||
|
|
@ -312,9 +317,11 @@ _git-guilt() {
|
||||||
|
|
||||||
_git-ignore() {
|
_git-ignore() {
|
||||||
_arguments -C \
|
_arguments -C \
|
||||||
'(--local -l)'{--local,-l}'[show local gitignore]' \
|
'(--local -l)'{--local,-l}'[set context to local gitignore]' \
|
||||||
'(--global -g)'{--global,-g}'[show global gitignore]' \
|
'(--global -g)'{--global,-g}'[set context to global gitignore]' \
|
||||||
'(--private -p)'{--private,-p}'[show repo gitignore]' \
|
'(--private -p)'{--private,-p}'[set context to private gitignore]' \
|
||||||
|
'(--edit -e)'{--edit,-e}'[set action to edit]' \
|
||||||
|
'(--remove -r)'{--remove,-r}'[set action to remove]' \
|
||||||
'*:filename:_files'
|
'*:filename:_files'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,6 +428,7 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
|
||||||
count:'show commit count' \
|
count:'show commit count' \
|
||||||
create-branch:'create branches' \
|
create-branch:'create branches' \
|
||||||
delete-branch:'delete branches' \
|
delete-branch:'delete branches' \
|
||||||
|
delete-gone-branches:'delete branches whose remote is gone' \
|
||||||
delete-merged-branches:'delete merged branches' \
|
delete-merged-branches:'delete merged branches' \
|
||||||
delete-squashed-branches:'delete squashed branches' \
|
delete-squashed-branches:'delete squashed branches' \
|
||||||
delete-submodule:'delete submodules' \
|
delete-submodule:'delete submodules' \
|
||||||
|
|
@ -437,7 +445,7 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \
|
||||||
graft:'merge and destroy a given branch' \
|
graft:'merge and destroy a given branch' \
|
||||||
guilt:'calculate change between two revisions' \
|
guilt:'calculate change between two revisions' \
|
||||||
ignore-io:'get sample gitignore file' \
|
ignore-io:'get sample gitignore file' \
|
||||||
ignore:'add .gitignore patterns' \
|
ignore:'Modify or display .gitignore files' \
|
||||||
info:'returns information on current repository' \
|
info:'returns information on current repository' \
|
||||||
local-commits:'list local commits' \
|
local-commits:'list local commits' \
|
||||||
lock:'lock a file excluded from version control' \
|
lock:'lock a file excluded from version control' \
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ set __fish_git_extras_commands \
|
||||||
"graft:Merge and destroy a given branch" \
|
"graft:Merge and destroy a given branch" \
|
||||||
"guilt:calculate change between two revisions" \
|
"guilt:calculate change between two revisions" \
|
||||||
"ignore-io:Get sample gitignore file" \
|
"ignore-io:Get sample gitignore file" \
|
||||||
"ignore:Add .gitignore patterns" \
|
"ignore:Modify or display .gitignore files" \
|
||||||
"info:Returns information on current repository" \
|
"info:Returns information on current repository" \
|
||||||
"local-commits:List local commits" \
|
"local-commits:List local commits" \
|
||||||
"lock:Lock a file excluded from version control" \
|
"lock:Lock a file excluded from version control" \
|
||||||
|
|
@ -162,9 +162,11 @@ complete -c git -f -n '__fish_git_using_command guilt' -s e -l email -d 'display
|
||||||
complete -c git -f -n '__fish_git_using_command guilt' -s d -l debug -d 'output debug information'
|
complete -c git -f -n '__fish_git_using_command guilt' -s d -l debug -d 'output debug information'
|
||||||
complete -c git -f -n '__fish_git_using_command guilt' -s h -d 'output usage information'
|
complete -c git -f -n '__fish_git_using_command guilt' -s h -d 'output usage information'
|
||||||
# ignore
|
# ignore
|
||||||
complete -c git -f -n '__fish_git_using_command ignore' -s l -l local -d 'show local gitignore'
|
complete -c git -f -n '__fish_git_using_command ignore' -s l -l local -d 'set context to local gitignore'
|
||||||
complete -c git -f -n '__fish_git_using_command ignore' -s g -l global -d 'show global gitignore'
|
complete -c git -f -n '__fish_git_using_command ignore' -s g -l global -d 'set context to global gitignore'
|
||||||
complete -c git -f -n '__fish_git_using_command ignore' -s p -l private -d 'show repo gitignore'
|
complete -c git -f -n '__fish_git_using_command ignore' -s p -l private -d 'set context to private gitignore'
|
||||||
|
complete -c git -f -n '__fish_git_using_command ignore' -s e -l edit -d 'set action to edit'
|
||||||
|
complete -c git -f -n '__fish_git_using_command ignore' -s r -l remove -d 'set action to remove'
|
||||||
# ignore-io
|
# ignore-io
|
||||||
function __fish_git_extra_get_ignore_io_types
|
function __fish_git_extra_get_ignore_io_types
|
||||||
# we will first remove every tab spaces, and then append `\t` at the end to remove the default description
|
# we will first remove every tab spaces, and then append `\t` at the end to remove the default description
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
is_git_repo() {
|
is_git_repo() {
|
||||||
git rev-parse --show-toplevel > /dev/null 2>&1
|
# --git-dir succeeds for both normal and bare repositories, whereas
|
||||||
|
# --show-toplevel fails inside a bare repository (it has no working tree).
|
||||||
|
git rev-parse --git-dir > /dev/null 2>&1
|
||||||
result=$?
|
result=$?
|
||||||
if test $result != 0; then
|
if test $result != 0; then
|
||||||
>&2 echo 'Not a git repo!'
|
>&2 echo 'Not a git repo!'
|
||||||
|
|
|
||||||
47
install.cmd
47
install.cmd
|
|
@ -39,13 +39,34 @@ goto :defaultpath
|
||||||
rem remove the last slash
|
rem remove the last slash
|
||||||
SET bindir=%bindir:~0,-1%
|
SET bindir=%bindir:~0,-1%
|
||||||
for %%G in ("%bindir%") do set installdir=%%~dpG
|
for %%G in ("%bindir%") do set installdir=%%~dpG
|
||||||
set PREFIX=%installdir%mingw64
|
rem strip a trailing slash so we can inspect the leaf directory name
|
||||||
|
if "%installdir:~-1%"=="\" set installdir=%installdir:~0,-1%
|
||||||
|
rem git.exe found under <GitRoot>\mingw64\bin or <GitRoot>\clangarm64\bin
|
||||||
|
rem means installdir already is the architecture directory itself
|
||||||
|
for %%G in ("%installdir%") do set archdir=%%~nxG
|
||||||
|
if /I "%archdir%"=="mingw64" (
|
||||||
|
set PREFIX=%installdir%
|
||||||
|
) else if /I "%archdir%"=="clangarm64" (
|
||||||
|
set PREFIX=%installdir%
|
||||||
|
) else if exist "%installdir%\mingw64" (
|
||||||
|
set PREFIX=%installdir%\mingw64
|
||||||
|
) else if exist "%installdir%\clangarm64" (
|
||||||
|
set PREFIX=%installdir%\clangarm64
|
||||||
|
) else (
|
||||||
|
set PREFIX=%installdir%\mingw64
|
||||||
|
)
|
||||||
goto :foundprefix
|
goto :foundprefix
|
||||||
|
|
||||||
:defaultpath
|
:defaultpath
|
||||||
:: default for Git for Windows 2.x
|
:: default for Git for Windows 2.x
|
||||||
if exist "%ProgramFiles%\Git" (
|
if exist "%ProgramFiles%\Git" (
|
||||||
set PREFIX=%ProgramFiles%\Git\mingw64
|
if exist "%ProgramFiles%\Git\mingw64" (
|
||||||
|
set PREFIX=%ProgramFiles%\Git\mingw64
|
||||||
|
) else if exist "%ProgramFiles%\Git\clangarm64" (
|
||||||
|
set PREFIX=%ProgramFiles%\Git\clangarm64
|
||||||
|
) else (
|
||||||
|
set PREFIX=%ProgramFiles%\Git\mingw64
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
:foundprefix
|
:foundprefix
|
||||||
|
|
@ -55,9 +76,11 @@ IF NOT "%~1"=="" (
|
||||||
REM just supplying the git dir is enough...
|
REM just supplying the git dir is enough...
|
||||||
if exist "%~1\mingw64" (
|
if exist "%~1\mingw64" (
|
||||||
set PREFIX=%~1\mingw64
|
set PREFIX=%~1\mingw64
|
||||||
|
) else if exist "%~1\clangarm64" (
|
||||||
|
set PREFIX=%~1\clangarm64
|
||||||
) else (
|
) else (
|
||||||
echo Using git install path "%~1" as PREFIX, please make sure it's really a
|
echo Using git install path "%~1" as PREFIX, please make sure it's really a
|
||||||
echo path to the mingw64 directory...
|
echo path to the mingw64 or clangarm64 directory...
|
||||||
echo.
|
echo.
|
||||||
SET PREFIX=%~1
|
SET PREFIX=%~1
|
||||||
)
|
)
|
||||||
|
|
@ -72,12 +95,14 @@ set GIT_INSTALL_DIR=!GIT_INSTALL_DIR:"=!
|
||||||
IF %GIT_INSTALL_DIR:~-1%==\ SET GIT_INSTALL_DIR=%GIT_INSTALL_DIR:~0,-1%
|
IF %GIT_INSTALL_DIR:~-1%==\ SET GIT_INSTALL_DIR=%GIT_INSTALL_DIR:~0,-1%
|
||||||
|
|
||||||
if not exist "%GIT_INSTALL_DIR%\mingw64" (
|
if not exist "%GIT_INSTALL_DIR%\mingw64" (
|
||||||
echo No mingw64 folder found in %GIT_INSTALL_DIR%.
|
if not exist "%GIT_INSTALL_DIR%\clangarm64" (
|
||||||
echo.
|
echo No mingw64 or clangarm64 folder found in %GIT_INSTALL_DIR%.
|
||||||
echo Please supply a proper "Git for Windows 2.x" install path:
|
echo.
|
||||||
echo "install.cmd c:\[git-install-path]"
|
echo Please supply a proper "Git for Windows 2.x" install path:
|
||||||
set ERROR=1
|
echo "install.cmd c:\[git-install-path]"
|
||||||
goto :exit
|
set ERROR=1
|
||||||
|
goto :exit
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
echo Installing to %PREFIX%
|
echo Installing to %PREFIX%
|
||||||
|
|
@ -116,7 +141,7 @@ FOR /R "%GITEXTRAS%\bin" %%i in (*.*) DO (
|
||||||
TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%~ni"
|
TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%~ni"
|
||||||
TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%~ni"
|
TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%~ni"
|
||||||
TYPE "%GITEXTRAS%\helper\is-git-repo" >> "%PREFIX%\bin\%%~ni"
|
TYPE "%GITEXTRAS%\helper\is-git-repo" >> "%PREFIX%\bin\%%~ni"
|
||||||
|
|
||||||
REM Added /E option for installation fix on Windows 10.0.17134 and higher
|
REM Added /E option for installation fix on Windows 10.0.17134 and higher
|
||||||
MORE /E +2 "%GITEXTRAS%\bin\%%~ni" >> "%PREFIX%\bin\%%~ni"
|
MORE /E +2 "%GITEXTRAS%\bin\%%~ni" >> "%PREFIX%\bin\%%~ni"
|
||||||
)
|
)
|
||||||
|
|
@ -127,7 +152,7 @@ FOR %%i in (%COMMANDS_WITHOUT_REPO%) DO (
|
||||||
ECHO #^^!/usr/bin/env bash > "%PREFIX%\bin\%%i"
|
ECHO #^^!/usr/bin/env bash > "%PREFIX%\bin\%%i"
|
||||||
TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%i"
|
TYPE "%GITEXTRAS%\helper\reset-env" >> "%PREFIX%\bin\%%i"
|
||||||
TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%i"
|
TYPE "%GITEXTRAS%\helper\git-extra-utility" >> "%PREFIX%\bin\%%i"
|
||||||
|
|
||||||
REM Added /E option for installation fix on Windows 10.0.17134 and higher
|
REM Added /E option for installation fix on Windows 10.0.17134 and higher
|
||||||
MORE /E +2 "%GITEXTRAS%\bin\%%i" >> "%PREFIX%\bin\%%i"
|
MORE /E +2 "%GITEXTRAS%\bin\%%i" >> "%PREFIX%\bin\%%i"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
54
man/git-delete-gone-branches.1
Normal file
54
man/git-delete-gone-branches.1
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
.\" generated with Ronn/v0.7.3
|
||||||
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||||
|
.
|
||||||
|
.TH "GIT\-DELETE\-GONE\-BRANCHES" "1" "March 2026" "" "Git Extras"
|
||||||
|
.
|
||||||
|
.SH "NAME"
|
||||||
|
\fBgit\-delete\-gone\-branches\fR \- Delete branches whose remote is gone
|
||||||
|
.
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
\fBgit\-delete\-gone\-branches\fR [\-n|\-\-dry\-run] [\-f|\-\-force] [\-p|\-\-prune]
|
||||||
|
.
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
Deletes all local branches whose remote\-tracking branch has been deleted\. This commonly happens after a pull request is merged and the remote branch is removed\. By default, lists the branches and prompts for confirmation before deleting\.
|
||||||
|
.
|
||||||
|
.SH "OPTIONS"
|
||||||
|
\-n, \-\-dry\-run
|
||||||
|
.
|
||||||
|
.P
|
||||||
|
Show the branches that would be deleted without actually deleting them\.
|
||||||
|
.
|
||||||
|
.P
|
||||||
|
\-f, \-\-force
|
||||||
|
.
|
||||||
|
.P
|
||||||
|
Skip the confirmation prompt and delete branches immediately\.
|
||||||
|
.
|
||||||
|
.P
|
||||||
|
\-p, \-\-prune
|
||||||
|
.
|
||||||
|
.P
|
||||||
|
Run \fBgit fetch \-\-prune\fR before checking for gone branches, to ensure remote\-tracking refs are up to date\.
|
||||||
|
.
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
.
|
||||||
|
.nf
|
||||||
|
|
||||||
|
$ git delete\-gone\-branches
|
||||||
|
|
||||||
|
$ git delete\-gone\-branches \-\-dry\-run
|
||||||
|
|
||||||
|
$ git delete\-gone\-branches \-\-force
|
||||||
|
|
||||||
|
$ git delete\-gone\-branches \-\-prune
|
||||||
|
.
|
||||||
|
.fi
|
||||||
|
.
|
||||||
|
.SH "AUTHOR"
|
||||||
|
Written by Utsav Sabharwal <\fIhttps://github\.com/codersofthedark\fR>
|
||||||
|
.
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
<\fIhttps://github\.com/tj/git\-extras/issues\fR>
|
||||||
|
.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
<\fIhttps://github\.com/tj/git\-extras\fR>
|
||||||
135
man/git-delete-gone-branches.html
Normal file
135
man/git-delete-gone-branches.html
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
||||||
|
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
|
||||||
|
<title>git-delete-gone-branches(1) - Delete branches whose remote is gone</title>
|
||||||
|
<style type='text/css' media='all'>
|
||||||
|
/* style: man */
|
||||||
|
body#manpage {margin:0}
|
||||||
|
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
||||||
|
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
||||||
|
.mp h2 {margin:10px 0 0 0}
|
||||||
|
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
||||||
|
.mp h3 {margin:0 0 0 4ex}
|
||||||
|
.mp dt {margin:0;clear:left}
|
||||||
|
.mp dt.flush {float:left;width:8ex}
|
||||||
|
.mp dd {margin:0 0 0 9ex}
|
||||||
|
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
||||||
|
.mp pre {margin-bottom:20px}
|
||||||
|
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
||||||
|
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
||||||
|
.mp img {display:block;margin:auto}
|
||||||
|
.mp h1.man-title {display:none}
|
||||||
|
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
||||||
|
.mp h2 {font-size:16px;line-height:1.25}
|
||||||
|
.mp h1 {font-size:20px;line-height:2}
|
||||||
|
.mp {text-align:justify;background:#fff}
|
||||||
|
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
||||||
|
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
||||||
|
.mp u {text-decoration:underline}
|
||||||
|
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
||||||
|
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
||||||
|
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
||||||
|
.mp b.man-ref {font-weight:normal;color:#434241}
|
||||||
|
.mp pre {padding:0 4ex}
|
||||||
|
.mp pre code {font-weight:normal;color:#434241}
|
||||||
|
.mp h2+pre,h3+pre {padding-left:0}
|
||||||
|
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
||||||
|
ol.man-decor {width:100%}
|
||||||
|
ol.man-decor li.tl {text-align:left}
|
||||||
|
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
||||||
|
ol.man-decor li.tr {text-align:right;float:right}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<!--
|
||||||
|
The following styles are deprecated and will be removed at some point:
|
||||||
|
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
||||||
|
|
||||||
|
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
||||||
|
.man-navigation should be used instead.
|
||||||
|
-->
|
||||||
|
<body id='manpage'>
|
||||||
|
<div class='mp' id='man'>
|
||||||
|
|
||||||
|
<div class='man-navigation' style='display:none'>
|
||||||
|
<a href="#NAME">NAME</a>
|
||||||
|
<a href="#SYNOPSIS">SYNOPSIS</a>
|
||||||
|
<a href="#DESCRIPTION">DESCRIPTION</a>
|
||||||
|
<a href="#OPTIONS">OPTIONS</a>
|
||||||
|
<a href="#EXAMPLES">EXAMPLES</a>
|
||||||
|
<a href="#AUTHOR">AUTHOR</a>
|
||||||
|
<a href="#REPORTING-BUGS">REPORTING BUGS</a>
|
||||||
|
<a href="#SEE-ALSO">SEE ALSO</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ol class='man-decor man-head man head'>
|
||||||
|
<li class='tl'>git-delete-gone-branches(1)</li>
|
||||||
|
<li class='tc'>Git Extras</li>
|
||||||
|
<li class='tr'>git-delete-gone-branches(1)</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2 id="NAME">NAME</h2>
|
||||||
|
<p class="man-name">
|
||||||
|
<code>git-delete-gone-branches</code> - <span class="man-whatis">Delete branches whose remote is gone</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
||||||
|
|
||||||
|
<p><code>git-delete-gone-branches</code> [-n|--dry-run] [-f|--force] [-p|--prune]</p>
|
||||||
|
|
||||||
|
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
||||||
|
|
||||||
|
<p> Deletes all local branches whose remote-tracking branch has been deleted.
|
||||||
|
This commonly happens after a pull request is merged and the remote branch
|
||||||
|
is removed. By default, lists the branches and prompts for confirmation
|
||||||
|
before deleting.</p>
|
||||||
|
|
||||||
|
<h2 id="OPTIONS">OPTIONS</h2>
|
||||||
|
|
||||||
|
<p> -n, --dry-run</p>
|
||||||
|
|
||||||
|
<p> Show the branches that would be deleted without actually deleting them.</p>
|
||||||
|
|
||||||
|
<p> -f, --force</p>
|
||||||
|
|
||||||
|
<p> Skip the confirmation prompt and delete branches immediately.</p>
|
||||||
|
|
||||||
|
<p> -p, --prune</p>
|
||||||
|
|
||||||
|
<p> Run <code>git fetch --prune</code> before checking for gone branches, to ensure
|
||||||
|
remote-tracking refs are up to date.</p>
|
||||||
|
|
||||||
|
<h2 id="EXAMPLES">EXAMPLES</h2>
|
||||||
|
|
||||||
|
<pre><code>$ git delete-gone-branches
|
||||||
|
|
||||||
|
$ git delete-gone-branches --dry-run
|
||||||
|
|
||||||
|
$ git delete-gone-branches --force
|
||||||
|
|
||||||
|
$ git delete-gone-branches --prune
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<h2 id="AUTHOR">AUTHOR</h2>
|
||||||
|
|
||||||
|
<p>Written by Utsav Sabharwal <<a href="mailto:utsav.sabharwal@sysdig.com" data-bare-link="true">utsav.sabharwal@sysdig.com</a>></p>
|
||||||
|
|
||||||
|
<h2 id="REPORTING-BUGS">REPORTING BUGS</h2>
|
||||||
|
|
||||||
|
<p><<a href="https://github.com/tj/git-extras/issues" data-bare-link="true">https://github.com/tj/git-extras/issues</a>></p>
|
||||||
|
|
||||||
|
<h2 id="SEE-ALSO">SEE ALSO</h2>
|
||||||
|
|
||||||
|
<p><<a href="https://github.com/tj/git-extras" data-bare-link="true">https://github.com/tj/git-extras</a>></p>
|
||||||
|
|
||||||
|
|
||||||
|
<ol class='man-decor man-foot man foot'>
|
||||||
|
<li class='tl'></li>
|
||||||
|
<li class='tc'>March 2026</li>
|
||||||
|
<li class='tr'>git-delete-gone-branches(1)</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
50
man/git-delete-gone-branches.md
Normal file
50
man/git-delete-gone-branches.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
git-delete-gone-branches(1) -- Delete branches whose remote is gone
|
||||||
|
====================================================================
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
|
||||||
|
`git-delete-gone-branches` [-n|--dry-run] [-f|--force] [-p|--prune]
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
Deletes all local branches whose remote-tracking branch has been deleted.
|
||||||
|
This commonly happens after a pull request is merged and the remote branch
|
||||||
|
is removed. By default, lists the branches and prompts for confirmation
|
||||||
|
before deleting.
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
|
||||||
|
-n, --dry-run
|
||||||
|
|
||||||
|
Show the branches that would be deleted without actually deleting them.
|
||||||
|
|
||||||
|
-f, --force
|
||||||
|
|
||||||
|
Skip the confirmation prompt and delete branches immediately.
|
||||||
|
|
||||||
|
-p, --prune
|
||||||
|
|
||||||
|
Run `git fetch --prune` before checking for gone branches, to ensure
|
||||||
|
remote-tracking refs are up to date.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
$ git delete-gone-branches
|
||||||
|
|
||||||
|
$ git delete-gone-branches --dry-run
|
||||||
|
|
||||||
|
$ git delete-gone-branches --force
|
||||||
|
|
||||||
|
$ git delete-gone-branches --prune
|
||||||
|
|
||||||
|
## AUTHOR
|
||||||
|
|
||||||
|
Written by Utsav Sabharwal <<https://github.com/codersofthedark>>
|
||||||
|
|
||||||
|
## REPORTING BUGS
|
||||||
|
|
||||||
|
<<https://github.com/tj/git-extras/issues>>
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
|
||||||
|
<<https://github.com/tj/git-extras>>
|
||||||
183
man/git-extras.1
183
man/git-extras.1
|
|
@ -0,0 +1,183 @@
|
||||||
|
.\" generated with Ronn-NG/v0.10.1
|
||||||
|
.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
|
||||||
|
.TH "GIT\-EXTRAS" "1" "May 2026" "" "Git Extras"
|
||||||
|
.SH "NAME"
|
||||||
|
\fBgit\-extras\fR \- Awesome GIT utilities
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
\fBgit\-extras\fR [\-v,\-\-version] [\-h,\-\-help] [update]
|
||||||
|
.SH "OPTIONS"
|
||||||
|
\-v, \-\-version
|
||||||
|
.P
|
||||||
|
Show git\-extras version number\.
|
||||||
|
.P
|
||||||
|
\-h, \-\-help
|
||||||
|
.P
|
||||||
|
Show this help\. This option can also be used for any of the extras commands\.
|
||||||
|
.P
|
||||||
|
update
|
||||||
|
.P
|
||||||
|
Self update\.
|
||||||
|
.SH "ENVIRONMENT AND CONFIGURATION VARIABLES"
|
||||||
|
\fBgit config \-\-add git\-extras\.default\-branch $BRANCH\fR
|
||||||
|
.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 "\(bu" 4
|
||||||
|
\fBgit\-abort(1)\fR Abort current git operation
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-alias(1)\fR Define, search and show aliases
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-archive\-file(1)\fR Export the current HEAD of the git repository to an archive
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-authors(1)\fR Generate authors report
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-browse\-ci(1)\fR View the CI page for the current repository
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-browse(1)\fR View the web page for the current repository
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-brv(1)\fR List branches sorted by their last commit date
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-bulk(1)\fR Run git commands on multiple repositories
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-changelog(1)\fR Generate a changelog report
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-clear\-soft(1)\fR Soft clean up a repository
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-clear(1)\fR Rigorously clean up a repository
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-coauthor(1)\fR Add a co\-author to the last commit
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-commits\-since(1)\fR Show commit logs since some date
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-continue(1)\fR Continue current git operation
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-contrib(1)\fR Show user's contributions
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-count(1)\fR Show commit count
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-cp(1)\fR Copy a file keeping its history
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-create\-branch(1)\fR Create branches
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-delete\-branch(1)\fR Delete branches
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-delete\-merged\-branches(1)\fR Delete merged branches
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-delete\-squashed\-branches(1)\fR Delete branches that were squashed
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-delete\-submodule(1)\fR Delete submodules
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-delete\-tag(1)\fR Delete tags
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-delta(1)\fR Lists changed files
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-effort(1)\fR Show effort statistics on file(s)
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-feature(1)\fR Create/Merge feature branch
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-force\-clone(1)\fR overwrite local repositories with clone
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-fork(1)\fR Fork a repo on github
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-fresh\-branch(1)\fR Create fresh branches
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-get(1)\fR Clone a Git repository under a configured directory
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-gh\-pages(1)\fR Create the GitHub Pages branch
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-graft(1)\fR Merge and destroy a given branch
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-guilt(1)\fR calculate change between two revisions
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-ignore\-io(1)\fR Get sample gitignore file
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-ignore(1)\fR Add \.gitignore patterns
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-info(1)\fR Returns information on current repository
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-local\-commits(1)\fR List local commits
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-lock(1)\fR Lock a file excluded from version control
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-locked(1)\fR ls files that have been locked
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-magic(1)\fR Automate add/commit/push routines
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-merge\-into(1)\fR Merge one branch into another
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-merge\-repo(1)\fR Merge two repo histories
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-missing(1)\fR Show commits missing from another branch
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-mr(1)\fR Checks out a merge request locally
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-obliterate(1)\fR rewrite past commits to remove some files
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-paste(1)\fR Send patches to pastebin for chat conversations
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-pr(1)\fR Checks out a pull request locally
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-psykorebase(1)\fR Rebase a branch with a merge commit
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-pull\-request(1)\fR Create pull request for GitHub project
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-reauthor(1)\fR Rewrite history to change author's identity
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-rebase\-patch(1)\fR Rebases a patch
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-release(1)\fR Commit, tag and push changes to the repository
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-rename\-branch(1)\fR rename local branch and push to remote
|
||||||
|
.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 "\(bu" 4
|
||||||
|
\fBgit\-rename\-remote(1)\fR Rename a remote
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-rename\-tag(1)\fR Rename a tag
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-repl(1)\fR git read\-eval\-print\-loop
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-reset\-file(1)\fR Reset one file
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-root(1)\fR show path of root
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-scp(1)\fR Copy files to SSH compatible \fBgit\-remote\fR
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-sed(1)\fR replace patterns in git\-controlled files
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-setup(1)\fR Set up a git repository
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-show\-merged\-branches(1)\fR Show merged branches
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-show\-tree(1)\fR show branch tree of commit history
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-show\-unmerged\-branches(1)\fR Show unmerged branches
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-squash(1)\fR squash N last changes up to a ref'ed commit
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-stamp(1)\fR Stamp the last commit message
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-standup(1)\fR Recall the commit history
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-summary(1)\fR Show repository summary
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-sync(1)\fR Sync local branch with remote branch
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-touch(1)\fR Touch and add file to the index
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-undo(1)\fR Remove latest commits
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBgit\-unlock(1)\fR Unlock a file excluded from version control
|
||||||
|
.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>
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
<\fIhttps://github\.com/tj/git\-extras/issues\fR>
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
<\fIhttps://github\.com/tj/git\-extras\fR>
|
||||||
|
|
@ -111,11 +111,9 @@
|
||||||
<li>
|
<li>
|
||||||
<strong><a class="man-ref" href="git-authors.html">git-authors<span class="s">(1)</span></a></strong> Generate authors report</li>
|
<strong><a class="man-ref" href="git-authors.html">git-authors<span class="s">(1)</span></a></strong> Generate authors report</li>
|
||||||
<li>
|
<li>
|
||||||
<strong><a class="man-ref" href="git-browse-ci.html">git-browse-ci<span class="s">(1)</span></a></strong> <var>View the web page for the current repository</var>
|
<strong><a class="man-ref" href="git-browse-ci.html">git-browse-ci<span class="s">(1)</span></a></strong> View the CI page for the current repository</li>
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<strong><a class="man-ref" href="git-browse.html">git-browse<span class="s">(1)</span></a></strong> <var>View the web page for the current repository</var>
|
<strong><a class="man-ref" href="git-browse.html">git-browse<span class="s">(1)</span></a></strong> View the web page for the current repository</li>
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<strong><a class="man-ref" href="git-brv.html">git-brv<span class="s">(1)</span></a></strong> List branches sorted by their last commit date</li>
|
<strong><a class="man-ref" href="git-brv.html">git-brv<span class="s">(1)</span></a></strong> List branches sorted by their last commit date</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
@ -273,7 +271,7 @@
|
||||||
|
|
||||||
<ol class='man-decor man-foot man foot'>
|
<ol class='man-decor man-foot man foot'>
|
||||||
<li class='tl'></li>
|
<li class='tl'></li>
|
||||||
<li class='tc'>February 2026</li>
|
<li class='tc'>May 2026</li>
|
||||||
<li class='tr'>git-extras(1)</li>
|
<li class='tr'>git-extras(1)</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ git-extras(1) -- Awesome GIT utilities
|
||||||
- **git-alias(1)** Define, search and show aliases
|
- **git-alias(1)** Define, search and show aliases
|
||||||
- **git-archive-file(1)** Export the current HEAD of the git repository to an archive
|
- **git-archive-file(1)** Export the current HEAD of the git repository to an archive
|
||||||
- **git-authors(1)** Generate authors report
|
- **git-authors(1)** Generate authors report
|
||||||
- **git-browse-ci(1)** <View the web page for the current repository>
|
- **git-browse-ci(1)** View the CI page for the current repository
|
||||||
- **git-browse(1)** <View the web page for the current repository>
|
- **git-browse(1)** View the web page for the current repository
|
||||||
- **git-brv(1)** List branches sorted by their last commit date
|
- **git-brv(1)** List branches sorted by their last commit date
|
||||||
- **git-bulk(1)** Run git commands on multiple repositories
|
- **git-bulk(1)** Run git commands on multiple repositories
|
||||||
- **git-changelog(1)** Generate a changelog report
|
- **git-changelog(1)** Generate a changelog report
|
||||||
|
|
@ -46,6 +46,7 @@ git-extras(1) -- Awesome GIT utilities
|
||||||
- **git-cp(1)** Copy a file keeping its history
|
- **git-cp(1)** Copy a file keeping its history
|
||||||
- **git-create-branch(1)** Create branches
|
- **git-create-branch(1)** Create branches
|
||||||
- **git-delete-branch(1)** Delete branches
|
- **git-delete-branch(1)** Delete branches
|
||||||
|
- **git-delete-gone-branches(1)** Delete branches whose remote is gone
|
||||||
- **git-delete-merged-branches(1)** Delete merged branches
|
- **git-delete-merged-branches(1)** Delete merged branches
|
||||||
- **git-delete-squashed-branches(1)** Delete branches that were squashed
|
- **git-delete-squashed-branches(1)** Delete branches that were squashed
|
||||||
- **git-delete-submodule(1)** Delete submodules
|
- **git-delete-submodule(1)** Delete submodules
|
||||||
|
|
@ -61,7 +62,7 @@ git-extras(1) -- Awesome GIT utilities
|
||||||
- **git-graft(1)** Merge and destroy a given branch
|
- **git-graft(1)** Merge and destroy a given branch
|
||||||
- **git-guilt(1)** calculate change between two revisions
|
- **git-guilt(1)** calculate change between two revisions
|
||||||
- **git-ignore-io(1)** Get sample gitignore file
|
- **git-ignore-io(1)** Get sample gitignore file
|
||||||
- **git-ignore(1)** Add .gitignore patterns
|
- **git-ignore(1)** Modify or display .gitignore files
|
||||||
- **git-info(1)** Returns information on current repository
|
- **git-info(1)** Returns information on current repository
|
||||||
- **git-local-commits(1)** List local commits
|
- **git-local-commits(1)** List local commits
|
||||||
- **git-lock(1)** Lock a file excluded from version control
|
- **git-lock(1)** Lock a file excluded from version control
|
||||||
|
|
|
||||||
186
man/git-ignore.1
186
man/git-ignore.1
|
|
@ -1,77 +1,88 @@
|
||||||
.\" generated with Ronn/v0.7.3
|
.\" generated with Ronn-NG/v0.10.1
|
||||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
|
||||||
.
|
.TH "GIT\-IGNORE" "1" "June 2026" "" "Git Extras"
|
||||||
.TH "GIT\-IGNORE" "1" "April 2018" "" "Git Extras"
|
|
||||||
.
|
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
\fBgit\-ignore\fR \- Add \.gitignore patterns
|
\fBgit\-ignore\fR \- Modify or display \.gitignore files
|
||||||
.
|
|
||||||
.SH "SYNOPSIS"
|
.SH "SYNOPSIS"
|
||||||
\fBgit\-ignore\fR [<context>] [<pattern> [<pattern>]\.\.\.]
|
\fBgit\-ignore\fR [<context>] [<action>] [\-\-] [<pattern> [<pattern>]\|\.\|\.\|\.]
|
||||||
.
|
|
||||||
.SH "DESCRIPTION"
|
.SH "DESCRIPTION"
|
||||||
Adds the given _pattern_s to a \.gitignore file if it doesn\'t already exist\.
|
Modifies or shows \.gitignore of selected \fIcontext\fR\.
|
||||||
.
|
.P
|
||||||
|
Possible actions are: addition of new \fIpattern\fRs, removal of existing \fIpattern\fRs from gitignore files, opening these files in text editor or simply displaying their contents in current terminal (for details see flags/usage)\.
|
||||||
|
.P
|
||||||
|
Contexts are global (e\.g\. $HOME/\.gitignore or $HOME/\.local/\.git/\.gitignore), private (e\.g\. \|\.\|\.\|\./project/\.git/info/exclude) and local (e\.g\. \|\.\|\.\|\./project/\.gitignore)
|
||||||
|
.P
|
||||||
|
Adding only happens for \fIpattern\fRs that don't already exist in the file; removing \fIpattern\fRs that are not present does not do anything; editing fails if the file is not yet present\.
|
||||||
|
.P
|
||||||
|
At most 1 \fIcontext\fR and 1 \fIaction\fR matter for the program, the ones selected are the latest encountered in the flag list\.
|
||||||
|
.P
|
||||||
|
Default behavior:
|
||||||
|
.IP "\(bu" 4
|
||||||
|
If no flags or arguments are passed, prints gitignores of all 3 \fIcontext\fRs\.
|
||||||
|
.IP "\(bu" 4
|
||||||
|
Passing arguments with no \fIaction\fR implies addition of \fIpattern\fRs to gitignore\.
|
||||||
|
.IP "\(bu" 4
|
||||||
|
If an \fIaction\fR is chosen, but not \fIcontext\fR, \fIaction\fR is performed on local gitignore\.
|
||||||
|
.IP "\(bu" 4
|
||||||
|
If \fIcontext\fR is chosen but not action (not even implied), gitignore of chosen \fIcontext\fR is printed\.
|
||||||
|
.IP "" 0
|
||||||
.SH "OPTIONS"
|
.SH "OPTIONS"
|
||||||
<context>
|
<context>
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
\-l, \-\-local
|
\-l, \-\-local
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
Sets the context to the \.gitignore file in the current working directory\. (default)
|
Sets the context to the \.gitignore file in the current working directory\. (default)
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
\-g, \-\-global
|
\-g, \-\-global
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
Sets the context to the global gitignore file for the current user\.
|
Sets the context to the global gitignore file for the current user\.
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
\-p, \-\-private
|
\-p, \-\-private
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
Sets the context to the private exclude file for the repository (\fB\.git/info/exclude\fR)\.
|
Sets the context to the private exclude file for the repository (\fB\.git/info/exclude\fR)\.
|
||||||
.
|
.P
|
||||||
|
<action>
|
||||||
|
.P
|
||||||
|
\-e, \-\-edit
|
||||||
|
.P
|
||||||
|
Sets action to edit\. This will open gitignore in a text editor (editor selection is the same as for git commit)\. If no \fIcontext\fR is provided local is assumed\. Any \fIpattern\fR; provided in additional arguments is silently omitted\.
|
||||||
|
.P
|
||||||
|
\-r, \-\-remove
|
||||||
|
.P
|
||||||
|
Sets action to remove\. This will look for \fIpattern\fRs in gitignore of selected \fIcontext\fR and remove if finds them\. If no \fIpattern\fR is provided does nothing\. All characters in \fIpattern\fR are treated literally, no interpretation of metacharacters is performed as would be, for example, for a regex pattern\.
|
||||||
.P
|
.P
|
||||||
<pattern>
|
<pattern>
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
A space delimited list of patterns to append to the file in context\.
|
A space delimited list of patterns to append to the file in context\.
|
||||||
.
|
.P
|
||||||
|
\-\-
|
||||||
|
.P
|
||||||
|
End of options delimeter\. Everything to the right of it is treated as positional arguments or more precisely a \fIpattern\fR\.
|
||||||
|
.P
|
||||||
|
\-h, \-\-help
|
||||||
|
.P
|
||||||
|
Print a brief usage summary\.
|
||||||
.SS "PATTERN FORMAT"
|
.SS "PATTERN FORMAT"
|
||||||
Pattern format as described in the git manual
|
Pattern format as described in the git manual
|
||||||
.
|
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
A blank line matches no files, so it can serve as a separator for readability\. To append a blank line use empty quotes ""\.
|
A blank line matches no files, so it can serve as a separator for readability\. To append a blank line use empty quotes ""\.
|
||||||
.
|
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
A line starting with # serves as a comment\. For example, "# This is a comment"
|
A line starting with # serves as a comment\. For example, "# This is a comment"
|
||||||
.
|
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again\. If a negated pattern matches, this will override lower precedence patterns sources\. To use an exclamation ! as command line argument it is best placed between single quotes \'\'\. For example, \'!src\'
|
An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again\. If a negated pattern matches, this will override lower precedence patterns sources\. To use an exclamation ! as command line argument it is best placed between single quotes ''\. For example, '!src'
|
||||||
.
|
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory\. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in git)\.
|
If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory\. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in git)\.
|
||||||
.
|
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the \.gitignore file (relative to the top level of the work tree if not from a \.gitignore file)\.
|
If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the \.gitignore file (relative to the top level of the work tree if not from a \.gitignore file)\.
|
||||||
.
|
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
Otherwise, git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname\. For example, "Documentation/*\.html" matches "Documentation/git\.html" but not "Documentation/ppc/ppc\.html" or "tools/perf/Documentation/perf\.html"\.
|
Otherwise, git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname\. For example, "Documentation/*\.html" matches "Documentation/git\.html" but not "Documentation/ppc/ppc\.html" or "tools/perf/Documentation/perf\.html"\.
|
||||||
.
|
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
A leading slash matches the beginning of the pathname\. For example, "/*\.c" matches "cat\-file\.c" but not "mozilla\-sha1/sha1\.c"\.
|
A leading slash matches the beginning of the pathname\. For example, "/*\.c" matches "cat\-file\.c" but not "mozilla\-sha1/sha1\.c"\.
|
||||||
.
|
|
||||||
.IP "" 0
|
.IP "" 0
|
||||||
.
|
|
||||||
.SH "EXAMPLES"
|
.SH "EXAMPLES"
|
||||||
All arguments are optional so calling git\-ignore alone will display first the global then the local gitignore files:
|
All arguments are optional so calling git\-ignore alone will display global, local and private gitignore files in order:
|
||||||
.
|
|
||||||
.IP "" 4
|
.IP "" 4
|
||||||
.
|
|
||||||
.nf
|
.nf
|
||||||
|
|
||||||
$ git ignore
|
$ git ignore
|
||||||
Global gitignore: /home/alice/\.gitignore
|
Global gitignore: /home/alice/\.gitignore
|
||||||
# Numerous always\-ignore extensions
|
# Numerous always\-ignore extensions
|
||||||
|
|
@ -86,92 +97,101 @@ Global gitignore: /home/alice/\.gitignore
|
||||||
*\.sass\-cache
|
*\.sass\-cache
|
||||||
|
|
||||||
# OS or Editor folders
|
# OS or Editor folders
|
||||||
\.DS_Store
|
\&\.DS_Store
|
||||||
\.Trashes
|
\&\.Trashes
|
||||||
\._*
|
\&\._*
|
||||||
Thumbs\.db
|
Thumbs\.db
|
||||||
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
|
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
|
||||||
Local gitignore: \.gitignore
|
Local gitignore: \.gitignore
|
||||||
\.cache
|
\&\.cache
|
||||||
\.project
|
\&\.project
|
||||||
\.settings
|
\&\.settings
|
||||||
\.tmproj
|
\&\.tmproj
|
||||||
nbproject
|
nbproject
|
||||||
.
|
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
|
||||||
|
Private gitignore: \.git/info/exclude
|
||||||
|
notes\.txt
|
||||||
|
test\.sh
|
||||||
|
\&\.env\.local
|
||||||
|
config\.json
|
||||||
.fi
|
.fi
|
||||||
.
|
|
||||||
.IP "" 0
|
.IP "" 0
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
If you only want to see the global context use the \-\-global argument (for local use \-\-local):
|
If you only want to see the global context use the \-\-global argument (for local use \-\-local, for private use \-\-private):
|
||||||
.
|
|
||||||
.IP "" 4
|
.IP "" 4
|
||||||
.
|
|
||||||
.nf
|
.nf
|
||||||
|
|
||||||
$ git ignore
|
$ git ignore
|
||||||
Global gitignore: /home/alice/\.gitignore
|
Global gitignore: /home/alice/\.gitignore
|
||||||
\.DS_Store
|
\&\.DS_Store
|
||||||
\.Trashes
|
\&\.Trashes
|
||||||
\._*
|
\&\._*
|
||||||
Thumbs\.db
|
Thumbs\.db
|
||||||
.
|
|
||||||
.fi
|
.fi
|
||||||
.
|
|
||||||
.IP "" 0
|
.IP "" 0
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
To quickly append a new pattern to the default/local context simply:
|
To quickly append a new pattern to the default/local context simply:
|
||||||
.
|
|
||||||
.IP "" 4
|
.IP "" 4
|
||||||
.
|
|
||||||
.nf
|
.nf
|
||||||
|
|
||||||
$ git ignore *\.log
|
$ git ignore *\.log
|
||||||
Adding pattern(s) to: \.gitignore
|
Adding pattern(s) to: \.gitignore
|
||||||
\.\.\. adding \'*\.log\'
|
\|\.\|\.\|\. adding '*\.log'
|
||||||
.
|
|
||||||
.fi
|
.fi
|
||||||
.
|
|
||||||
.IP "" 0
|
.IP "" 0
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
You can now configure any patterns without ever using an editor, with a context and pattern arguments: The resulting configuration is also returned for your convenience\.
|
You can now configure any patterns without ever using an editor (or use a shortcut to open file in editor when needed), with a context and pattern arguments: The resulting configuration is also returned for your convenience\.
|
||||||
.
|
|
||||||
.IP "" 4
|
.IP "" 4
|
||||||
.
|
|
||||||
.nf
|
.nf
|
||||||
|
$ git ignore \-\-local "" "# Temporary files" *\.tmp "*\.log" tmp/* "" "# Files I'd like to keep" '!work' ""
|
||||||
$ git ignore \-\-local "" "# Temporary files" *\.tmp "*\.log" tmp/* "" "# Files I\'d like to keep" \'!work\' ""
|
|
||||||
Adding pattern(s) to: \.gitignore
|
Adding pattern(s) to: \.gitignore
|
||||||
\.\.\. adding \'\'
|
\|\.\|\.\|\. adding ''
|
||||||
\.\.\. adding \'# Temporary files\'
|
\|\.\|\.\|\. adding '# Temporary files'
|
||||||
\.\.\. adding \'index\.tmp\'
|
\|\.\|\.\|\. adding '*\.tmp'
|
||||||
\.\.\. adding \'*\.log\'
|
\|\.\|\.\|\. adding '*\.log'
|
||||||
\.\.\. adding \'tmp/*\'
|
\|\.\|\.\|\. adding 'tmp/*'
|
||||||
\.\.\. adding \'\'
|
\|\.\|\.\|\. adding ''
|
||||||
\.\.\. adding \'# Files I\'d like to keep\'
|
\|\.\|\.\|\. adding '# Files I'd like to keep'
|
||||||
\.\.\. adding \'!work\'
|
\|\.\|\.\|\. adding '!work'
|
||||||
\.\.\. adding \'\'
|
\|\.\|\.\|\. adding ''
|
||||||
|
|
||||||
Local gitignore: \.gitignore
|
Local gitignore: \.gitignore
|
||||||
|
|
||||||
# Temporary files
|
# Temporary files
|
||||||
index\.tmp
|
*\.tmp
|
||||||
*\.log
|
*\.log
|
||||||
|
tmp/*
|
||||||
|
|
||||||
# Files I\'d like to keep
|
# Files I'd like to keep
|
||||||
!work
|
!work
|
||||||
.
|
|
||||||
.fi
|
.fi
|
||||||
.
|
|
||||||
.IP "" 0
|
.IP "" 0
|
||||||
.
|
.P
|
||||||
|
Removing patterns works in a similar way\. Select context and pass patterns to be removed from the gitignore file\.
|
||||||
|
.IP "" 4
|
||||||
|
.nf
|
||||||
|
$ git ignore \-l \-r "*\.log"
|
||||||
|
Removing patterns from local gitignore: \.gitignore
|
||||||
|
\|\.\|\.\|\. removing '*\.log'
|
||||||
|
|
||||||
|
Local gitignore: \.gitignore
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*\.tmp
|
||||||
|
tmp/*
|
||||||
|
|
||||||
|
# Files I'd like to keep
|
||||||
|
!work
|
||||||
|
.fi
|
||||||
|
.IP "" 0
|
||||||
|
.P
|
||||||
|
If you want to open gitignore in an editor and do changes manually pass \-e flag:
|
||||||
|
.IP "" 4
|
||||||
|
.nf
|
||||||
|
$ git ignore \-p \-e
|
||||||
|
.fi
|
||||||
|
.IP "" 0
|
||||||
.SH "AUTHOR"
|
.SH "AUTHOR"
|
||||||
Written by Tj Holowaychuk <\fItj@vision\-media\.ca\fR> and Tema Bolshakov <\fItweekane@gmail\.com\fR> and Nick Lombard <\fIgithub@jigsoft\.co\.za\fR>
|
Written by Tj Holowaychuk <\fItj@vision\-media\.ca\fR> and Tema Bolshakov <\fItweekane@gmail\.com\fR> and Nick Lombard <\fIgithub@jigsoft\.co\.za\fR> and Kirill Dergachev <\fIkdergachev1062@gmail\.com\fR>
|
||||||
.
|
|
||||||
.SH "REPORTING BUGS"
|
.SH "REPORTING BUGS"
|
||||||
<\fIhttps://github\.com/tj/git\-extras/issues\fR>
|
<\fIhttps://github\.com/tj/git\-extras/issues\fR>
|
||||||
.
|
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
<\fIhttps://github\.com/tj/git\-extras\fR>
|
<\fIhttps://github\.com/tj/git\-extras\fR>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
<meta http-equiv='content-type' content='text/html;charset=utf-8'>
|
||||||
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
|
<meta name='generator' content='Ronn-NG/v0.10.1 (http://github.com/apjanke/ronn-ng/tree/0.10.1)'>
|
||||||
<title>git-ignore(1) - Add .gitignore patterns</title>
|
<title>git-ignore(1) - Modify or display .gitignore files</title>
|
||||||
<style type='text/css' media='all'>
|
<style type='text/css' media='all'>
|
||||||
/* style: man */
|
/* style: man */
|
||||||
body#manpage {margin:0}
|
body#manpage {margin:0}
|
||||||
|
|
@ -69,57 +69,114 @@
|
||||||
<li class='tr'>git-ignore(1)</li>
|
<li class='tr'>git-ignore(1)</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2 id="NAME">NAME</h2>
|
|
||||||
<p class="man-name">
|
|
||||||
<code>git-ignore</code> - <span class="man-whatis">Add .gitignore patterns</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
<h2 id="NAME">NAME</h2>
|
||||||
|
<p class="man-name">
|
||||||
|
<code>git-ignore</code> - <span class="man-whatis">Modify or display .gitignore files</span>
|
||||||
|
</p>
|
||||||
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
||||||
|
|
||||||
<p><code>git-ignore</code> [<context>] [<pattern> [<pattern>]...]</p>
|
<p><code>git-ignore</code> [<context>] [<action>] [--] [<pattern> [<pattern>]...]</p>
|
||||||
|
|
||||||
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
||||||
|
|
||||||
<p>Adds the given _pattern_s to a .gitignore file if it doesn't already exist.</p>
|
<p>Modifies or shows .gitignore of selected <em>context</em>.</p>
|
||||||
|
|
||||||
|
<p>Possible actions are: addition of new <em>pattern</em>s, removal of existing <em>pattern</em>s from gitignore files, opening these files in text editor or simply displaying their contents in current terminal (for details see flags/usage).</p>
|
||||||
|
|
||||||
|
<p>Contexts are global (e.g. $HOME/.gitignore or $HOME/.local/.git/.gitignore), private (e.g. .../project/.git/info/exclude) and local (e.g. .../project/.gitignore)</p>
|
||||||
|
|
||||||
|
<p>Adding only happens for <em>pattern</em>s that don't already exist in the file; removing <em>pattern</em>s that are not present does not do anything; editing fails if the file is not yet present.</p>
|
||||||
|
|
||||||
|
<p>At most 1 <em>context</em> and 1 <em>action</em> matter for the program, the ones selected are the latest encountered in the flag list.</p>
|
||||||
|
|
||||||
|
<p>Default behavior:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>If no flags or arguments are passed, prints gitignores of all 3 <em>context</em>s.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Passing arguments with no <em>action</em> implies addition of <em>pattern</em>s to gitignore.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>If an <em>action</em> is chosen, but not <em>context</em>, <em>action</em> is performed on local gitignore.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>If <em>context</em> is chosen but not action (not even implied), gitignore of chosen <em>context</em> is printed.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2 id="OPTIONS">OPTIONS</h2>
|
<h2 id="OPTIONS">OPTIONS</h2>
|
||||||
|
|
||||||
<p> <context></p>
|
<p><context></p>
|
||||||
|
|
||||||
<p> -l, --local</p>
|
<p>-l, --local</p>
|
||||||
|
|
||||||
<p> Sets the context to the .gitignore file in the current working directory. (default)</p>
|
<p>Sets the context to the .gitignore file in the current working directory. (default)</p>
|
||||||
|
|
||||||
<p> -g, --global</p>
|
<p>-g, --global</p>
|
||||||
|
|
||||||
<p> Sets the context to the global gitignore file for the current user.</p>
|
<p>Sets the context to the global gitignore file for the current user.</p>
|
||||||
|
|
||||||
<p> -p, --private</p>
|
<p>-p, --private</p>
|
||||||
|
|
||||||
<p> Sets the context to the private exclude file for the repository (<code>.git/info/exclude</code>).</p>
|
<p>Sets the context to the private exclude file for the repository (<code>.git/info/exclude</code>).</p>
|
||||||
|
|
||||||
<p> <pattern></p>
|
<p><action></p>
|
||||||
|
|
||||||
<p> A space delimited list of patterns to append to the file in context.</p>
|
<p>-e, --edit</p>
|
||||||
|
|
||||||
|
<p>Sets action to edit. This will open gitignore in a text editor (editor selection is the same as for git commit). If no <em>context</em> is provided local is assumed. Any <em>pattern</em>; provided in additional arguments is silently omitted.</p>
|
||||||
|
|
||||||
|
<p>-r, --remove</p>
|
||||||
|
|
||||||
|
<p>Sets action to remove. This will look for <em>pattern</em>s in gitignore of selected <em>context</em> and remove if finds them. If no <em>pattern</em> is provided does nothing. All characters in <em>pattern</em> are treated literally, no interpretation of metacharacters is performed as would be, for example, for a regex pattern.</p>
|
||||||
|
|
||||||
|
<p><pattern></p>
|
||||||
|
|
||||||
|
<p>A space delimited list of patterns to append to the file in context.</p>
|
||||||
|
|
||||||
|
<p>--</p>
|
||||||
|
|
||||||
|
<p>End of options delimeter. Everything to the right of it is treated as positional arguments or more precisely a <em>pattern</em>.</p>
|
||||||
|
|
||||||
|
<p>-h, --help</p>
|
||||||
|
|
||||||
|
<p>Print a brief usage summary.</p>
|
||||||
|
|
||||||
<h3 id="PATTERN-FORMAT">PATTERN FORMAT</h3>
|
<h3 id="PATTERN-FORMAT">PATTERN FORMAT</h3>
|
||||||
|
|
||||||
<p>Pattern format as described in the git manual</p>
|
<p>Pattern format as described in the git manual</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><p>A blank line matches no files, so it can serve as a separator for readability. To append a blank line use empty quotes "".</p></li>
|
<li>
|
||||||
<li><p>A line starting with # serves as a comment. For example, "# This is a comment"</p></li>
|
<p>A blank line matches no files, so it can serve as a separator for readability. To append a blank line use empty quotes "".</p>
|
||||||
<li><p>An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again. If a negated pattern matches, this will override lower precedence patterns sources. To use an exclamation ! as command line argument it is best placed between single quotes ''. For example, '!src'</p></li>
|
</li>
|
||||||
<li><p>If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in git).</p></li>
|
<li>
|
||||||
<li><p>If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the .gitignore file (relative to the top level of the work tree if not from a .gitignore file).</p></li>
|
<p>A line starting with # serves as a comment. For example, "# This is a comment"</p>
|
||||||
<li><p>Otherwise, git treats the pattern as a shell glob suitable for consumption by <span class="man-ref">fnmatch<span class="s">(3)</span></span> with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname. For example, "Documentation/*.html" matches "Documentation/git.html" but not "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html".</p></li>
|
</li>
|
||||||
<li><p>A leading slash matches the beginning of the pathname. For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".</p></li>
|
<li>
|
||||||
|
<p>An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again. If a negated pattern matches, this will override lower precedence patterns sources. To use an exclamation ! as command line argument it is best placed between single quotes ''. For example, '!src'</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in git).</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the .gitignore file (relative to the top level of the work tree if not from a .gitignore file).</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Otherwise, git treats the pattern as a shell glob suitable for consumption by <span class="man-ref">fnmatch<span class="s">(3)</span></span> with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname. For example, "Documentation/*.html" matches "Documentation/git.html" but not "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html".</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>A leading slash matches the beginning of the pathname. For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".</p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
<h2 id="EXAMPLES">EXAMPLES</h2>
|
<h2 id="EXAMPLES">EXAMPLES</h2>
|
||||||
|
|
||||||
<p> All arguments are optional so calling git-ignore alone will display first the global then the local gitignore files:</p>
|
<p>All arguments are optional so calling git-ignore alone will display global, local and private gitignore files in order:</p>
|
||||||
|
|
||||||
<pre><code>$ git ignore
|
<pre><code>$ git ignore
|
||||||
Global gitignore: /home/alice/.gitignore
|
Global gitignore: /home/alice/.gitignore
|
||||||
|
|
@ -146,9 +203,15 @@ Local gitignore: .gitignore
|
||||||
.settings
|
.settings
|
||||||
.tmproj
|
.tmproj
|
||||||
nbproject
|
nbproject
|
||||||
|
---------------------------------
|
||||||
|
Private gitignore: .git/info/exclude
|
||||||
|
notes.txt
|
||||||
|
test.sh
|
||||||
|
.env.local
|
||||||
|
config.json
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
|
||||||
<p>If you only want to see the global context use the --global argument (for local use --local):</p>
|
<p>If you only want to see the global context use the --global argument (for local use --local, for private use --private):</p>
|
||||||
|
|
||||||
<pre><code>$ git ignore
|
<pre><code>$ git ignore
|
||||||
Global gitignore: /home/alice/.gitignore
|
Global gitignore: /home/alice/.gitignore
|
||||||
|
|
@ -165,14 +228,14 @@ Adding pattern(s) to: .gitignore
|
||||||
... adding '*.log'
|
... adding '*.log'
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
|
||||||
<p>You can now configure any patterns without ever using an editor, with a context and pattern arguments:
|
<p>You can now configure any patterns without ever using an editor (or use a shortcut to open file in editor when needed), with a context and pattern arguments:
|
||||||
The resulting configuration is also returned for your convenience.</p>
|
The resulting configuration is also returned for your convenience.</p>
|
||||||
|
|
||||||
<pre><code>$ git ignore --local "" "# Temporary files" *.tmp "*.log" tmp/* "" "# Files I'd like to keep" '!work' ""
|
<pre><code>$ git ignore --local "" "# Temporary files" *.tmp "*.log" tmp/* "" "# Files I'd like to keep" '!work' ""
|
||||||
Adding pattern(s) to: .gitignore
|
Adding pattern(s) to: .gitignore
|
||||||
... adding ''
|
... adding ''
|
||||||
... adding '# Temporary files'
|
... adding '# Temporary files'
|
||||||
... adding 'index.tmp'
|
... adding '*.tmp'
|
||||||
... adding '*.log'
|
... adding '*.log'
|
||||||
... adding 'tmp/*'
|
... adding 'tmp/*'
|
||||||
... adding ''
|
... adding ''
|
||||||
|
|
@ -183,17 +246,39 @@ Adding pattern(s) to: .gitignore
|
||||||
Local gitignore: .gitignore
|
Local gitignore: .gitignore
|
||||||
|
|
||||||
# Temporary files
|
# Temporary files
|
||||||
index.tmp
|
*.tmp
|
||||||
*.log
|
*.log
|
||||||
|
tmp/*
|
||||||
|
|
||||||
# Files I'd like to keep
|
# Files I'd like to keep
|
||||||
!work
|
!work
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
|
||||||
|
<p>Removing patterns works in a similar way. Select context and pass patterns to be removed from the gitignore file.</p>
|
||||||
|
|
||||||
|
<pre><code>$ git ignore -l -r "*.log"
|
||||||
|
Removing patterns from local gitignore: .gitignore
|
||||||
|
... removing '*.log'
|
||||||
|
|
||||||
|
Local gitignore: .gitignore
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
tmp/*
|
||||||
|
|
||||||
|
# Files I'd like to keep
|
||||||
|
!work
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>If you want to open gitignore in an editor and do changes manually pass -e flag:</p>
|
||||||
|
|
||||||
|
<pre><code>$ git ignore -p -e
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
<h2 id="AUTHOR">AUTHOR</h2>
|
<h2 id="AUTHOR">AUTHOR</h2>
|
||||||
|
|
||||||
<p>Written by Tj Holowaychuk <<a href="mailto:tj@vision-media.ca" data-bare-link="true">tj@vision-media.ca</a>> and Tema Bolshakov <<a href="mailto:tweekane@gmail.com" data-bare-link="true">tweekane@gmail.com</a>>
|
<p>Written by Tj Holowaychuk <<a href="mailto:tj@vision-media.ca" data-bare-link="true">tj@vision-media.ca</a>> and Tema Bolshakov <<a href="mailto:tweekane@gmail.com" data-bare-link="true">tweekane@gmail.com</a>>
|
||||||
and Nick Lombard <<a href="mailto:github@jigsoft.co.za" data-bare-link="true">github@jigsoft.co.za</a>></p>
|
and Nick Lombard <<a href="mailto:github@jigsoft.co.za" data-bare-link="true">github@jigsoft.co.za</a>&gt and Kirill Dergachev <<a href="mailto:kdergachev1062@gmail.com" data-bare-link="true">kdergachev1062@gmail.com</a>></p>
|
||||||
|
|
||||||
<h2 id="REPORTING-BUGS">REPORTING BUGS</h2>
|
<h2 id="REPORTING-BUGS">REPORTING BUGS</h2>
|
||||||
|
|
||||||
|
|
@ -203,10 +288,9 @@ and Nick Lombard <<a href="mailto:g

|
||||||
|
|
||||||
<p><<a href="https://github.com/tj/git-extras" data-bare-link="true">https://github.com/tj/git-extras</a>></p>
|
<p><<a href="https://github.com/tj/git-extras" data-bare-link="true">https://github.com/tj/git-extras</a>></p>
|
||||||
|
|
||||||
|
|
||||||
<ol class='man-decor man-foot man foot'>
|
<ol class='man-decor man-foot man foot'>
|
||||||
<li class='tl'></li>
|
<li class='tl'></li>
|
||||||
<li class='tc'>April 2018</li>
|
<li class='tc'>June 2026</li>
|
||||||
<li class='tr'>git-ignore(1)</li>
|
<li class='tr'>git-ignore(1)</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,31 @@
|
||||||
git-ignore(1) -- Add .gitignore patterns
|
git-ignore(1) -- Modify or display .gitignore files
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
## SYNOPSIS
|
## SYNOPSIS
|
||||||
|
|
||||||
`git-ignore` [<context>] [<pattern> [<pattern>]...]
|
`git-ignore` [<context>] [<action>] [--] [<pattern> [<pattern>]...]
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
|
|
||||||
Adds the given _pattern_s to a .gitignore file if it doesn't already exist.
|
Modifies or shows .gitignore of selected *context*.
|
||||||
|
|
||||||
|
Possible actions are: addition of new *pattern*s, removal of existing *pattern*s from gitignore files, opening these files in text editor or simply displaying their contents in current terminal (for details see flags/usage).
|
||||||
|
|
||||||
|
Contexts are global (e.g. $HOME/.gitignore or $HOME/.local/.git/.gitignore), private (e.g. .../project/.git/info/exclude) and local (e.g. .../project/.gitignore)
|
||||||
|
|
||||||
|
Adding only happens for *pattern*s that don't already exist in the file; removing *pattern*s that are not present does not do anything; editing fails if the file is not yet present.
|
||||||
|
|
||||||
|
At most 1 *context* and 1 *action* matter for the program, the ones selected are the latest encountered in the flag list.
|
||||||
|
|
||||||
|
Default behavior:
|
||||||
|
|
||||||
|
* If no flags or arguments are passed, prints gitignores of all 3 *context*s.
|
||||||
|
|
||||||
|
* Passing arguments with no *action* implies addition of *pattern*s to gitignore.
|
||||||
|
|
||||||
|
* If an *action* is chosen, but not *context*, *action* is performed on local gitignore.
|
||||||
|
|
||||||
|
* If *context* is chosen but not action (not even implied), gitignore of chosen *context* is printed.
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
|
|
||||||
|
|
@ -25,10 +43,28 @@ Adds the given _pattern_s to a .gitignore file if it doesn't already exist.
|
||||||
|
|
||||||
Sets the context to the private exclude file for the repository (`.git/info/exclude`).
|
Sets the context to the private exclude file for the repository (`.git/info/exclude`).
|
||||||
|
|
||||||
|
<action>
|
||||||
|
|
||||||
|
-e, --edit
|
||||||
|
|
||||||
|
Sets action to edit. This will open gitignore in a text editor (editor selection is the same as for git commit). If no *context* is provided local is assumed. Any *pattern*; provided in additional arguments is silently omitted.
|
||||||
|
|
||||||
|
-r, --remove
|
||||||
|
|
||||||
|
Sets action to remove. This will look for *pattern*s in gitignore of selected *context* and remove if finds them. If no *pattern* is provided does nothing. All characters in *pattern* are treated literally, no interpretation of metacharacters is performed as would be, for example, for a regex pattern.
|
||||||
|
|
||||||
<pattern>
|
<pattern>
|
||||||
|
|
||||||
A space delimited list of patterns to append to the file in context.
|
A space delimited list of patterns to append to the file in context.
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
End of options delimiter. Everything to the right of it is treated as positional arguments or more precisely a *pattern*.
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
|
||||||
|
Print a brief usage summary.
|
||||||
|
|
||||||
### PATTERN FORMAT
|
### PATTERN FORMAT
|
||||||
|
|
||||||
Pattern format as described in the git manual
|
Pattern format as described in the git manual
|
||||||
|
|
@ -43,14 +79,14 @@ Pattern format as described in the git manual
|
||||||
|
|
||||||
* If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the .gitignore file (relative to the top level of the work tree if not from a .gitignore file).
|
* If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the .gitignore file (relative to the top level of the work tree if not from a .gitignore file).
|
||||||
|
|
||||||
* Otherwise, git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname. For example, "Documentation/*.html" matches "Documentation/git.html" but not "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html".
|
* Otherwise, git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM\_PATHNAME flag: wildcards in the pattern will not match a / in the pathname. For example, "Documentation/\*.html" matches "Documentation/git.html" but not "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html".
|
||||||
|
|
||||||
* A leading slash matches the beginning of the pathname. For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
* A leading slash matches the beginning of the pathname. For example, "/\*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
||||||
|
|
||||||
|
|
||||||
## EXAMPLES
|
## EXAMPLES
|
||||||
|
|
||||||
All arguments are optional so calling git-ignore alone will display first the global then the local gitignore files:
|
All arguments are optional so calling git-ignore alone will display global, local and private gitignore files in order:
|
||||||
|
|
||||||
$ git ignore
|
$ git ignore
|
||||||
Global gitignore: /home/alice/.gitignore
|
Global gitignore: /home/alice/.gitignore
|
||||||
|
|
@ -77,8 +113,14 @@ Pattern format as described in the git manual
|
||||||
.settings
|
.settings
|
||||||
.tmproj
|
.tmproj
|
||||||
nbproject
|
nbproject
|
||||||
|
---------------------------------
|
||||||
|
Private gitignore: .git/info/exclude
|
||||||
|
notes.txt
|
||||||
|
test.sh
|
||||||
|
.env.local
|
||||||
|
config.json
|
||||||
|
|
||||||
If you only want to see the global context use the --global argument (for local use --local):
|
If you only want to see the global context use the --global argument (for local use --local, for private use --private):
|
||||||
|
|
||||||
$ git ignore
|
$ git ignore
|
||||||
Global gitignore: /home/alice/.gitignore
|
Global gitignore: /home/alice/.gitignore
|
||||||
|
|
@ -93,34 +135,55 @@ To quickly append a new pattern to the default/local context simply:
|
||||||
Adding pattern(s) to: .gitignore
|
Adding pattern(s) to: .gitignore
|
||||||
... adding '*.log'
|
... adding '*.log'
|
||||||
|
|
||||||
You can now configure any patterns without ever using an editor, with a context and pattern arguments:
|
You can now configure any patterns without ever using an editor (or use a shortcut to open file in editor when needed), with a context and pattern arguments:
|
||||||
The resulting configuration is also returned for your convenience.
|
The resulting configuration is also returned for your convenience.
|
||||||
|
|
||||||
$ git ignore --local "" "# Temporary files" *.tmp "*.log" tmp/* "" "# Files I'd like to keep" '!work' ""
|
$ git ignore --local "" "# Temporary files" *.tmp "*.log" tmp/* "" "# Files I'd like to keep" '!work' ""
|
||||||
Adding pattern(s) to: .gitignore
|
Adding pattern(s) to: .gitignore
|
||||||
... adding ''
|
... adding ''
|
||||||
... adding '# Temporary files'
|
... adding '# Temporary files'
|
||||||
... adding 'index.tmp'
|
... adding '*.tmp'
|
||||||
... adding '*.log'
|
... adding '*.log'
|
||||||
... adding 'tmp/*'
|
... adding 'tmp/*'
|
||||||
... adding ''
|
... adding ''
|
||||||
... adding '# Files I'd like to keep'
|
... adding '# Files I'd like to keep'
|
||||||
... adding '!work'
|
... adding '!work'
|
||||||
... adding ''
|
... adding ''
|
||||||
|
|
||||||
Local gitignore: .gitignore
|
Local gitignore: .gitignore
|
||||||
|
|
||||||
# Temporary files
|
# Temporary files
|
||||||
index.tmp
|
*.tmp
|
||||||
*.log
|
*.log
|
||||||
|
tmp/*
|
||||||
|
|
||||||
# Files I'd like to keep
|
# Files I'd like to keep
|
||||||
!work
|
!work
|
||||||
|
|
||||||
|
Removing patterns works in a similar way. Select context and pass patterns to be removed from the gitignore file.
|
||||||
|
|
||||||
|
$ git ignore -l -r "*.log"
|
||||||
|
Removing patterns from local gitignore: .gitignore
|
||||||
|
... removing '*.log'
|
||||||
|
|
||||||
|
Local gitignore: .gitignore
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
tmp/*
|
||||||
|
|
||||||
|
# Files I'd like to keep
|
||||||
|
!work
|
||||||
|
|
||||||
|
If you want to open gitignore in an editor and do changes manually pass -e flag:
|
||||||
|
|
||||||
|
$ git ignore -p -e
|
||||||
|
|
||||||
|
|
||||||
## AUTHOR
|
## AUTHOR
|
||||||
|
|
||||||
Written by Tj Holowaychuk <<tj@vision-media.ca>> and Tema Bolshakov <<tweekane@gmail.com>>
|
Written by Tj Holowaychuk <<tj@vision-media.ca>> and Tema Bolshakov <<tweekane@gmail.com>>
|
||||||
and Nick Lombard <<github@jigsoft.co.za>>
|
and Nick Lombard <<github@jigsoft.co.za>> and Kirill Dergachev <<kdergachev1062@gmail.com>>
|
||||||
|
|
||||||
## REPORTING BUGS
|
## REPORTING BUGS
|
||||||
|
|
||||||
|
|
|
||||||
18
man/git-mr.1
18
man/git-mr.1
|
|
@ -28,7 +28,7 @@ The name of the remote to fetch from\. Defaults to \fBorigin\fR\.
|
||||||
<url>
|
<url>
|
||||||
.
|
.
|
||||||
.P
|
.P
|
||||||
GitLab merge request URL in the format \fBhttps://gitlab\.tld/owner/repository/merge_requests/453\fR\.
|
GitLab merge request URL in the format \fBhttps://gitlab\.tld/owner/repository/merge_requests/453\fR, or a Forgejo/Codeberg pull request URL in the format \fBhttps://codeberg\.tld/owner/repository/pulls/453\fR\.
|
||||||
.
|
.
|
||||||
.SH "EXAMPLES"
|
.SH "EXAMPLES"
|
||||||
This checks out merge request \fB!51\fR from remote \fBorigin\fR to branch \fBmr/51\fR\.
|
This checks out merge request \fB!51\fR from remote \fBorigin\fR to branch \fBmr/51\fR\.
|
||||||
|
|
@ -46,6 +46,22 @@ Switched to branch \'mr/51\'
|
||||||
.
|
.
|
||||||
.IP "" 0
|
.IP "" 0
|
||||||
.
|
.
|
||||||
|
.P
|
||||||
|
This checks out pull request \fB#51\fR from a Forgejo/Codeberg URL to branch \fBmr/51\fR\.
|
||||||
|
.
|
||||||
|
.IP "" 4
|
||||||
|
.
|
||||||
|
.nf
|
||||||
|
|
||||||
|
$ git mr https://codeberg\.org/owner/repository/pulls/51
|
||||||
|
From codeberg\.org:owner/repository
|
||||||
|
* [new ref] refs/pull/51/head \-> mr/51
|
||||||
|
Switched to branch \'mr/51\'
|
||||||
|
.
|
||||||
|
.fi
|
||||||
|
.
|
||||||
|
.IP "" 0
|
||||||
|
.
|
||||||
.SH "AUTHOR"
|
.SH "AUTHOR"
|
||||||
Written by Étienne BERSAC \fIbersace03@gmail\.com\fR from git\-pr(1)\.
|
Written by Étienne BERSAC \fIbersace03@gmail\.com\fR from git\-pr(1)\.
|
||||||
.
|
.
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,9 @@
|
||||||
<p> <url></p>
|
<p> <url></p>
|
||||||
|
|
||||||
<p> GitLab merge request URL in the format
|
<p> GitLab merge request URL in the format
|
||||||
<code>https://gitlab.tld/owner/repository/merge_requests/453</code>.</p>
|
<code>https://gitlab.tld/owner/repository/merge_requests/453</code>, or a
|
||||||
|
Forgejo/Codeberg pull request URL in the format
|
||||||
|
<code>https://codeberg.tld/owner/repository/pulls/453</code>.</p>
|
||||||
|
|
||||||
<h2 id="EXAMPLES">EXAMPLES</h2>
|
<h2 id="EXAMPLES">EXAMPLES</h2>
|
||||||
|
|
||||||
|
|
@ -106,6 +108,14 @@ From gitlab.com:owner/repository
|
||||||
Switched to branch 'mr/51'
|
Switched to branch 'mr/51'
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
|
||||||
|
<p>This checks out pull request <code>#51</code> from a Forgejo/Codeberg URL to branch <code>mr/51</code>.</p>
|
||||||
|
|
||||||
|
<pre><code>$ git mr https://codeberg.org/owner/repository/pulls/51
|
||||||
|
From codeberg.org:owner/repository
|
||||||
|
* [new ref] refs/pull/51/head -> mr/51
|
||||||
|
Switched to branch 'mr/51'
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
<h2 id="AUTHOR">AUTHOR</h2>
|
<h2 id="AUTHOR">AUTHOR</h2>
|
||||||
|
|
||||||
<p>Written by Étienne BERSAC <a href="mailto:bersace03@gmail.com" data-bare-link="true">bersace03@gmail.com</a> from <a class="man-ref" href="git-pr.html">git-pr<span class="s">(1)</span></a>.</p>
|
<p>Written by Étienne BERSAC <a href="mailto:bersace03@gmail.com" data-bare-link="true">bersace03@gmail.com</a> from <a class="man-ref" href="git-pr.html">git-pr<span class="s">(1)</span></a>.</p>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,9 @@ git-mr(1) -- Checks out a merge request locally
|
||||||
<url>
|
<url>
|
||||||
|
|
||||||
GitLab merge request URL in the format
|
GitLab merge request URL in the format
|
||||||
`https://gitlab.tld/owner/repository/merge_requests/453`.
|
`https://gitlab.tld/owner/repository/merge_requests/453`, or a
|
||||||
|
Forgejo/Codeberg pull request URL in the format
|
||||||
|
`https://codeberg.tld/owner/repository/pulls/453`.
|
||||||
|
|
||||||
|
|
||||||
## EXAMPLES
|
## EXAMPLES
|
||||||
|
|
@ -33,6 +35,13 @@ This checks out merge request `!51` from remote `origin` to branch `mr/51`.
|
||||||
* [new ref] refs/merge-requests/51/head -> mr/51
|
* [new ref] refs/merge-requests/51/head -> mr/51
|
||||||
Switched to branch 'mr/51'
|
Switched to branch 'mr/51'
|
||||||
|
|
||||||
|
This checks out pull request `#51` from a Forgejo/Codeberg URL to branch `mr/51`.
|
||||||
|
|
||||||
|
$ git mr https://codeberg.org/owner/repository/pulls/51
|
||||||
|
From codeberg.org:owner/repository
|
||||||
|
* [new ref] refs/pull/51/head -> mr/51
|
||||||
|
Switched to branch 'mr/51'
|
||||||
|
|
||||||
## AUTHOR
|
## AUTHOR
|
||||||
|
|
||||||
Written by Étienne BERSAC <bersace03@gmail.com> from git-pr(1).
|
Written by Étienne BERSAC <bersace03@gmail.com> from git-pr(1).
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ git-scp(1) -- Copy files to SSH compatible `git-remote`
|
||||||
## SYNOPSIS
|
## SYNOPSIS
|
||||||
|
|
||||||
`git scp` -h|help|?
|
`git scp` -h|help|?
|
||||||
`git scp` <remote> [<commits>...|<path>...]
|
`git scp` [options] <remote> [<commits>...|<path>...]
|
||||||
`git rscp` <remote> <path>
|
`git rscp` [options] <remote> <path>
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
|
|
||||||
|
|
@ -29,6 +29,18 @@ Internally this script uses `rsync` and not `scp` as the name suggests.
|
||||||
|
|
||||||
The <paths> parameters, when given, are used to limit the diff to the named paths (you can give directory names and get diff for all files under them).
|
The <paths> parameters, when given, are used to limit the diff to the named paths (you can give directory names and get diff for all files under them).
|
||||||
|
|
||||||
|
-v, --verbose
|
||||||
|
|
||||||
|
Print what will be synced before syncing: the diff for `git scp`, or a preview (rsync dry-run) for `git rscp`.
|
||||||
|
|
||||||
|
-i, --interactive
|
||||||
|
|
||||||
|
Same as `--verbose`, but also prompt for confirmation before syncing. Implies `--verbose`
|
||||||
|
|
||||||
|
-n, --dry-run
|
||||||
|
|
||||||
|
Show what would be synced, change nothing. Implies `--verbose`, and no staging or remote writes/deletes happen.
|
||||||
|
|
||||||
## GIT CONFIGS
|
## GIT CONFIGS
|
||||||
|
|
||||||
To sanitize files using `dos2unix` before copying files
|
To sanitize files using `dos2unix` before copying files
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ git-count(1) git-count
|
||||||
git-cp(1) git-cp
|
git-cp(1) git-cp
|
||||||
git-create-branch(1) git-create-branch
|
git-create-branch(1) git-create-branch
|
||||||
git-delete-branch(1) git-delete-branch
|
git-delete-branch(1) git-delete-branch
|
||||||
|
git-delete-gone-branches(1) git-delete-gone-branches
|
||||||
git-delete-merged-branches(1) git-delete-merged-branches
|
git-delete-merged-branches(1) git-delete-merged-branches
|
||||||
git-delete-squashed-branches(1) git-delete-squashed-branches
|
git-delete-squashed-branches(1) git-delete-squashed-branches
|
||||||
git-delete-submodule(1) git-delete-submodule
|
git-delete-submodule(1) git-delete-submodule
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections.abc import Callable # compat
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, List, Dict, Any # compat
|
from typing import Any
|
||||||
|
|
||||||
# This file checks Bash and Shell scripts for violations not found with
|
# This file checks Bash and Shell scripts for violations not found with
|
||||||
# shellcheck or existing methods. You can use it in several ways:
|
# shellcheck or existing methods. You can use it in several ways:
|
||||||
|
|
@ -21,7 +23,7 @@ from typing import Callable, List, Dict, Any # compat
|
||||||
# Check to ensure all regular expressions are working as intended:
|
# Check to ensure all regular expressions are working as intended:
|
||||||
# $ ./scripts/checkstyle.py --internal-test-regex
|
# $ ./scripts/checkstyle.py --internal-test-regex
|
||||||
|
|
||||||
Rule = Dict[str, Any]
|
Rule = dict[str, Any]
|
||||||
|
|
||||||
class c:
|
class c:
|
||||||
RED = '\033[91m'
|
RED = '\033[91m'
|
||||||
|
|
@ -91,7 +93,7 @@ def noVerboseRedirectionFixer(line: str, m: Any) -> str:
|
||||||
|
|
||||||
return f'{prestr}&>/dev/null{poststr}'
|
return f'{prestr}&>/dev/null{poststr}'
|
||||||
|
|
||||||
def lintfile(file: Path, rules: List[Rule], options: Dict[str, Any]):
|
def lintfile(file: Path, rules: list[Rule], options: dict[str, Any]):
|
||||||
content_arr = file.read_text().split('\n')
|
content_arr = file.read_text().split('\n')
|
||||||
|
|
||||||
for line_i, line in enumerate(content_arr):
|
for line_i, line in enumerate(content_arr):
|
||||||
|
|
@ -100,15 +102,15 @@ def lintfile(file: Path, rules: List[Rule], options: Dict[str, Any]):
|
||||||
|
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
should_run = False
|
should_run = False
|
||||||
if 'sh' in rule['fileTypes']:
|
if 'sh' in rule['fileTypes'] and file.name.endswith('.sh'):
|
||||||
if file.name.endswith('.sh'):
|
should_run = True
|
||||||
should_run = True
|
if 'bash' in rule['fileTypes'] and (
|
||||||
if 'bash' in rule['fileTypes']:
|
file.name.endswith('.bash') or file.name.endswith('.bats') or file.name.startswith('git-')
|
||||||
if file.name.endswith('.bash') or file.name.endswith('.bats') or file.name.startswith('git-'):
|
):
|
||||||
should_run = True
|
should_run = True
|
||||||
|
|
||||||
if options['verbose']:
|
if options['verbose']:
|
||||||
print(f'{str(file)}: {should_run}')
|
print(f'{file!s}: {should_run}')
|
||||||
|
|
||||||
if not should_run:
|
if not should_run:
|
||||||
continue
|
continue
|
||||||
|
|
@ -134,7 +136,7 @@ def lintfile(file: Path, rules: List[Rule], options: Dict[str, Any]):
|
||||||
file.write_text('\n'.join(content_arr))
|
file.write_text('\n'.join(content_arr))
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
rules: List[Rule] = [
|
rules: list[Rule] = [
|
||||||
{
|
{
|
||||||
'name': 'no-pwd-capture',
|
'name': 'no-pwd-capture',
|
||||||
'regex': '(?P<match>\\$\\(pwd\\))',
|
'regex': '(?P<match>\\$\\(pwd\\))',
|
||||||
|
|
@ -238,7 +240,12 @@ def main():
|
||||||
lintfile(p, rules, options)
|
lintfile(p, rules, options)
|
||||||
else:
|
else:
|
||||||
for file in Path.cwd().glob('**/*'):
|
for file in Path.cwd().glob('**/*'):
|
||||||
if '.git' in str(file.absolute()):
|
skip = False
|
||||||
|
for name in ('.git', 'vendor', '__pycache__'):
|
||||||
|
if name in str(file.absolute()):
|
||||||
|
skip = True
|
||||||
|
break
|
||||||
|
if skip:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if file.is_file():
|
if file.is_file():
|
||||||
|
|
@ -255,8 +262,8 @@ def main():
|
||||||
|
|
||||||
# exit
|
# exit
|
||||||
if grand_total == 0:
|
if grand_total == 0:
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
Originally, the tests were written in pytest. However, tests are in the process of being converted to Bats so coverage can be calculated.
|
Tests are written in Bats so coverage can be calculated for shell commands.
|
||||||
|
|
||||||
## Bats Testing
|
## Bats Testing
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ We require a somewhat recent version of Bats. Version v1.8.1 is tested in CI. On
|
||||||
bats ./tests
|
bats ./tests
|
||||||
```
|
```
|
||||||
|
|
||||||
We highly recommend adding tests for new features and fixes.
|
We highly recommend adding Bats tests for new features and fixes.
|
||||||
|
|
||||||
### Code Coverage
|
### Code Coverage
|
||||||
|
|
||||||
|
|
@ -21,49 +21,3 @@ bashcov -- bats ./tests
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, the report will be generated in `./coverage/index.html`.
|
By default, the report will be generated in `./coverage/index.html`.
|
||||||
|
|
||||||
## Python Testing
|
|
||||||
|
|
||||||
The test part depends on:
|
|
||||||
|
|
||||||
* python >= 3.11
|
|
||||||
* poetry >= 1.8.0
|
|
||||||
* pytest >= 8.1.2
|
|
||||||
* gitpython >= 3.1.43
|
|
||||||
|
|
||||||
So the versions are higher than above is recommended.
|
|
||||||
|
|
||||||
### How to run the tests
|
|
||||||
|
|
||||||
1. Install `poetry`
|
|
||||||
2. Install the dependencies via `poetry install`
|
|
||||||
3. Run `poetry run pytest`
|
|
||||||
|
|
||||||
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. 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.
|
|
||||||
|
|
||||||
### 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,
|
|
||||||
|
|
||||||
1. The `git-alias` should have a test suite, so create `test_git_alias.py` in the directory `test`
|
|
||||||
2. Create a test class `TestGitAlias` in the `test_git_alias.py`
|
|
||||||
3. Create a test case `test_init`, and some test fixtures can be used, `temp_repo`, `named_temp_repo` etc.
|
|
||||||
* `temp_repo` is module scoped fixture which create a temporary directory and available in the test suite `test_git_alias.py`.
|
|
||||||
* `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
|
|
||||||
|
|
||||||
* [poetry](https://github.com/python-poetry/poetry)
|
|
||||||
* [pytest](https://github.com/pytest-dev/pytest/)
|
|
||||||
* [git python](https://github.com/gitpython-developers/GitPython)
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
# Sharing fixtures
|
|
||||||
# Ref: https://docs.pytest.org/en/6.2.x/fixture.html#scope-sharing-fixtures-across-classes-modules-packages-or-session
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from helper import TempRepository
|
|
||||||
|
|
||||||
|
|
||||||
def create_repo(dirname=None):
|
|
||||||
repo = TempRepository(dirname)
|
|
||||||
repo.create_tmp_file() # tmp_file_a
|
|
||||||
repo.create_tmp_file() # tmp_file_b
|
|
||||||
repo.switch_cwd_under_repo()
|
|
||||||
return repo
|
|
||||||
|
|
||||||
|
|
||||||
def init_repo_git_status(repo):
|
|
||||||
git = repo.get_repo_git()
|
|
||||||
git.add(".")
|
|
||||||
git.config("--local", "user.name", "test")
|
|
||||||
git.config("--local", "user.email", "test@git-extras.com")
|
|
||||||
git.commit("-m", "chore: initial commit")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
|
||||||
def temp_repo():
|
|
||||||
repo = create_repo()
|
|
||||||
init_repo_git_status(repo)
|
|
||||||
return repo
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
|
||||||
def named_temp_repo(request):
|
|
||||||
dirname = request.param
|
|
||||||
repo = create_repo(dirname)
|
|
||||||
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
|
|
||||||
|
|
@ -4,6 +4,7 @@ source "$BATS_TEST_DIRNAME/test_util.sh"
|
||||||
|
|
||||||
setup_file() {
|
setup_file() {
|
||||||
test_util.setup_file
|
test_util.setup_file
|
||||||
|
test_util.install_command archive-file
|
||||||
}
|
}
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
|
|
@ -37,7 +38,7 @@ setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "archive file on any not tags branch with default branch" {
|
@test "archive file on any not tags branch with default branch" {
|
||||||
skip "Not working as expected"
|
git config git-extras.default-branch main
|
||||||
|
|
||||||
run git archive-file
|
run git archive-file
|
||||||
assert_success
|
assert_success
|
||||||
|
|
@ -58,9 +59,32 @@ setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "archive file on dirname has backslash" {
|
@test "archive file on dirname has backslash" {
|
||||||
skip
|
local repo_dir="$BATS_TEST_TMPDIR/backslash\\dir"
|
||||||
|
mkdir "$repo_dir"
|
||||||
|
cd "$repo_dir"
|
||||||
|
|
||||||
|
test_util.git_init
|
||||||
|
printf '%s\n' 'data' > tmpfile
|
||||||
|
git add .
|
||||||
|
git commit -m 'test: add data'
|
||||||
|
git checkout -b default
|
||||||
|
|
||||||
|
run git archive-file
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
local describe_output=
|
||||||
|
describe_output=$(git describe --always --long)
|
||||||
|
assert_file_exists "backslash-dir.$describe_output.default.zip"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "archive file on tag name has slash" {
|
@test "archive file on tag name has slash" {
|
||||||
skip
|
git tag --delete 0.1.0
|
||||||
|
git tag 0.1.0/slash -m 'bump: 0.1.0'
|
||||||
|
|
||||||
|
run git archive-file
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
local describe_output=
|
||||||
|
describe_output=$(git describe --always --long)
|
||||||
|
assert_file_exists "${PWD##*/}.${describe_output//\//-}.zip"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,16 @@ get_ci_uri() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mock_uname() {
|
||||||
|
local output=$1
|
||||||
|
local mock_bin="$BATS_TEST_TMPDIR/bin"
|
||||||
|
|
||||||
|
mkdir -p "$mock_bin"
|
||||||
|
printf '#!/usr/bin/env bash\nprintf "%%s\\n" "%s"\n' "$output" > "$mock_bin/uname"
|
||||||
|
chmod +x "$mock_bin/uname"
|
||||||
|
PATH="$mock_bin:$PATH"
|
||||||
|
}
|
||||||
|
|
||||||
@test "works with mac and github" {
|
@test "works with mac and github" {
|
||||||
get_ci_uri 'github'
|
get_ci_uri 'github'
|
||||||
local expected_url=$REPLY
|
local expected_url=$REPLY
|
||||||
|
|
@ -96,6 +106,110 @@ get_ci_uri() {
|
||||||
assert_success
|
assert_success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "works with WSL and github" {
|
||||||
|
get_ci_uri 'github'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname microsoft
|
||||||
|
|
||||||
|
git remote add upstream https://github.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse-ci upstream
|
||||||
|
assert_line "powershell.exe -NoProfile start $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with WSL and gitlab" {
|
||||||
|
get_ci_uri 'gitlab'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname microsoft
|
||||||
|
|
||||||
|
git remote add upstream https://gitlab.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse-ci upstream
|
||||||
|
assert_line "powershell.exe -NoProfile start $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with WSL and bitbucket" {
|
||||||
|
get_ci_uri 'bitbucket'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname microsoft
|
||||||
|
|
||||||
|
git remote add upstream https://bitbucket.org/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse-ci upstream
|
||||||
|
assert_line "powershell.exe -NoProfile start $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with linux without Microsoft kernel and github" {
|
||||||
|
get_ci_uri 'github'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname no-micro-soft
|
||||||
|
|
||||||
|
git remote add upstream https://github.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse-ci upstream
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with linux without Microsoft kernel and gitlab" {
|
||||||
|
get_ci_uri 'gitlab'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname no-micro-soft
|
||||||
|
|
||||||
|
git remote add upstream https://gitlab.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse-ci upstream
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with linux without Microsoft kernel and bitbucket" {
|
||||||
|
get_ci_uri 'bitbucket'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname no-micro-soft
|
||||||
|
|
||||||
|
git remote add upstream https://bitbucket.org/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse-ci upstream
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "falls back to xdg-open on an unknown OS with github" {
|
||||||
|
get_ci_uri 'github'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
|
||||||
|
git remote add upstream https://github.com/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse-ci upstream
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "falls back to xdg-open on an unknown OS with gitlab" {
|
||||||
|
get_ci_uri 'gitlab'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
|
||||||
|
git remote add upstream https://gitlab.com/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse-ci upstream
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "falls back to xdg-open on an unknown OS with bitbucket" {
|
||||||
|
get_ci_uri 'bitbucket'
|
||||||
|
local expected_url=$REPLY
|
||||||
|
|
||||||
|
git remote add upstream https://bitbucket.org/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse-ci upstream
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "opens an empty URL for an unknown site" {
|
||||||
|
git remote add upstream https://unknown-site.com/tj/git-extras.git
|
||||||
|
|
||||||
|
OSTYPE=unique-system run git browse-ci upstream
|
||||||
|
assert_output "xdg-open "
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
@test "works with linux and gitlab" {
|
@test "works with linux and gitlab" {
|
||||||
get_ci_uri 'gitlab'
|
get_ci_uri 'gitlab'
|
||||||
local expected_url=$REPLY
|
local expected_url=$REPLY
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,16 @@ get_file_uri() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mock_uname() {
|
||||||
|
local output=$1
|
||||||
|
local mock_bin="$BATS_TEST_TMPDIR/bin"
|
||||||
|
|
||||||
|
mkdir -p "$mock_bin"
|
||||||
|
printf '#!/usr/bin/env bash\nprintf "%%s\\n" "%s"\n' "$output" > "$mock_bin/uname"
|
||||||
|
chmod +x "$mock_bin/uname"
|
||||||
|
PATH="$mock_bin:$PATH"
|
||||||
|
}
|
||||||
|
|
||||||
@test "works with mac and github" {
|
@test "works with mac and github" {
|
||||||
get_file_uri github ./browse_this
|
get_file_uri github ./browse_this
|
||||||
local expected_url=$REPLY
|
local expected_url=$REPLY
|
||||||
|
|
@ -102,6 +112,148 @@ get_file_uri() {
|
||||||
assert_success
|
assert_success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "works with WSL and github" {
|
||||||
|
get_file_uri github ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname microsoft
|
||||||
|
|
||||||
|
git remote add upstream https://github.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse upstream ./browse_this
|
||||||
|
assert_line "powershell.exe -NoProfile start $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with WSL and gitlab" {
|
||||||
|
get_file_uri gitlab ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname microsoft
|
||||||
|
|
||||||
|
git remote add upstream https://gitlab.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse upstream ./browse_this
|
||||||
|
assert_line "powershell.exe -NoProfile start $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with WSL and bitbucket" {
|
||||||
|
get_file_uri bitbucket ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname microsoft
|
||||||
|
|
||||||
|
git remote add upstream https://bitbucket.org/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse upstream ./browse_this
|
||||||
|
assert_line "powershell.exe -NoProfile start $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with linux without Microsoft kernel and github" {
|
||||||
|
get_file_uri github ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname no-micro-soft
|
||||||
|
|
||||||
|
git remote add upstream https://github.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse upstream ./browse_this
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with linux without Microsoft kernel and gitlab" {
|
||||||
|
get_file_uri gitlab ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname no-micro-soft
|
||||||
|
|
||||||
|
git remote add upstream https://gitlab.com/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse upstream ./browse_this
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "works with linux without Microsoft kernel and bitbucket" {
|
||||||
|
get_file_uri bitbucket ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
mock_uname no-micro-soft
|
||||||
|
|
||||||
|
git remote add upstream https://bitbucket.org/tj/git-extras
|
||||||
|
OSTYPE=linux run git browse upstream ./browse_this
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "falls back to xdg-open on an unknown OS with github" {
|
||||||
|
get_file_uri github ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
|
||||||
|
git remote add upstream https://github.com/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse upstream ./browse_this
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "falls back to xdg-open on an unknown OS with gitlab" {
|
||||||
|
get_file_uri gitlab ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
|
||||||
|
git remote add upstream https://gitlab.com/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse upstream ./browse_this
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "falls back to xdg-open on an unknown OS with bitbucket" {
|
||||||
|
get_file_uri bitbucket ./browse_this
|
||||||
|
local expected_url=$REPLY
|
||||||
|
|
||||||
|
git remote add upstream https://bitbucket.org/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse upstream ./browse_this
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "adds github line numbers" {
|
||||||
|
get_file_uri github ./browse_this
|
||||||
|
local expected_url="$REPLY#L10-L20"
|
||||||
|
|
||||||
|
git remote add upstream https://github.com/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse upstream ./browse_this 10 20
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "adds gitlab line numbers" {
|
||||||
|
get_file_uri gitlab ./browse_this
|
||||||
|
local expected_url="$REPLY#L10-20"
|
||||||
|
|
||||||
|
git remote add upstream https://gitlab.com/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse upstream ./browse_this 10 20
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "adds bitbucket line numbers" {
|
||||||
|
get_file_uri bitbucket ./browse_this
|
||||||
|
local expected_url="$REPLY#lines-10:20"
|
||||||
|
|
||||||
|
git remote add upstream https://bitbucket.org/tj/git-extras
|
||||||
|
OSTYPE=unique-system run git browse upstream ./browse_this 10 20
|
||||||
|
assert_output "xdg-open $expected_url"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "opens an unknown site repository" {
|
||||||
|
git remote add upstream https://unknown-site.com/tj/git-extras.git
|
||||||
|
|
||||||
|
OSTYPE=unique-system run git browse upstream
|
||||||
|
assert_output "xdg-open https://unknown-site.com/tj/git-extras"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "does not add file or line information for an unknown site" {
|
||||||
|
git remote add upstream https://unknown-site.com/tj/git-extras.git
|
||||||
|
|
||||||
|
OSTYPE=unique-system run git browse upstream ./browse_this 10 20
|
||||||
|
assert_output "xdg-open https://unknown-site.com/tj/git-extras"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
@test "works with linux and gitlab" {
|
@test "works with linux and gitlab" {
|
||||||
get_file_uri gitlab ./browse_this
|
get_file_uri gitlab ./browse_this
|
||||||
local expected_url=$REPLY
|
local expected_url=$REPLY
|
||||||
|
|
|
||||||
68
tests/git-mr.bats
Normal file
68
tests/git-mr.bats
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
# 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 ./tracked
|
||||||
|
git add ./tracked
|
||||||
|
git commit -m 'Initial commit'
|
||||||
|
|
||||||
|
# Local bare "remote" standing in for a real GitLab/Forgejo instance,
|
||||||
|
# with a merge-requests ref and a pull ref pointing at the same commit.
|
||||||
|
remote_dir="$BATS_TEST_TMPDIR/remote.git"
|
||||||
|
git init --bare --initial-branch main "$remote_dir"
|
||||||
|
git push "$remote_dir" HEAD:refs/merge-requests/51/head
|
||||||
|
git push "$remote_dir" HEAD:refs/pull/51/head
|
||||||
|
|
||||||
|
git remote add origin "$remote_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "checks out a merge request by numeric id" {
|
||||||
|
run git mr 51
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "refs/merge-requests/51/head"
|
||||||
|
|
||||||
|
run git rev-parse --abbrev-ref HEAD
|
||||||
|
assert_output 'mr/51'
|
||||||
|
|
||||||
|
run git config --get branch.mr/51.merge
|
||||||
|
assert_output 'refs/merge-requests/51/head'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "checks out a merge request by numeric id and explicit remote" {
|
||||||
|
run git mr 51 "$remote_dir"
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "refs/merge-requests/51/head"
|
||||||
|
|
||||||
|
run git rev-parse --abbrev-ref HEAD
|
||||||
|
assert_output 'mr/51'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "checks out a Forgejo/Codeberg pull request by URL" {
|
||||||
|
# git-mr only recognizes http(s) URLs, so rewrite the fake Codeberg URL
|
||||||
|
# to our local bare "remote" via insteadOf rather than needing a real
|
||||||
|
# HTTP(S) endpoint.
|
||||||
|
git config --local url."$remote_dir".insteadOf "https://codeberg.org/owner/repository.git"
|
||||||
|
|
||||||
|
run git mr "https://codeberg.org/owner/repository/pulls/51"
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "refs/pull/51/head"
|
||||||
|
|
||||||
|
run git rev-parse --abbrev-ref HEAD
|
||||||
|
assert_output 'mr/51'
|
||||||
|
|
||||||
|
run git config --get branch.mr/51.merge
|
||||||
|
assert_output 'refs/pull/51/head'
|
||||||
|
|
||||||
|
run git config --get branch.mr/51.remote
|
||||||
|
assert_output 'https://codeberg.org/owner/repository.git'
|
||||||
|
}
|
||||||
46
tests/git-scp.bats
Normal file
46
tests/git-scp.bats
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
source "$BATS_TEST_DIRNAME/test_util.sh"
|
||||||
|
|
||||||
|
setup_file() {
|
||||||
|
test_util.setup_file
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
test_util.cd_test
|
||||||
|
|
||||||
|
test_util.git_init
|
||||||
|
printf '%s\n' 'hello' > tracked.txt
|
||||||
|
git add tracked.txt
|
||||||
|
git commit -m 'Initial commit'
|
||||||
|
|
||||||
|
# never created: neither test below should ever touch it
|
||||||
|
git remote add fake "$BATS_TEST_TMPDIR/never-created"
|
||||||
|
|
||||||
|
printf '%s\n' 'world' >> tracked.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "dry-run previews the sync without changing anything" {
|
||||||
|
run git scp -n fake
|
||||||
|
assert_success
|
||||||
|
assert_line -p 'tracked.txt'
|
||||||
|
assert_line -p 'DRY RUN'
|
||||||
|
|
||||||
|
run git status --short
|
||||||
|
assert_line -p 'M tracked.txt'
|
||||||
|
|
||||||
|
run test -e "$BATS_TEST_TMPDIR/never-created"
|
||||||
|
assert_failure
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "interactive mode aborts without syncing when declined" {
|
||||||
|
run bash -c 'echo n | git scp -i fake'
|
||||||
|
assert_success
|
||||||
|
assert_line -p 'Aborted, nothing synced.'
|
||||||
|
|
||||||
|
run git status --short
|
||||||
|
assert_line -p 'M tracked.txt'
|
||||||
|
|
||||||
|
run test -e "$BATS_TEST_TMPDIR/never-created"
|
||||||
|
assert_failure
|
||||||
|
}
|
||||||
130
tests/helper.py
130
tests/helper.py
|
|
@ -1,130 +0,0 @@
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
from git import Repo, GitCommandError
|
|
||||||
|
|
||||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
GIT_EXTRAS_BIN = os.path.abspath(os.path.join(CURRENT_DIR, "..", "bin"))
|
|
||||||
GIT_EXTRAS_HELPER = os.path.abspath(os.path.join(CURRENT_DIR, "..", "helper"))
|
|
||||||
|
|
||||||
GITHUB_ORIGIN = "https://github.com/tj/git-extras.git"
|
|
||||||
GITLAB_ORIGIN = "https://gitlab.com/tj/git-extras.git"
|
|
||||||
BITBUCKET_ORIGIN = "https://bitbucket.org/tj/git-extras.git"
|
|
||||||
|
|
||||||
|
|
||||||
class TempRepository:
|
|
||||||
def __init__(self, repo_work_dir=None):
|
|
||||||
self._system_tmpdir = tempfile.gettempdir()
|
|
||||||
if repo_work_dir is None:
|
|
||||||
repo_work_dir = tempfile.mkdtemp()
|
|
||||||
else:
|
|
||||||
repo_work_dir = os.path.join(self._system_tmpdir, repo_work_dir)
|
|
||||||
self._cwd = repo_work_dir
|
|
||||||
self._tempdirname = self._cwd[len(self._system_tmpdir) + 1 :]
|
|
||||||
self._git_repo = Repo.init(repo_work_dir, b="default")
|
|
||||||
self._files = []
|
|
||||||
self.change_origin_to_github()
|
|
||||||
|
|
||||||
def switch_cwd_under_repo(self):
|
|
||||||
os.chdir(self._cwd)
|
|
||||||
print(f"The current work directory has switched to {self._cwd}")
|
|
||||||
|
|
||||||
def get_cwd(self):
|
|
||||||
return self._cwd
|
|
||||||
|
|
||||||
def get_repo_dirname(self):
|
|
||||||
return self._tempdirname
|
|
||||||
|
|
||||||
def get_repo_git(self):
|
|
||||||
return self._git_repo.git
|
|
||||||
|
|
||||||
def get_file(self, index):
|
|
||||||
return self._files[index]
|
|
||||||
|
|
||||||
def get_filename(self, index):
|
|
||||||
file = self._files[index]
|
|
||||||
return file[1:]
|
|
||||||
|
|
||||||
def get_files(self):
|
|
||||||
return self._files
|
|
||||||
|
|
||||||
def create_tmp_dir(self):
|
|
||||||
tmp_dir = tempfile.mkdtemp()
|
|
||||||
return tmp_dir
|
|
||||||
|
|
||||||
def create_tmp_file(self, temp_dir=None):
|
|
||||||
if temp_dir is None:
|
|
||||||
temp_dir = self._cwd
|
|
||||||
|
|
||||||
tmp_file = tempfile.mkstemp(dir=temp_dir)
|
|
||||||
self._files.append(tmp_file[1])
|
|
||||||
return tmp_file
|
|
||||||
|
|
||||||
def remove_tmp_file(self, file_path):
|
|
||||||
os.remove(file_path)
|
|
||||||
print(f"File {file_path} has been removed")
|
|
||||||
|
|
||||||
def writefile(self, temp_file, data):
|
|
||||||
if data is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
with open(temp_file, "w", encoding="utf-8") as f:
|
|
||||||
f.write(data)
|
|
||||||
|
|
||||||
def teardown(self):
|
|
||||||
shutil.rmtree(self._cwd, ignore_errors=True)
|
|
||||||
print(f"The temp directory {self._cwd} has been removed")
|
|
||||||
|
|
||||||
def invoke_extras_command(self, name, *params):
|
|
||||||
command_name = "git-" + name
|
|
||||||
print(f"Invoke the git-extras command - {command_name} at {self._cwd}")
|
|
||||||
script = [os.path.join(GIT_EXTRAS_BIN, command_name), *list(params)]
|
|
||||||
print(f"Run the script \"{' '.join(script)}\"")
|
|
||||||
return subprocess.run(script, capture_output=True)
|
|
||||||
|
|
||||||
def invoke_installed_extras_command(self, name, *params):
|
|
||||||
command_name = "git-" + name
|
|
||||||
print(f"Invoke the git-extras command - {command_name} at {self._cwd}")
|
|
||||||
origin_extras_command = os.path.join(GIT_EXTRAS_BIN, command_name)
|
|
||||||
temp_extras_command = os.path.join(self._cwd, command_name)
|
|
||||||
helpers = [
|
|
||||||
os.path.join(GIT_EXTRAS_HELPER, "git-extra-utility"),
|
|
||||||
os.path.join(GIT_EXTRAS_HELPER, "is-git-repo"),
|
|
||||||
]
|
|
||||||
|
|
||||||
if not os.path.exists(temp_extras_command):
|
|
||||||
whole = []
|
|
||||||
with open(temp_extras_command, "w") as t:
|
|
||||||
for helper in helpers:
|
|
||||||
with open(helper) as h:
|
|
||||||
content = h.read()
|
|
||||||
whole.extend(content.splitlines())
|
|
||||||
with open(origin_extras_command) as o:
|
|
||||||
content = o.read()
|
|
||||||
first, *rest = content.splitlines()
|
|
||||||
whole.extend(rest)
|
|
||||||
whole.insert(0, first)
|
|
||||||
t.write("\n".join(whole))
|
|
||||||
print(f"Update file {temp_extras_command}")
|
|
||||||
os.chmod(temp_extras_command, 0o775)
|
|
||||||
|
|
||||||
script = [temp_extras_command, *params]
|
|
||||||
print(f'Run the script "{script}"')
|
|
||||||
return subprocess.run(script, capture_output=True)
|
|
||||||
|
|
||||||
def change_origin(self, origin_url):
|
|
||||||
try:
|
|
||||||
self._git_repo.git.remote("add", "origin", origin_url)
|
|
||||||
except GitCommandError as err:
|
|
||||||
print(err)
|
|
||||||
self._git_repo.git.remote("set-url", "origin", origin_url)
|
|
||||||
|
|
||||||
def change_origin_to_github(self):
|
|
||||||
self.change_origin(GITHUB_ORIGIN)
|
|
||||||
|
|
||||||
def change_origin_to_gitlab(self):
|
|
||||||
self.change_origin(GITLAB_ORIGIN)
|
|
||||||
|
|
||||||
def change_origin_to_bitbucket(self):
|
|
||||||
self.change_origin(BITBUCKET_ORIGIN)
|
|
||||||
32
tests/is-git-repo.bats
Normal file
32
tests/is-git-repo.bats
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
source "$BATS_TEST_DIRNAME/test_util.sh"
|
||||||
|
|
||||||
|
setup_file() {
|
||||||
|
test_util.setup_file
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
test_util.cd_test
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "is_git_repo succeeds inside a normal (non-bare) repository" {
|
||||||
|
test_util.git_init
|
||||||
|
|
||||||
|
run bash "$BATS_TEST_DIRNAME/../helper/is-git-repo"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "is_git_repo succeeds inside a bare repository (see #1238)" {
|
||||||
|
git init --bare --initial-branch main repo.git
|
||||||
|
cd repo.git
|
||||||
|
|
||||||
|
run bash "$BATS_TEST_DIRNAME/../helper/is-git-repo"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "is_git_repo fails outside of a repository" {
|
||||||
|
run bash "$BATS_TEST_DIRNAME/../helper/is-git-repo"
|
||||||
|
assert_failure
|
||||||
|
assert_output 'Not a git repo!'
|
||||||
|
}
|
||||||
148
tests/poetry.lock
generated
148
tests/poetry.lock
generated
|
|
@ -1,148 +0,0 @@
|
||||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "codespell"
|
|
||||||
version = "2.4.0"
|
|
||||||
description = "Fix common misspellings in text files"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{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 = ["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"
|
|
||||||
version = "0.4.6"
|
|
||||||
description = "Cross-platform colored terminal text."
|
|
||||||
optional = false
|
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
|
||||||
files = [
|
|
||||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
|
||||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gitdb"
|
|
||||||
version = "4.0.11"
|
|
||||||
description = "Git Object Database"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"},
|
|
||||||
{file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
smmap = ">=3.0.1,<6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gitpython"
|
|
||||||
version = "3.1.43"
|
|
||||||
description = "GitPython is a Python library used to interact with Git repositories"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"},
|
|
||||||
{file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
gitdb = ">=4.0.1,<5"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"]
|
|
||||||
test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iniconfig"
|
|
||||||
version = "2.0.0"
|
|
||||||
description = "brain-dead simple config-ini parsing"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
|
||||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "packaging"
|
|
||||||
version = "24.2"
|
|
||||||
description = "Core utilities for Python packages"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
|
|
||||||
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pluggy"
|
|
||||||
version = "1.5.0"
|
|
||||||
description = "plugin and hook calling mechanisms for python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
|
||||||
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["pre-commit", "tox"]
|
|
||||||
testing = ["pytest", "pytest-benchmark"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pytest"
|
|
||||||
version = "8.1.2"
|
|
||||||
description = "pytest: simple powerful testing with Python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "pytest-8.1.2-py3-none-any.whl", hash = "sha256:6c06dc309ff46a05721e6fd48e492a775ed8165d2ecdf57f156a80c7e95bb142"},
|
|
||||||
{file = "pytest-8.1.2.tar.gz", hash = "sha256:f3c45d1d5eed96b01a2aea70dee6a4a366d51d38f9957768083e4fecfc77f3ef"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
|
||||||
iniconfig = "*"
|
|
||||||
packaging = "*"
|
|
||||||
pluggy = ">=1.4,<2.0"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smmap"
|
|
||||||
version = "5.0.1"
|
|
||||||
description = "A pure Python implementation of a sliding window memory map manager"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"},
|
|
||||||
{file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "testpath"
|
|
||||||
version = "0.6.0"
|
|
||||||
description = "Test utilities for code working with files and commands"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">= 3.5"
|
|
||||||
files = [
|
|
||||||
{file = "testpath-0.6.0-py3-none-any.whl", hash = "sha256:8ada9f80a2ac6fb0391aa7cdb1a7d11cfa8429f693eda83f74dde570fe6fa639"},
|
|
||||||
{file = "testpath-0.6.0.tar.gz", hash = "sha256:2f1b97e6442c02681ebe01bd84f531028a7caea1af3825000f52345c30285e0f"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
test = ["pytest"]
|
|
||||||
|
|
||||||
[metadata]
|
|
||||||
lock-version = "2.0"
|
|
||||||
python-versions = "^3.12"
|
|
||||||
content-hash = "fcf0ba3dd6655735da5c1e21ba32b94011d32bc5a30fbdaf31146d3820c1442a"
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
[tool.poetry]
|
|
||||||
name = "git-extras-tests"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "tests for git extras"
|
|
||||||
package-mode = false
|
|
||||||
authors = ["vanpipy <vanpipy@gmail.com>"]
|
|
||||||
license = "MIT"
|
|
||||||
readme = "README.md"
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
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.4"
|
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
|
||||||
minversion = "7.4"
|
|
||||||
addopts = "-ra -q"
|
|
||||||
testpaths = ["."]
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
# Exclude a variety of commonly ignored directories.
|
|
||||||
exclude = [
|
|
||||||
".bzr",
|
|
||||||
".direnv",
|
|
||||||
".eggs",
|
|
||||||
".git",
|
|
||||||
".git-rewrite",
|
|
||||||
".hg",
|
|
||||||
".ipynb_checkpoints",
|
|
||||||
".mypy_cache",
|
|
||||||
".nox",
|
|
||||||
".pants.d",
|
|
||||||
".pyenv",
|
|
||||||
".pytest_cache",
|
|
||||||
".pytype",
|
|
||||||
".ruff_cache",
|
|
||||||
".svn",
|
|
||||||
".tox",
|
|
||||||
".venv",
|
|
||||||
".vscode",
|
|
||||||
"__pypackages__",
|
|
||||||
"_build",
|
|
||||||
"buck-out",
|
|
||||||
"build",
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"site-packages",
|
|
||||||
"venv",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Same as Black.
|
|
||||||
line-length = 88
|
|
||||||
indent-width = 4
|
|
||||||
|
|
||||||
# Assume Python 3.9
|
|
||||||
target-version = "py39"
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
|
||||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
||||||
select = ["E4", "E7", "E9", "F"]
|
|
||||||
ignore = []
|
|
||||||
|
|
||||||
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
||||||
fixable = ["ALL"]
|
|
||||||
unfixable = []
|
|
||||||
|
|
||||||
# Allow unused variables when underscore-prefixed.
|
|
||||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
||||||
|
|
||||||
[tool.ruff.format]
|
|
||||||
# Like Black, use double quotes for strings.
|
|
||||||
quote-style = "double"
|
|
||||||
|
|
||||||
# Like Black, indent with spaces, rather than tabs.
|
|
||||||
indent-style = "space"
|
|
||||||
|
|
||||||
# Like Black, respect magic trailing commas.
|
|
||||||
skip-magic-trailing-comma = false
|
|
||||||
|
|
||||||
# Like Black, automatically detect the appropriate line ending.
|
|
||||||
line-ending = "auto"
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
from git import GitCommandError
|
|
||||||
|
|
||||||
|
|
||||||
class TestGitAbort:
|
|
||||||
def test_init(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
tmp_file = temp_repo.get_file(0)
|
|
||||||
git.branch("A")
|
|
||||||
git.branch("B")
|
|
||||||
git.checkout("A")
|
|
||||||
temp_repo.writefile(tmp_file, "a")
|
|
||||||
git.add(".")
|
|
||||||
git.commit("-m", "A")
|
|
||||||
git.checkout("B")
|
|
||||||
temp_repo.writefile(tmp_file, "b")
|
|
||||||
git.add(".")
|
|
||||||
git.commit("-m", "B")
|
|
||||||
git.status()
|
|
||||||
|
|
||||||
def test_cherry_pick(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
try:
|
|
||||||
git.cherry_pick("A")
|
|
||||||
except GitCommandError as err:
|
|
||||||
print(err)
|
|
||||||
result = git.status()
|
|
||||||
assert "Unmerged path" in result
|
|
||||||
temp_repo.invoke_extras_command("abort")
|
|
||||||
result = git.status()
|
|
||||||
assert "nothing to commit, working tree clean" in result
|
|
||||||
|
|
||||||
def test_merge(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
try:
|
|
||||||
git.merge("A")
|
|
||||||
except GitCommandError as err:
|
|
||||||
print(err)
|
|
||||||
result = git.status()
|
|
||||||
assert "Unmerged path" in result
|
|
||||||
temp_repo.invoke_extras_command("abort")
|
|
||||||
result = git.status()
|
|
||||||
assert "nothing to commit, working tree clean" in result
|
|
||||||
|
|
||||||
def test_rebase(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
try:
|
|
||||||
git.rebase("A")
|
|
||||||
except GitCommandError as err:
|
|
||||||
print(err)
|
|
||||||
result = git.status()
|
|
||||||
assert "Unmerged path" in result
|
|
||||||
temp_repo.invoke_extras_command("abort")
|
|
||||||
result = git.status()
|
|
||||||
assert "nothing to commit, working tree clean" in result
|
|
||||||
|
|
||||||
def test_revert(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
try:
|
|
||||||
git.revert("A")
|
|
||||||
except GitCommandError as err:
|
|
||||||
print(err)
|
|
||||||
result = git.status()
|
|
||||||
assert "Unmerged path" in result
|
|
||||||
temp_repo.invoke_extras_command("abort")
|
|
||||||
result = git.status()
|
|
||||||
assert "nothing to commit, working tree clean" in result
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
class TestGitAlias:
|
|
||||||
def test_init(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
git.config("--global", "alias.globalalias", "status")
|
|
||||||
git.config("--global", "alias.x", "status")
|
|
||||||
git.config("--local", "alias.localalias", "status")
|
|
||||||
git.config("--local", "alias.y", "status")
|
|
||||||
|
|
||||||
def test_list_all(self, temp_repo):
|
|
||||||
actual = temp_repo.invoke_extras_command("alias")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "globalalias = status" in actual
|
|
||||||
assert "x = status" in actual
|
|
||||||
assert "localalias = status" in actual
|
|
||||||
assert "y = status" in actual
|
|
||||||
|
|
||||||
def test_list_all_globally(self, temp_repo):
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "--global")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "globalalias = status" in actual
|
|
||||||
|
|
||||||
def test_list_all_locally(self, temp_repo):
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "--local")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "localalias = status" in actual
|
|
||||||
|
|
||||||
def test_search_globally(self, temp_repo):
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "--global", "global")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "globalalias = status" in actual
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "--global", "local")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "" == actual
|
|
||||||
|
|
||||||
def test_search_locally(self, temp_repo):
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "--local", "local")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "localalias = status" in actual
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "--local", "global")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "" == actual
|
|
||||||
|
|
||||||
def test_get_alias_globally_and_defaultly(self, temp_repo):
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "globalalias")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "globalalias = status" in actual
|
|
||||||
|
|
||||||
def test_set_alias_globally_and_defaultly(self, temp_repo):
|
|
||||||
temp_repo.invoke_extras_command("alias", "globalalias", "diff")
|
|
||||||
actual = temp_repo.invoke_extras_command("alias")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "globalalias = diff" in actual
|
|
||||||
|
|
||||||
def test_get_alias_locally(self, temp_repo):
|
|
||||||
actual = temp_repo.invoke_extras_command("alias", "--local", "localalias")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "localalias = status" in actual
|
|
||||||
|
|
||||||
def test_set_alias_locally(self, temp_repo):
|
|
||||||
temp_repo.invoke_extras_command("alias", "--local", "localalias", "diff")
|
|
||||||
actual = temp_repo.invoke_extras_command("alias")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert "localalias = diff" in actual
|
|
||||||
|
|
||||||
def test_teardown(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
git.config("--global", "--unset", "alias.globalalias")
|
|
||||||
git.config("--global", "--unset", "alias.x")
|
|
||||||
git.config("--local", "--unset", "alias.localalias")
|
|
||||||
git.config("--local", "--unset", "alias.y")
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
import os
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
class TestGitArchiveFile:
|
|
||||||
def test_init(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
tmp_file = temp_repo.get_file(0)
|
|
||||||
temp_repo.writefile(tmp_file, "data")
|
|
||||||
git.add(".")
|
|
||||||
git.commit("-m", "test: add data")
|
|
||||||
git.tag("0.1.0", "-m", "bump: 0.1.0")
|
|
||||||
|
|
||||||
def test_archive_file_on_tags_branch(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
git.checkout("-b", "tags0.1.0")
|
|
||||||
temp_repo.invoke_installed_extras_command("archive-file")
|
|
||||||
filename = "{0}.{1}.zip".format(temp_repo.get_repo_dirname(), git.describe())
|
|
||||||
assert filename in os.listdir()
|
|
||||||
|
|
||||||
def test_archive_file_on_any_not_tags_branch_without_default_branch(
|
|
||||||
self, temp_repo
|
|
||||||
):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
git.checkout("-b", "not-tags-branch")
|
|
||||||
temp_repo.invoke_installed_extras_command("archive-file")
|
|
||||||
filename = "{0}.{1}.{2}.zip".format(
|
|
||||||
temp_repo.get_repo_dirname(),
|
|
||||||
git.describe("--always", "--long"),
|
|
||||||
"not-tags-branch",
|
|
||||||
)
|
|
||||||
assert filename in os.listdir()
|
|
||||||
|
|
||||||
def test_archive_file_on_any_not_tags_branch_with_default_branch(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
git.checkout("default")
|
|
||||||
git.config("git-extras.default-branch", "default")
|
|
||||||
temp_repo.invoke_installed_extras_command("archive-file")
|
|
||||||
filename = "{0}.{1}.zip".format(
|
|
||||||
temp_repo.get_repo_dirname(), git.describe("--always", "--long")
|
|
||||||
)
|
|
||||||
assert filename in os.listdir()
|
|
||||||
|
|
||||||
def test_archive_file_on_branch_name_has_slash(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
git.checkout("-b", "feature/slash")
|
|
||||||
temp_repo.invoke_installed_extras_command("archive-file")
|
|
||||||
filename = "{0}.{1}.{2}.zip".format(
|
|
||||||
temp_repo.get_repo_dirname(),
|
|
||||||
git.describe("--always", "--long"),
|
|
||||||
"feature-slash",
|
|
||||||
)
|
|
||||||
assert filename in os.listdir()
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("named_temp_repo", ["backslash\\dir"], indirect=True)
|
|
||||||
def test_archive_file_on_dirname_has_backslash(self, named_temp_repo):
|
|
||||||
named_temp_repo.invoke_installed_extras_command("archive-file")
|
|
||||||
git = named_temp_repo.get_repo_git()
|
|
||||||
filename = "{0}.{1}.{2}.zip".format(
|
|
||||||
"backslash-dir", git.describe("--always", "--long"), "default"
|
|
||||||
)
|
|
||||||
assert filename in os.listdir()
|
|
||||||
|
|
||||||
def test_archive_file_on_tag_name_has_slash(self, temp_repo):
|
|
||||||
temp_repo.switch_cwd_under_repo()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
git.checkout("default")
|
|
||||||
git.tag("--delete", "0.1.0")
|
|
||||||
git.tag("0.1.0/slash", "-m", "bump: 0.1.0")
|
|
||||||
temp_repo.invoke_installed_extras_command("archive-file")
|
|
||||||
description_include_version = git.describe("--always", "--long")
|
|
||||||
filename = "{0}.{1}.zip".format(
|
|
||||||
temp_repo.get_repo_dirname(), description_include_version.replace("/", "-")
|
|
||||||
)
|
|
||||||
assert filename in os.listdir()
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
expected_authors_list = (
|
|
||||||
"test <test@git-extras.com>\ntestagain <testagain@git-extras.com>\n"
|
|
||||||
)
|
|
||||||
expected_authors_list_without_email = "test\ntestagain\n"
|
|
||||||
authors_file = "AUTHORS"
|
|
||||||
|
|
||||||
|
|
||||||
class TestGitAuthors:
|
|
||||||
def test_init(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
tmp_file = temp_repo.get_file(0)
|
|
||||||
temp_repo.writefile(tmp_file, "A")
|
|
||||||
git.add(".")
|
|
||||||
git.commit("-m", "test: add data A")
|
|
||||||
git.config("--local", "user.name", "testagain")
|
|
||||||
git.config("--local", "user.email", "testagain@git-extras.com")
|
|
||||||
temp_repo.writefile(tmp_file, "B")
|
|
||||||
git.add(".")
|
|
||||||
git.commit("-m", "test: add data B")
|
|
||||||
|
|
||||||
def test_output_authors_has_email_without_any_parameter(self, temp_repo):
|
|
||||||
temp_repo.get_repo_git()
|
|
||||||
temp_repo.invoke_extras_command("authors")
|
|
||||||
with open(authors_file) as f:
|
|
||||||
content = f.read()
|
|
||||||
print(content)
|
|
||||||
print(expected_authors_list)
|
|
||||||
assert content == expected_authors_list
|
|
||||||
|
|
||||||
def test_list_authors_has_email_defaultly(self, temp_repo):
|
|
||||||
temp_repo.get_repo_git()
|
|
||||||
actual = temp_repo.invoke_extras_command("authors", "--list")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert actual == expected_authors_list
|
|
||||||
actual = temp_repo.invoke_extras_command("authors", "-l")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert actual == expected_authors_list
|
|
||||||
|
|
||||||
def test_list_authors_has_not_email(self, temp_repo):
|
|
||||||
temp_repo.get_repo_git()
|
|
||||||
actual = temp_repo.invoke_extras_command("authors", "--list", "--no-email")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert actual == expected_authors_list_without_email
|
|
||||||
actual = temp_repo.invoke_extras_command("authors", "-l", "--no-email")
|
|
||||||
actual = actual.stdout.decode()
|
|
||||||
assert actual == expected_authors_list_without_email
|
|
||||||
|
|
@ -1,244 +0,0 @@
|
||||||
from testpath import MockCommand, modified_env
|
|
||||||
|
|
||||||
UNKNOWN_SITE_ORIGIN = "https://unknown-site.com/tj/git-extras.git"
|
|
||||||
|
|
||||||
|
|
||||||
def get_file_uri(mode, filename, git):
|
|
||||||
commit_hash = git.rev_parse("HEAD")
|
|
||||||
if mode == "github":
|
|
||||||
return "https://github.com/tj/git-extras/blob/" + commit_hash + "/" + filename
|
|
||||||
if mode == "gitlab":
|
|
||||||
return "https://gitlab.com/tj/git-extras/-/blob/" + commit_hash + "/" + filename
|
|
||||||
if mode == "bitbucket":
|
|
||||||
return "https://bitbucket.org/tj/git-extras/src/" + commit_hash + "/" + filename
|
|
||||||
|
|
||||||
|
|
||||||
class TestGitBrowse:
|
|
||||||
def test_browse_github_file_on_mac(self, temp_repo):
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with modified_env({"OSTYPE": "darwin"}), MockCommand("open") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("github", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_file_on_mac(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with modified_env({"OSTYPE": "darwin"}), MockCommand("open") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("gitlab", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_file_on_mac(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with modified_env({"OSTYPE": "darwin"}), MockCommand("open") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("bitbucket", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_file_on_git_bash_on_window(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with modified_env({"OSTYPE": "msys"}), MockCommand("start") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("github", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_file_on_git_bash_on_window(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with modified_env({"OSTYPE": "msys"}), MockCommand("start") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("gitlab", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_file_on_git_bash_on_window(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with modified_env({"OSTYPE": "msys"}), MockCommand("start") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("bitbucket", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_file_on_WSL_with_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "microsoft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("powershell.exe") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("github", filename, git)
|
|
||||||
openCommand.assert_called(["-NoProfile", "start", expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_file_on_WSL_with_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "microsoft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("powershell.exe") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("gitlab", filename, git)
|
|
||||||
openCommand.assert_called(["-NoProfile", "start", expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_file_on_WSL_with_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "microsoft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("powershell.exe") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("bitbucket", filename, git)
|
|
||||||
openCommand.assert_called(["-NoProfile", "start", expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_file_on_WSL_without_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "no-micro-soft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("github", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_file_on_WSL_without_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "no-micro-soft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("gitlab", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_file_on_WSL_without_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "no-micro-soft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("bitbucket", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_file_not_mac_or_msys_or_linux(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("github", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_file_not_mac_or_msys_or_linux(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("gitlab", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_file_not_mac_or_msys_or_linux(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename)
|
|
||||||
expected_url = get_file_uri("bitbucket", filename, git)
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_file_with_line_number(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename, "10", "20")
|
|
||||||
expected_url = get_file_uri("github", filename, git)
|
|
||||||
openCommand.assert_called([expected_url + "#L10-L20"])
|
|
||||||
|
|
||||||
def test_browse_gitlab_file_with_line_number(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename, "10", "20")
|
|
||||||
expected_url = get_file_uri("gitlab", filename, git)
|
|
||||||
openCommand.assert_called([expected_url + "#L10-20"])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_file_with_line_number(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
git = temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename, "10", "20")
|
|
||||||
expected_url = get_file_uri("bitbucket", filename, git)
|
|
||||||
openCommand.assert_called([expected_url + "#lines-10:20"])
|
|
||||||
|
|
||||||
def test_browse_unknown_site_file(self, temp_repo):
|
|
||||||
temp_repo.change_origin(UNKNOWN_SITE_ORIGIN)
|
|
||||||
temp_repo.get_repo_git()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin")
|
|
||||||
openCommand.assert_called([UNKNOWN_SITE_ORIGIN[0:-4]])
|
|
||||||
|
|
||||||
def test_browse_unknown_site_file_with_line_number(self, temp_repo):
|
|
||||||
temp_repo.get_repo_git()
|
|
||||||
filename = temp_repo.get_filename(0)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse", "origin", filename, "10", "20")
|
|
||||||
openCommand.assert_called([UNKNOWN_SITE_ORIGIN[0:-4]])
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
from testpath import MockCommand, modified_env
|
|
||||||
|
|
||||||
UNKNOWN_SITE_ORIGIN = "https://unknown-site.com/tj/git-extras.git"
|
|
||||||
|
|
||||||
|
|
||||||
def get_ci_uri_by_domain(mode):
|
|
||||||
if mode == "github":
|
|
||||||
return "https://github.com/tj/git-extras/actions"
|
|
||||||
if mode == "gitlab":
|
|
||||||
return "https://gitlab.com/tj/git-extras/-/pipelines"
|
|
||||||
if mode == "bitbucket":
|
|
||||||
return "https://bitbucket.org/tj/git-extras/addon/pipelines/home"
|
|
||||||
|
|
||||||
|
|
||||||
class TestGitBrowse:
|
|
||||||
def test_browse_github_ci_on_mac(self, temp_repo):
|
|
||||||
with modified_env({"OSTYPE": "darwin"}), MockCommand("open") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("github")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_ci_on_mac(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
with modified_env({"OSTYPE": "darwin"}), MockCommand("open") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("gitlab")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_ci_on_mac(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
with modified_env({"OSTYPE": "darwin"}), MockCommand("open") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("bitbucket")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_ci_on_git_bash_on_window(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
with modified_env({"OSTYPE": "msys"}), MockCommand("start") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("github")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_ci_on_git_bash_on_window(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
with modified_env({"OSTYPE": "msys"}), MockCommand("start") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("gitlab")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_ci_on_git_bash_on_window(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
with modified_env({"OSTYPE": "msys"}), MockCommand("start") as openCommand:
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("bitbucket")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_ci_on_WSL_with_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "microsoft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("powershell.exe") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("github")
|
|
||||||
openCommand.assert_called(["-NoProfile", "start", expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_ci_on_WSL_with_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "microsoft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("powershell.exe") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("gitlab")
|
|
||||||
openCommand.assert_called(["-NoProfile", "start", expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_ci_on_WSL_with_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "microsoft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("powershell.exe") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("bitbucket")
|
|
||||||
openCommand.assert_called(["-NoProfile", "start", expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_ci_on_WSL_without_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "no-micro-soft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("github")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_ci_on_WSL_without_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "no-micro-soft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("gitlab")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_ci_on_WSL_without_microsoft_key(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "linux"}),
|
|
||||||
MockCommand.fixed_output("uname", "no-micro-soft"),
|
|
||||||
MockCommand.fixed_output("command", "/powershell.exe"),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("bitbucket")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_github_ci_not_mac_or_msys_or_linux(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_github()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("github")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_gitlab_ci_not_mac_or_msys_or_linux(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_gitlab()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("gitlab")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_bitbucket_ci_not_mac_or_msys_or_linux(self, temp_repo):
|
|
||||||
temp_repo.change_origin_to_bitbucket()
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
expected_url = get_ci_uri_by_domain("bitbucket")
|
|
||||||
openCommand.assert_called([expected_url])
|
|
||||||
|
|
||||||
def test_browse_unknown_site_file(self, temp_repo):
|
|
||||||
temp_repo.change_origin(UNKNOWN_SITE_ORIGIN)
|
|
||||||
with (
|
|
||||||
modified_env({"OSTYPE": "unique-system"}),
|
|
||||||
MockCommand("xdg-open") as openCommand,
|
|
||||||
):
|
|
||||||
temp_repo.invoke_extras_command("browse-ci", "origin")
|
|
||||||
openCommand.assert_called([""])
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -13,6 +13,29 @@ test_util.setup_file() {
|
||||||
PATH="$BATS_TEST_DIRNAME/../bin:$PATH"
|
PATH="$BATS_TEST_DIRNAME/../bin:$PATH"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_util.install_command() {
|
||||||
|
local command_name="git-$1"
|
||||||
|
local install_dir="$BATS_FILE_TMPDIR/bin"
|
||||||
|
local target="$install_dir/$command_name"
|
||||||
|
|
||||||
|
mkdir -p "$install_dir"
|
||||||
|
{
|
||||||
|
head -n 1 "$BATS_TEST_DIRNAME/../bin/$command_name"
|
||||||
|
cat "$BATS_TEST_DIRNAME/../helper/reset-env"
|
||||||
|
cat "$BATS_TEST_DIRNAME/../helper/git-extra-utility"
|
||||||
|
if ! grep -qx "$command_name" "$BATS_TEST_DIRNAME/../not_need_git_repo"; then
|
||||||
|
cat "$BATS_TEST_DIRNAME/../helper/is-git-repo"
|
||||||
|
fi
|
||||||
|
if grep -qx "$command_name" "$BATS_TEST_DIRNAME/../need_git_commit"; then
|
||||||
|
cat "$BATS_TEST_DIRNAME/../helper/has-git-commit"
|
||||||
|
fi
|
||||||
|
tail -n +2 "$BATS_TEST_DIRNAME/../bin/$command_name"
|
||||||
|
} > "$target"
|
||||||
|
chmod 775 "$target"
|
||||||
|
|
||||||
|
PATH="$install_dir:$PATH"
|
||||||
|
}
|
||||||
|
|
||||||
test_util.cd_test() {
|
test_util.cd_test() {
|
||||||
cd "$BATS_TEST_TMPDIR"
|
cd "$BATS_TEST_TMPDIR"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue