Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[testcase notes] #27: git rebase options #66

Open
dorawyy opened this issue Nov 6, 2017 · 3 comments
Open

[testcase notes] #27: git rebase options #66

dorawyy opened this issue Nov 6, 2017 · 3 comments

Comments

@dorawyy
Copy link
Owner

dorawyy commented Nov 6, 2017

To test git rebase parameter options

git rebase testcases scratches--> #76

References

git-rebase documentation

Synopsis

git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
	[<upstream> [<branch>]]
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
	--root [<branch>]
git rebase --continue | --skip | --abort | --quit | --edit-todo

--keep-empty

Keep the commits that do not change anything from its parents in the result.

--skip

Restart the rebasing process by skipping the current patch.

--edit-todo

Edit the todo list during an interactive rebase.

-m / --merge

Use merging strategies to rebase. When the recursive (default) merge strategy is used, this allows rebase to be aware of renames on the upstream side.

Note that a rebase merge works by replaying each commit from the working branch on top of the <upstream> branch. Because of this, when a merge conflict happens, the side reported as ours is the so-far rebased series, starting with <upstream>, and theirs is the working branch. In other words, the sides are swapped.

-s <strategy> / --strategy=<strategy>

Use the given merge strategy. If there is no -s option git merge-recursive is used instead. This implies --merge.

Because git rebase replays each commit from the working branch on top of the <upstream> branch using the given strategy, using the ours strategy simply discards all patches from the <branch>, which makes little sense.

-X <strategy-option> / --strategy-option=<strategy-option>

Pass the <strategy-option> through to the merge strategy. This implies --merge and, if no strategy has been specified, -s recursive. Note the reversal of ours and theirs as noted above for the -m option.

-C<n>

Ensure at least <n> lines of surrounding context match before and after each change. When fewer lines of surrounding context exist they all must match. By default no context is ever ignored.

-f / --force-rebase

Force a rebase even if the current branch is up to date and the command without --force would return without doing anything.

You may find this (or --no-ff with an interactive rebase) helpful after reverting a topic branch merge, as this option recreates the topic branch with fresh commits so it can be remerged successfully without needing to "revert the reversion".

-i / --interactive

Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits.

The commit list format can be changed by setting the configuration option rebase.instructionFormat. A customized instruction format will automatically have the long commit hash prepended to the format.

-p / --preserve-merges

Recreate merge commits instead of flattening the history by replaying commits a merge commit introduces. Merge conflict resolutions or manual amendments to merge commits are not preserved.

This uses the --interactive machinery internally, but combining it with the --interactive option explicitly is generally not a good idea unless you know what you are doing.
Ref: Differences between default merge and merge-preserving merge

-x <cmd> / --exec <cmd>

Append "exec " after each line creating a commit in the final history. will be interpreted as one or more shell commands. Generally, use together with -i

--root

Rebase all commits reachable from <branch>, instead of limiting them with an <upstream>. This allows you to rebase the root commit(s) on a branch. When used with --onto, it will skip changes already contained in <newbase> (instead of <upstream>) whereas without --onto it will operate on every change. When used together with both --onto and --preserve-merges, all root commits will be rewritten to have <newbase> as parent instead.
If not --onto branch is specified, --root will root back the second commit of the repo (the one following the initial commit)

--autosquash & --no-autosquash

When the commit log message begins with "squash! …​" (or "fixup! …​"), and there is already a commit in the todo list that matches the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup). A commit matches the ... if the commit subject matches, or if the ... refers to the commit’s hash. As a fall-back, partial matches of the commit subject work, too. The recommended way to create fixup/squash commits is by using the --fixup/--squash options of git-commit.

This option is only valid when the --interactive option is used.

--autostash & --no-autostash

Automatically create a temporary stash entry before the operation begins, and apply it after the operation ends. This means that you can run rebase on a dirty worktree. However, use with care: the final stash application after a successful rebase might result in non-trivial conflicts.

