I/we need a lesson in git and/or gitlab and/or merge requests

Fred Wright fw at fwright.net
Sun Nov 19 04:44:48 UTC 2023


In general, it's a good idea to read an actual book on git, rather than 
trying to understand it purely through manpages.  The one I used (almost a 
decade ago) is this one:

 	https://www.amazon.com/gp/product/1449316387/

It doesn't tell you everything you might want to know, but it covers most 
of it, while giving you enough backround to understand more esoteric stuff 
that you might find via Google.

On Sat, 11 Nov 2023, Hal Murray via devel wrote:

> Merge requests seem reasonable if all goes well.  My work flow is roughly:
>  download the patch  (URL plus ".patch")
>  scan it
>  maybe apply and test
>  approve and merge
>
> But things go downhill if I don't like something.  What I get from James is an
> update to the MR, a patch to the patch.  That makes reading/checking the patch
> harder and clutters up the git log.
>
> What if I don't like the description of a patch?
>
> Merge has an option to reduce all the patches to one.  But often that isn't
> appropriate.
>
> git works so well for most things.  I think I/we are missing something in the
> workflow.

Indeed.  The best way to obtain the content of an MR locally is via "git 
fetch", a highly underrated command.  It's also good for updating your 
local repo in general.  It tends to get overlooked because so many people 
use "git pull", which is a combination of "git fetch" and either "git 
merge" or "git rebase", depending on options.  But performing the two 
steps separately gives you better control.  Personally, I never use "git 
pull".

The first form of "git fetch" is:

 	git fetch <remote>

This fetches all new commits from all branches in <remote> to your local 
repo, and updates the *local* heads of the *remote* branches to reflect 
that state.  It does not touch your local branches, your index, or your 
working directory, so it's completely safe to use at any time (well, 
except during another git operation :-)).

The second form is:

 	git fetch <remote> [+]<remote_branch>:<local_branch>

This updates <local_branch> to match <remote_branch>, creating 
<local_branch> if necessary.  If <local_branch> already exists and the 
update wouldn't be a "fast forward" (appending new commits without 
replacing existing ones), then it fails unless the '+' is included (the 
equivalent of '-f' on "git push").  It does *not* touch the index or 
working directory, so it's safe to use in the middle of other activities, 
but for that reason it refuses to fetch into the currently checked out 
local branch.  When that's a problem, there are multiple ways around it, 
with the choice depending on circumstances.

To get the contents of an MR locally:

 	git fetch <MR_repo> <MR_branch>:<local_test_branch>

The one annoying thing is that GitLab doesn't provide a feature to get 
"<MR_remote> <MR_branch>" into the clipboard, so that requires some minor 
chasing.

This pulls in the content of the MR to <local_test_branch>, not just as a 
patch, but as the exact same set of commits as in <MR_branch>, complete 
with identical commit IDs.  If you simply checkout <local_test_branch>, 
you now have exact same content as the MR head, but you can also rebase 
it, cherry-pick it, merge it, or do whatever else you want with it for 
testing.  This includes being able to compare whatever different states 
you want with normal git tools.

If the MR is updated, you can simply repeat the same fetch to update your 
local copy.  If the update was a forced update (e.g., after rebasing it to 
a later master), you'll need to use the '+' on the fetch to allow the 
local forced update.

BTW, it's best not to develop on the local master branch, but instead to 
use a separate local branch.  That way, the local master can be 
fast-forwarded to the upstream master at any time.  It also makes it less 
likely that you'll have the local master checked out and hence blocking 
the fetch to master.  Even if the intent is to push to the upstream master 
directly rather than going through the MR mechanism, you can develop in 
another branch and push the final state to master.

> Should we be throwing away merges and making new ones rather than patching
> them?

Generally no, though I wouldn't exactly say that "patching them" is the 
right way to look at it.

> How do I backup a bunch of commits that turned into a MR so I can make them
> better and try again?

You don't need to explicitly "backup" anything.  You can simply update 
your branch and then push it to the MR's source branch.  This requires 
'-f' on the push if the changes are anything other than adding new 
commits.  If you're referring to editing someone else's MR, just fetch it 
(see above) and go from there.

You can modify the latest commit on the current branch with "git commit 
--amend", though during development it may make more sense to just add new 
commits and clean it up afterward.  Such cleanups don't necessarily have 
to be done before updating the MR for review, but should be done before 
actually merging it.

The general mechanism for updating a whole set of recent commits (in the 
curently checked-out branch) is interactive rebase.  This serves a very 
different purpose than normal rebase, but it uses the same underlying 
mechanism, and consequently the same command.  It starts with:

 	git rebase -i <starting_commit>

Note that <starting commit> is actually the *last* commit *not* to be 
modified, i.e., the parent of the first commit to be potentially modified. 
In the particular case of cleaning up a short-term development branch, 
typical usage (assuming your branch is up to date with master) would be:

 	git rebase -i master

This puts you in your editor with a "sequence file" listing all commits 
from the starting point to HEAD, in a one-line format, in *forward* 
chronological order.  Each is preceded by an action keyword, initially 
'pick', with means "apply this commit as is".  You can drop commits either 
by changing the action to 'drop', or by simply deleting them from the 
list.  You can change the order of the commits (which may introduce 
conflcts that need to be resolved.  You can modify the content of any 
commit by changing its action to 'edit'.  You can change a commit message 
without changing its content by specifying 'reword'.  And you can combine 
two or more consecutive commits by changing the action of all but the 
first to 'squash'.  Note that this is selective - it's not all or nothing 
like the WebGUI option (AIUI).

Beware that the default behavior of interactive rebase is probably not 
what you want if it straddles merge commits.  There's a '-r' option that 
fixes this by adding tags to the sequence file so that it can preserve the 
branch structure, but its setup involves processing *all* lines of descent 
from the starting point, and may take a while.  But this project makes 
very little use of merge commits, so the issue is mostly moot.

> I'm on a list or two where patches are distributed via email.  git has several
> commands for that.  Iterations usually have a v1 v2 ... as part of the
> Subject.  Often individual parts will be approved.  It's a lof of clutter in
> the email stream but the discussion gets archived in email rather than hidden
> over in a MR.

Patches via email are more tedious, more error-prone, and sooo 20th 
century. :-)

MR discussions aren't "hidden" - they remain forever in the MR history 
(though the default display is limited to open MRs).  Admittedly, having 
to use a hidden sidebar to get to the MR list has poor discoverability; I 
like GitHub's UI better.

> Is there a way in gitlab to approve only one of the patches rather than all of
> them?  I think I could do that by downloading the patch which is several email
> messages, editing out the one I want...  Again, if that was the right thing to
> be doing, I'd expect git to support it which it probably does if you use their
> email mode.

I don't think this can be done from the WebGUI.  Of course, once you have 
the content in your local repo, you can push anything you want, but the 
end run around the WebGUI probably confuses the history.  With or without 
this problem, it's a bad idea to combine multiple unrelated changes into a 
single MR anyway.  It's best to stick to one topic per branch, both 
locally and in any MRs derived from such branches.  As long as the topical 
changes don't conflict, you can easily create a merge branch with whatever 
set of branches you want at any given time, so putting multiple topics in 
one development branch just to combine them for testing is unnecessary.


On Sat, 11 Nov 2023, James Browning via devel wrote:

> "git rebase -i" allows you to selectively drop, reorder, squash,
> and/or amend commits.  At least, that is what I have heard.

Yes - see above.

>  I use GUI tools.

I avoid most GUI tools since they hide too much and aren't scriptable.  I 
do, however, sometimes use gitk to view the branch structure graphically.

Fred Wright


More information about the devel mailing list