~sircmpwn/sr.ht-dev

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
5 3

[PATCH hg.sr.ht] Add server-side stripping UI

Details
Message ID
<5509aeba1166d371110d.1635904454@devahi>
DKIM signature
pass
Download raw message
Patch: +75 -0
# HG changeset patch
# User Ludovic Chabant <ludovic@chabant.com>
# Date 1556349248 0
#      Sat Apr 27 07:14:08 2019 +0000
# Node ID 5509aeba1166d371110da6dc8954269c32293d46
# Parent  136a036210496b90498a3f7e146e67ca63636aa4
Add server-side stripping UI.

diff --git a/hgsrht/blueprints/extramanage.py b/hgsrht/blueprints/extramanage.py
--- a/hgsrht/blueprints/extramanage.py
+++ b/hgsrht/blueprints/extramanage.py
@@ -34,3 +34,38 @@
    db.session.commit()
    generate_and_write_hgrc(repo)  # TODO: maybe do this in a celery job or something
    return redirect(f"/{owner_name}/{repo_name}/settings/features")

@extramanage.route("/<owner_name>/<repo_name>/settings/histedit")
@loginrequired
def manage_histedit(owner_name, repo_name):
    owner, repo = check_access(owner_name, repo_name, UserAccess.manage)
    if isinstance(repo, BaseRedirectMixin):
        return redirect(url_for(".manage_histedit",
            owner_name=owner_name, repo_name=repo.new_repo.name))
    return render_template("histedit.html", owner=owner, repo=repo)

@extramanage.route("/<owner_name>/<repo_name>/settings/histedit", methods=['POST'])
@loginrequired
def manage_histedit_POST(owner_name, repo_name):
    owner, repo = check_access(owner_name, repo_name, UserAccess.manage)
    if isinstance(repo, BaseRedirectMixin):
        # Normally we'd redirect be we don't want to fuck up some other repo
        abort(404)
    valid = Validation(request)
    rev_id = valid.require("rev")
    if not valid.ok:
        return render_template(".manage_histedit", owner=owner, repo=repo)
    with HgRepository(repo.path) as hg_repo:
        strip_error = None
        strip_msg = f"changeset {rev_id} was successfully stripped!"
        rev_id = rev_id.strip()
        try:
            hg_repo.rawcommand(b"debugstrip", rev=rev_id.encode(),
                               force=True, no_backup=True)
        except HgCommandError as exc:
            print("got hg error!")
            strip_error = exc.err.decode()
            print(strip_error)
    return render_template("histedit.html", owner=owner, repo=repo,
                           strip_msg=strip_msg, strip_error=strip_error)

diff --git a/hgsrht/templates/histedit.html b/hgsrht/templates/histedit.html
new file mode 100644
--- /dev/null
+++ b/hgsrht/templates/histedit.html
@@ -0,0 +1,39 @@
{% extends "settings.html" %}
{% block content %}
<div class="row">
  <div class="col-md-12">
    <h5>Strip</h5>
    <p>Stripping commits <strong>changes history</strong> by removing the
       specified commit along with all its descendants.</p>
    <p>If someone else has already pulled those commits, they might be pushed
       again, unless they also strip this commit locally. Look into <em>changeset
       evolution</em> as an elegant solution to collaborative draft branches. Use
       stripping only for exceptional cases.</p>
    <p>This cannot be undone.</p>
    <form method="POST">
      {{csrf_token()}}
      <div class="form-group">
        <label for="rev">Revision</label>
        <input 
           type="text"
           class="form-control"
           name="rev"
           id="rev" />
      </div>
      {% if strip_error %}
        <div class="alert-danger">{{ strip_error }}</div>
      {% elif strip_msg %}
        <div class="alert-success">{{ strip_msg }}</div>
      {% endif %}
      <button type="submit" class="btn btn-danger">
        Strip commit from {{repo.name}} {{icon("caret-right")}}
      </button>
      <a
        href="/{{ owner.canonical_name }}/{{ repo.name }}"
        class="btn btn-default"
      >Nevermind</a>
    </form>
  </div>
</div>
{% endblock %}

diff --git a/hgsrht/templates/settingstabs.html b/hgsrht/templates/settingstabs.html
--- a/hgsrht/templates/settingstabs.html
+++ b/hgsrht/templates/settingstabs.html
@@ -2,4 +2,5 @@

