5

I made a fatal mistake using Gerrit and was wondering if anybody could provide a solution/idea to this:

Current situation is that I have a branch feature-foo onto which our team is pushing changes for review. Some of these changes are already submitted/merged, and a great number is still open for review. Now, yesterday I decided to push a patchset to a specific change, one that has around 15 preceeding changes (not merged yet).

I accidentally pushed past code review (yes, I do have these permissions, and yes I was stupid enough to not deactivate them for my own safety - lesson learned). This resulted in those 15 changes/commits to be pushed directly onto the branch instead of into code review. So now all those changes are marked MERGED in Gerrit. My immediate idea was to do a push -f using the commit that I knew was originally the tip of feature-foo.

This correctly reset the branch to where it was supposed to be. But - those 15 changes are still marked MERGED in Gerrit. What I want: I need those changes to be back to state "Review in Progress", because they are in fact still being worked on.

Any ideas, anybody? I cannot imagine that this has not happened to somebody before...

regards,

--qu

EDIT 1: To clarify - the incorrectly pushed commit resulted in a fast-forward - not a merge. Nevertheless, for Gerrit those changes/commits are still "MERGED", as if somebody pressed "Submit Change" in the Gerrit-Webinterface. So - this question is in fact about Gerrit, not Git itself.

keywords: accidental push, accidental merge

sehe
  • 374,641
  • 47
  • 450
  • 633
