一、git 全局配置
全局配置:
设置编码 git config --global gui.encoding utf-8 git config --global i18n.commitencoding utf-8 git config --global i18n.logoutputencoding utf-8 指定git或gitlab的用户名与邮箱 bogon:~ yuanjicai$ git config --global user.name meteor bogon:~ yuanjicai$ git config --global user.email meteor@163.com bogon:~ yuanjicai$ git config --global color.ui true 换行设置 git config --global core.autocrlf input #提交时转换为LF,检出时不转换 git config --global core.safecrlf warn #提交包含混合换行符的文件时给出警告
查看配置:
bogon:~ yuanjicai$ git config --list | tail -3 user.name=meteor user.email=meteor@163.com color.ui=true bogon:~ yuanjicai$
实质上是将配置写入了~/.gitconfig文件(当然直接修改该文件也可以达到配置的目的)
bogon:~ yuanjicai$ ls .gitconfig .gitconfig bogon:~ yuanjicai$ cat .gitconfig [user] name = meteor email = meteor@163.com [color] ui = true bogon:~ yuanjicai$
二、git新建项目及初始化
方法一、新建立项目:
bogon:~ yuanjicai$ mkdir -p github/project bogon:~ yuanjicai$ cd github/project/ bogon:project yuanjicai$ git init #初始化仓库 Initialized empty Git repository in /Users/yuanjicai/github/project/.git/ bogon:project yuanjicai$ ls -a ....git bogon:project yuanjicai$ cd .git bogon:.git yuanjicai$ ls HEAD config hooks objects branches description inforefs bogon:.git yuanjicai$ #git 保存分支的地方
方法二:
bogon:github yuanjicai$ pwd /Users/yuanjicai/github bogon:github yuanjicai$ ls project bogon:github yuanjicai$ git clone https://github.com/kennethreitz/requests.git Cloning into 'requests'... remote: Counting objects: 17906, done. remote: Compressing objects: 100% (35/35), done. remote: Total 17906 (delta 11), reused 0 (delta 0), pack-reused 17871 Receiving objects: 100% (17906/17906), 4.79 MiB | 643.00 KiB/s, done. Resolving deltas: 100% (11665/11665), done. Checking connectivity... done. bogon:github yuanjicai$ ls projectrequests bogon:github yuanjicai$ ls requests/ AUTHORS.rstMakefilerequests CONTRIBUTING.mdNOTICErequirements-to-freeze.txt HISTORY.rstREADME.rstrequirements.txt LICENSEdocssetup.py MANIFEST.inexttests bogon:github yuanjicai$ ls requests/.git HEADconfighooksinfoobjectsrefs branchesdescriptionindexlogspacked-refs bogon:github yuanjicai$
三、建立文件并提交
bogon:github yuanjicai$ cd project/ bogon:project yuanjicai$ ls .git/ COMMIT_EDITMSGORIG_HEADconfighooksinfoobjects HEADbranchesdescriptionindexlogsrefs bogon:project yuanjicai$ ls bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ python test1.py hello world bogon:project yuanjicai$ vim test2.py bogon:project yuanjicai$ python test2.py hello YeeCall bogon:project yuanjicai$ git status On branch master Untracked files: (use "git add <file>..." to include in what will be committed) test1.py test2.py nothing added to commit but untracked files present (use "git add" to track) bogon:project yuanjicai$ git add test1.py test2.py bogon:project yuanjicai$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: test1.py new file: test2.py bogon:project yuanjicai$ git commit -m "init commit" [master 547a6b0] init commit 2 files changed, 2 insertions(+) create mode 100644 test1.py create mode 100644 test2.py bogon:project yuanjicai$ git status On branch master nothing to commit, working tree clean bogon:project yuanjicai$
四、git status
bogon:project yuanjicai$ vim test2.py bogon:project yuanjicai$ python test2.py hello YeeCall hello git bogon:project yuanjicai$ 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: test2.py no changes added to commit (use "git add" and/or "git commit -a") bogon:project yuanjicai$ echo '*~' > .gitignore bogon:project yuanjicai$ git add .gitignore bogon:project yuanjicai$ git status -s A .gitignore M test2.py #第二位的M表示working directory有修改 bogon:project yuanjicai$ git add .gitignore bogon:project yuanjicai$ git add test2.py bogon:project yuanjicai$ git status -s A .gitignore M test2.py bogon:project yuanjicai$ vim test2.py bogon:project yuanjicai$ python test2.py hello YeeCall hello git hello maven bogon:project yuanjicai$ git status -s A .gitignore MM test2.py #第一个M表示staging有修改,第二个M表示working directory有修改
五、git diff
bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ python test1.py hello world hello op bogon:project yuanjicai$ git status -s M test1.py bogon:project yuanjicai$ git diff #默认git diff 只检查第二个标志位(即检查working directory与staging的数据是否一致) diff --git a/test1.py b/test1.py index a968078..6e43c62 100644 --- a/test1.py +++ b/test1.py #表示本行为新添加的内容 @@ -1 +1,2 @@ print 'hello world' +print 'hello op' bogon:project yuanjicai$ git add test1.py bogon:project yuanjicai$ git status -s M test1.py bogon:project yuanjicai$ git diff bogon:project yuanjicai$ git diff --staged #表示检查stage与history中的数据是否一致 diff --git a/test1.py b/test1.py index a968078..6e43c62 100644 --- a/test1.py +++ b/test1.py @@ -1 +1,2 @@ print 'hello world' +print 'hello op' bogon:project yuanjicai$ git diff HEAD #检查working directory与history是否一致,HEAD指针就指向最后一次提交的内容 diff --git a/test1.py b/test1.py index a968078..6e43c62 100644 --- a/test1.py +++ b/test1.py @@ -1 +1,2 @@ print 'hello world' +print 'hello op' bogon:project yuanjicai$ bogon:project yuanjicai$ git diff --stat #输出简要信息 bogon:project yuanjicai$ git diff --staged --stat test1.py | 1 + 1 file changed, 1 insertion(+) bogon:project yuanjicai$ git diff HEAD --stat test1.py | 1 + 1 file changed, 1 insertion(+) bogon:project yuanjicai$ bogon:project yuanjicai$ git commit -m 'second commit' #从stage中将数据提交致history中 [master b89e642] second commit 1 file changed, 1 insertion(+) bogon:project yuanjicai$
六、下载
bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ cat test1.py print 'hello world' print 'hello op.....' bogon:project yuanjicai$ git diff --stat test1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ git add test1.py bogon:project yuanjicai$ git diff --stat bogon:project yuanjicai$ git diff --staged --stat #比较stage与history中的数据是否一致 test1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ git reset test1.py #下载历史history中的最后一个版本致stage中 Unstaged changes after reset: Mtest1.py bogon:project yuanjicai$ git diff --staged --stat bogon:project yuanjicai$ git diff HEAD --stat #比较working directory与history中的数据是否一致 test1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ git checkout test1.py #下载stage中的数据致working directory bogon:project yuanjicai$ git diff HEAD --stat bogon:project yuanjicai$ echo "xxxxx" >> test1.py bogon:project yuanjicai$ git diff HEAD --stat #发现stage与working directory已经不一致 test1.py | 1 + 1 file changed, 1 insertion(+) bogon:project yuanjicai$ git checkout HEAD test1.py #从history中直接下载历史版本到working directory bogon:project yuanjicai$ git diff HEAD --stat bogon:project yuanjicai$ bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ cat test1.py print 'hello world' print 'hello op !!' bogon:project yuanjicai$ git commit -am 'third commit' #直接由working directory提交到history [master bcc815a] third commit 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ git diff --staged
七、删除数据
bogon:project yuanjicai$ echo "newfile" > test3.txt #新建立文件test3.txt bogon:project yuanjicai$ git add test3.txt添加并提交 bogon:project yuanjicai$ git commit -m "add test3.txt" [master 5626d79] add test3.txt 1 file changed, 1 insertion(+) create mode 100644 test3.txt bogon:project yuanjicai$ git rm test3.txt #删除test3.txt rm 'test3.txt' bogon:project yuanjicai$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: test3.txt bogon:project yuanjicai$ git status -s D test3.txt bogon:project yuanjicai$ git commit -m "delete test3.txt" #提交删除 [master 6e928e5] delete test3.txt 1 file changed, 1 deletion(-) delete mode 100644 test3.txt bogon:project yuanjicai$ git status -s bogon:project yuanjicai$ bogon:project yuanjicai$ bogon:project yuanjicai$ git rm --cached test2.py #仅删除staging中的文件 rm 'test2.py' bogon:project yuanjicai$ git status -s D test2.py ?? test2.py bogon:project yuanjicai$ git reset test2.py #再次从history中下载最后一个版本的文件 bogon:project yuanjicai$ git status -s bogon:project yuanjicai$ bogon:project yuanjicai$ git rm --cache test2.py #删除staging中的文件,将 working directory中的数据改名,并再次添加、提交 rm 'test2.py' bogon:project yuanjicai$ mv test2.py test4.py bogon:project yuanjicai$ git add test4.py bogon:project yuanjicai$ git status -s R test2.py -> test4.py bogon:project yuanjicai$ git commit -m 'rename test2.py to test4.py' [master 9845bda] rename test2.py to test4.py 1 file changed, 0 insertions(+), 0 deletions(-) rename test2.py => test4.py (100%) bogon:project yuanjicai$ git status -s bogon:project yuanjicai$
八、stash暂存
bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ cat test1.py #修改源文件 print 'hello world' print 'hello op !!' print 'xxxxxxxxxxx' print 'yyyyyyyyyyy' bogon:project yuanjicai$ git stash #暂存当前正在进行的工作 Saved working directory and index state WIP on master: 9845bda rename test2.py to test4.py HEAD is now at 9845bda rename test2.py to test4.py bogon:project yuanjicai$ git status On branch master nothing to commit, working tree clean bogon:project yuanjicai$ cat test1.py #查看文件的内容(已经是之前没有修改前的内容) print 'hello world' print 'hello op !!' bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ cat test1.py #编辑源代码文件(加了两个感叹号) print 'hello world!!' print 'hello op !!' bogon:project yuanjicai$ git commit -am 'quick update' #提交修改后的代码 [master 23a727c] quick update 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ git stash list #查看之前的暂存区 stash@{0}: WIP on master: 9845bda rename test2.py to test4.py stash@{1}: filter-branch: rewrite stash@{2}: WIP on master: 7f68a1a update bogon:project yuanjicai$ git stash pop #取出上次的暂存 Auto-merging test1.py 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: test1.py no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (33fb283741bb2747c55a9ddee45118f84b65c498) bogon:project yuanjicai$ cat test1.py #查看文件的内容(已经又回到了暂存时状态) print 'hello world!!' print 'hello op !!' print 'xxxxxxxxxxx' print 'yyyyyyyyyyy' bogon:project yuanjicai$
九、history 详解
git commit 之后就会提交到history area。然后history area 中存储 本次提交 的commit history 。每次的commit history 又由 parent 和 tree组成。而每个 parent 都会指向它前一个commit history 。最后一次提交的commit history被标记为 HEAD。前一次的被标记为 HEAD~ ,再前一次的被标记为HEAD~2 … 依次类推。
每个tree 又由多个文件或子目录组成,每个子目录下又可以由多个文件或子目录组成。
输入git log 可以看到之前提交的记录,以及每次提交时,每个commit history的hash码。
git cat-file -t HEAD 可以查看最后一次commit history 的指向类型
git cat-file -p HEAD 可以查看最后一次commit history中的内容,包括 tree 、parent的hash值
git cat-file -t Tree的hash 说明: -t 查看类型 -p 查看内容
可以查看到每个文件或目录也对应一个hash 值 。其中blob表示二进制
bogon:project yuanjicai$ git log --oneline 23a727c quick update 9845bda rename test2.py to test4.py 6e928e5 delete test3.txt 5626d79 add test3.txt bcc815a third commit b89e642 second commit f492923 first commit 547a6b0 init commit bogon:project yuanjicai$ git cat-file -t 23a727c commit bogon:project yuanjicai$ git cat-file -p 23a727c tree bfbf9746b6279366e43117c3a94ff38692d1b60f parent 9845bda97fdaca363b07f703a88259976a58261c author meteor <meteor@163.com> 1471422986 +0800 committer meteor <meteor@163.com> 1471422986 +0800 quick update bogon:project yuanjicai$ git cat-file -t HEAD #等同于git cat-file -t 23a727c命令 commit bogon:project yuanjicai$ git cat-file -p HEAD #等同于git cat-file -p 23a727c命令 tree bfbf9746b6279366e43117c3a94ff38692d1b60f parent 9845bda97fdaca363b07f703a88259976a58261c author meteor <meteor@163.com> 1471422986 +0800 committer meteor <meteor@163.com> 1471422986 +0800 quick update bogon:project yuanjicai$ git cat-file -p HEAD~ #等同于git cat-file -p 9845bda tree bee185fc174697cf400998818e4ca099ca00e275 parent 6e928e51ab05ad2c60b0d51cfd6b7d0cae783ec3 author meteor <meteor@163.com> 1471422174 +0800 committer meteor <meteor@163.com> 1471422174 +0800 rename test2.py to test4.py bogon:project yuanjicai$ git cat-file -p bee185f 100644 blob b25c15b81fae06e1c55946ac6270bfdb293870e8.gitignore 100644 blob b351508c3cadddc4ea579967d1c3177364fd5a48test1.py 100644 blob 5de2b690c836079832d879f9d2902ac58043f5b9test4.py bogon:project yuanjicai$ bogon:project yuanjicai$ git cat-file -p b3515 print 'hello world' print 'hello op !!' bogon:project yuanjicai$ git show -p b3515 print 'hello world' print 'hello op !!' bogon:project yuanjicai$
十、tree-ish
bogon:project yuanjicai$ ls -A .git.gitignoretest1.pytest4.py bogon:project yuanjicai$ cd .git bogon:.git yuanjicai$ ls COMMIT_EDITMSGORIG_HEADconfighooksinfoobjects HEADbranchesdescriptionindexlogsrefs bogon:.git yuanjicai$ cat HEAD ref: refs/heads/master说明 HEAD指向master分支 bogon:.git yuanjicai$ cd refs/ bogon:refs yuanjicai$ tree . |____heads | |____mastermaster实质上是一个分支(branch) |____original | |____refs | | |____heads | | | |____master | | |____stash |____stash |____tags bogon:refs yuanjicai$ cat heads/master #master又指向23a72 23a727c534d620339d3382d8284859f6a75f3194 bogon:refs yuanjicai$ git log --oneline | head -2 #hash23a727就是最后一次的提交 23a727c quick update 9845bda rename test2.py to test4.py bogon:refs yuanjicai$ git cat-file -t 23a727 #查看hash 23a727的类型是commit history commit bogon:refs yuanjicai$ #从上述可以看到 HEAD实质上是指几最后一个commit history的master branch ,而master branch再指向本次的commit history(另外 master~ 等同于HEAD~) bogon:refs yuanjicai$ git rev-parse HEAD 23a727c534d620339d3382d8284859f6a75f3194 bogon:refs yuanjicai$ git rev-parse master 23a727c534d620339d3382d8284859f6a75f3194 bogon:refs yuanjicai$ bogon:refs yuanjicai$ git rev-parse HEAD~ #说明:git rev-parse master~ 等同 git rev-parse HEAD~ 9845bda97fdaca363b07f703a88259976a58261c bogon:refs yuanjicai$ git rev-parse master~ 9845bda97fdaca363b07f703a88259976a58261c bogon:refs yuanjicai$ bogon:refs yuanjicai$ git rev-parse HEAD~^{tree} #说明:查看倒数第二次commit history中的tree的hash值 bee185fc174697cf400998818e4ca099ca00e275 bogon:refs yuanjicai$ git rev-parse HEAD~:test1.py b351508c3cadddc4ea579967d1c3177364fd5a48 bogon:refs yuanjicai$ git cat-file -p b3515 print 'hello world' print 'hello op !!' bogon:refs yuanjicai$ git show HEAD~:test1.py print 'hello world' print 'hello op !!' bogon:refs yuanjicai$
十一、branch分支
bogon:project yuanjicai$ 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: test1.py no changes added to commit (use "git add" and/or "git commit -a") bogon:project yuanjicai$ cat test1.py print 'hello world!!' print 'hello op !!' print 'xxxxxxxxxxx' print 'yyyyyyyyyyy' bogon:project yuanjicai$ git commit -am "stash commit" [master 26415ad] stash commit 1 file changed, 2 insertions(+) bogon:project yuanjicai$ git branch #查看分支 (*表示当前的 branch) * master bogon:project yuanjicai$ git branch newidea #创建分支 bogon:project yuanjicai$ git branch #再次查看分支 * master newidea bogon:project yuanjicai$ git checkout newidea #切换新的分支 Switched to branch 'newidea' bogon:project yuanjicai$ cat .git/HEAD #查看当前HEAD指向newidea分支 ref: refs/heads/newidea bogon:project yuanjicai$ ls .git/refs/heads/ #查看现有分支的存储情况 masternewidea bogon:project yuanjicai$ cat .git/refs/heads/* #查看两个分支都指向同一个commit history 26415adb026d04fd2e375adedd6afcad8c0322ea 26415adb026d04fd2e375adedd6afcad8c0322ea bogon:project yuanjicai$ git checkout master #现次切换回master分支 Switched to branch 'master' bogon:project yuanjicai$ git branch -d newidea #删除指定分支 Deleted branch newidea (was 26415ad). bogon:project yuanjicai$ git branch #再次查看分支 * master bogon:project yuanjicai$ bogon:project yuanjicai$ git checkout -b newcode #参数-b 作用:检查如果没有branch则新建立 Switched to a new branch 'newcode' bogon:project yuanjicai$ ls test1.pytest4.py bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ cat test1.py print 'hello world!!' print 'hello op !!' print 'xxxxxxxxxxx' print 'yyyyyyyyyyy' print 'zzzzzzzzzzz' bogon:project yuanjicai$ git commit -am 'newcode update' [newcode 6c10be1] newcode update 1 file changed, 1 insertion(+) bogon:project yuanjicai$ git checkout master Switched to branch 'master' bogon:project yuanjicai$ git branch * master newcode bogon:project yuanjicai$ git branch -d newcode #此时删除 新分支时无法删除 error: The branch 'newcode' is not fully merged. If you are sure you want to delete it, run 'git branch -D newcode'. bogon:project yuanjicai$ git merge newcode #合并分支 Updating 26415ad..6c10be1 Fast-forward #合并类型为Fast-forward test1.py | 1 + 1 file changed, 1 insertion(+) bogon:project yuanjicai$ bogon:project yuanjicai$ git branch -d newcode #合并分支后就可以删除newcode分支了 Deleted branch newcode (was 6c10be1). bogon:project yuanjicai$ git branch * master bogon:project yuanjicai$ bogon:project yuanjicai$ git checkout -b bugfix Switched to a new branch 'bugfix' bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ git commit -am 'bug fix1' [bugfix eba1606] bug fix1 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ git commit -am 'bug fix2' print 'hello world!!' [bugfix 48a5fd6] bug fix2 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ git log --oneline | head -3 48a5fd6 bug fix2 eba1606 bug fix1 6c10be1 newcode update bogon:project yuanjicai$ git checkout master Switched to branch 'master' bogon:project yuanjicai$ vim test1.py bogon:project yuanjicai$ git commit -am 'last update' [master 329577a] last update 1 file changed, 1 insertion(+), 1 deletion(-) bogon:project yuanjicai$ git log --oneline | head -2 329577a last update 6c10be1 newcode update bogon:project yuanjicai$ git merge bugfix #合并分支 Auto-merging test1.py CONFLICT (content): Merge conflict in test1.py #冲突内容:合并冲突在 test1.py文件中 Automatic merge failed; fix conflicts and then commit the result. #自动合并失败;修改冲突然后提交修改后的结果 然后查看test.py源文件内容,如下 <<<<<<<< HEAD 当前分支的代码 =============== 被合并分支的代码 >>>>>>>>>>>>>>>
这种冲突首先先分析两部分代码是实现相同功能而写的重复的代码,还是各自实现的不同的功能的代码。如果是重复代码:两个二选一删除一个,然后再把这些冲突标示符删除即可;如果不是重复代码,两个都需要保留,只需要把冲突符号删除即可。工程编译通过之后可以重新提交。如下所示:
bogon:project yuanjicai$ vim test1.py #删除重复代码,保留需要的部分 bogon:project yuanjicai$ git add test1.py #重新添加 bogon:project yuanjicai$ git commit -m 'merge update' #重新提交 [master 2b677c0] merge update bogon:project yuanjicai$ git branch -d bugfix #删除无用的分支 Deleted branch bugfix (was 48a5fd6). bogon:project yuanjicai$ git branch #查看当前分支 * master bogon:project yuanjicai$