11. VCS - Git

11.1. GUI

The standard GUI for git(1) is:

gitk
gitk --all

The commit tool is:

git gui

Other programs:

cola:The highly caffeinated Git GUI
qgit:QT interface to git trees, with stgit support.
gitg:Git repository viewer

11.2. github fork

After cloning a github fork and adding an upstream repository, everday repeating operation is syncing the github fork with upstream.

11.2.1. Syncing the github fork with upstream

  1. Clean up:

    git stash
    git checkout master # -f
    
  2. Update upstream:

    git fetch upstream
    
  3. Merge upstream:

    git merge upstream/master
    
  4. Get current work:

    git stash pop / apply
    git stash clear # if anything left on stash
    

11.2.2. Cloning a github fork

git clone git@github.com:wolfmanx/REPOSITORY.git

11.2.3. Adding an upstream repository

git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git

Example:

sheckley:.../3rd-party/w2ui/w2ui$ git remote -v
origin      git@github.com:wolfmanx/w2ui.git (fetch)
origin      git@github.com:wolfmanx/w2ui.git (push)
upstream    https://github.com/vitmalina/w2ui.git (fetch)
upstream    https://github.com/vitmalina/w2ui.git (push)

11.3. Quickstart

See status:

git status

Get updated info from remote repositories:

git fetch --all

Get and merge remote into local:

git pull

Show branches:

git branch
git branch -a

Update remote refs along with associated objects:

git push

Show remotes:

git remote -v

Go to specific commit (revision):

git checkout revision

Fix # HEAD detached at 1.7.1:

git checkout master -f

If you want to revert changes made to your working copy, do this:

git checkout . -f

If you want to revert changes made to the index (i.e., that you have added), do this:

git reset

If you want to revert a change that you have committed, do this:

git revert ...

Abort a merge:

git reset --hard HEAD

Remove changes:

git branch
git reset --hard HEAD

Create/apply patches:

git format-patch deadbeef..b00b5 # => 001-title.patch, 002-title.patch, ...
git format-patch master.. # everything on a branch

git format-patch master...branch # from merge point

git am *.patch # separate commits
# if there are problems
git am --continue
git am --skip
git am --abort

git apply *.patch # no commit, single patch

11.4. Tricks

11.4.1. Find the first commit of a branch

git log master..branch --oneline | tail -1

E.g.:

git log master..i18n-default-locale --oneline | tail -1
# 40f5c99 option i18n.notrans for language alias of C locale

git diff 40f5c99~1 i18n-default-locale >tg2-i18n-notrans.patch

11.4.2. Delete commits

See git rebase - Delete commits from a branch in Git - Stack Overflow

Assuming you are sitting on that commit, then this command will wack it…

git reset --hard HEAD~1

The HEAD~1 means the commit before head.

Or, you could look at the output of git log, find the commit id of the commit you want to back up to, and then do this:

git reset --hard <sha1-commit-id>

If you already pushed it, you will need to do a force push to get rid of it…

git push origin HEAD --force

However, if others may have pulled it, then you would be better off starting a new branch. Because when they pull, it will just merge it into their work, and you will get it pushed back up again.

If you already pushed, it may be better to use git revert, to create a “mirror image” commit that will undo the changes. However, both commits will be in the log.


FYI – git reset --hard HEAD is great if you want to get rid of WORK IN PROGRESS. It will reset you back to the most recent commit, and erase all the changes in your working tree and index.

Lastly, if you need to find a commit that you “deleted”, it is typically present in git reflog unless you have garbage collected your repository.

11.4.3. How to modify existing, unpushed commits

Best answer on Stack Overflow is: git - How to modify existing, unpushed commits?:

Question:

I wrote the wrong thing in a commit message. Alternatively, I’ve forgotten to include some files.

How can I change the commit message/files? The commit has not been pushed yet.


Amending the most recent commit message

git commit --amend

will open your editor, allowing you to change the commit message of the most recent commit. Additionally, you can set the commit message directly in the command line with:

git commit --amend -m "New commit message"

However, this can make multi-line commit messages or small corrections more cumbersome to enter.

Make sure you don’t have any working copy changes staged before doing this or they will get committed too. (Unstaged changes will not get committed.)

Changing the message of a commit that you’ve already pushed to your remote branch

If you’ve already pushed your commit up to your remote branch, then you’ll need to force push the commit with:

git push <remote> <branch> --force
# Or
git push <remote> <branch> -f

Warning: force-pushing will overwrite the remote branch with the state of your local one. If there are commits on the remote branch that you don’t have in your local branch, you will lose those commits.

Warning: be cautious about amending commits that you have already shared with other people. Amending commits essentially rewrites them to have different SHA IDs, which poses a problem if other people have copies of the old commit that you’ve rewritten. Anyone who has a copy of the old commit will need to synchronize their work with your newly re-written commit, which can sometimes be difficult, so make sure you coordinate with others when attempting to rewrite shared commit history, or just avoid rewriting shared commits altogether.


Use interactive rebase

Another option is to use interactive rebase. This allows you to edit any message you want to update even if it’s not the latest message.

In order to do a git squash, follow these steps:

# X is the number of commits to the last commit you want to be able to edit
git rebase -i HEAD~X

