Notes and Tips for Git

1 Very helpful documents

2 Configuring git

2.1 Global configure

$HOME/.gitconfig my global .gitconfig

2.2 Repository-wise configuration

  • Run git config WITHOUT --global, or
  • Edit path/to/git_repo/.git/config instead of $HOME/.gitconfig.
  • You may want to set the global ones to the most conservative values (or leave them empty), for example:
    # global/default
    git config --global user.name "lgfang"
    git config --global user.email "no-spam@lungang.cn"
    # project specific/private
    git config user.name "myhandle"
    git config user.email "mymail@mycompany.com"
    

2.3 Command alias

In $HOME/.gitcofig, add lines in the "alias" section. For example:

[alias]
    topo = log --graph --pretty=format:"%d" --simplify-by-decoration --all

2.4 Remember remote username and password

For example:

git remote set-url origin https://username@github.com/...
git config --global credential.helper "cache --timeout=3600"

2.5 Ignore files

Put file name globs (one per line) into path/to/git_repo/.gitignore, for instance:

*~
*.pyc

my global .gitignore

2.6 Git after proxy

To push/fetch a project after a proxy server.

# Examples of setting proxy
git config http.proxy http://proxyuser:proxypwd@proxy.server.com:8080
git config http.proxy localhost:8888
# To clear
git config http.proxy ""

I defined two aliases ("hp" and "nohp") for the commands, refer to my configure file.

3 Clone & sync

3.1 About "pull"

  • "pull" is basically a combination of "fetch" and "merge". It will download all, but only merge current branch.
  • "pull" is NOT recommended, use "fetch" + "rebase" (or the equivalent command pull --rebase) instead.

3.2 Push all branches, tags

All branches, tags are NOT pushed by default.

  • For tags
    # push tag "mytag"
    git push origin mytag
    # push all tags
    git push --tags
    
  • For branches
    1. "fetch" always retrieve all stuff (both branches and tags) from remote.
    2. All "tracking branches" are pushed by default.
    3. "fetched" branches are all tracking branches.
    4. Locally created branches become tracked ones (and are pushed) by the following commands.
      git push -u origin branch_name
      # Or
      git push -u origin --all
      

3.3 Set up a "centralized" git repository

http://git-scm.com/book/en/Git-on-the-Server-Getting-Git-on-a-Server

If a handful of people are working on the same project, chances are that you want a git repository on a server which everyone can pull and push.

The major characteristic of this kind of respositorise is they contain no working directory.

To set up this, just setup a "normal" repository and then clone it with 'bare'.

cd ~/tmp/autoit && git init && cd ~/.repo git clone --bare ~/tmp/autoit auto.git

# later on, others can on the same machine
git clone file:///lgfang/.repo/auto.git
# or, on other machines with ssh to the_host
git clone login@the_host:///lgfang/.repo/auto.git

3.4 Work with a peer server on which git is privately installed

It complains "bash: git-upload-pack: command not found" because There is less "env" when running commands remotely using ssh non-interactive session.

Solution given in above link as well.

3.5 Clone part of the history

Sometimes, we don't care the past of a project. Since each commit creates new copies of corresponding files, we want to clone only commits we want so that we not only get a cleaner history but also save a little bit disk space. This is achieved by --depath N.

git clone --depth 1 <remote repository>

3.6 Clone a single branch only

git clone -b branch_name --single-branch <remote repo> <local dir>

3.7 Archive current branch

git archive -o archive.zip HEAD

This one makes a zip file content of which does not contain ".git" directory. Another way is to clone a single branch without history. I.e.

git clone -b branch-name --single-branch --depth 1 <remote repo> <local dir>

4 Referring certain commit

  1. Use the hash code, e.g.
    git checkout 31cbd89
    

    NOTE: you need not enter the full hash string, just enough number of leading characters to unambiguously locate the version.

  2. Use relative position
    git checkout HEAD~3
    

    Above command checkout the snapshot 3 commits ahead of current HEAD.

5 Branch

5.1 Delete

Example:

git branch -d foo
git push origin --delete foo

5.2 Rename

Example:

git branch -m foo bar

To rename current branch, simply:

git branch -m bar

5.3 Branch topology

You may want to create an alias for it.

git log --graph --pretty=format:"%d:%h" --simplify-by-decoration --all

5.4 Switch to another branch without commit current one

In short, use git stash.

  1. git stash
  2. git co another_branch
  3. do some work there
  4. git co master
  5. git stash pop

Or, as a more complex scenario, if both branches use stash, specify stash explicitly.

git stash list
git stash pop stash@{1}

Note that, stashing increases size of ".git" as well until a garbage collection run (either automatically or manually).

6 Commit

NOTE: If the commits already pushed to other repositories, you have to run "push" with -f, which is risky. Hence, be really cautious and double check before "push".

6.1 To commit changes

  • "add" and "commit"
    git add file1 file2
    git add file3
    git commit
    
  • Changes made after git add are NOT committed

    Hence you usually "add" right before "commit".

  • Commit without "add"

    In fact, For files already being tracked, you can do "add" and "commit" in one step. Just run the following command.

    git commit file1 file2 ...
    # Or, use the brave "commit -a"
    

6.2 Discard local changes

Simply checkout the file again, i.e.

git checkout -- file_name

If you already run "git add", then you have to "reset" first.

git reset file_name
# Or just "git reset"

6.3 Rollback latest commits

I.e. delete/undo the latest commits.

git reset --hard HEAD~1

6.4 Revert a commit

I.e. undo a previous commit but keep commits newer than it.

git revert HEAD~1
git revert e0c0698

6.5 Stage your changes

