@@ -73,6 +73,13 @@ On successful cherry-pick, an entry is appended to ``.pickman-history`` with:
This file is committed automatically and included in the MR description when
using ``-p``.
+After successfully applying commits, update the database to record progress::
+
+ ./tools/pickman/pickman commit-source us/next <commit-hash>
+
+This updates the last cherry-picked commit for the source branch, so subsequent
+``next-set`` and ``apply`` commands will start from the new position.
+
Requirements
------------
@@ -45,6 +45,11 @@ def parse_args(argv):
apply_cmd.add_argument('-t', '--target', default='master',
help='Target branch for MR (default: master)')
+ commit_src = subparsers.add_parser('commit-source',
+ help='Update database with last commit')
+ commit_src.add_argument('source', help='Source branch name')
+ commit_src.add_argument('commit', help='Commit hash to record')
+
subparsers.add_parser('compare', help='Compare branches')
subparsers.add_parser('list-sources', help='List tracked source branches')
@@ -401,6 +401,41 @@ def do_apply(args, dbs): # pylint: disable=too-many-locals
return 0 if success else 1
+def do_commit_source(args, dbs):
+ """Update the database with the last cherry-picked commit
+
+ Args:
+ args (Namespace): Parsed arguments with 'source' and 'commit' attributes
+ dbs (Database): Database instance
+
+ Returns:
+ int: 0 on success, 1 on failure
+ """
+ source = args.source
+ commit = args.commit
+
+ # Resolve the commit to a full hash
+ try:
+ full_hash = run_git(['rev-parse', commit])
+ except Exception: # pylint: disable=broad-except
+ tout.error(f"Could not resolve commit '{commit}'")
+ return 1
+
+ old_commit = dbs.source_get(source)
+ if not old_commit:
+ tout.error(f"Source '{source}' not found in database")
+ return 1
+
+ dbs.source_set(source, full_hash)
+ dbs.commit()
+
+ short_old = old_commit[:12]
+ short_new = full_hash[:12]
+ tout.info(f"Updated '{source}': {short_old} -> {short_new}")
+
+ return 0
+
+
def do_test(args, dbs): # pylint: disable=unused-argument
"""Run tests for this module.
@@ -424,6 +459,7 @@ def do_test(args, dbs): # pylint: disable=unused-argument
COMMANDS = {
'add-source': do_add_source,
'apply': do_apply,
+ 'commit-source': do_commit_source,
'compare': do_compare,
'list-sources': do_list_sources,
'next-set': do_next_set,
@@ -127,6 +127,13 @@ class TestParseArgs(unittest.TestCase):
self.assertEqual(args.source, 'us/next')
self.assertEqual(args.branch, 'my-branch')
+ def test_parse_commit_source(self):
+ """Test parsing commit-source command."""
+ args = pickman.parse_args(['commit-source', 'us/next', 'abc123'])
+ self.assertEqual(args.cmd, 'commit-source')
+ self.assertEqual(args.source, 'us/next')
+ self.assertEqual(args.commit, 'abc123')
+
def test_parse_compare(self):
"""Test parsing compare command."""
args = pickman.parse_args(['compare'])
@@ -942,6 +949,64 @@ class TestApply(unittest.TestCase):
self.assertIn('No new commits to cherry-pick', stdout.getvalue())
+class TestCommitSource(unittest.TestCase):
+ """Tests for commit-source command."""
+
+ def setUp(self):
+ """Set up test fixtures."""
+ fd, self.db_path = tempfile.mkstemp(suffix='.db')
+ os.close(fd)
+ os.unlink(self.db_path)
+ self.old_db_fname = control.DB_FNAME
+ control.DB_FNAME = self.db_path
+ database.Database.instances.clear()
+
+ def tearDown(self):
+ """Clean up test fixtures."""
+ control.DB_FNAME = self.old_db_fname
+ if os.path.exists(self.db_path):
+ os.unlink(self.db_path)
+ database.Database.instances.clear()
+ command.TEST_RESULT = None
+
+ def test_commit_source_not_found(self):
+ """Test commit-source with unknown source."""
+ with terminal.capture():
+ dbs = database.Database(self.db_path)
+ dbs.start()
+ dbs.close()
+
+ database.Database.instances.clear()
+ command.TEST_RESULT = command.CommandResult(stdout='fullhash123')
+
+ args = argparse.Namespace(cmd='commit-source', source='unknown',
+ commit='abc123')
+ with terminal.capture() as (_, stderr):
+ ret = control.do_pickman(args)
+ self.assertEqual(ret, 1)
+ self.assertIn("Source 'unknown' not found", stderr.getvalue())
+
+ def test_commit_source_success(self):
+ """Test commit-source updates database."""
+ with terminal.capture():
+ dbs = database.Database(self.db_path)
+ dbs.start()
+ dbs.source_set('us/next', 'oldcommit12345')
+ dbs.commit()
+ dbs.close()
+
+ database.Database.instances.clear()
+ command.TEST_RESULT = command.CommandResult(stdout='newcommit67890')
+
+ args = argparse.Namespace(cmd='commit-source', source='us/next',
+ commit='abc123')
+ with terminal.capture() as (stdout, _):
+ ret = control.do_pickman(args)
+ self.assertEqual(ret, 0)
+ self.assertIn('oldcommit123', stdout.getvalue())
+ self.assertIn('newcommit678', stdout.getvalue())
+
+
class TestParseUrl(unittest.TestCase):
"""Tests for parse_url function."""