From 2f4b57d52b98a6b8b303f6b2228e05be62de83cb Mon Sep 17 00:00:00 2001 From: oikarinen <7252104+oikarinen@users.noreply.github.com> Date: Thu, 28 Nov 2024 10:50:29 +0200 Subject: [PATCH] feat: add git-continue (#1176) * feat: add git-continue Also refactor `git-abort` to parse action from the called file name. There's a promise for this in #865 but that was a long time ago. * fix: add git-continue address review comments, fix the script, remove unnecessary testpath import. --- Commands.md | 11 +++- bin/git-abort | 51 +++++++++++---- bin/git-continue | 1 + etc/git-extras-completion.zsh | 1 + man/git-continue.1 | 19 ++++++ man/git-continue.html | 114 ++++++++++++++++++++++++++++++++++ man/git-continue.md | 30 +++++++++ man/git-extras.1 | 2 + man/git-extras.html | 4 +- man/git-extras.md | 1 + man/index.txt | 1 + tests/README.md | 3 +- tests/conftest.py | 12 +++- tests/test_git_continue.py | 84 +++++++++++++++++++++++++ 14 files changed, 314 insertions(+), 20 deletions(-) create mode 120000 bin/git-continue create mode 100644 man/git-continue.1 create mode 100644 man/git-continue.html create mode 100644 man/git-continue.md create mode 100644 tests/test_git_continue.py diff --git a/Commands.md b/Commands.md index 9fc626ee8..de98cef4c 100644 --- a/Commands.md +++ b/Commands.md @@ -12,6 +12,7 @@ - [`git clear-soft`](#git-clear-soft) - [`git coauthor`](#git-coauthor) - [`git commits-since`](#git-commits-since) + - [`git continue`](#git-continue) - [`git contrib`](#git-contrib) - [`git count`](#git-count) - [`git cp`](#git-cp) @@ -415,9 +416,9 @@ $ git coauthor user user@email.com 2 files changed, 145 insertions(+), 0 deletions(-) create mode 100644 README.md create mode 100644 CONTRIBUTING.md - + $ git log -1 - + commit b62ceae2685e6ece071f3c3754e9b77fd0a35c88 (HEAD -> master) Author: user person Date: Sat Aug 17 17:33:53 2019 -0500 @@ -1368,7 +1369,7 @@ Switched to branch 'mr/51' With full URL, the head is fetched from a temporary remote pointing to the base URL. ``` bash -$ git mr https://gitlab.com/owner/repository/merge_requests/51 +$ git mr https://gitlab.com/owner/repository/merge_requests/51 From gitlab.com:owner/repository * [new ref] refs/merge-requests/51/head -> mr/51 Switched to branch 'mr/51' @@ -1623,3 +1624,7 @@ Abort current revert, rebase, merge or cherry-pick, without the need to find exa ## git magic Commits changes with a generated message. + +## git continue + +Continue current revert, rebase, merge or cherry-pick, without the need to find exact command in history. diff --git a/bin/git-abort b/bin/git-abort index 9c173805e..201fa4814 100755 --- a/bin/git-abort +++ b/bin/git-abort @@ -1,17 +1,42 @@ #!/usr/bin/env bash -gitdir="$(git rev-parse --git-dir)" || exit -opfound= -fcnt= -for i in cherry-pick merge rebase revert; do - f=${i^^} - f=${f/-/_} - test -f "${gitdir}/${f}_HEAD" && fcnt=1$fcnt && opfound=$i -done +set -euo pipefail -if [ "${fcnt}" != 1 ]; then - echo "I don't know what to abort" >&2 - exit 1 -fi +function discover_op() { + local gitdir + # git rev-parse emits an error if not in a git repo so only need to bail out + gitdir="$(git rev-parse --git-dir)" || exit + local op + for op in cherry_pick merge rebase revert ; do + if [ -f "${gitdir}/${op^^}_HEAD" ]; then + echo "${op/_/-}" + fi + done +} -git "${opfound}" --abort +function validate_op() { + local op="$1" + if [ -z "$op" ]; then + echo "No active operation found" >&2 + exit 1 + fi + if [[ "$(echo "$op" | wc -l)" -gt 1 ]]; then + echo "Multiple active operations found: $op" >&2 + exit 1 + fi +} + +function discover_action() { + local action=${1/git-/} + if [ "$action" != "abort" ] && [ "$action" != "continue" ]; then + echo "Invalid action: $1" >&2 + exit 1 + fi + echo "$action" +} + +action=$(discover_action "$(basename "$0")") +op=$(discover_op) +validate_op "$op" + +git "$op" "--$action" diff --git a/bin/git-continue b/bin/git-continue new file mode 120000 index 000000000..31ceda590 --- /dev/null +++ b/bin/git-continue @@ -0,0 +1 @@ +git-abort \ No newline at end of file diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh index 3a44d6b88..05f271b80 100644 --- a/etc/git-extras-completion.zsh +++ b/etc/git-extras-completion.zsh @@ -379,6 +379,7 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \ clear:'rigorously clean up a repository' \ coauthor:'add a co-author to the last commit' \ commits-since:'show commit logs since some date' \ + continue:'continue current revert, merge, rebase, or cherry-pick process' \ contrib:'show user contributions' \ count:'show commit count' \ create-branch:'create branches' \ diff --git a/man/git-continue.1 b/man/git-continue.1 new file mode 100644 index 000000000..828d79c78 --- /dev/null +++ b/man/git-continue.1 @@ -0,0 +1,19 @@ +.\" generated with Ronn-NG/v0.8.0 +.\" http://github.com/apjanke/ronn-ng/tree/0.8.0 +.TH "GIT\-CONTINUE" "1" "November 2024" "" "Git Extras" +.SH "NAME" +\fBgit\-continue\fR \- Continue current git operation +.SH "SYNOPSIS" +\fBgit\-continue\fR +.SH "DESCRIPTION" +Continue current git revert, rebase, merge or cherry\-pick process\. +.SH "OPTIONS" +There are no options, it just continues current operation\. +.SH "EXAMPLES" +\fBgit\-continue\fR +.SH "AUTHOR" +Written by oikarinen +.SH "REPORTING BUGS" +<\fI\%https://github\.com/tj/git\-extras/issues\fR> +.SH "SEE ALSO" +<\fI\%https://github\.com/tj/git\-extras\fR> diff --git a/man/git-continue.html b/man/git-continue.html new file mode 100644 index 000000000..2fdc4ed94 --- /dev/null +++ b/man/git-continue.html @@ -0,0 +1,114 @@ + + + + + + git-continue(1) - Continue current git operation + + + + +
+ + + +
    +
  1. git-continue(1)
  2. +
  3. Git Extras
  4. +
  5. git-continue(1)
  6. +
+ + + +

NAME

+

+ git-continue - Continue current git operation +

+

SYNOPSIS

+ +

git-continue

+ +

DESCRIPTION

+ +

Continue current git revert, rebase, merge or cherry-pick process.

+ +

OPTIONS

+ +

There are no options, it just continues current operation.

+ +

EXAMPLES

+ +

git-continue

+ +

AUTHOR

+ +

Written by oikarinen

+ +

REPORTING BUGS

+ +

<https://github.com/tj/git-extras/issues>

+ +

SEE ALSO

+ +

<https://github.com/tj/git-extras>

+ +
    +
  1. +
  2. November 2024
  3. +
  4. git-continue(1)
  5. +
+ +
+ + diff --git a/man/git-continue.md b/man/git-continue.md new file mode 100644 index 000000000..439a95cf6 --- /dev/null +++ b/man/git-continue.md @@ -0,0 +1,30 @@ +git-continue(1) -- Continue current git operation +================================ + +## SYNOPSIS + +`git-continue` + +## DESCRIPTION + + Continue current git revert, rebase, merge or cherry-pick process. + +## OPTIONS + + There are no options, it just continues current operation. + +## EXAMPLES + + `git-continue` + +## AUTHOR + +Written by oikarinen + +## REPORTING BUGS + +<> + +## SEE ALSO + +<> diff --git a/man/git-extras.1 b/man/git-extras.1 index c7d4af0ab..8f09cbfad 100644 --- a/man/git-extras.1 +++ b/man/git-extras.1 @@ -49,6 +49,8 @@ Change the default branch to \fB$BRANCH\fR\. If \fBgit\-extras\.default\-branch\ .IP "\[ci]" 4 \fBgit\-commits\-since(1)\fR Show commit logs since some date .IP "\[ci]" 4 +\fBgit\-continue(1)\fR Continue current git operation +.IP "\[ci]" 4 \fBgit\-contrib(1)\fR Show user's contributions .IP "\[ci]" 4 \fBgit\-count(1)\fR Show commit count diff --git a/man/git-extras.html b/man/git-extras.html index 00ed5d0fc..36448ef77 100644 --- a/man/git-extras.html +++ b/man/git-extras.html @@ -69,7 +69,7 @@
  • git-extras(1)
  • - +

    NAME

    @@ -131,6 +131,8 @@

    COMMANDS

  • git-commits-since(1) Show commit logs since some date
  • +git-continue(1) Continue current git operation
  • +
  • git-contrib(1) Show user's contributions
  • git-count(1) Show commit count
  • diff --git a/man/git-extras.md b/man/git-extras.md index 8ea9d88c3..c53d708bb 100644 --- a/man/git-extras.md +++ b/man/git-extras.md @@ -40,6 +40,7 @@ git-extras(1) -- Awesome GIT utilities - **git-clear(1)** Rigorously clean up a repository - **git-coauthor(1)** Add a co-author to the last commit - **git-commits-since(1)** Show commit logs since some date + - **git-continue(1)** Continue current git operation - **git-contrib(1)** Show user's contributions - **git-count(1)** Show commit count - **git-cp(1)** Copy a file keeping its history diff --git a/man/index.txt b/man/index.txt index 21c65034d..c4c716814 100644 --- a/man/index.txt +++ b/man/index.txt @@ -13,6 +13,7 @@ git-clear(1) git-clear git-coauthor(1) git-coauthor git-commits-since(1) git-commits-since git-contrib(1) git-contrib +git-continue(1) git-continue git-count(1) git-count git-cp(1) git-cp git-create-branch(1) git-create-branch diff --git a/tests/README.md b/tests/README.md index d97c07712..7c60a0783 100644 --- a/tests/README.md +++ b/tests/README.md @@ -22,7 +22,8 @@ It is done or go without `poetry`, 1. Install python >= 3.11 2. Install pytest >= 8.1.2 3. Install gitpython >= 3.1.43 -4. Run `pytest` +4. Install testpath >= 0.6.0 +5. Run `pytest` The second way maybe blocked the some missing dependencies at someday, so the first one is recommended. diff --git a/tests/conftest.py b/tests/conftest.py index 4f9e0db60..3b3190825 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,8 +7,8 @@ def create_repo(dirname=None): repo = TempRepository(dirname) - repo.create_tmp_file() - repo.create_tmp_file() + repo.create_tmp_file() # tmp_file_a + repo.create_tmp_file() # tmp_file_b repo.switch_cwd_under_repo() return repo @@ -35,3 +35,11 @@ def named_temp_repo(request): init_repo_git_status(repo) yield repo repo.teardown() + + +@pytest.fixture(scope="function") +def temp_repo_clean(): + """Create a temporary repository that is reset for each function call.""" + repo = create_repo() + init_repo_git_status(repo) + return repo diff --git a/tests/test_git_continue.py b/tests/test_git_continue.py new file mode 100644 index 000000000..8fd7d5e26 --- /dev/null +++ b/tests/test_git_continue.py @@ -0,0 +1,84 @@ +from git import GitCommandError + + +class TestGitContinue: + + @classmethod + def _init_repo(cls, repo): + git = repo.get_repo_git() + tmp_file = repo.get_file(0) + git.branch("A") + git.branch("B") + git.branch("C") + git.checkout("A") + repo.writefile(tmp_file, "a") + git.add(".") + git.commit("-m", "A") + git.checkout("B") + repo.writefile(tmp_file, "b") + git.add(".") + git.commit("-m", "B") + + def test_init(self, temp_repo_clean): + TestGitContinue._init_repo(temp_repo_clean) + git = temp_repo_clean.get_repo_git() + git.status() + + def test_cherry_pick(self, temp_repo_clean): + TestGitContinue._init_repo(temp_repo_clean) + git = temp_repo_clean.get_repo_git() + try: + git.cherry_pick("A") + except GitCommandError as err: + print(err) + result = git.status() + assert "Unmerged path" in result + git.add(".") + temp_repo_clean.invoke_extras_command("continue") + result = git.status() + assert "nothing to commit, working tree clean" in result + + def test_merge(self, temp_repo_clean): + TestGitContinue._init_repo(temp_repo_clean) + git = temp_repo_clean.get_repo_git() + try: + git.merge("A") + except GitCommandError as err: + print(err) + result = git.status() + assert "Unmerged path" in result + git.add(".") + git.commit("-m", "resolve conflict") + temp_repo_clean.invoke_extras_command("continue") + result = git.status() + assert "nothing to commit, working tree clean" in result + + def test_rebase(self, temp_repo_clean): + TestGitContinue._init_repo(temp_repo_clean) + git = temp_repo_clean.get_repo_git() + try: + git.rebase("A") + except GitCommandError as err: + print(err) + result = git.status() + assert "Unmerged path" in result + git.add(".") + git.commit("-m", "resolve conflict") + temp_repo_clean.invoke_extras_command("continue") + result = git.status() + assert "nothing to commit, working tree clean" in result + + def test_revert(self, temp_repo_clean): + TestGitContinue._init_repo(temp_repo_clean) + git = temp_repo_clean.get_repo_git() + try: + git.revert("A") + except GitCommandError as err: + print(err) + result = git.status() + assert "Unmerged path" in result + git.add(".") + git.commit("-m", "resolve conflict") + temp_repo_clean.invoke_extras_command("continue") + result = git.status() + assert "nothing to commit, working tree clean" in result