Common Git Use Cases
09 Dec 2013 CommentsGit Branching
了解一个 VCS(Version Control System) 很重要的一个方面就是了解它的 branch 机制,我画了一张图,从中可以对 git 的 branch 机制有一个初步的感觉。
这个图的最后给出了两种 merge:
-
fast-forward
对应第一个 merge,这种 merge 不创建新的 commit,只是将 master 的指针直接移到了 b1 指向的那个 commit object。
-
three-way merge
对应第二个 merge,这种 merge 创建了新的 commit,同时将这 commit 的 parent 指针当前 master 和 b1 指向的那两个 object。
git branch 是一个非常轻量的 object,可以理解为就是一个指针,这个指针的内容是 SHA1sum,指向某一个 commit。在 git 里改动 branch 指向非常简单,就是将它 set 到另一个 SHA1sum 即可:
git reset --hard <SHA1sum of some commit>
这个命令将当前 branch reset 到你指定的那个 commit。
Git Command Reference
以下列出我遇到的一些 git 需求。
-
添加一个 remote repository
git remote add [name] [url]
这个命令没什么特别的,运行完后 name 就变成了 url 的一个 alias,就好比 c 语言中的
#define name url
-
创建一个跟踪 remote branch 的 local branch
git checkout -b foo origin/foo
运行完这个命令后,你就可以通过 foo 这个 local branch 去对 remote server 上的 foo branch 做修改了。这里的
-b
选项会把你的当前 branch 直接转到 foo。另外,你可以不用 foo 这个名字,比如
git checkout -b bar origin/foo
。 -
如果出现 merge conflict,则编辑 conflict 的文件,然后安正常流程 add, commit 即可。
-
merge remote change (假设要 merge 到 master)
有两种方式:
git fetch origin && git merge origin/master
或者:
git pull origin master
实际上
git pull
就等价于上面两个命令。git fetch
只会把 remote server 做的修改下下来,但不会 merge 到你的 local branch 中。git fetch
会输出 orgin 改动的信息,如:9af15df..ace0aa3 master -> origin/master * [new branch] b1 -> origin/b1
第一行的意思是 remote server 的 master branch 已经从 9af15df 前进到 ace0aa3 了 (fast-forward)
第二行的意思是 remote server 创建了一个新的叫 b1 的 branch
运行
git pull
要注意这么一种情况,如果你当前 local branch 是 b1,然后你git pull origin b2
,这个命令会将 remote branch b2 上的更新 merge 到你 local 的 b1, 并且会完成 commit,这有时候不是你想要的。所以我觉得比较好的运行 pull 的方法还是将 local branch 对应到 remote branch,然后你想 pull 的时候,就 checkout 到你想更新的 branch,运行
git pull
, 这样保证不会 merge 错 branch。git push
也是同理。 -
git clone 会自动设置 origin 到 remote server,并且会 set local master -> origin/master,但 origin 的其他 branch 不会自动对应到某个 local branch.
-
查看 local branch 和 remote branch 的对应关系
git branch -vv
-
查看 origin 对应的 remote server 的相关信息
git remote show origin
-
revert a file
git checkout -- file
-
查看 remote branch 和 local branch 是否不同
git diff --stat origin/master
-
rename branch
git branch -m old_name new_name
-
Discard changes
git clean -df git checkout .
第一个命令清除所有 untracked file。
-d
选项用于清除目录。注意这个命令只在运行这个命令的目录及其子目录生效,所以如果要删除一个 project 里所有的 untracked file,要到根目录。第二个命令清除所有 unstaged 的改动。
-
Change last commit
git commit --amend
这个命令干了这么一件事:取出你当前的 staging area 和前一个 commit 的 staging area 合并,然后去掉前一个 commit,然后 commit
如果你当前的 staging area 为空,那 git commit –amend 所能做的事就是改变你的 commit message。
-
Change the author and email of last commit
git commit --amend --author="username <email>"
-
Create a remote branch from local. (from stackoverflow)
First, you create your branch locally
git checkout -b your_branch
The remote branch is automatically created when you push it to the remote server. So when you feel for it, you can just do
git push <remote-name> <branch-name>
Your colleagues would then just pull that branch, and it’s automatically created locally.
Note however that formally, the format is:
git push <remote-name> <local-branch-name>:<remote-branch-name>
But when you omit one, it assumes both names are the same.
-
Unstage an added file
git reset HEAD path/to/file
-
从一个 branch 拷文件到另一个 branch
假设你要将一个叫 foo 的文件从 test branch 拷到 master:
git checkout master git checkout test -- path/to/foo
这个 path/to/foo 要注意,假设你的 project root directory 是 ~/project/ 然后你要拷的文件是 test branch 的 ~/project/bar/foo,而你在 master branch 的 ~/project/zee/ 下,那拷贝的命令是
git checkout test -- ../bar/foo
。 -
在 master 上做了一堆 commit,发现这些修改还是在一个 branch 上做更合适,这时候应该这么做:
-
先创建一个 branch 指向当前 master 顶端
git branch foo
-
运行
git log
找到你觉得 master 应该指向的那个 commit 的 SHA1sum,然后 resetgit reset --hard <SHA1sum>
这就完成了你在 local repository 的操作,为了同步 remote repository,还需要
git push origin foo:foo git push -f origin master
注意最后一个命令需要
-f
,不然 remote 不会接受。 -
-
不小心把所有的修改都 commit 了,但是其实只想 commit 其中一部分,其余的修改还没完成
git reset HEAD~1
HEAD~1
指向HEAD
的前一个 commit。这个命令将HEAD
对应的 commit 删除,但所有你对文件的修改不会丢,运行git status
就可以看到。如果你用git reset --hard
的话,你的修改会没掉。 -
在修改 foo branch,但需要临时去一下 bar barch,又不想 commit foo branch 的东西。
git stash # on foo branch git checkout bar # do something on bar git checkout foo git stash apply
git stash
会暂存你当前的修改,git stash apply
会把暂存的修改重新 apply。 -
Git cache password
如果你不想每次
git push
的时候都输入用户名密码git config --global credential.helper 'cache --timeout=3600'
其中
3600
表示以秒为单位的时间
Git Multiple Accounts
这里配置针对这么一个场景,我的机子目前配置的是 github 的帐号,但我现在有一个公司的项目,在公司的 git server 上,这样我就要配置我的机子来支持同时存在多个服务器的 account。
我用 ssh 访问公司 git server,并且不用密码。具体配置流程如下:
-
在本地生成针对公司 git server 的 ssh key pair
ssh-keygen -t rsa -f /path/to/key -C "add some comment"
这里 /path/… 如果不指定,默认是保存到 ~/.ssh/id_rsa,对于不同的 account 这个文件肯定要是不一样的。
-
将 /path/to/key.pub 提交到 git server 上
-
在 ~/.ssh/config 文件中登记你的 private key,格式如下:
Host git.server.address IdentityFile /path/to/key
其中 Host 和 IdentityFile 是关键字,git.server.address 替换为你的 git server,如 github.com。
不同的 git server 对应不同的配置项,git 每次访问 server 会根据这个文件找到相应服务器的 key。
-
创建 git repo
mkdir repo cd repo git init vim README git add README git commit -m 'add README'
-
配置当前 repo 对应到公司的 user name 和 email
git config user.name "name" git config user.email "email"
-
然后就可以添加 remote branch 并提交了
git add remote origin <company server repo address> git push origin branch:branch
注意,git config --global
配置的是你的全局的用户名和邮箱,是默认 account,针对每个具体的 repo,可以配置自己的用户名和邮箱,配置完可以从 repo/.git/config 文件中看出。
Git SSL Certificate Problem
通过代理用 git 可能遇到这个错误:
fatal: unable to access 'https://github.com/juscodit/uva.git/': SSL certificate problem: unable to get local issuer certificate
To disable ssl certificate
git config --local http.sslVerify false
Git VS Svn
-
svn 中为了生成一个 branch,你需要 copy 整个 repository,而 git 只不过是赋一个 SHA1sum 即可。
-
git 在 commit 前需要先 add 你想提交的修改,svn commit 直接提交当前目录下所有的修改。显然 git 的方式更灵活,很多时候我都希望我修改的文件能分批次 commit。
-
git commit 是提交到 local repository,svn commit 则直接提交到 remote repository。git 需要在 commit 之后再 push 一下才能提交到 remote repository。这样做实现了分布式的代码管理,你不需要网络依然可以提交,push 会把你在本地做的所有 commit 完完整整得 push 到 remote repository,所以你的 local repository 和 remote repository 是完全平等的,完全一样的。svn 则不然,所有的代码管理都在 server 端。
-
svn 会在你的每个子目录下创建一个 .svn 文件夹,这个很讨厌。但 git 不会,它只在 root dir 创建一个 .git 文件夹。
Git 杂谈
-
a git branch can usually be defined as “line of development”, but more precisely, it’s directed acyclic graph of development. (from git-fetch-and-merge)
确实,说成 DAG 确实要更准确些,一个 three-way merge 就会得到一个 non-linear branch.
-
Remember that the commit records the snapshot you set up in your staging area. Anything you didn’t stage is still sitting there modified; you can do another commit to add it to your history. Every time you perform a commit, you’re recording a snapshot of your project that you can revert to or compare to later. (from git-basics)