Once you squash your commits - choose the e/r for editing the message

Important note about Interactive rebase

When you use the git rebase -i HEAD~X there can be more than X commits. Git will “collect” all the commits in the last X commits and if there was a merge somewhere in between that range you will see all the commits as well so the outcome will be X+.

Good tip:

If you have to do it for more than a single branch and you might face conflicts when amending the content set up the git rerere and let git resolve those conflicts automatically for you.

11.5. Create branches

Branching allows you to build new features or test out ideas without putting your main project at risk. In git, branch is a sort of bookmark that references the last commit made in the branch. This makes branches very small and easy to work with.

How do I use branches?

Branches are pretty easy to work with and will save you a lot of headaches, especially when working with multiple people. To create a branch and begin working in it, run these commands:

git branch mybranch
# Creates a new branch called "mybranch"

git checkout mybranch
# Makes "mybranch" the active branch

Alternatively, you can use the shortcut:

git checkout -b mybranch
# Creates a new branch called "mybranch" and makes it the active branch

Create a branch tracking a remote branch, if it was created remotely:

git checkout -b serverfix origin/serverfix

Or push it, when ready:

git push <remote-name> <branch-name>

Where <remote-name> is typically origin, the name which git gives to the remote you cloned from. Note however that formally, the format is:

git push <remote-name> <local-branch-name>:<remote-branch-name>

To switch between branches, use git checkout:

git checkout master
# Makes "master" the active branch

git checkout mybranch
# Makes "mybranch" the active branch

Once you’re finished working on your branch and are ready to combine it back into the master branch, use merge:

git checkout master
# Makes "master" the active branch

git merge mybranch
# Merges the commits from "mybranch" into "master"

git branch -d mybranch
# Deletes the "mybranch" branch

Tip: When you switch between branches, the files that you work on (the “working copy”) are updated to reflect the changes in the new branch. If you have changes you have not committed, git will ensure you do not lose them. Git is also very careful during merges and pulls to ensure you don’t lose any changes. When in doubt, commit early and commit often.

11.6. Patches

See Create a git patch from the changes in the current working directory - Stack Overflow:

git format-patch master..my-branch

See How to apply a patch generated with git format-patch? - Stack Overflow

Note: You can first preview what your patch will do:

First the stats:

git apply --stat a_file.patch

Then a dry run to detect errors:

git apply --check a_file.patch
Finally, you can use git am to apply your patch as a commit: it allows you to sign off an applied patch.
This can be useful for later reference.
git am --signoff < a_file.patch

11.7. Github Forking

11.7.1. Step 3: Configure remotes

When a repository is cloned, it has a default remote called origin that points to your fork on GitHub, not the original repository it was forked from. To keep track of the original repository, you need to add another remote named upstream:

cd Spoon-Knife
# Changes the active directory in the prompt to the newly cloned "Spoon-Knife" directory

git remote add upstream https://github.com/octocat/Spoon-Knife.git
# Assigns the original repository to a remote called "upstream"

git fetch upstream
# Pulls in changes not present in your local repository, without modifying your files

More Things You Can Do

You’ve successfully forked a repository, but get a load of these other cool things you can do: Push commits

Once you’ve made some commits to a forked repository and want to push it to your forked project, you do it the same way you would with a regular repository:

git push origin master
# Pushes commits to your remote repository stored on GitHub

Pull in upstream changes

If the original repository you forked your project from gets updated, you can add those updates to your fork by running the following code:

git fetch upstream
# Fetches any new changes from the original repository

git merge upstream/master
# Merges any changes fetched into your working files

What is the difference between fetch and pull?

There are two ways to get commits from a remote repository or branch: git fetch and git pull. While they might seem similar at first, there are distinct differences you should consider.

Pull:

git pull upstream master
# Pulls commits from 'upstream' and stores them in the local repository

When you use git pull, git tries to automatically do your work for you. It is context sensitive, so git will merge any pulled commits into the branch you are currently working in. One thing to keep in mind is that git pull automatically merges the commits without letting you review them first. If you don’t closely manage your branches you may run into frequent conflicts.

Fetch & Merge:

git fetch upstream
# Fetches any new commits from the original repository

git merge upstream/master
# Merges any fetched commits into your working files

When you git fetch, git retrieves any commits from the target remote that you do not have and stores them in your local repository. However, it does not merge them with your current branch. This is particularly useful if you need to keep your repository up to date but are working on something that might break if you update your files. To integrate the commits into your local branch, you use git merge. This combines the specified branches and prompts you if there are any conflicts.

11.8. Resolving merge conflicts with git and kdiff3

From http://jeroen.haegebaert.com/post/2008/08/26/Resolving-merge-conflicts-with-git-and-kdiff3

To fix the conflicts using kdiff3, first you need to tweak the kdiff3 configuration a bit: start kdiff3, and go to Settings, Configure KDiff3, Integration. Under ‘Command line options to ignore’, add ‘–’ separated by a ‘;’ if necessary. To resolve conflicts, you can now use:

git mergetool -t kdiff3

To permanently configure kdiff3 as the merge tool (so you don’t need to specify the ‘-t kdiff3’ anymore):

git config merge.tool kdiff3