--no-ff

With --interactive, cherry-pick all rebased commits instead of fast-forwarding over the unchanged ones. This ensures that the entire history of the rebased branch is composed of new commits.

Without --interactive, this is a synonym for --force-rebase.

You may find this helpful after reverting a topic branch merge, as this option recreates the topic branch with fresh commits so it can be remerged successfully without needing to "revert the reversion".

Tools/command to use

  • git log branch1..branch2: list all commits on branch2 after the last common commit of branch1 and branch2

git rebase Interactive mode

Rebasing interactively means that you have a chance to edit the commits which are rebased. You can reorder the commits, and you can remove them (weeding out bad or otherwise unwanted patches). The interactive mode is meant for this type of workflow:

  1. have a wonderful idea
  2. hack on the code
  3. prepare a series for submission
  4. submit

where point 2. consists of several instances of

a) regular use
* finish something worthy of a commit
* commit
b) independent fixup
* realize that something does not work
* fix that
* commit it

Sometimes the thing fixed in b.2. cannot be amended to the not-quite perfect commit it fixes, because that commit is buried deeply in a patch series. That is exactly what interactive rebase is for: use it after plenty of "a"s and "b"s, by rearranging and editing commits, and squashing multiple commits into one.

Start it with the last commit you want to retain as-is:

git rebase -i <after-this-commit>

An editor will be fired up with all the commits in your current branch (ignoring merge commits), which come after the given commit. You can reorder the commits in this list to your heart’s content, and you can remove them. The list looks more or less like this:

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

The oneline descriptions are purely for your pleasure; git rebase will not look at them but at the commit names ("deadbee" and "fa1afe1" in this example), so do not delete or edit the names.

By replacing the command "pick" with the command "edit", you can tell git rebase to stop after applying that commit, so that you can edit the files and/or the commit message, amend the commit, and continue rebasing.

If you just want to edit the commit message for a commit, replace the command "pick" with the command "reword".

To drop a commit, replace the command "pick" with "drop", or just delete the matching line.

If you want to fold two or more commits into one, replace the command "pick" for the second and subsequent commits with "squash" or "fixup". If the commits had different authors, the folded commit will be attributed to the author of the first commit. The suggested commit message for the folded commit is the concatenation of the commit messages of the first commit and of those with the "squash" command, but omits the commit messages of commits with the "fixup" command.

git rebase will stop when "pick" has been replaced with "edit" or when a command fails due to merge errors. When you are done editing and/or resolving conflicts you can continue with git rebase --continue.

For example, if you want to reorder the last 5 commits, such that what was HEAD~4 becomes the new HEAD. To achieve that, you would call git rebase like this:

git rebase -i HEAD~5

And move the first patch to the end of the list.
You might want to preserve merges, if you have a history like this:

           X
            \
         A---M---B
        /
---o---O---P---Q

Suppose you want to rebase the side branch starting at "A" to "Q". Make sure that the current HEAD is "B", and call

git rebase -i -p --onto Q O

Reordering and editing commits usually creates untested intermediate steps. You may want to check that your history editing did not break anything by running a test, or at least recompiling at intermediate points in history by using the "exec" command (shortcut "x"). You may do so by creating a todo list like this one:

pick deadbee Implement feature XXX
fixup f1a5c00 Fix to feature XXX
exec make
pick c0ffeee The oneline of the next commit
edit deadbab The oneline of the commit after
exec cd subdir; make test
...

git rebase interactive mode options

# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
@dorawyy
Copy link
Owner Author

dorawyy commented Nov 6, 2017

Test1 (multiple conflicts)

Scenario setting up

# 2
git checkout mt5
git commit test26_merge_test  -m "mt5: x=1"

# 3
git checkout mt6
git commit test26_merge_test  -m "mt6: x=2"

# 4
git checkout mt5
git commit test26_merge_test  -m "mt5: x=2, y=1"

