How to modify a specified commit?

后端 未结 16 945
清酒与你
清酒与你 2020-11-22 01:53

I usually submit a list of commits for review. If I have the following commits:

  1. HEAD
  2. Commit3
  3. Commit2
相关标签:
16条回答
  • 2020-11-22 02:39

    git stash + rebase automation

    For when I need to modify an old commit a lot of times for Gerrit reviews, I've been doing:

    git-amend-old() (
      # Stash, apply to past commit, and rebase the current branch on to of the result.
      current_branch="$(git rev-parse --abbrev-ref HEAD)"
      apply_to="$1"
      git stash
      git checkout "$apply_to"
      git stash apply
      git add -u
      git commit --amend --no-edit
      new_sha="$(git log --format="%H" -n 1)"
      git checkout "$current_branch"
      git rebase --onto "$new_sha" "$apply_to"
    )
    

    GitHub upstream.

    Usage:

    • modify source file, no need to git add if already in repo
    • git-amend-old $old_sha

    I like this over --autosquash as it does not squash other unrelated fixups.

    0 讨论(0)
  • 2020-11-22 02:40

    Well, this solution might sound very silly, but can save you in certain conditions.

    A friend of mine just ran into accidentally committing very some huge files (four auto-generated files ranging between 3GB to 5GB each) and then made some additional code commits on top of that before realizing the problem that git push wasn't working any longer!

    The files had been listed in .gitignore but after renaming the container folder, they got exposed and committed! And now there were a few more commits of the code on top of that, but push was running forever (trying to upload GB of data!) and finally would fail due to Github's file size limits.

    The problem with interactive rebase or anything similar was that they would deal with poking around these huge files and would take forever to do anything. Nevertheless, after spending almost an hour in the CLI, we weren't sure if the files (and deltas) are actually removed from the history or simply not included in the current commits. The push wasn't working either and my friend was really stuck.

    So, the solution I came up with was:

    1. Rename current git folder to ~/Project-old.
    2. Clone the git folder again from github (to ~/Project).
    3. Checkout to the same branch.
    4. Manually cp -r the files from ~/Project-old folder to ~/Project.
    5. Make sure the massive files, that are not needed to be checked in are mved, and included in .gitignore properly.
    6. Also make sure you don't overwrite .git folder in the recently-cloned ~/Project by the old one. That's where the logs of the problematic history lives!
    7. Now review the changes. It should be the union of all the recent commits, excluding the problematic files.
    8. Finally commit the changes, and it's good to be push'ed.

    The biggest problem with this solution is, it deals with manual copying some files, and also it merges all the recent commits into one (obviously with a new commit-hash.) B

    The big benefits are that, it is very clear in every step, it works great for huge files (as well as sensitive ones), and it doesn't leave any trace in history behind!

    0 讨论(0)
  • 2020-11-22 02:42

    To get a non-interactive command, put a script with this content in your PATH:

    #!/bin/sh
    #
    # git-fixup
    # Use staged changes to modify a specified commit
    set -e
    cmt=$(git rev-parse $1)
    git commit --fixup="$cmt"
    GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"
    

    Use it by staging your changes (with git add) and then run git fixup <commit-to-modify>. Of course, it will still be interactive if you get conflicts.

    0 讨论(0)
  • 2020-11-22 02:48

    You can use git rebase. For example, if you want to modify commit bbc643cd, run

    $ git rebase --interactive 'bbc643cd^'
    

    Please note the caret ^ at the end of the command, because you need actually to rebase back to the commit before the one you wish to modify.

    In the default editor, modify pick to edit in the line mentioning 'bbc643cd'.

    Save the file and exit: git will interpret and automatically execute the commands in the file. You will find yourself in the previous situation in which you just had created commit bbc643cd.

    At this point, bbc643cd is your last commit and you can easily amend it: make your changes and then commit them with the command:

    $ git commit --all --amend --no-edit
    

    After that, type:

    $ git rebase --continue
    

    to return back to the previous HEAD commit.

    WARNING: Note that this will change the SHA-1 of that commit as well as all children -- in other words, this rewrites the history from that point forward. You can break repos doing this if you push using the command git push --force

    0 讨论(0)
  • 2020-11-22 02:49

    The best option is to use "Interactive rebase command".

    The git rebase command is incredibly powerful. It allows you to edit commit messages, combine commits, reorder them ...etc.

    Every time you rebase a commit a new SHA will be created for each commit regardless of the content will be changed or not! You should be careful when to use this command cause it may have drastic implications especially if you work in collaboration with other developers. They may start working with your commit while you're rebasing some. After you force to push the commits they will be out of sync and you may find out later in a messy situation. So be careful!

    It's recommended to create a backup branch before rebasing so whenever you find things out of control you can return back to the previous state.

    Now how to use this command?

    git rebase -i <base> 
    

    -i stand for "interactive". Note that you can perform a rebase in non-interactive mode. ex:

    #interactivly rebase the n commits from the current position, n is a given number(2,3 ...etc)
    git rebase -i HEAD~n 
    

    HEAD indicates your current location(can be also branch name or commit SHA). The ~n means "n beforeé, so HEAD~n will be the list of "n" commits before the one you are currently on.

    git rebase has different command like:

    • p or pick to keep commit as it is.
    • r or reword: to keep the commit's content but alter the commit message.
    • s or squash: to combine this commit's changes into the previous commit(the commit above it in the list).
    • ... etc.

      Note: It's better to get Git working with your code editor to make things simpler. Like for example if you use visual code you can add like this git config --global core.editor "code --wait". Or you can search in Google how to associate you preferred your code editor with GIT.

    Example of git rebase

    I wanted to change the last 2 commits I did so I process like this:

    1. Display the current commits:
      #This to show all the commits on one line
      $git log --oneline
      4f3d0c8 (HEAD -> documentation) docs: Add project description and included files"
      4d95e08 docs: Add created date and project title"
      eaf7978 (origin/master , origin/HEAD, master) Inital commit
      46a5819 Create README.md
      
    2. Now I use git rebase to change the 2 last commits messages: $git rebase -i HEAD~2 It opens the code editor and show this:

      pick 4d95e08 docs: Add created date and project title
      pick 4f3d0c8 docs: Add project description and included files
      
      # Rebase eaf7978..4f3d0c8 onto eaf7978 (2 commands)
      #
      # Commands:
      # p, pick <commit> = use commit
      # r, reword <commit> = use commit, but edit the commit message
      ...
      

      Since I want to change the commit message for this 2 commits. So I will type r or reword in place of pick. Then Save the file and close the tab. Note that rebase is executed in a multi-step process so the next step is to update the messages. Note also that the commits are displayed in reverse chronological order so the last commit is displayed in that one and the first commit in the first line and so forth.

    3. Update the messages: Update the first message:

      docs: Add created date and project title to the documentation "README.md"
      
      # Please enter the commit message for your changes. Lines starting
      # with '#' will be ignored, and an empty message aborts the commit.
      ...
      

      save and close Edit the second message

      docs: Add project description and included files to the documentation "README.md"
      
      # Please enter the commit message for your changes. Lines starting
      # with '#' will be ignored, and an empty message aborts the commit.
      ...
      

      save and close.

    4. You will get a message like this by the end of the rebase: Successfully rebased and updated refs/heads/documentation which means that you succeed. You can display the changes:

      5dff827 (HEAD -> documentation) docs: Add project description and included files to the documentation "README.md"
      4585c68 docs: Add created date and project title to the documentation "README.md"
      eaf7978 (origin/master, origin/HEAD, master) Inital commit
      46a5819 Create README.md
      

      I wish that may help the new users :).

    0 讨论(0)
  • 2020-11-22 02:51

    If for some reason you don't like interactive editors, you can use git rebase --onto.

    Say you want to modify Commit1. First, branch from before Commit1:

    git checkout -b amending [commit before Commit1]
    

    Second, grab Commit1 with cherry-pick:

    git cherry-pick Commit1
    

    Now, amend your changes, creating Commit1':

    git add ...
    git commit --amend -m "new message for Commit1"
    

    And finally, after having stashed any other changes, transplant the rest of your commits up to master on top of your new commit:

    git rebase --onto amending Commit1 master
    

    Read: "rebase, onto the branch amending, all commits between Commit1 (non-inclusive) and master (inclusive)". That is, Commit2 and Commit3, cutting the old Commit1 out entirely. You could just cherry-pick them, but this way is easier.

    Remember to clean up your branches!

    git branch -d amending
    
    0 讨论(0)
提交回复
热议问题