{% block extratabs %}
<li class="nav-item">{{ link("/features", "features") }}</li>
<li class="nav-item">{{ link("/histedit", "histedit") }}</li>
{% endblock %}
Details
Message ID
<461163c6-517e-4c39-92db-2a6d76ef12a0@www.fastmail.com>
In-Reply-To
<5509aeba1166d371110d.1635904454@devahi> (view parent)
DKIM signature
pass
Download raw message
I forgot to add in the commit description that this relies on the core
"debugstrip" command, so it requires Mercurial 5.7 at least (which was
released in January 2021) to be installed on your sourcehut server.
Details
Message ID
<CFG0X81QLOC1.2MHI7GSBIMORY@taiga>
In-Reply-To
<461163c6-517e-4c39-92db-2a6d76ef12a0@www.fastmail.com> (view parent)
DKIM signature
fail
Download raw message
DKIM signature: fail
On Wed Nov 3, 2021 at 2:56 AM CET, Ludovic Chabant wrote:
> I forgot to add in the commit description that this relies on the core
> "debugstrip" command, so it requires Mercurial 5.7 at least (which was
> released in January 2021) to be installed on your sourcehut server.

5.7 made it into Alpine 3.14 so we should be good to go on this front.

+1 to the patch
Details
Message ID
<27f1c620-cb66-62d0-3a60-c69e45d613f3@laxalde.org>
In-Reply-To
<461163c6-517e-4c39-92db-2a6d76ef12a0@www.fastmail.com> (view parent)
DKIM signature
missing
Download raw message
Ludovic Chabant a écrit :
> I forgot to add in the commit description that this relies on the core
> "debugstrip" command, so it requires Mercurial 5.7 at least (which was
> released in January 2021) to be installed on your sourcehut server.

Alternatively, you can invoke the "strip" command shipped with the 
(core) extension "strip" that's been there for longer.
E.g. "hg --config extensions.strip= strip ...".

[PATCH hg.sr.ht v2] Add server-side stripping UI

Details
Message ID
<a24a55ba7b474833b7fa.1635957663@devahi>
In-Reply-To
<5509aeba1166d371110d.1635904454@devahi> (view parent)
DKIM signature
pass
Download raw message
Patch: +84 -0
# HG changeset patch
# User Ludovic Chabant <ludovic@chabant.com>
# Date 1556349248 0
#      Sat Apr 27 07:14:08 2019 +0000
# Node ID a24a55ba7b474833b7fa2a1fe8aec980e387bc60
# Parent  70074fdce7a252d8433e41b9ad569f5a2bfb6cba
Add server-side stripping UI.

This adds a new "histedit" tab in the repo settings page. The page contains
a form where a repo owner/admin can strip changesets from the repo history.
This uses `hg debugstrip` under the hood.

diff --git a/hgsrht/blueprints/extramanage.py b/hgsrht/blueprints/extramanage.py
--- a/hgsrht/blueprints/extramanage.py
+++ b/hgsrht/blueprints/extramanage.py
@@ -1,3 +1,4 @@
import re
from flask import Blueprint, Response, render_template, abort, request
from flask import redirect, url_for
from hgsrht.hg import HgRepository, HgCommandError
@@ -9,6 +10,8 @@
from srht.oauth import loginrequired
from srht.validation import Validation

re_changeset_hash = re.compile(r"^[0-9a-fA-F]{12,40}$")

extramanage = Blueprint('extramanage', __name__)

@extramanage.route("/<owner_name>/<repo_name>/settings/features")
@@ -34,3 +37,42 @@
    db.session.commit()
    generate_and_write_hgrc(repo)  # TODO: maybe do this in a celery job or something
    return redirect(f"/{owner_name}/{repo_name}/settings/features")

@extramanage.route("/<owner_name>/<repo_name>/settings/histedit")
@loginrequired
def manage_histedit(owner_name, repo_name):
    owner, repo = check_access(owner_name, repo_name, UserAccess.manage)
    if isinstance(repo, BaseRedirectMixin):
        return redirect(url_for(".manage_histedit",
            owner_name=owner_name, repo_name=repo.new_repo.name))
    return render_template("histedit.html", owner=owner, repo=repo)