# 5
git checkout mt6
git commit test26_merge_test  -m "mt6: x=3, z=2"

# 6
git checkout mt5
git commit test26_merge_test  -m "mt5: x=3, z=2"

Option1: normal rebase

git checkout mt5
git rebase mt6

Replay commit2 on top of commit5 failed, due to conflict, requires manually resolution:
screen shot 2017-11-06 at 10 19 25 am
screen shot 2017-11-06 at 10 21 16 am

Resolve the conflict when reapplying commit2, continue rebase
screen shot 2017-11-06 at 10 23 26 am

Then, meeting the second conflict:
screen shot 2017-11-06 at 10 24 55 am
screen shot 2017-11-06 at 10 24 15 am

Resolve the conflict when reapplying commit4 (same it exactly the same as commit6, continue rebase:
screen shot 2017-11-06 at 10 26 18 am

screen shot 2017-11-06 at 10 28 16 am

As the last commit changes are the same, the previsous commit6 is omitted.

Option2: -p / --preserve-merges

git checkout mt5
git rebase --preserve-merges mt6 
  • Applying commit2, conflict

screen shot 2017-11-06 at 10 49 59 am

screen shot 2017-11-06 at 10 50 56 am

screen shot 2017-11-06 at 10 53 18 am

Resolve the conflict when applying commit2 to head:
screen shot 2017-11-06 at 10 53 52 am
screen shot 2017-11-06 at 10 54 52 am

Then, keep rebasing, generates a commit, here is the commit message:
screen shot 2017-11-06 at 10 55 10 am

Here is the project tree after rebasing commit2 with -p param:
screen shot 2017-11-06 at 11 00 29 am

  • Apply commit4, meeting conflict again:

screen shot 2017-11-06 at 10 59 19 am

screen shot 2017-11-06 at 11 01 10 am

Resolve the conflict, then continue rebasing:
screen shot 2017-11-06 at 11 01 55 am

  • Apply commit6, the previous cherry-picking commit commit6, now becomes an empty-commit (compared with head)
    Rebase asks the developer to commit commit6 or not?
    If still commit, (git commit --allow-empty), then a commit is created:

screen shot 2017-11-06 at 11 11 39 am

screen shot 2017-11-06 at 11 12 27 am

screen shot 2017-11-06 at 11 13 23 am

The commit is empty, with no change inside:

screen shot 2017-11-06 at 11 14 19 am

@dorawyy
Copy link
Owner Author

dorawyy commented Nov 6, 2017

Test2 (fast-forward)

Scenario setting up

#2 
git checkout mt8
 git commit test26_merge_test -m "mt8:x=2,y=1"

#3
git checkout mt8
git commit test26_merge_test -m "mt8:x=3,z=2"

Project tree becomes:
screen shot 2017-11-06 at 11 30 48 am

Option1: rebase directly

git checkout mt8
git rebase mt9

Nothing changed, as mt8 is already up-to-date
screen shot 2017-11-06 at 11 34 22 am

Option2: --force-rebase

git checkout mt8
git rebase mt9 --force-rebase

The rebase process is forced to executed on mt8 (SHA changed):
screen shot 2017-11-06 at 11 37 49 am

Option3: --force-rebase & --preserve-merges

git checkout mt8
git rebase mt9 --force-rebase --preserve-merges

screen shot 2017-11-06 at 12 17 27 pm

Commit on mt8 are rebased, but no visible changes on mt8.

Option4: --preserve-merges

git checkout mt8
git rebase mt9 --preserve-merges

Notified as rebasing finished, no visible changes.
screen shot 2017-11-06 at 12 21 32 pm

@dorawyy
Copy link
Owner Author

dorawyy commented Nov 6, 2017

Test3 (with merge commit)

Scenario setting up

Option1: normal rebase

git checkout mt11
git rebase mt12

Option2: --preserve-merges

git checkout mt11
git rebase mt12 --preserve-merges

( details in scratch)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant