How to undo the unpushed commits in magit?

[Origin]: https://scripter.co/how-to-undo-the-unpushed-commits-in-magit/

In Magit buffer *magit: ..*,

  • Hit x
  • Select origin/master

Above action will undo all the commits that haven’t been pushed to origin/master. It will still preserve the modified states of the yet-to-be staged/committed files.

Advertisements

Add change to a previous commit with Magit

[Origin]: https://emacs.stackexchange.com/questions/22557/add-change-to-a-previous-commit-with-magit

I have 2 commits, A then B, ready to be pushed. I realize I forgot to add something in A.

How can I add this change to A using Magit? I don’t even know which part of the Git documentation I should look at.

shareimprove this question

Let’s pretend for a moment that you want to add something to the HEAD commit, i.e. “the second commit B” in your example.

The commit popup on c features a binding “a Amend”. Pressing that key will “amend” the staged changes to the HEAD commit. Since commits are not mutable in Git, this will actually replace the old commit with a new commit. A buffer with the old commit message will pop up, so that you can modify it in case the added change also requires that you adjust the message. As always, press C-c C-c when you are done editing the message. This is equivalent to running git commit --amend on the command line.

  • a Amend — add the staged changes to HEAD and edit its commit message

Because it often happens that you only have to adjust the change or the message, Magit provides two additional variants:

  • e Extend — add the staged changes to HEAD without editing the commit message
  • w Reword— change the message of HEAD without adding the staged changes to it

When you want to edit a commit that isn’t HEAD, then the above won’t work. These commands always “modify” (i.e. replace) the HEAD commit. Git doesn’t provide a single command for modifying a commit other than HEAD so this is a bit more involved.

Magit does provide such a command, but because there are situations in which it is preferable to do this in multiple steps, we will discuss that first.

Modifying a commit other than HEAD can be broken down into three steps:

  1. Temporarily make that other commit (A) the HEAD.
  2. Modify the HEAD (as described above), resulting in commit A'.
  3. Tell Git to reapply the commits that followed A, but on top of A'.

This can be done using an interactive rebase. Type r to show the rebase popup. Then type mto invoke the “edit a commit” rebase variant. A buffer with recent commits appears. Move to the commit you want to modify and type C-c C-c to select it. Git then rewinds history to that commit and shows information about the ongoing rebase in the status buffer.

Modify HEAD as described above. Then tell Git that you are done by typing r r. If A' and Bconflict then rebase will stop at B and you have to resolve the conflict. After you have done so press r r to continue.

If you know that your changes to A will result in conflicts with B, then proceed as describe above, otherwise use the following approach.


Git allows creating “fixup commits” using git commit --fixup A. This creates a new commit, which records changes which “should have been made in another commit”. That commit becomes the new HEAD. There also exists a --squash variant. For information about the differences see the git-commit man page.

To actually combine the A commit and the new commit A' and then reapply B on top of that you have to use rebase. Magit provides a convenient command for doing so on r f.

The main difference to the above approach is that here we first create a new commit and then we rebase to combine that with the “target” and reapply B. Above we began with rebasing instead of committing.

In Magit both the --fixup and the --squash variants are available from the commit popup, on fand s. But Magit also provides “instant” variants of the fixup and squash commands on F and S. These variants create a new commit like the “non-instant” variants, but then they instantly combine the fixup commit with the target commit using rebase, without you having to invoke another command.

“Instant fixup” (c F) is essentially the same thing as “extend HEAD” (c e), except that it works for any commit, not just HEAD.


Further reading:

shareimprove this answer

Driving Git With Emacs – Pure Magic With Magit – Part One

http://jr0cket.co.uk/2012/12/driving-git-with-emacs-pure-magic-with.html.html

Getting to grips with Git was not to much of a learning curve, although I found it quicker to work on the command line than using graphical tools. Using git status and git log made it easy to keep a handle on my code changes.

As I do most of my Clojure development in Emacs, it was great to discover I could drive git from Emacs using Magit. What follows is a flow through the first steps with Magit.

In part two I look at Git logs with Magit

I use Emacs Live from Sam Aaron as my Emacs configuration (Emacs 24 from Emacs for MacOSX), so the packages andkey-bindings mentioned here are all available in this configuration unless otherwise stated.

Getting started

I start a new Clojure project using Leiningen, using the command line to do so. Instead of having a seperate terminal window for this, I can open up a command line shell window in Emacs

M-x eshell