quaylar
  • 2,617
  • 1
  • 17
  • 31
  • possible duplicate of [Revert back changes made by merge](http://stackoverflow.com/questions/8323029/revert-back-changes-made-by-merge) – sehe Jan 17 '14 at 09:01
  • @sehe Well - no. The question is: How do i tell Gerrit that those changes/commits are in fact _not_ merged. I already reverted the incorrect push. So the branch is already reset. I just need to let Gerrit know about that... – quaylar Jan 17 '14 at 09:12
  • Oh. Sorry for missing the clue there. Have a +1 – sehe Jan 17 '14 at 09:25
  • have you tried to push the change to remote review branch after reseting the remote branch? – laplasz Jan 17 '14 at 11:10
  • @HiB I haven't. I was actually quite sure that Gerrit would not allow me to push another patchset onto a Change that is already in state MERGED. – quaylar Jan 17 '14 at 12:27
  • ok, then give it a try, since you dont have any error case now – laplasz Jan 17 '14 at 13:44

5 Answers5

2

Update for Gerrit 3+ (using NoteDB):

When submitting a change (or even just pushing it, apparently) Gerrit creates a new patchset to a change, which comes with a "merged" attribute. This needs to be undone.

For a changeset 12345, you'll need to check out the git history of refs/changes/45/12345/meta. That "branch" contains the discussion to the changeset.

By resetting that branch to the last commit before the submission (the submission should have a message like "Create patch set 2", with an attribute "Status: merged" - take its parent) you remove all knowledge that the change has ever been merged.

You might have to flush the caches, too - I just did "gerrit flush-caches --all" on the SSH interface to be sure.

Patrick Georgi
  • 688
  • 4
  • 8
1

The only way I know is to push it again, using a new Change-Id. This results in a new change being opened.

StephenKing
  • 36,187
  • 11
  • 83
  • 112
  • I was considering this, but abandoned the thought because I would lose the whole review history (~ 10 patchsets per commit) :( – quaylar Jan 17 '14 at 11:25
  • See my answer for how I solved it, +1 anyway for yours is a possible solution as well. – quaylar Jan 28 '14 at 15:48
0

Well, it turns out that this did the trick:

  1. Reset the branch to where it is supposed to be (force push past code review)
  2. Power down Gerrit, access the underlying H2 database and reset the Status of all affected Change-IDs to "Review in Progress" (standard SQL, use project name, branch name and Change-ID in the WHERE-Clause).
quaylar
  • 2,617
  • 1
  • 17
  • 31
  • 1
    Any hints on how to actually do this? Is this still relevant in Gerrit 3 which IIUC has transitioned away from SQL DBs for the most part? – nattgris Nov 29 '19 at 09:11
0

UPDATE: This answer is not very reliable. Please check the alternative one HERE

I had run into issue where I basically had to rebase 100s of commits that were already merged in gerrit.

Inspired by @Patrick Georgi's answer, I got this to work for me with the steps below:

NOTE: In my case refs/changes/xx/yy/meta were non-existent BUT they were instead in the gerrit server project's /path/to/PROJECT.git/packed-refs file.

  1. Query all current ref: refs/changes/xx/yy and save it to a temp file
gerrit query --current-patch-set 'project:LineageOS/android_packages_apps_Settings status:merged' | egrep "^    ref:" | cut -d' ' -f6 >/tmp/CHANGES_NUMBERS

Example output

$ head /tmp/CHANGES_NUMBERS
refs/changes/23/1823/1
refs/changes/39/39/2
refs/changes/44/144/2
  1. Replace the last digit in the ref with meta
for i in `cat /tmp/CHANGES_NUMBERS`; do echo "$(dirname $i)/meta"; done > /tmp/CHANGES_NUMBERS2

Example output

$ head /tmp/CHANGES_NUMBERS2
refs/changes/23/1823/meta
refs/changes/39/39/meta
refs/changes/44/144/meta
  1. Copy /tmp/CHANGES_NUMBERS2 to the gerrit server

  2. IMPORTANT!! Stop the gerrit instance and cd into the project's .git directory

    Make sure git log refs/changes/xx/yy/meta returns a log that has the string

Update patch set...

for i in `cat /tmp/CHANGES_NUMBERS2`; do git show --oneline $i; done

Output:

b288a6a98839 Update patch set 9
ac55d825c205 Update patch set 9
948061566ed5 Update patch set 9
bc3e67f03d3d Update patch set 9
351ca7578bad Update patch set 9
098d49fd8ce3 Update patch set 8
376626634cbc Update patch set 5
  1. Backup the packed-refs file somewhere safe

  2. Replace all matching refs' commit hash from /tmp/CHANGES_NUMBERS2 with refs/changes/xx/yy/meta~1

for i in `cat /tmp/CHANGES_NUMBERS2`
do
    CURRENT="$(git rev-parse $i)";
    REPLACE="$(git rev-parse $i~1)";
    sed -i "s|$CURRENT $i|$REPLACE $i|g;" packed-refs
done
RuMAN S
  • 123
  • 7
0

My previous answer was not working well. Here is an alternative approach.

One major issue with these approaches is that the Gerrit UI needs to be refreshed for each patch inorder for Gerrit to accept the new state.

I will try to show the steps with real examples. Please replace the Branches/Names/Projects etc based on your usecase

Scenario

Gerrit Project: LineageOS/android_packages_apps_Settings
Gerrit URL: https://localhost:8443
Gerrit SSH alias: lgerrit (This is the Gerrit's SSH config set in ~/.ssh/config)
Gerrit Branch: refs/heads/lineage-19.1
Gerrit Push Branch: refs/for/lineage-19.1

Currently I have 100+ commits that are status: merged in gerrit for this project.
All my refs/for/lineage-19.1 are equal to refs/heads/lineage-19.1 as all commits are merged. There are few status: open commits that were used to debug stuff. I want to preserve these commits for future

Task

I want to UNMERGE the 100+ commits, WITHOUT creating a new branch.
I want to reset my refs/heads/lineage-19.1 branch to the UPSTREAM lineage-19.1 branch.
I want all my 100+ commits to be status: open on top of `refs/heads/lineage-19.1

Steps

Prerequisites:

You need to have access to the gerrit server. You need to install the screen package.

1) Force reset refs/heads/lineage-19.1 to Upstream lineage-19.1

git push lgerrit:LineageOS/android_packages_apps_Settings LineageOS/lineage-19.1:refs/heads/lineage-19.1 -f -o skip-validation=true

Here if i try to push my status, we get an error that the chnges are closed, which is as expected:

remote: commit b2a1a05: warning: subject >50 characters; use shorter first paragraph
To lgerrit:LineageOS/android_packages_apps_Settings
 ! [remote rejected]         HEAD -> refs/for/lineage-19.1 (change https://localhost:8443/c/LineageOS/android_packages_apps_Settings/+/19 closed)
error: failed to push some refs to 'lgerrit:LineageOS/android_packages_apps_Settings'

2) STOP the gerrit server gerrit.sh stop


From Now we are in our gerrit server


3) Navigate to the Project's folder in your Gerrit server and run a git gc

I did this as I think it puts all the refs/changes mappings in packed-refs which is needed later on

~/gerrit/git/LineageOS/android_packages_apps_Settings.git 10s
❯ git gc
Enumerating objects: 978412, done.
Counting objects: 100% (978412/978412), done.
Delta compression using up to 10 threads
Compressing objects: 100% (167229/167229), done.
Writing objects: 100% (978412/978412), done.
Selecting bitmap commits: 118017, done.
Building bitmaps: 100% (328/328), done.
Total 978412 (delta 631271), reused 978344 (delta 631203), pack-reused 0

4) Write a bash function to export all changes that are MERGED (delimited by a :

export_merged() {
    for i in $(grep meta packed-refs | cut -d' ' -f2)
    do
        echo "$i:$(git log --oneline $i --grep='autogenerated:gerrit:merged')" | grep patch
    done | cut -d' ' -f1
}

5) Export merged commits to a text file

$ export_merged | tee /tmp/export_merged.txt
refs/changes/01/101/meta:d9971cb5b4
refs/changes/02/102/meta:f9e2c6c713
refs/changes/02/2/meta:bafba9cb72
...

6) Write a test loop that updates the refs from above to one commit behind the current commit. i.e refs/changes/01/101/meta~1

for i in $(cat /tmp/export_merged.txt); do
    REF="$(echo $i | cut -d':' -f1)"
    MERGED="$(echo $i | cut -d':' -f2)"
    NEWREF="$(git rev-parse $MERGED~1)"
    echo git update-ref $REF $NEWREF
done

The output will be like:

git update-ref refs/changes/01/101/meta 71bb5ae0eebc0b3696fef22260cb121ff1045b82
git update-ref refs/changes/02/102/meta 952501d0bc52e44c0e022928dc52fc6ccc3b6254
...

7) IMPORTANT Verify that the commit hash matches refs/changes/xx/yy/meta~1

In my case it does:

$ git rev-parse refs/changes/01/101/meta~1
71bb5ae0eebc0b3696fef22260cb121ff1045b82

If it does not, stop here and ABORT.

8) Backup packed-refs file somewhere safe.

9) If all looks good, remove the echo from the for loop above and run git gc again

for i in $(cat /tmp/export_merged.txt); do
    REF="$(echo $i | cut -d':' -f1)"
    MERGED="$(echo $i | cut -d':' -f2)"
    NEWREF="$(git rev-parse $MERGED~1)"
    git update-ref $REF $NEWREF
done

git gc

Now the packed-refs file will change. Here is the diff in my case:

$ diff packed-refs /tmp/packed-refsback | head -n4
8c8
< 71bb5ae0eebc0b3696fef22260cb121ff1045b82 refs/changes/01/101/meta
---
> d9971cb5b4c4e5b8a8311f2d5c50eeba08b86f84 refs/changes/01/101/meta
...

10) Start gerrit And flush caches

ssh lgerrit gerrit flush-caches --all

Making GERRIT notice the new changes.

Gerrit for some reason still thinks that the changes are merged. This is fixed when you open the change from the UI however that is cumbersome for large number of changes.

Here you see gerrit still rejecting my push.

To lgerrit:LineageOS/android_packages_apps_Settings
 ! [remote rejected]         HEAD -> refs/for/lineage-19.1 (change https://localhost:8443/c/LineageOS/android_packages_apps_Settings/+/19 closed)
error: failed to push some refs to 'lgerrit:LineageOS/android_packages_apps_Settings'

Then I open my browser and simply visit this change. Notice the change 19 is no longer error-ing out but it is the next change number 20.

To lgerrit:LineageOS/android_packages_apps_Settings
 ! [remote rejected]         HEAD -> refs/for/lineage-19.1 (change https://localhost:8443/c/LineageOS/android_packages_apps_Settings/+/20 closed)
error: failed to push some refs to 'lgerrit:LineageOS/android_packages_apps_Settings'

So we need to load all the changes for gerrit to reflect the new status. A curl on the change url does not work, but we need another URL.

Last step: Loop through ALL changes in project through curl asynchronously

Note Change your Project URL accordingly. Notice the %2F replacing /!

for i in $(grep meta packed-refs| cut -d' ' -f2)
do
    CHANGE=$(basename $(dirname $i))
    screen -d -m curl -s "https://localhost:8443/changes/LineageOS%2Fandroid_packages_apps_Settings~$CHANGE/revisions/current/mergeable" -k -o /tmp/log
    sleep 0.1
done

Results :)

remote:   https://localhost:8443/c/LineageOS/android_packages_apps_Settings/+/148 SoftAp: add back AP Band preference into tether settings
remote:
To lgerrit:LineageOS/android_packages_apps_Settings
 * [new reference]           HEAD -> refs/for/lineage-19.1

Helper script: Link

RuMAN S
  • 123
  • 7