Git 常见问题集锦
创建日期:2014-08-28 18:35

Git的基本命令

Git真正是一个面向程序员的工具,它的内部数据结构是一个有向无环图,并且,你必须理解它的内部数据结构后,才能掌握它。因为你的很多操作,都其实对应的是这个有向无环图的操作。比如:

git commit就是增加一个结点。
git commit amend就是改前一个结点。
git reset就是修改HEAD指向的结点。

另外,Git内部包括三个区域:工作区,暂存区和版本库。

git add 是将工作区的内容保存到暂存区
git checkout 是将暂存区的内容覆盖工作区
git commit 是将暂存区的内容保存到版本库
git reset 默认情况下是将版本库的内容覆盖工作区
git diff 也有三种情况,分别是比较工作区与暂存区,工作区与版本库,暂存区与版本库之间的差别

了解了Git的内部结构,对于这些Git的命令就更加理解了。

Git的坑

在windows下的文件的权限因为无法和linux上完全一致,所以用Git检出的文件权限可能显示为被更改。 另外因为windows下的换行和linux上也不一样,协作开发时也容易出问题。所以在windows上使用Git的同学需要加上以下2行配置参数:

git config --global core.filemode false
git config --global core.autocrlf true

第一句是忽略文件权限的改动。
第二句是将文件checkout时自动把LF转成CRLF,check in 时自动把CRLF转成LF

强制推送

一旦推送到远程仓库后,就不要用类似git reset, git commit –-amend, git rebase等破坏性提交了,否则远程仓库会因为你的新推送不是Fast Forward而拒绝提交(关于什么是Fast Forward要讲的太多了,自已看书吧)。如果实在不小心做了。在确定别人没有检出前,用git push -f可以强制推送到远程仓库中。

强制上传的命令

git push --force [远程名] [本地分支名]:[远程分支名]

例如

git push origin master --force

删除不在git管理下的文件

如果你需要删除Git下没有加入到版本库中的文件,可以使用:

git clean -nd 测试删除
git clean -fd 真实删除

搭建自己的远程仓库

搭建一个Git远程仓库相当简单,直接在一台带SSH的服务器上用git init –bare dirname即可。本地可以用git remote命令来设置多个远程分支。另外,第一次提交的时候,因为远程仓库中没有任何分支,需要用如下指令建立master分支:

git remote add origin yourname@yourhost.com:~/path/repository_name
git remote add add2 yourname@yourhost.com:~/path/repository_name
git push origin master
git push add2 master

// 如果git remote add设置地址写错了,可以用git remote set-url更改:

git remote set-url origin yourname@yourhost.com:~/path/repository_name

如何用Git将一个文件的历史提交恢复?

上次遇到一个问题,我某次提交改动了很多文件,但是其中有一个是不应该改的。所以我需要把这次提交中关于那个文件的改动撤销。直接用git checkout命令可以检出某一个文件的历史版本,然后就可以将对这个文件的改动取消了。如下:

git checkout CommitId fileName
git ci -m "revert a file modification"

本地工作区还有未提交的内容时,不能pull?
可以先用 git stash 将内容暂存,然后再pull,成功后再git stash pop将修改恢复。

提交的邮箱错了?

有些时候,因为同时在github和公司内部做提交,所以用2个不同的邮箱。如果一个新工程clone下来,忘了用git config 来设置提交用户名和邮箱,就有可能用错误的邮箱作为账号名提交。这个时候,如果你只是错了最近的一次提交而已,可以用如下命令来将最近的一次提交作者名和邮箱修改:

git config user.email your-email@163.com
git config user.name your-name
git commit --amend --reset-author

如果等你发现的时候,已经错了很多提交了。可以用如下命令来一次性修改多个提交的用户名和邮箱:

git filter-branch -f --env-filter "
GIT_AUTHOR_NAME='your-name'
GIT_AUTHOR_EMAIL='your-email@163.com'
GIT_COMMITTER_NAME='your-name'
GIT_COMMITTER_EMAIL='your-email@163.com'
" HEAD

提交的时候自动去掉源码末尾的空格