I then navigate to my projects folder using `cd ~/projects/clojure and then create a new Clojure project using Leiningen, the build automation tool for Clojure.

lein new project-name

I could continue to use the terminal to manage the git repository, although once I got used to magit then using git inside Emacs seems more natural.

Using eshell, you can also easily open files using find-filefilenameand see all the files and folders usingdired folder`. So lets see what Leiningen has created for us

dired project-name

You can use the mouse or cursor to open any file listed, or navigate to sub-folders. In this case lets select the Leiningen project configuration file, project.clj.

Initialising a repository in git

Now I have a Clojure project, I can put that project under git version control

M-x magit-init

The emacs mini-buffer will prompt you for the top level folder of the files you want to version with git. If you are still in the top level project folder in the shell, magit-init will pick up the path, saving you some typing.

You will now be able to see the .git folder inside your project directory.

Git Status in Emacs

The most commonly used git command is git status – showing you what files have changes, which are staged, untracked files, pending commits and remote updates. To see the git status of your project in Emacs

M-x magit-status

Short-cut key-binding: C-x g

Keyboard shortcuts are defined in ~/emacs.d/packs/dev/bindings-pack/config/default-bindings.el

(global-set-key (kbd "C-x g") 'magit-status)

You will be prompted for the location of the git repository (the one you just created). Magit does a good job of guessing the folder assuming you already have a file open that is somewhere in a folder hierachy that has a .git folder in it.

With a newly versioned project then there will not be that much to see, except all the files not yet put under version control (untracked files).

The Local: line show the branch you are working on (master) and location of your project.

The Head: line shows you the last commit you made to your repository.

Staging changes

To version control your files you need to tell git about them. This is done by staging the files using the git addcommand. You can add individual files or all files that are untracked. Using magit, this is even easier.

In the magit-status buffer in Emacs, you can use the following key shortcuts to stage and unstage files

s – stage a specific untracked file highlighted by cursor (stage all untracked files when cursor over Untracked files title)

k – delete a specific untracked file highlighted by cursor (delete all untracked files when cursor over “Untracked files” title). See Delete really deletes section below.

i – add file to the project .gitignore file

u – unstage a specific staged change highlighted by cursor

C-u s – stages everything – tracked and untracked changes (Note: this failed when I tried it)

Under the hood

Using the $ key opens another buffer that shows you the command happening when you press keys in the magit-status window. Its a handy way to learn the command syntax and confirm magit is doing what you expected.

Note: All the files matching pattern in the project .gitignore or personal ~/.gitignore_global files will not be shown as untracked files.Delete really deletes

Using the k command in magit-status really deletes the local file, so be sure that is what you want. You get a prompt in the mini-buffer to confirm the delete though.

Testing this delete out, I noticed that not only was the file deleted but so was the folder that contained the file. As you can see in the dired my-project buffer, the doc folder has gone. If you delete files then this buffer may need a refresh, using g.

Committing your changes

Before you make too many changes you should commit them to your local repository. The more commits you make the smaller and easier they will be to mange.

On the command line you would run git commit -m "useful commit message", from the magit-status buffer its much easier:

c

This pops up another buffer for you to type in your commit message. Once you have typed your message then C-c C-c commits all the changes to your local repository. C-c C-k cancels the commit.

Now all our staged files have been added as a commit to our local repository. We can see this by looking at the Head: line in the magit-status window. We can also see that there are no changes (tracked or otherwise) shown.

Working with a remote Github repository

Assuming we have created a repository on Github it can be added as a remote source very easily

M-x magit-add-remote

You are prompted for the name you want to give the remote, origin, upstream or github are common names depending on your context. The name acts as an alias for the address of the Github repository, so keep it short but meaningful.

Now you are prompted for the Internet address of your repository. On the Github website your repository name is shown at the top of the page. Use the SSH version of the address, the one that starts git@github:....

Magit will now add the remote name and address to the project .git/config file.

You can now push your local commits up to Github using the key: P

The first time you press P the push menu appears.

Pressing P again pushes to the remote repository.

And there is more magit

There is a lot more you can do with magit, next up is Git logs with Magit.

If you are really keen you can have a look at the Magit documentation, or if you know what you want to do then theMagit cheet sheet may be more useful.

Magit not cutting the mustard ?

For situations when Magit doesn’t do everything you need, you can run raw Git commands using : (colon). This will prompt for a Git command, run it, and refresh the status buffer. The output can be viewed by typing $.

Thank you.