@extramanage.route("/<owner_name>/<repo_name>/settings/histedit", methods=['POST'])
@loginrequired
def manage_histedit_POST(owner_name, repo_name):
    owner, repo = check_access(owner_name, repo_name, UserAccess.manage)
    if isinstance(repo, BaseRedirectMixin):
        # Normally we'd redirect be we don't want to fuck up some other repo
        abort(404)
    valid = Validation(request)
    rev_id = valid.require("rev")
    if not valid.ok:
        return render_template("histedit.html", owner=owner, repo=repo, **valid.kwargs)
    rev_id = rev_id.strip()
    valid.expect(re_changeset_hash.match(rev_id), "You must pass a changeset hash.",
            field="rev")
    if not valid.ok:
        return render_template("histedit.html", owner=owner, repo=repo, **valid.kwargs)
    with HgRepository(repo.path) as hg_repo:
        strip_error = None
        strip_msg = f"changeset {rev_id} was successfully stripped!"
        try:
            hg_repo.rawcommand(b"debugstrip", rev=rev_id.encode(),
                               force=True, no_backup=True)
        except HgCommandError as exc:
            print("got hg error!")
            strip_error = exc.err.decode()
            print(strip_error)
    return render_template("histedit.html", owner=owner, repo=repo,
                           strip_msg=strip_msg, strip_error=strip_error)

diff --git a/hgsrht/templates/histedit.html b/hgsrht/templates/histedit.html
new file mode 100644
--- /dev/null
+++ b/hgsrht/templates/histedit.html
@@ -0,0 +1,41 @@
{% extends "settings.html" %}
{% block content %}
<div class="row">
  <div class="col-md-12">
    <h5>Strip</h5>
    <p>Stripping commits <strong>changes history</strong> by removing the
       specified commit along with all its descendants.</p>
    <p>If someone else has already pulled those commits, they might be pushed
       again, unless they also strip this commit locally. Look into <em>changeset
       evolution</em> as an elegant solution to collaborative draft branches. Use
       stripping only for exceptional cases.</p>
    <p>This cannot be undone.</p>
    <form method="POST">
      {{csrf_token()}}
      <div class="form-group">
        <label for="rev">Revision</label>
        <input 
           type="text"
           class="form-control {{valid.cls("rev")}}"
           name="rev"
           placeholder="revision hash"
           id="rev" />
        {{valid.summary("rev")}}
      </div>
      {% if strip_error %}
        <div class="alert-danger">{{ strip_error }}</div>
      {% elif strip_msg %}
        <div class="alert-success">{{ strip_msg }}</div>
      {% endif %}
      <button type="submit" class="btn btn-danger">
        Strip commit from {{repo.name}} {{icon("caret-right")}}
      </button>
      <a
        href="/{{ owner.canonical_name }}/{{ repo.name }}"
        class="btn btn-default"
      >Nevermind</a>
    </form>
  </div>
</div>
{% endblock %}

diff --git a/hgsrht/templates/settingstabs.html b/hgsrht/templates/settingstabs.html
--- a/hgsrht/templates/settingstabs.html
+++ b/hgsrht/templates/settingstabs.html
@@ -2,4 +2,5 @@

{% block extratabs %}
<li class="nav-item">{{ link("/features", "features") }}</li>
<li class="nav-item">{{ link("/histedit", "histedit") }}</li>
{% endblock %}
Details
Message ID
<fb65f33c-c65d-48df-8e5d-3fa1022a79c6@www.fastmail.com>
In-Reply-To
<27f1c620-cb66-62d0-3a60-c69e45d613f3@laxalde.org> (view parent)
DKIM signature
pass
Download raw message
> 5.7 made it into Alpine 3.14 so we should be good to go on this front.

Thanks for the info, Drew!


> Alternatively, you can invoke the "strip" command shipped with the
> (core) extension "strip" that's been there for longer.
> E.g. "hg --config extensions.strip= strip ...".

I considered doing that but that's when I realized the extension was
deprecated, and the functionality was now in the core. We can implement
support for that if someone has a good reason to prefer it that way, but
I doubt it ;)

The v2 patch I just sent has some better user input validation. And by
"better" I mean it actually has some :D
Reply to thread Export thread (mbox)