源码末尾的空格几乎都是无意义的,应该去掉的。大多数review系统,都会将源码末尾的空格标红。所以,我们何不在提交时让git自动帮我们去掉这些空格呢?这个可以通过设置git的hook来实现,具体方法如下:

用vim编辑一个名为pre-commit的文件:

vim .git/hooks/pre-commit

输入如下代码,保存退出vim

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/sh
if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
    against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -E 's/:[0-9]+:.*//' | uniq` ;
do
    # Fix them!
    sed -i '' -E 's/[[:space:]]*$//' "$FILE"
    git add "$FILE"
done

增加pre-commit的运行权根:

chmod +x .git/hooks/pre-commit

让常用操作自动带颜色

默认的git diff, status, log什么的都是不带颜色的,可以用如下命令让它们都带上颜色。另外还有一些有趣的命令,一并列在下面。

git config --global --add user.email "email@163.com"
git config --global --add user.name "your name"

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status -s
git config --global alias.l log --oneline --decorate -12

git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
git config --global merge.tool kdiff3
git config --global meregtool.kdiff3.path "/usr/bin/kdiff3"
git config --global alias.visual "!gitk"

自动补全git命令

  1. 安装bash-completion:

    brew install bash-completion
    
  2. 按要求把以下代码增加到 .bash_profile文件中:

    if [ -f `brew --prefix`/etc/bash_completion ]; then
        . `brew --prefix`/etc/bash_completion
    fi
    
  3. 下载bash-completion对于Git的支持

    cd /usr/local/etc/bash_completion.d/
    sudo curl -O https://raw.github.com/git/git/master/contrib/completion/git-completion.bash
    

配置别名

有没有经常敲错命令?比如git status?status这个单词真心不好记。

如果敲git st就表示git status那就简单多了,当然这种偷懒的办法我们是极力赞成的。

我们只需要敲一行命令,告诉Git,以后st就表示status:

git config --global alias.st status

好了,现在敲git st看看效果。

当然还有别的命令可以简写,很多人都用co表示checkout,ci表示commit,br表示branch:

git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch

以后提交就可以简写成:

git ci -m "bala bala bala..."

--global参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用。

命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区。既然是一个unstage操作,就可以配置一个unstage别名:

git config --global alias.unstage 'reset HEAD'

当你敲入命令:

git unstage test.py

实际上Git执行的是:

git reset HEAD test.py

配置一个git last,让其显示最后一次提交信息:

git config --global alias.last 'log -1'

这样,用git last就能显示最近一次的提交:

git last

commit adca45d317e6d8a4b23f9811c3d7b7f0f180bfe2
Merge: bd6ae48 291bea8
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Thu Aug 22 22:49:22 2013 +0800

merge & fix hello.py

甚至还有人丧心病狂地把lg配置成了:

git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

修改commit

修改最近一次commit

commit提供了一个--amend参数,可以修改最后一次提交的信息,但是如果你已经push过了,那么其历史最后一次,永远也不能修改了。

# git commit --amend

然后在出来的编辑界面,直接编辑注释的信息。。

修改历史提交

git使用amend选项提供了最后一次commit的反悔。但是对于历史提交呢,就必须使用rebase了。

git rebase -i HEAD~3

表示要修改当前版本的倒数第三次状态。
这个命令出来之后,会出来三行东东:

pick:*******
pick:*******
pick:*******

如果你要修改哪个,就把那行的pick改成edit,然后退出。

这时通过git log你可以发现,git的最后一次提交已经变成你选的那个了,这时再使用:

git commit --amend

来对commit进行修改。

修改完了之后,要回来对不对?

git rebase --continue

OK,一切都搞定了。

合并分支

git rebase -i HEAD~5 //最后五次

显示结果如下,修改第二到第五个 picks ,并 :wq 保存退出

pick 92b495b 2009-08-08: ×××××××
pick 92b423b 2009-08-08: ×××××××
pick 92b489b 2009-08-08: ×××××××
pick 92b428b 2009-08-08: ×××××××
pick 92b409b 2009-08-08: ×××××××

相关资源

Rebasing http://magazine.joomla.org/issues/Issue-Nov-2011/item/586-Rebasing