Git-gpush-scripts
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine. Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean. |
The git-gpush Script Suite
This page contains some background information for the git-gpush, git-gpick, and git-gpull scripts found in the qt/qtrepotools repository.
Overview
The general idea of these scripts is to make working with Gerrit more transparent:
- You can pull as often as you want, including right before pushing, as you would usually do in a git pull —rebase based workflow. Without git-gpush, this would unnecessarily rebase previously pushed Changes, which would make Gerrit's inter-diff feature slower and much less useful.
- You can keep many unrelated Changes locally without creating spurious dependencies between them on Gerrit. While you can achieve the same by having a separate local branch for every series, having all Changes in a single branch is much more convenient.
- You can push updates to specific Changes without invalidating reviews of unrelated Changes.
You may have noticed that these major features are geared towards making not your life easier, but that of your reviewers. Until roles are switched, that is.
The way how these scripts achieve this is by letting you manage "virtual branches" (called "series") within your local working branch. When a series is pushed, its commits are rebased (usually to a commit from the upstream branch) to isolate it from other local Changes. The working tree is not affected by this in any way, so no unnecessary rebuilds result from this.
More Features
git gpush
- The scripts remember which Changes belong to a series, so once you pushed it, the complete series can be identified by specifying a single Change (or none at all, if it's HEAD). This saves you from needing to keep track of how many Changes are in the series. You don't even need to use the currently last Change in the series. Also, commits can be specified by Change-Id. Taken together, these features make it possible to use the same command line to repeatedly re-push the same series regardless of any (interactive) rebasing and amending.
- An overview of the pushed Changes and their status relative to Gerrit is displayed.
- Pushes which would be rejected by Gerrit are detected upfront.
- Updates to the Changes pushed from other clones of the target repository (or made directly on Gerrit) are detected, so you won't accidentally overwrite them.
- WIP markers are detected and the Changes accordingly pushed as Drafts.
- Reviewers can be conveniently added by their IRC nickname.
git gpick
This script allows you to download pending Changes into your local branch, for local testing, reviewing and amending.
- Notable differences from Gerrit's built-in copy&paste-able download command lines:
- Unlike 'cherry-pick', it can download an entire series in one go, so you just need to copy the Change-Id of the last Change and execute a single short command.
- Unlike 'checkout', your local Changes are not "hidden" on another branch, so you don't need to "switch contexts" (rebuild lots of stuff, deal with bugs you already fixed, etc.).
- Unlike 'pull', no merge commit is introduced, so rebasing remains "natural".
- The downloaded Changes are a separate series, so there is no danger that you accidentally re-push them.
- Changes which already exist locally are replaced, so it is possible to pull in updates which have been pushed from other clones (or done directly on Gerrit).
- This checks for conflicts, so you won't lose concurrent local modifications.
- There also exists a mode which only checks for updates on Gerrit without replacing any local commits.
Examples
As usual, you should start with some solid RTFM: ;)
$ git gpush -h
$ git gpick -h
$ git gpull -h
Get all your pending changes on the current branch, in case you don't do this anyway:
$ git cherry-pick …
Alternatively, you could merge all your local branches and then rebase.
If you have pending Changes for the given branch on Gerrit, you need to synchronize the state:
$ git gpick —check
Then create a series consisting of two Changes, and push it, adding two reviewers:
<hack hack hack>
$ git commit -a
<hack hack hack>
$ git commit -a
$ git gpush –2 +someguy +anotherguy@example.com
Amend and re-push the series:
$ git rebase -i{u}
<…>
$ git gpush
Add another Change to the series:
<hack hack hack>
$ git commit -a
$ git gpush -a
Re-push the series when it's not at the top any more:
$ git gpush I123415affe:
Download somebody else's series:
$ git gpick +I987ac23f1
No need to worry about accidentally re-pushing it any more!
Update your local copy after somebody has been messing with your Change (gpush will tell you about it, so you won't overwrite the Changes accidentally):
$ git gpick I9ce6d9e7a3
pull/rebase your local series:
$ git gpull
Answering Criticism
Shouldn't this be implemented by the server instead of client-side scripts?
Some of the problems could be addressed on the server side, but others are inherently client-side:
- The automatic "downbasing" to keep a Change's base constant even after it has been rebased locally could be done on the server, indeed.
- The management of series could- in principle - be done on the server. However:
' This requires additional parameters which would need to be passed to git-receive-pack on Gerrit. Nobody wants to do that by hand, so you'd end up with a script anyway. ' This would upload unrelated Changes just to discard them immediately.
- The status report can be done by the server - in fact, it already is done, though without giving per-Change information. However, for obvious reasons this happens only after uploading the Changes, which means you lose the potential for entirely avoiding doomed uploads.
- The concurrent update overwrite protection is inherently client-side, as only the particular clone can know what was pushed from it the last time.
- The reviewer add function also needs to wrap git-receive-pack and thus invites client-side scripting.
- Identifying commits by Change-Id must be done client-side, as otherwise git doesn't even know what to push to the server.
- git-gpick is rather obviously a client-side tool.
This changes my workflow! This is unacceptable!
If you refuse to take advantage of the automated series management gpush offers, the only necessary change in workflow would be consistently using 'git gpush –1' for the first local Change and 'git gpush -a' for subsequent ones, instead of 'git push gerrit HEAD:refs/for/foo'. If you are convinced that this is inacceptable, you should probably rethink your priorities.
It's a new tool! It's harder for newbies!
Pushing to Gerrit already requires installing the Change-Id-creating commit-msg hook. Just cloning another (small) repository to get access to the scripts doesn't seem terribly hard in comparison. Also, qt5's init-repository script will clone the repository by default anyway, so the only thing necessary is adding the bin directory to git's PATH. And the basic usage of gpush is unarguably easier than that of plain git.
It makes Qt different from other Gerrit-using projects!
These scrips are in no way specific to Qt. Anyone can use them with any Gerrit installation. Upstreaming will be considered at some point.