mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 07:26:17 -04:00
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.
This commit is contained in:
parent
c23da8ae9c
commit
2f4b57d52b
11
Commands.md
11
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 <userperson@email.com>
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
1
bin/git-continue
Symbolic link
1
bin/git-continue
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
git-abort
|
||||
|
|
@ -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' \
|
||||
|
|
|
|||
19
man/git-continue.1
Normal file
19
man/git-continue.1
Normal file
|
|
@ -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>
|
||||
114
man/git-continue.html
Normal file
114
man/git-continue.html
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='content-type' content='text/html;charset=utf8'>
|
||||
<meta name='generator' content='Ronn-NG/v0.8.0 (http://github.com/apjanke/ronn-ng/tree/0.8.0)'>
|
||||
<title>git-continue(1) - Continue current git operation</title>
|
||||
<style type='text/css' media='all'>
|
||||
/* style: man */
|
||||
body#manpage {margin:0}
|
||||
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
||||
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
||||
.mp h2 {margin:10px 0 0 0}
|
||||
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
||||
.mp h3 {margin:0 0 0 4ex}
|
||||
.mp dt {margin:0;clear:left}
|
||||
.mp dt.flush {float:left;width:8ex}
|
||||
.mp dd {margin:0 0 0 9ex}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
||||
.mp pre {margin-bottom:20px}
|
||||
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
||||
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
||||
.mp img {display:block;margin:auto}
|
||||
.mp h1.man-title {display:none}
|
||||
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
||||
.mp h2 {font-size:16px;line-height:1.25}
|
||||
.mp h1 {font-size:20px;line-height:2}
|
||||
.mp {text-align:justify;background:#fff}
|
||||
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
||||
.mp u {text-decoration:underline}
|
||||
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
||||
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
||||
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
||||
.mp b.man-ref {font-weight:normal;color:#434241}
|
||||
.mp pre {padding:0 4ex}
|
||||
.mp pre code {font-weight:normal;color:#434241}
|
||||
.mp h2+pre,h3+pre {padding-left:0}
|
||||
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
||||
ol.man-decor {width:100%}
|
||||
ol.man-decor li.tl {text-align:left}
|
||||
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
||||
ol.man-decor li.tr {text-align:right;float:right}
|
||||
</style>
|
||||
</head>
|
||||
<!--
|
||||
The following styles are deprecated and will be removed at some point:
|
||||
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
||||
|
||||
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
||||
.man-navigation should be used instead.
|
||||
-->
|
||||
<body id='manpage'>
|
||||
<div class='mp' id='man'>
|
||||
|
||||
<div class='man-navigation' style='display:none'>
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#SYNOPSIS">SYNOPSIS</a>
|
||||
<a href="#DESCRIPTION">DESCRIPTION</a>
|
||||
<a href="#OPTIONS">OPTIONS</a>
|
||||
<a href="#EXAMPLES">EXAMPLES</a>
|
||||
<a href="#AUTHOR">AUTHOR</a>
|
||||
<a href="#REPORTING-BUGS">REPORTING BUGS</a>
|
||||
<a href="#SEE-ALSO">SEE ALSO</a>
|
||||
</div>
|
||||
|
||||
<ol class='man-decor man-head man head'>
|
||||
<li class='tl'>git-continue(1)</li>
|
||||
<li class='tc'>Git Extras</li>
|
||||
<li class='tr'>git-continue(1)</li>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
<h2 id="NAME">NAME</h2>
|
||||
<p class="man-name">
|
||||
<code>git-continue</code> - <span class="man-whatis">Continue current git operation</span>
|
||||
</p>
|
||||
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
||||
|
||||
<p><code>git-continue</code></p>
|
||||
|
||||
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
||||
|
||||
<p> Continue current git revert, rebase, merge or cherry-pick process.</p>
|
||||
|
||||
<h2 id="OPTIONS">OPTIONS</h2>
|
||||
|
||||
<p> There are no options, it just continues current operation.</p>
|
||||
|
||||
<h2 id="EXAMPLES">EXAMPLES</h2>
|
||||
|
||||
<p> <code>git-continue</code></p>
|
||||
|
||||
<h2 id="AUTHOR">AUTHOR</h2>
|
||||
|
||||
<p>Written by oikarinen</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'>November 2024</li>
|
||||
<li class='tr'>git-continue(1)</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
30
man/git-continue.md
Normal file
30
man/git-continue.md
Normal file
|
|
@ -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
|
||||
|
||||
<<https://github.com/tj/git-extras/issues>>
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
<<https://github.com/tj/git-extras>>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
<li class='tr'>git-extras(1)</li>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="NAME">NAME</h2>
|
||||
<p class="man-name">
|
||||
|
|
@ -131,6 +131,8 @@
|
|||
<li>
|
||||
<strong><a class="man-ref" href="git-commits-since.html">git-commits-since<span class="s">(1)</span></a></strong> Show commit logs since some date</li>
|
||||
<li>
|
||||
<strong><a class="man-ref" href="git-continue.html">git-continue<span class="s">(1)</span></a></strong> Continue current git operation</li>
|
||||
<li>
|
||||
<strong><a class="man-ref" href="git-contrib.html">git-contrib<span class="s">(1)</span></a></strong> Show user's contributions</li>
|
||||
<li>
|
||||
<strong><a class="man-ref" href="git-count.html">git-count<span class="s">(1)</span></a></strong> Show commit count</li>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ from helper import TempRepository
|
|||
|
||||
def create_repo(dirname=None):
|
||||
repo = TempRepository(dirname)
|
||||
repo.create_tmp_file()
|
||||
repo.create_tmp_file()
|
||||
repo.create_tmp_file() # tmp_file_a
|
||||
repo.create_tmp_file() # tmp_file_b
|
||||
repo.switch_cwd_under_repo()
|
||||
return repo
|
||||
|
||||
|
|
@ -35,3 +35,11 @@ def named_temp_repo(request):
|
|||
init_repo_git_status(repo)
|
||||
yield repo
|
||||
repo.teardown()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def temp_repo_clean():
|
||||
"""Create a temporary repository that is reset for each function call."""
|
||||
repo = create_repo()
|
||||
init_repo_git_status(repo)
|
||||
return repo
|
||||
|
|
|
|||
84
tests/test_git_continue.py
Normal file
84
tests/test_git_continue.py
Normal file
|
|
@ -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
|
||||
Loading…
Reference in a new issue