"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Josh Rickmar <openbsd+lists@zettaport.com>
Subject:
Re: add histedit -e option
To:
gameoftrees@openbsd.org
Date:
Thu, 30 Sep 2021 20:11:25 -0400

Download raw body.

Thread
On Thu, Sep 30, 2021 at 09:43:08PM +0200, Stefan Sperling wrote:
> Sometimes I find myself using only the 'edit' command in histedit scripts.
> This can happen after a couple of patches that depend on each other
> have received code review.
> 
> This can be useful when there are a handful of commits that need to be
> tweaked. I can now skip the histedit scripting step in this case:
> 
>   got up -c origin/main
>   got he -e
> 
> ok?
>  
> diff 3a9ff2603178620aa1e31e55cdac42a879e22dc8 5b6479f3914e22c83a9396309ece5795c48361e7
> blob - 237c161ab95271094fff0f6ca83316e7b1d60e7d
> blob + 450e4baab5541257e9c371762a5fe893fa1c19bb
> --- got/got.1
> +++ got/got.1
> @@ -1863,7 +1863,7 @@ None of the other options can be used together with
>  .It Cm rb
>  Short alias for
>  .Cm rebase .
> -.It Cm histedit Oo Fl a Oc Oo Fl c Oc Oo Fl f Oc Oo Fl F Ar histedit-script Oc Oo Fl m Oc Oo Fl l Oc Oo Fl X Oc Op Ar branch
> +.It Cm histedit Oo Fl a Oc Oo Fl c Oc Oo Fl e Oc Oo Fl f Oc Oo Fl F Ar histedit-script Oc Oo Fl m Oc Oo Fl l Oc Oo Fl X Oc Op Ar branch
>  Edit commit history between the work tree's current base commit and
>  the tip commit of the work tree's current branch.
>  .Pp
> @@ -2011,6 +2011,15 @@ If this option is used, no other command-line argument
>  .It Fl c
>  Continue an interrupted histedit operation.
>  If this option is used, no other command-line arguments are allowed.
> +.It Fl e
> +Interrupt the histedit operation for editing after merging each commit.
> +This option is a quick equivalent to a histedit script which uses the
> +.Cm edit
> +command for all commits.
> +The
> +.Fl e
> +option can only be used when starting a new histedit operation.
> +If this option is used, no other command-line arguments are allowed.
>  .It Fl f
>  Fold all commits into a single commit.
>  This option is a quick equivalent to a histedit script which folds all
> blob - 2a84457cfb630de071de04ba1bcc66b3700a19ae
> blob + 5bfbe6c3567efc8de019a1c22570d5a47bee75be
> --- got/got.c
> +++ got/got.c
> @@ -9162,7 +9162,7 @@ done:
>  __dead static void
>  usage_histedit(void)
>  {
> -	fprintf(stderr, "usage: %s histedit [-a] [-c] [-f] "
> +	fprintf(stderr, "usage: %s histedit [-a] [-c] [-e] [-f] "
>  	    "[-F histedit-script] [-m] [-l] [-X] [branch]\n",
>  	    getprogname());
>  	exit(1);
> @@ -9230,7 +9230,8 @@ done:
>  
>  static const struct got_error *
>  histedit_write_commit_list(struct got_object_id_queue *commits,
> -    FILE *f, int edit_logmsg_only, int fold_only, struct got_repository *repo)
> +    FILE *f, int edit_logmsg_only, int fold_only, int edit_only,
> +    struct got_repository *repo)
>  {
>  	const struct got_error *err = NULL;
>  	struct got_object_qid *qid;
> @@ -9241,7 +9242,9 @@ histedit_write_commit_list(struct got_object_id_queue 
>  
>  	STAILQ_FOREACH(qid, commits, entry) {
>  		histedit_cmd = got_histedit_cmds[0].name;
> -		if (fold_only && STAILQ_NEXT(qid, entry) != NULL)
> +		if (edit_only)
> +			histedit_cmd = "edit";
> +		else if (fold_only && STAILQ_NEXT(qid, entry) != NULL)
>  			histedit_cmd = "fold";
>  		err = histedit_write_commit(qid->id, histedit_cmd, f, repo);
>  		if (err)
> @@ -9655,7 +9658,8 @@ histedit_edit_list_retry(struct got_histedit_list *, c
>  static const struct got_error *
>  histedit_edit_script(struct got_histedit_list *histedit_cmds,
>      struct got_object_id_queue *commits, const char *branch_name,
> -    int edit_logmsg_only, int fold_only, struct got_repository *repo)
> +    int edit_logmsg_only, int fold_only, int edit_only,
> +    struct got_repository *repo)
>  {
>  	const struct got_error *err;
>  	FILE *f = NULL;
> @@ -9670,11 +9674,11 @@ histedit_edit_script(struct got_histedit_list *histedi
>  		goto done;
>  
>  	err = histedit_write_commit_list(commits, f, edit_logmsg_only,
> -	    fold_only, repo);
> +	    fold_only, edit_only, repo);
>  	if (err)
>  		goto done;
>  
> -	if (edit_logmsg_only || fold_only) {
> +	if (edit_logmsg_only || fold_only || edit_only) {
>  		rewind(f);
>  		err = histedit_parse_list(histedit_cmds, f, repo);
>  	} else {
> @@ -9805,7 +9809,7 @@ histedit_edit_list_retry(struct got_histedit_list *his
>  		} else if (resp == 'r') {
>  			histedit_free_list(histedit_cmds);
>  			err = histedit_edit_script(histedit_cmds,
> -			    commits, branch_name, 0, 0, repo);
> +			    commits, branch_name, 0, 0, 0, repo);
>  			if (err) {
>  				if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
>  				    err->code != GOT_ERR_HISTEDIT_CMD)
> @@ -9997,7 +10001,7 @@ cmd_histedit(int argc, char *argv[])
>  	int ch, rebase_in_progress = 0, merge_in_progress = 0;
>  	struct got_update_progress_arg upa;
>  	int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
> -	int edit_logmsg_only = 0, fold_only = 0;
> +	int edit_logmsg_only = 0, fold_only = 0, edit_only = 0;
>  	int list_backups = 0, delete_backups = 0;
>  	const char *edit_script_path = NULL;
>  	struct got_object_id_queue commits;
> @@ -10012,7 +10016,7 @@ cmd_histedit(int argc, char *argv[])
>  	TAILQ_INIT(&merged_paths);
>  	memset(&upa, 0, sizeof(upa));
>  
> -	while ((ch = getopt(argc, argv, "acfF:mlX")) != -1) {
> +	while ((ch = getopt(argc, argv, "acefF:mlX")) != -1) {
>  		switch (ch) {
>  		case 'a':
>  			abort_edit = 1;
> @@ -10020,6 +10024,9 @@ cmd_histedit(int argc, char *argv[])
>  		case 'c':
>  			continue_edit = 1;
>  			break;
> +		case 'e':
> +			edit_only = 1;
> +			break;
>  		case 'f':
>  			fold_only = 1;
>  			break;
> @@ -10064,7 +10071,15 @@ cmd_histedit(int argc, char *argv[])
>  	if (fold_only && edit_logmsg_only)
>  		option_conflict('f', 'm');
>  	if (edit_script_path && fold_only)
> -		option_conflict('F', 'f');
> +		option_conflict('F', 'e');

this should remain the same, otherwise:

$ got he -F script -f
got: -F and -e options are mutually exclusive

> +	if (abort_edit && edit_only)
> +		option_conflict('a', 'e');
> +	if (continue_edit && edit_only)
> +		option_conflict('c', 'e');
> +	if (edit_only && edit_logmsg_only)
> +		option_conflict('e', 'm');
> +	if (edit_script_path && edit_only)
> +		option_conflict('F', 'e');
>  	if (list_backups) {
>  		if (abort_edit)
>  			option_conflict('l', 'a');
> @@ -10076,6 +10091,8 @@ cmd_histedit(int argc, char *argv[])
>  			option_conflict('l', 'm');
>  		if (fold_only)
>  			option_conflict('l', 'f');
> +		if (edit_only)
> +			option_conflict('l', 'e');
>  		if (delete_backups)
>  			option_conflict('l', 'X');
>  		if (argc != 0 && argc != 1)
> @@ -10091,6 +10108,8 @@ cmd_histedit(int argc, char *argv[])
>  			option_conflict('X', 'm');
>  		if (fold_only)
>  			option_conflict('X', 'f');
> +		if (edit_only)
> +			option_conflict('X', 'e');
>  		if (list_backups)
>  			option_conflict('X', 'l');
>  		if (argc != 0 && argc != 1)
> @@ -10181,6 +10200,13 @@ cmd_histedit(int argc, char *argv[])
>  		    "before the -f option can be used");
>  		goto done;
>  	}
> +	if (edit_in_progress && edit_only) {
> +		error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
> +		    "histedit operation is in progress in this "
> +		    "work tree and must be continued or aborted "
> +		    "before the -e option can be used");
> +		goto done;
> +	}
>  
>  	if (edit_in_progress && abort_edit) {
>  		error = got_worktree_histedit_continue(&resume_commit_id,
> @@ -10316,7 +10342,8 @@ cmd_histedit(int argc, char *argv[])
>  			if (strncmp(branch_name, "refs/heads/", 11) == 0)
>  				branch_name += 11;
>  			error = histedit_edit_script(&histedit_cmds, &commits,
> -			    branch_name, edit_logmsg_only, fold_only, repo);
> +			    branch_name, edit_logmsg_only, fold_only,
> +			    edit_only, repo);
>  			if (error) {
>  				got_worktree_histedit_abort(worktree, fileindex,
>  				    repo, branch, base_commit_id,
> blob - 61bcebf99d9b2d05bc608cd43b07f0c9e3cdf818
> blob + 65ef292945ab5a14c0c1674cbd8aec70fc5e9418
> --- regress/cmdline/histedit.sh
> +++ regress/cmdline/histedit.sh
> @@ -1766,6 +1766,153 @@ EOF
>  	test_done "$testroot" "$ret"
>  }
>  
> +test_histedit_edit_only() {
> +	local testroot=`test_init histedit_edit_only`
> +
> +	local orig_commit=`git_show_head $testroot/repo`
> +
> +	echo "modified alpha on master" > $testroot/repo/alpha
> +	(cd $testroot/repo && git rm -q beta)
> +	echo "new file on master" > $testroot/repo/epsilon/new
> +	(cd $testroot/repo && git add epsilon/new)
> +	git_commit $testroot/repo -m "committing changes"
> +	local old_commit1=`git_show_head $testroot/repo`
> +
> +	echo "modified zeta on master" > $testroot/repo/epsilon/zeta
> +	git_commit $testroot/repo -m "committing to zeta on master"
> +	local old_commit2=`git_show_head $testroot/repo`
> +
> +	got checkout -c $orig_commit $testroot/repo $testroot/wt > /dev/null
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		test_done "$testroot" "$ret"
> +		return 1
> +	fi
> +
> +	(cd $testroot/wt && got histedit -e > $testroot/stdout)
> +
> +	local short_old_commit1=`trim_obj_id 28 $old_commit1`
> +	local short_old_commit2=`trim_obj_id 28 $old_commit2`
> +
> +	echo "G  alpha" > $testroot/stdout.expected
> +	echo "D  beta" >> $testroot/stdout.expected
> +	echo "A  epsilon/new" >> $testroot/stdout.expected
> +	echo "Stopping histedit for amending commit $old_commit1" \
> +		>> $testroot/stdout.expected
> +	cmp -s $testroot/stdout.expected $testroot/stdout
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		diff -u $testroot/stdout.expected $testroot/stdout
> +		test_done "$testroot" "$ret"
> +		return 1
> +	fi
> +
> +	echo "edited modified alpha on master" > $testroot/wt/alpha
> +
> +	cat > $testroot/editor.sh <<EOF
> +#!/bin/sh
> +sed -i 's/.*/committing edited changes 1/' "\$1"
> +EOF
> +	chmod +x $testroot/editor.sh
> +
> +	(cd $testroot/wt && env EDITOR="$testroot/editor.sh" \
> +		VISUAL="$testroot/editor.sh" got histedit -c > $testroot/stdout)
> +
> +	local new_commit1=$(cd $testroot/wt && got info | \
> +		grep '^work tree base commit: ' | cut -d: -f2 | tr -d ' ')
> +	local short_new_commit1=`trim_obj_id 28 $new_commit1`
> +
> +	echo -n "$short_old_commit1 -> $short_new_commit1: " \
> +		> $testroot/stdout.expected
> +	echo "committing edited changes 1" >> $testroot/stdout.expected
> +	echo "G  epsilon/zeta" >> $testroot/stdout.expected
> +	echo "Stopping histedit for amending commit $old_commit2" \
> +		>> $testroot/stdout.expected
> +	cmp -s $testroot/stdout.expected $testroot/stdout
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		diff -u $testroot/stdout.expected $testroot/stdout
> +		test_done "$testroot" "$ret"
> +		return 1
> +	fi
> +
> +	echo "edited zeta on master" > $testroot/wt/epsilon/zeta
> +
> +	cat > $testroot/editor.sh <<EOF
> +#!/bin/sh
> +sed -i 's/.*/committing edited changes 2/' "\$1"
> +EOF
> +	chmod +x $testroot/editor.sh
> +
> +	(cd $testroot/wt && env EDITOR="$testroot/editor.sh" \
> +		VISUAL="$testroot/editor.sh" got histedit -c > $testroot/stdout)
> +
> +	local new_commit2=`git_show_head $testroot/repo`
> +	local short_new_commit2=`trim_obj_id 28 $new_commit2`
> +
> +	echo -n "$short_old_commit2 -> $short_new_commit2: " \
> +		> $testroot/stdout.expected
> +	echo "committing edited changes 2" >> $testroot/stdout.expected
> +	echo "Switching work tree to refs/heads/master" \
> +		>> $testroot/stdout.expected
> +
> +	cmp -s $testroot/stdout.expected $testroot/stdout
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		diff -u $testroot/stdout.expected $testroot/stdout
> +		test_done "$testroot" "$ret"
> +		return 1
> +	fi
> +
> +	echo "edited modified alpha on master" > $testroot/content.expected
> +	cat $testroot/wt/alpha > $testroot/content
> +	cmp -s $testroot/content.expected $testroot/content
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		diff -u $testroot/content.expected $testroot/content
> +		test_done "$testroot" "$ret"
> +		return 1
> +	fi
> +
> +	if [ -e $testroot/wt/beta ]; then
> +		echo "removed file beta still exists on disk" >&2
> +		test_done "$testroot" "1"
> +		return 1
> +	fi
> +
> +	echo "new file on master" > $testroot/content.expected
> +	cat $testroot/wt/epsilon/new > $testroot/content
> +	cmp -s $testroot/content.expected $testroot/content
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		diff -u $testroot/content.expected $testroot/content
> +		test_done "$testroot" "$ret"
> +		return 1
> +	fi
> +
> +	(cd $testroot/wt && got status > $testroot/stdout)
> +
> +	echo -n > $testroot/stdout.expected
> +	cmp -s $testroot/stdout.expected $testroot/stdout
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		diff -u $testroot/stdout.expected $testroot/stdout
> +		test_done "$testroot" "$ret"
> +		return 1
> +	fi
> +
> +	(cd $testroot/wt && got log -l3 | grep ^commit > $testroot/stdout)
> +	echo "commit $new_commit2 (master)" > $testroot/stdout.expected
> +	echo "commit $new_commit1" >> $testroot/stdout.expected
> +	echo "commit $orig_commit" >> $testroot/stdout.expected
> +	cmp -s $testroot/stdout.expected $testroot/stdout
> +	ret="$?"
> +	if [ "$ret" != "0" ]; then
> +		diff -u $testroot/stdout.expected $testroot/stdout
> +	fi
> +	test_done "$testroot" "$ret"
> +}
> +
>  test_parseargs "$@"
>  run_test test_histedit_no_op
>  run_test test_histedit_swap
> @@ -1784,3 +1931,4 @@ run_test test_histedit_duplicate_commit_in_script
>  run_test test_histedit_fold_add_delete
>  run_test test_histedit_fold_only
>  run_test test_histedit_fold_only_empty_logmsg
> +run_test test_histedit_edit_only
> 

ok except for the one inline comment.