With two-stage commit (git add), you can stage your changes without having to commit them.

  1. You are working on a complicated problem.
  2. You make some progress, then you git add to stage your code.
  3. You continue and make more changes.
  4. You find the changes are inappropriate.
  5. You git checkout to discard the inappropriate changes and restart from where staged with another try.
  6. After several rounds, all done.
  7. You commit your code.

6.6 Conflict

When a conflict happened during pulling, there are some "tricky" things to manually fix the conflict and check-in.

  1. You cannot git commit a_file_with_conflicts, you have to git add all_files_with_conflicts and then git commit. The rational is that, in this case, you should commit all or none in this case.
  2. Alternatively, git commit -a should combine those two steps (add and commit) for you.

6.7 Stage (and commit) only part of a change

git add -p file_name

Refer to tip 6 of http://www.sitepoint.com/10-tips-git-next-level/

6.8 Keep commit history clean

6.8.1 Edit commit history

  • To edit the last commit:
    git commit --amend -m 'new message'
    git commit --amend --author="lgfang <lgfang@xxx.com>"
    
  • To edit previous commits

    For example, to edit the "author" of previous commits:

    git rebase -i HEAD~5
    # Go 5 commits back.
    
    # In the editor, change "pick" to "edit" for designated commits.
    
    # Continue to submit
    git rebase --continue
    

    Other useful actions when of rebase includes:

    • r, reword = use commit, but edit the commit message
    • s, squash = use commit, but meld into previous commit

6.8.2 Rebase and squash

Based on this.

Here is an example:

  1. Find target to rebase
    $ git log --graph --pretty=format:"%d:%h" --all
    *  (HEAD, master):8112e7a
    * :e19ea56
    * :7786ded
    | *  (emacs23):1f98b87
    |/
    * :ff42d6a
    * :eb32bff
    * :0b4df9f
    * :b54c413
    | *  (legacy-emacs):32897c9
    |/
    * :5e53df9
    
  2. Rebase to "combine" serveral commits into one.

    The git rebase fires an editor with the following content

    $ git rebase -i 5e53df9
    pick b54c413 commit log ...
    pick 0b4df9f ...
    pick eb32bff ...
    pick ff42d6a ...
    pick 7786ded ...
    pick e19ea56 ...
    pick 8112e7a ...
    

    Edit it, replacing "pick" with "squash" for commits we don't want.

    NOTE: at least the first line should be leaved as "pick".

  3. After rebase, we got the new topology
    $ git log --graph --pretty=format:"%d:%h" --all
    *  (HEAD, master):87b59a0
    | *  (emacs23):1f98b87
    | * :ff42d6a
    | * :eb32bff
    | * :0b4df9f
    | * :b54c413
    |/
    | *  (legacy-emacs):32897c9
    |/
    * :5e53df9
    

6.8.3 Merge and squash

This applicable to picking up commits (merge) from other branches.

Exerpt from: http://alpha-blog.wanglianghome.org/2010/08/05/git-merge-squash/

判断是否使用 squash 选项最根本的标准是 :待合并分支上的历史是否有意义。

如果在开发分支上提交非常随意,甚至写成微博体,那么一定要使用 --squash 选项。版本历史记录的应该是代码的发展,而不是开发者在编码时的 活动。

--squash 选项的含义是:本地文件内容与不使用该选项的合并结果相同,但是 不提交、不移动 HEAD,因此需要一条额外的 commit 命令。其效果相当于将 another 分支上的多个 commit 合并成一个,放在当前分支上,原来的 commit 历史则没有拿过来。

git merge --squash another
git commit -m "message here"

7 History

7.1 Show log selectively

git log -3
git log origin..HEAD            # show commits not push yet
git log --author=lgfang
git log --grep='string in commit message'
git log --since=2.weeks.ago --until=2.days.ago

7.2 Show summary of changes made

git log --name-status
git log --name-status -3
git log --name-status -1 05d6deb

7.3 Show detailed changes made by certain commits

You can specify multiple commits at a time.

git show b64dd26
git show HEAD~3
git show HEAD~1 HEAD~2

7.4 View files without checkout

This works for either another version or another branch.

git show hash#:path/to/that/file/from/root/of/git/repo
git show branch:path/to/that/file/from/root/of/git/repo

7.5 Show differences between two commits

or between certain commits and current status

git diff HEAD~2
git diff HEAD~1..HEAD~3
git diff d151d9d..9a72743
git diff branch1..branch2 [path/to/afile]

7.6 Show what revision and author last modified each line of a file

Like cvs annotate

git blame file_name

8 Clean up disk space

NOTE:

  1. Re-think if the size is really an issue.
  2. Be careful, backup first.
  3. Do this before pushing commits to other repositories.

Git will run garbage collection once for a while automatically. However, if you cannot wait for that, do it manually this way:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --aggressive --prune=now

9 Github

10 Store and distribute configure files using git

  • Set up:
    1. All hosts can be visited via SSH
    2. On one host, say "mylaptop", put configure files (say, .vimrc .bashrc) into ~/.dotfiles.
    3. cd ~/.dotfiles && git init
    4. On other hosts git clone mylogin@mylaptop:.dotfiles.
      • If mylaptop does not turn on SSH service, scp the directory to other hosts.
  • Sync: on other hosts, run git fetch mylogin@mylaptop:.dotfiles.
    • If mylaptop disabled SSH service, we can push the changes to other hosts on mylaptop instead: git push mylogin@server1:.dotfiles branch
      • NOTE: you may want to save the URL for remote repository using git remote add server1 mylogin@server1:./dotfiles

Created: 2016-01-26 Tue 21:19 by Emacs 24.5.1 (Org mode 8.2.10)

comments powered by Disqus