Repository: red-hat-storage/rhcephpkg

Issue:
"merge-patches --hard-reset"
Currently the `rhcephpkg merge-patches` command can automatically merge patches from the rdopkg -patches branch *as long as* it is a fast-forward merge.

Sometimes if we need to back out a patch, this is no longer a fast-forward merge, and we have to do the following by hand instead:

```
$ git checkout patch-queue/ceph-2-ubuntu
$ git reset --hard patches/ceph-2-rhel-patches
```

It would be great to have a `rhcephpkg merge-patches --hard-reset` to do this automatically.

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/9866736/badge)](https://coveralls.io/builds/9866736)

Coverage decreased (-0.2%) to 68.107% when pulling **59466d6b4a5f87ab80c729ea80a5f09b21ba1f39 on merge-patches-force** into **ccc287abb2d2674338a8f80dbb825a10f834f2f5 on master**.


Patch:
diff --git a/rhcephpkg/merge_patches.py b/rhcephpkg/merge_patches.py
index 9db15c2..6cb48d1 100644
--- a/rhcephpkg/merge_patches.py
+++ b/rhcephpkg/merge_patches.py
@@ -14,23 +14,30 @@ that into our local patch-queue branch, so that both branches align.
 This command helps to align the patch series between our RHEL packages and our
 Ubuntu packages.
 
+Options:
+--force    Do a hard reset, rather than restricting to fast-forward merges
+           only. Use this option if the RHEL patches branch was amended or
+           rebased for some reason.
 """
     name = 'merge-patches'
 
     def __init__(self, argv):
         self.argv = argv
-        self.options = []
+        self.options = ['--force', '--hard-reset']
 
     def main(self):
         self.parser = Transport(self.argv, options=self.options)
         self.parser.catch_help = self.help()
         self.parser.parse_args()
-        self._run()
+        force = False
+        if self.parser.has(['--force', '--hard-reset']):
+            force = True
+        self._run(force)
 
     def help(self):
         return self._help
 
-    def _run(self):
+    def _run(self, force=False):
         # Determine the names of the relevant branches
         current_branch = util.current_branch()
         debian_branch = util.current_debian_branch()
@@ -43,6 +50,10 @@ Ubuntu packages.
             # For example: "git pull --ff-only patches/ceph-2-rhel-patches"
             cmd = ['git', 'pull', '--ff-only',
                    'patches/' + rhel_patches_branch]
+            if force:
+                # Do a hard reset on HEAD instead.
+                cmd = ['git', 'reset', '--hard',
+                       'patches/' + rhel_patches_branch]
         else:
             # HEAD is our debian branch. Use "git fetch" to update the
             # patch-queue ref. For example:
@@ -50,6 +61,10 @@ Ubuntu packages.
             #  patches/ceph-2-rhel-patches:patch-queue/ceph-2-ubuntu"
             cmd = ['git', 'fetch', '.',
                    'patches/%s:%s' % (rhel_patches_branch, patches_branch)]
+            if force:
+                # Do a hard push (with "+") instead.
+                cmd = ['git', 'push', '.', '+%s:patches/%s' %
+                       (patches_branch, rhel_patches_branch)]
         log.info(' '.join(cmd))
         subprocess.check_call(cmd)
 


Test patch:
diff --git a/rhcephpkg/tests/test_merge_patches.py b/rhcephpkg/tests/test_merge_patches.py
index 90da51c..308f0aa 100644
--- a/rhcephpkg/tests/test_merge_patches.py
+++ b/rhcephpkg/tests/test_merge_patches.py
@@ -42,6 +42,31 @@ class TestMergePatches(object):
         expected = ['git', 'pull', '--ff-only', 'patches/ceph-2-rhel-patches']
         assert self.last_cmd == expected
 
+    def test_force_on_debian_branch(self, monkeypatch):
+        monkeypatch.setenv('HOME', FIXTURES_DIR)
+        monkeypatch.setattr('subprocess.check_call', self.fake_check_call)
+        # set current_branch() to a debian branch:
+        monkeypatch.setattr('rhcephpkg.util.current_branch',
+                            lambda: 'ceph-2-ubuntu')
+        localbuild = MergePatches(())
+        localbuild._run(force=True)
+        # Verify that we run the "git push" command here.
+        expected = ['git', 'push', '.',
+                    '+patch-queue/ceph-2-ubuntu:patches/ceph-2-rhel-patches']
+        assert self.last_cmd == expected
+
+    def test_force_on_patch_queue_branch(self, monkeypatch):
+        monkeypatch.setenv('HOME', FIXTURES_DIR)
+        monkeypatch.setattr('subprocess.check_call', self.fake_check_call)
+        # set current_branch() to a patch-queue branch:
+        monkeypatch.setattr('rhcephpkg.util.current_branch',
+                            lambda: 'patch-queue/ceph-2-ubuntu')
+        localbuild = MergePatches(())
+        localbuild._run(force=True)
+        # Verify that we run the "git reset" command here.
+        expected = ['git', 'reset', '--hard', 'patches/ceph-2-rhel-patches']
+        assert self.last_cmd == expected
+
 
 class TestMergePatchesRhelPatchesBranch(object):