Git介绍
Git(the stupid content tracker)是一个源自Linux内核项目的源码管理工具。和传统的CVS、SVN不同,git是一个分布式源码管理工具。
Git命令 | 简单说明 |
---|---|
git init | 初始化一个本地的代码仓库。 |
git clone | 从远程复制一个代码仓库。 |
git config | git选项设置。 |
git add | 添加文件/目录。 |
git commit | 提交修改。 |
git status | 显示工作目录的状态以及缓冲区内的快照。 |
git log | 已提交快照的日志。 |
git branch | 创建分支。 |
git checkout | 迁出/拉出/切换到一个分支。 |
git merge | 合并分支。 |
git revert | 撤销commit快照。 |
git reset | 撤销本地工作目录的修改。 |
git clean | 删除代码仓库以外的文件。 |
git remote | 管理远程git。 |
git fetch | 从远程获取分支。 |
git pull | 从远程获取分支。 |
git push | 把代码推到远程分支。 |
这里列举几个常见的git命令,让大家过过目
基本操作
git init //初始化版本库
git clone //克隆版本库
07 >git add //添加新文件
git commit //提交到本地版本库
git checkout //检出(分支、标签)
分支
git branch //列出分支
git branch -r //列出远程分支
git branch -a //列出所有分支
git branch newBranch //基于当前分支创建新分支newBranch
git branch -D branchName //删除分支branchName
git branch -d branchName //仅删除已合并分支branchName
git merge <--> //合并分支<br>>git tag
历史
git log //显示全部历史
git log -p //显示版本历史,以及版本间的内容差异
git log -5 //显示最近的5个提交
git log -5 -p //显示最近的5个提交,以及版本间的内容差异
...(很多很多参数...)
git diff 112 115 //显示112和115版本的差别
基本概念
文件状态
Git仓库中的文件有几种状态:
- untracked - 还没添加到仓库中。
- unmodified - 自上次提交以来,文件未曾修改过。
- modified - 文件修改了还没提交。
- staged - 文件提交到了暂存区中。一旦执行git commit就会转换为unmodified状态。
当我们往工作目录添加一个文件的时候,这个文件默认是未跟踪状态的,我们肯定不希望编译生成的一大堆临时文件默认被跟踪还要我们每次手动将这些文件清除出去。用以下命令可以跟踪文件:
git add <file>
Git暂存区(Staged Area)的意思是:你把一个文件托付给Git跟踪(git add),然后又修改了它,此时这个文件就位于暂存区了。暂存区内的文件几乎只做一件事:等待你执行git commit,把它提交。
上图中右边3个状态都是已跟踪状态,其中的灰色箭头只表示untracked<-->tracked的转换而不是untracked<-->unmodified的转换,新添加的文件肯定算是被修改过的。那么,staged状态又是什么呢?这就要搞清楚GIT的三个工作区域:本地数据(仓库)目录,工作目录,暂存区,如下图所示:
git directory就是我们的本地仓库.git目录,里面保存了所有的版本信息等内容。
working driectory,工作目录,就是我们的工作目录,其中包括未跟踪文件及已跟踪文件,而已跟踪文件都是从git directory取出来的文件的某一个版本或新跟踪的文件。
staging area,暂存区,不对应一个具体目录,其时只是git directory中的一个特殊文件。
当我们修改了一些文件后,要将其放入暂存区然后才能提交,每次提交时其实都是提交暂存区的文件到git仓库,然后清除暂存区。而checkout某一版本时,这一版本的文件就从git仓库取出来放到了我们的工作目录。
快照(snapshot)
Git与其他版本控制系统的区别在于:Git只关心文件是否变化,而不关心文件内容的变化。大多数版本控制系统都会忠实地记录版本间的文件差异(diff),但Git不关心这些具体差异(哪一行有什么变动),Git只关心哪些文件修改了哪些没有修改,修改了的文件直接复制形成新的blob(这就是所谓的快照snapshot)。当你需要切换到或拉出一个分支时,Git就直接加载当时的文件快照即可,这就是Git快的原因。说起来,这也是用空间换取时间的经典案例。
从这个角度看,Git更像是一个小型文件系统,并在这个系统上提供一系列的工具来辅助开发。
Git的地理观
Git是一个分布式的版本控制系统,因此没有所谓的中心。粗略来看Git可分为本地库(local repository)和远程库(remote repository),细致地看可分为以下几个部分:
- Working Directory - 工作目录。Git仓库位于工作目录之下,工作目录下的文件有加入Git仓库(tracked)和没加入Git仓库(untracked)的区别。
- Stage Area - 暂存区。如上所述,已加入Git仓库并被修改(尚未提交)的文件。
- Local Repository - 本地仓库。
- Remote Repository - 远程仓库。
文件通常是:加入Git仓库(git add)-> 修改后即位于暂存区 -> 提交到本地库(git commit) -> 推送到远程库(git push)。
origin/master
这里主要笔记一些在Git上下文中经常遇见的术语。origin/master指远程仓库origin的master分支。
远程仓库/分支
这样的形式。虽然Git是分布式的系统,但通常把git clone的源头叫做origin,origin也被视为中心仓库(Central Repository)。
git入门
创建目录,并用git init
初始化:
$ mkdir learn-git && cd learn-git $ git init Initialized empty Git repository in /tmp/learn-git/.git/
从git init
输出可知,git
创建了一个名为.git
的隐藏目录。
创建一个文件,并用git add
添加到仓库,用git commit
提交:
$ echo "hello git" > README.txt $ git add . $ git commit -m "readme file" [master (root-commit) cd27ac1] readme file1 file changed, 1 insertion(+)create mode 100644 README.txt
接下来对已提交文件做一些修改,并新添加一个文件:
$ echo "learn files here" >> README.txt $ cp ~/.vimrc .
用git diff
查看文件差异(每次commit前应该先diff对比差异详情):
$ git diff diff --git a/README.txt b/README.txt index 8d0e412..0219596 100644 --- a/README.txt +++ b/README.txt @@ -1 +1,2 @@hello git +learn files here
差异对比可以用git diff --cached
保存下来(如此差异则不输出到屏幕)。
用git status
查看git仓库状态:
$ git status # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: README.txt # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .vimrc no changes added to commit (use "git add" and/or "git commit -a")
这里显示README.txt
被修改了,而.vimrc
则等待添加。接下来,我们将.vimrc
添加到git仓库中,且将所有修改一并提交(git commit -a
):
$ git commit -a -m "update readme && add vimrc" [master f6162f0] update readme && add vimrc2 files changed, 123 insertions(+)create mode 100755 .vimrc
git log
输出git日志,包括提交编号(如"f6162f04170e3665bc03744e43f764c903e4e38d"这样的字串)、提交者、提交日期和提交日志。
git log
的其他输出:
$ git log -p # 详细日志,并输出到分页程序 $ git log --stat --summary
美化git log输出
$ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --
修改全局配置:
$ 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 --" $ git lg
管理分支
创建一个分支:
$ git branch exp
查看当前git仓库的所有分支:
$ git branchexp * master
切换到exp
分支:
$ git checkout exp Switched to branch 'exp'
修改文件,并提交:
$ echo "start branch: exp" >> README.txt $ git commit -a -m "modified readme in exp branch" [exp 2e825a4] modified readme in exp branch1 file changed, 1 insertion(+)
切换回master
分支:
$ git checkout master Switched to branch 'master'
在master
分支检查文件,可见exp
分支的修改并没影响到master
分支(注意,在exp分支的修改都已提交;如果没有提交,则切换回master分支会看到文件已变)。接下来我们制造一个冲突:
$ echo "return branch: master" >> README.txt $ git commit -a -m "modified readme in master branch" [master 8dd9fb2] modified readme in master branch1 file changed, 1 insertion(+)
用git merge exp
合并分支:
$ git merge exp Auto-merging README.txt CONFLICT (content): Merge conflict in README.txt Automatic merge failed; fix conflicts and then commit the result.
从git输出可见,git尝试自动合并但失败了,因此提示需要解决冲突再提交
用git diff
查看差异,且差异文件被修改:
$ git diff ... $ cat README.txt hello git learn files here <<<<<<< HEAD return branch: master ======= start branch: exp >>>>>>> exp
手工解决冲突并再次提交:
(edit file) $ git commit -a -m "do merge"
接下来,可以删除exp
分支:
$ git branch -d exp Deleted branch exp (was 2e825a4).
git branch -d
删除分支时会检查分支是否完全合并到主干,如果不是,则会删除失败,并提示需要合并:
$ git branch exp # 建立exp分支 $ git checkout exp # 切换到exp分支 $ echo "exp again" >> README.txt # 修改并提交 $ git commit -a -m "exp again" [exp 868e68c] exp again1 file changed, 1 insertion(+) $ git checkout master # 切换回master Switched to branch 'master' $ git branch -d exp # 删除失败 error: The branch 'exp' is not fully merged. If you are sure you want to delete it, run 'git branch -D exp'.
可以用git branch -D exp
忽略修改,完全删除分支:
$ git branch -D exp Deleted branch exp (was 868e68c).
查看远端git
基础命令是:git remote show, git remote show X。
$ git remote show
origin
web
查看GitHub默认设置的origin
$ git remote show origin * remote originFetch URL: git@github.com:berlinix/blog.gitPush URL: git@github.com:berlinix/blog.gitHEAD branch: masterRemote branch:master trackedLocal branch configured for 'git pull':master merges with remote masterLocal ref configured for 'git push':master pushes to master (fast-forwardable)
git命令快查
以下列出一些常用的git命令
命令 | 说明 |
---|---|
基础操作 | |
git init | 初始化git仓库 |
git add X | 添加X文件/路径到git仓库 |
git commit -m "COMMENTS" | 提交更新 |
分支管理 | |
git branch X | 创建一个名为X的分支 |
git checkout X | 切换到X分支 |
git merge X | 自动合并X分支 |
git branch -d X | 删除X分支,需要先merge |
git branch -D X | 强制删除X分支,忽略其修改,无须先merge |
与远程git交互 | |
git remote show | 显示远程git仓库 |
git remote show X | 显示远程git一个名为X的仓库 |
git push origin master | 更新提交到GitHub |
Git日常问题
撤销commit
刚与master合并并提交后就后悔了现在要做的是撤销commit(revoke/undo merge/commit)。
查看当前所在分支:
$ git branchbs3 * coindevmaster
查看日志:
$ git log --oneline 9b7ba39 merged with master 73a66e8 update FAQ
用以下2个命令来撤销提交(把COMMIT_SHA替换为实际的SHA值;把HEAD~N中的N替换为一个数字,表示回退几步):
$ git reset --hard COMMIT_SHA $ git reset --hard HEAD~N
例如回退到合并前:
$ git reset --hard 73a66e8 HEAD is now at 73a66e8 update FAQ
回退后发现不对,因为现在这个commit还是在master中的(在merge之前master已经走的太远),赶紧再次reset到merge时的状态:
$ git reset --hard 9b7ba39 HEAD is now at 9b7ba39 merged with master
如此一来就是就是merge后commit之前的状态。接下来就是要完成undo merge(已经undo commit了):
$ git revert -m 1 9b7ba39
这下就彻底回到merge前了,以防万一再次检查:
$ git diff --name-status master..coin
看起来没什么问题了,检查下日志:
$ git log --oneline 2691516 Revert "merged with master" 9b7ba39 merged with master 73a66e8 update FAQ
用git找回已删除文件
首先找到与目标文件相关的最后一次commit。如果目标文件没有出现在HEAD commit中,那么在这次commit时,文件就被删除了:
$ git rev-list -n 1 HEAD -- htdocs/myfile.php 1e8182f58dc038c8e6bc2025e8430f463d372030
接下来就是恢复工作了:
$ git checkout 1e8182f58dc038c8e6bc2025e8430f463d372030^ -- htdocs/myfile.php
合并分支的部分文件
有时候只想合并分支里的部分文件,而不是整个分支,可以用这个命令:
git checkout BRANCH FILE ...
例如,从test_branch分支中合并file_modified文件:
$ git checkout test_branch file_modified
参考Git Tip: How to "Merge" Specific Files from Another Branch。