Editing your git commits
Say you need to make a change to a patch you've submitted for review. There are several ways to do this.
If the patch is your HEAD commit, you can run:
git commit --amend -v
That will allow you to edit the commit message. If you add changes to the staging area with `git add`, you can add those changes to your commit with the above command, along with your previously committed changes.
If you want to take the previous commit out of the git history, but leave the changes in your working tree, you can run:
git reset --mixed HEAD^
If you want to completely get rid of all your changes, and revert all files to their state before your commit, you can use the --hard flag instead of the --mixed flag. Use this flag with care!
Editing a patch in a series
Should you need to edit a patch that's not the head commit (say patch 1 of 3), you can use the "interactive" mode of git rebase. Simply pass git rebase the -i flag, followed by the commit ID of the patch before the patch you want to edit. For example, say we had this git log:
40336d6 USB: xHCI: override bogus bulk wMaxPacketSize values eaadde4 xhci: fix list access before init 1f35618 xhci-mem: init list heads at the beginning of init c1be5a5 Linux 3.9
If I needed to edit commit 1f35618, I would run:
git rebase -i 1f35618^
Git will then pop up a window with a list of commits:
pick 1f35618 xhci-mem: init list heads at the beginning of init pick eaadde4 xhci: fix list access before init pick 40336d6 USB: xHCI: override bogus bulk wMaxPacketSize values # Rebase c1be5a5..40336d6 onto c1be5a5 # # 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 # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. #
We want to edit commit 1f35618, so we change the word "pick" to "edit", and write and quit. Git will then rewind history to that commit, and you will be able to edit it with `git add` and `git commit --amend -v`. Once you've made your changes, you should run:
git rebase --continue
You can always abort the rebase by running:
git rebase --abort
Sometimes when you amend commits, you will run into conflicts. Git will place conflict markers ('<<<<' and '>>>>') in the effected files. You need to resolve those conflicts by editing the file, adding them with `git add` and then running `git rebase --continue`. Make sure to remove the "Conflicts" lines from your commits that git adds by default when there's a conflict.
Oops! I want my old branch back!
Say you rebased your branch accidentially, and you want to reset your HEAD to what it was before. You can probably still get back to your previous branch history, by digging a bit. If you run git reflog, you will get a log of your branch history, and which git commands were used.
For example, say my reflog shows:
$ git reflog fc17fe387718 HEAD@{0}: commit: Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/tiomap3430.c 152cb4232c4f HEAD@{1}: commit (amend): Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/io_sm.c 327f303c207c HEAD@{2}: commit: Staging: tidspbridge: Fix split strings. 4096ec993f10 HEAD@{3}: reset: moving to 4096ec993f10
What this shows is I reset my HEAD to commit 4096ec993f10, made a commit, amended it, and then made another commit. The log shows:
$ git log --pretty=oneline --abbrev-commit fc17fe387718 Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/tiomap3430.c 152cb4232c4f Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/io_sm.c 4096ec993f10 staging: rts5139: Fix quoted string split across lines
Then I decided to rebase my branch against the latest staging-next branch. Now git reflog shows:
$ git reflog de11fef9a1f9 HEAD@{0}: rebase finished: returning to refs/heads/master de11fef9a1f9 HEAD@{1}: rebase: Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/tiomap3430.c ec16347cbd0b HEAD@{2}: rebase: Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/io_sm.c f3c25d569045 HEAD@{3}: checkout: moving from master to f3c25d569045c8e0f274956291b51641241da174^0 fc17fe387718 HEAD@{4}: commit: Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/tiomap3430.c 152cb4232c4f HEAD@{5}: commit (amend): Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/io_sm.c 327f303c207c HEAD@{6}: commit: Staging: tidspbridge: Fix split strings. 4096ec993f10 HEAD@{7}: reset: moving to 4096ec993f10
What git did was check out the tip commit on the remote staging-next branch (commit f3c25d569045), and then applied my two commits on top of that branch. If I look at my git log, it now shows:
$ git log --pretty=oneline --abbrev-commit de11fef9a1f9 Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/tiomap3430.c ec16347cbd0b Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/io_sm.c f3c25d569045 Staging: comedi: Fix 80-char line limit style issue in addi_apci_1500.c 11899188beec Staging: comedi: addi-data: tidy up counter register map defines in hwdrv_apci1564.c
If I messed up that rebase somehow, I can check out the HEAD commit I had before the rebase. Again, if I look at the reflog, I can see that's commit fc17fe387718.
$ git reflog de11fef9a1f9 HEAD@{0}: rebase finished: returning to refs/heads/master de11fef9a1f9 HEAD@{1}: rebase: Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/tiomap3430.c ec16347cbd0b HEAD@{2}: rebase: Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/io_sm.c f3c25d569045 HEAD@{3}: checkout: moving from master to f3c25d569045c8e0f274956291b51641241da174^0 fc17fe387718 HEAD@{4}: commit: Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/tiomap3430.c 152cb4232c4f HEAD@{5}: commit (amend): Staging: tidspbridge: Fix split strings in drivers/staging/tidspbridge/core/io_sm.c 327f303c207c HEAD@{6}: commit: Staging: tidspbridge: Fix split strings. 4096ec993f10 HEAD@{7}: reset: moving to 4096ec993f10
I can checkout that commit on a new branch:
git checkout -b redo fc17fe387718
Or I can just look at the list of commits I had before the rebase by running:
git log --pretty=oneline --abbrev-commit fc17fe387718
Git branch tricks
At first Git might present a very different picture to a newbie, espcially if he/she has used other Source Code Management tools like Subversion or CVS. Most striking differnce is that there is no central repository for Git to operate. The repository is the .git directory created on a working tree.
This presented considerable confusion to me when I tried to learn Git. Finally I stumbled across the idea and took time to digest. There are pages explains how to make git emulate the central repository model. I have never used it and now I am much more comfortable with Git's model than central repository model.
Later when I started cloning (git-clone) and pulling (git-pull) from Linus' tree, I faced a very interesting problem. I created a branch by name experimental using the command,
$ git-branch experimental
I make my own modifications to this branch. At the same time, Linux kernel goes forward by adding many patches. So I needed to pull the latest branch. But I did not switch to master branch by doing
$ git-checkout master
As a result, always my experiemental tree was merged, and not my master. So whenever I try to generate a patch by
$ git-diff --patch-with-stat master experimental
I used to get all the differences applied to the main tree as well as the local patches I applied. So take care to do the git-checkout of the branch you wish to merge to.
Sometimes you may find git doing a diff of a number of files using the command diff --git a/path/to/file b/patch/to/file The reason is that git thinks there is a difference between them because of the time stamp stored and the time stamp of the file is different. So either do
$ git-status
or
$ git-update-index --refresh
This would get rid of the diff --git .... messages.