三个区的概念:
working tree指工作区
index指暂存区
HEAD指最近的版本库,即最近一次commit之后的版本
diff操作
git diff:是查看working tree与index file的差别的。
git diff --cached:是查看index file与commit的差别的。
git diff HEAD:是查看working tree和commit的差别的。(你一定没有忘记,HEAD代表的是最近的一次commit的信息)
reset 操作
git reset --mixed:此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,head和index信息都会被退回
git reset --soft:回退到某个版本,只回退了head的信息,不会恢复到index file一级。如果还要提交,直接commit即可
git reset --hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,此命令 慎用!
关于branch
branch不只是针对版本区,如果切换branch,工作区和暂存区,都会发生变化的。所以如果有工作没有commit,则不能执行rebase、checkout等操作。如果一定要执行,则要使用git stash命令把未commit的工作暂时”搁置“,等完成了相关执行,再用git stash pop命令回来继续工作。
如下,在master分支上有过一次修改,未add到index上,在执行rebase和checkout时,都提醒失败,要先stash:
lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) $ 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: git learning.txt no changes added to commit (use "git add" and/or "git commit -a") lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) $ git checkout dev_testrebase error: Your local changes to the following files would be overwritten by checkout: git learning.txt Please commit your changes or stash them before you switch branches. Aborting lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) $ git rebase dev_testrebase Cannot rebase: You have unstaged changes. Please commit or stash them.
执行add操作添加到index上后,未commit到heard,也是一样的提示失败:
1 $ git add "git learning.txt" 2 3 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) 4 $ git status 5 On branch master 6 Changes to be committed: 7 (use "git reset HEAD <file>..." to unstage) 8 9 modified: git learning.txt 10 11 12 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) 13 $ git rebase dev_testrebase 14 Cannot rebase: Your index contains uncommitted changes. 15 Please commit or stash them.
如果不想保存当前分支所做的未commit的修改,想强制切换到别的分支,可以使用checkout -f anotherbranch。因为chechout会把当前的工作区、index区和版本区都换成anotherbranch的(其实是清空了工作区和index区,一个分支checkout出来时总是干净的),所以所有未commit的内容都没了。再切换加来,运行git status,发现工作区和index区什么也没有了:
1 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) 2 $ git checkout -f dev_testrebase 3 Switched to branch 'dev_testrebase' 4 5 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (dev_testrebase) 6 $ git diff 7 8 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (dev_testrebase) 9 $ git status 10 On branch dev_testrebase 11 nothing to commit, working tree clean 12 13 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (dev_testrebase) 14 $ git checkout master 15 Switched to branch 'master' 16 17 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) 18 $ git status 19 On branch master 20 nothing to commit, working tree clean
rebase操作
rebase和merge相似,但是区别在于保留了更多的细节。从命令的名字就可以看出来,rebase是把当前的branch重置为为某一个版本(默认的是它所跟踪源branch的最新版本,即它是从哪里创建出来的,比如dev的源branch一般就是dev。也可以显式指定为别的branch),并把所做的修改看成是补丁(patch)。最后rebase成功后,用git log命令显示的版本号也是rebase_id>>patch1>>patch2>>patch3...(由旧到新)。merge操作的版本号这是按照各自修改的时间排序,如果有冲突并且解决了冲突后提交的merge,那么最后一个版本号是执行merge后提交的版本号。从这一点来看,rebase和merge最大的区别就是理念不同。一个是“重置”,一个是“合并”。
既然是重置,那么源branch可能和当时创建分支的时候相比,已经不一样的,比如,master已经有了其他人的提交,这时候就会产生冲突,冲突的结果有三种处理方式:--congtinue --skip --abort;
--continue:当手动处理好冲突之后,就使用continue,继续完成rebase,此时,就完成了合并。
--skip:就是强制rebase,把当前branch(dev)所做的修改全部放弃,直接回到创建之初的样子。
--abort:就是放弃这次rebase操作
stash操作
stash操作是在你新工作完成到一半时,测试发现原来版本有一个很关键bug要解决,而此时你不想将手头未完成的工作提交,只想在原来的版本上把bug修复,你可以用stash把未完成的工作先入栈,把工作区和index清理干净,然后你可以完成你的bug修复了。此时你有两种方式去修复bug,一种是在别的分支上修复(一般是在源分支上分离出一个新的bug分支完成后再rebase更新源分支),另一种是在当前分支上直接修复:
(1)第一种方式,等修复完成了,提交修复,源分支得到更新,bug得到了修复。你再用stash pop把之前未完成的新工作出栈,继续完成,完成之后提交,此时别忘了rebase一下,因为此时当前分支的上一个版本还是bug修复前的,所以要rebase到bug修复后的版本。完成这些后,你的当前版本就即修复了bug,又有了新功能了。
(2)第二种方式,如果修复bug时和新工作内容的文件有冲突,则需要解决冲突,然后再完成新工作,最后提交。
当前版本即有未commit 的index区,也有未add的工作区,都可以用stash入栈:
1 $ git status 2 On branch master 3 Changes to be committed: 4 (use "git reset HEAD <file>..." to unstage) 5 6 modified: git learning.txt 7 8 Changes not staged for commit: 9 (use "git add <file>..." to update what will be committed) 10 (use "git checkout -- <file>..." to discard changes in working directory) 11 12 modified: git learning.txt 13 14 15 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) 16 $ git stash 17 Saved working directory and index state WIP on master: a751bf2 merge dev_testrebase[1] into git learning.txt 18 19 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) 20 $ git status 21 On branch master 22 nothing to commit, working tree clean //可以看到,入栈之后,working tree 和index都被清空了
直接在当前分支修复bug时,stash pop 之后可能有冲突,此时git会把冲突文件合并,需要你去解决冲突,再完成工作,再提交:
$ git stash pop Auto-merging git learning.txt CONFLICT (content): Merge conflict in git learning.txt lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) $ git status On branch master Unmerged paths: (use "git reset HEAD <file>..." to unstage) (use "git add <file>..." to mark resolution) both modified: git learning.txt no changes added to commit (use "git add" and/or "git commit -a") lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) $ git add "git learning.txt" lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master)
$ git commit
[master 1e0a75e] handle confliction after unstash
1 file changed, 4 insertions(+), 1 deletion(-)
图1 冲突 图2 冲突解决后
完成这些之后,修复bug的提交和新工件的提交都成功入了版本库了:
1 lxl_lib1@DESKTOP-RGUV9II MINGW64 /f/Programming Learning/git learning (master) 2 $ git log 3 commit 1e0a75e5d287b2459609f33a5016e32d8cc24e3e (HEAD -> master) //新工作(其中包括了冲突解决)提交 4 Author: xlinliu <kexuetou@163.com> 5 Date: Sat Sep 16 12:28:46 2017 +0800 6 7 handle confliction after unstash 8 9 commit 4f2eae39dab242ca9a020e68a24b384fca96f4d4 //bug修复提交 10 Author: xlinliu <kexuetou@163.com> 11 Date: Sat Sep 16 11:44:15 2017 +0800 12 13 add master[5] after stash
和 .ignore设置有关的一些注意点
有如下结构目录
F:. │ .gitignore │ git learning.txt │ ├─path1 │ │ 新建文本文档.txt │ │ │ └─新建文件夹 │ 新建文本文档.txt │ └─path2 │ file1.txt │ file2.txt │ └─file.txt 新建文本文档.txt
1 .ignore文件中例出的条目,/开头的表示根目录开始,理解为绝对路径,如 /path1/file.txt 指path1路径下的file.txt,;不带/的为任意的,如单独的 file.txt ,指任意位置的file.txt。
2. 以/结尾的条目表示目录,不带/结尾的表示一切。如 path.ext/ 表示path.ext/目录,只忽略path.ext/目录下的内容(包括文件和文件夹);而 path.ext 即会忽略path.ext/目录下的所有内容,也会忽略任意位置所以名为path.ext的文件
3. 至于path/*和path/的区别,事实就是效果没有区别,但又有本质区别。path/*是使用通配符的特例,匹配所有,所以和path/忽略所有效果一样,所以说效果没区别。但是path/*本质是通配符匹配,比如path/*a,则只匹配以a结尾的文件或文件夹,所以说有本质区别
//.ignore
1 path1/ 2 path2/file*
git update-index --assume-unchanged filename操作
该命令用于commit时忽略当前分支特定文件的更改。后面只能跟具体文件名,而不能是文件夹。希望用文件夹来忽略文件夹下的所有文件的变化是行不通的。
另外,这个操作的本意是,当某个文件特别大的时候,不想每次更改都让git去扫描它,因为这样会浪费很多时间,等到最终把这个大文件完成之后,在用--no-assume-unchanged回复跟踪,再一次性提交。但是这个操作只对本地库的当前分支有效,而且如果index发生了改变(比如reset了),此操作就失效,而且reset --hard操作还是会回复工作区。
另一个类似的操作是--skip-worktree,这个操作是让git所有的操作都跳过指定文件,包括reset --hard。
无论是 --assume-unchanged 还是 --skip-worktree,当文件有改动时,都不能进行rebase merge 以及切换分支等要求工作区和Index区是干净的操作(因为这些操作都会改变工作区,所以要求把工作区为提交的更改提交或者stash)。
关于远程库同步
远程分支用origin/branchname来表示,但是那只是一个远程库分支的复本,并不是远程库分支本身,所以要看到最新的远程库,必需用git fetch更新origin库。如果想把本地分支也更新,可以用切换到对应分支(目标分支),用 git merge origin/branchname 或者 git rebase orgin/branchname 命令和origin分支合并;也可以用 git pull 命令一次性完成fetch和merge。
IntelliJ idea 这个IDE也需要手动点fetch,origin分支才能更新。