-1

I have a series of commits in git. Say the commit ids are commit1, commit2, commit3. Note that each subsequent commit includes the changes of the previous ones.

I need to amend commit1 (a source file was modified and I need to have that reflected in commit1), and then have the amendments propagate into commit2 and commit3. What is the best workflow in git to accomplish this?

user5965026
  • 465
  • 5
  • 16
  • I suggest you to not change the history of your repository. Simply modify your tree so that you have everything that you need and then commit as commit4. Changing the history is possible, but can easily become a mess, and is usually against the reason why you use a versioning software – ErniBrown Dec 10 '21 at 16:17
  • @ErniBrown I see. I am using a company internal tool that creates a pull request for each commit, which deters users from creating additional commits. When pull requests are reviewed and a change is needed, the preferred method is to amend the commit rather than create a new commit (and thus new pull request). That is why I am trying to do this – user5965026 Dec 10 '21 at 16:22
  • Note that an amended commit *is* a new commit. You can amend a commit and all of its descendents via the editing function of `git rebase`, but these will all be new commits as well: the originals will just be dropped. – Quentin Dec 10 '21 at 16:34
  • @Quentin Oh I see. With this internal tool I'm using, amending a commit just updates the pull request; it doesn't actually create a new pull request (unless I modify the commit message, in which case it does create a new pull request) – user5965026 Dec 10 '21 at 16:39
  • @user5965026 I think Quentin's comment, although technically correct, may be confusing in response to your stating "the preferred method is to amend the commit rather than create a new commit". Quentin is simply pointing out that even amending is "creating a new commit". I think though what you meant to say was "the preferred method is to amend the commit rather than create *an additional* commit". You obviously wish to *replace* the commit with a new better one, rather than add another commit with just the fix. This is just semantics. – TTT Dec 10 '21 at 16:53

1 Answers1

2

From the discussion in the comments we know your company's policy is to rewrite your commits in response to Code Review suggestions rather than add additional commits with the fixes:

When pull requests are reviewed and a change is needed, the preferred method is to amend the commit...

There are multiple ways to achieve this. However, you also mentioned:

a source file was modified and I need to have that reflected in commit1

and that statement suggests you need to rebase your branch onto the target branch. Fortunately, whenever your company policy is to rewrite, you can safely perform the rebase as often as you'd like without harm. I'd go as far as suggesting do it daily at least once, and again right before you create your PR. So, assuming your target branch is called main and your remote is called origin, you would regularly do this:

# Checkout (switch to) your branch if not already
git fetch
git rebase origin/main

That may be sufficient to update all of your commits with changes that have already been merged into main after the time you created your branch off of main. It's possible you'll get conflicts on files that you also modified, in which case the rebase will pause and you'll need to resolve them. Ask for help with conflict resolution if you need it and then continue the rebase.

If you have other code review suggestions that require you to amend commit1, after rebasing to get the latest code, one (of many) ways to achieve this is to make the changes you need and create a new temporary 4th commit with the changes. Set your commit message to something like:

squash into commit1: Title of commit1 here

Then you can interactive rebase:

git rebase -i @~4

Note @~4 means HEAD~4 which means the 4th parent of your current tip of the branch, which you need because you now have 4 commits. You could replace that with the commit ID hash of the parent of commit1, which by the way will also happen to be the merge-base of your branch and the target branch: git merge-base HEAD origin/main .

Once the rebase editor pops up, the commits are listed in chronological order that they will be re-written. Move the bottom commit up to be the second commit and change the pick to "squash" (or just "s") if you want to re-write the commit message, or "fixup" (or just "f") if you don't care to re-write the commit message. Then save and exit that file and the rebase will continue. When it's done you'll have 3 commits instead of 4, and the newly re-written commit1 will be modified as desired. The 2nd and 3rd commits will of course reflect the change as well.

Note that anytime you re-write commits on your branch you'll need to force push your branch afterwards to get it on the remote and update the PR:

git push --force-with-lease

Side Note: anytime you re-order commits it's possible to have conflicts, if you also modified the same files in commits getting re-written after that. Typically you just resolve them, but if you had many more than 3 commits and each one was conflicting, you might consider squashing them down to fewer commits first, and then performing the re-order.

TTT
  • 22,611
  • 8
  • 63
  • 69
  • Thanks. I haven't finished parsing through your answer yet, but wanted to pause and ask if `git fetch git rebase origin/main` is the same as `git pull --rebase origin main`? – user5965026 Dec 11 '21 at 02:20
  • @user5965026 Most of the time, yes, it is the same. See [this question](https://stackoverflow.com/q/66535801/184546) for an example edge case where it's not. – TTT Dec 11 '21 at 06:18