基本概念:
workispace(工作区): 本地电脑中clone 的文件夹下,就是工作区;
Repository(版本库): 工作区中的.git 隐藏文件就是git 的版本库; 这里存了很多东西,其中有stage(index) 暂存区,还有自动创建的第一个分支master, 以及指向master 的一个指针 HEAD;
remote(远程仓库): server 端保存的版本库信息,一般来讲这里包含了所有推送的结果;
对于这三者之间的一些操作关系如下:
本地工作区中文件含义:
1. .git/refs/ : heads/ #本地branch 相关信息 remotes/ #remote 端的相关信息
配置设定:
SSH key生成与配置, 实现local 与 remote 之间传输;
ssh-keygen -t rsa -C "youremail@example.com" #这里选择自己的mail cat ~/.ssh/id_rsa.pub # 然后将这个文件中的信息复制到github SSH keys 中 ssh -T git@github.com #配置完成以后,可以确认下配置成功 #可以使用下面的命令配置一些个人信息, 把下面“” 中内容换成个人信息 git config --global user.name "xxx" git config --global user.email "xxx@github.com"
.gitignore #忽略指定的文件
在使用过程中,对于一些特定的文件,只想留在本地自己用,如果每次都要手动选择,很麻烦,所以可以当前工作区建立.gitignore 文件,来忽略一些指定的文件(正则规则), 也可以将该文件上传; 如果只是自己用, 那么可以把.gitignore 也加入其中, 只是每次都clone 到本地都要新建这个文件;
git l-files --others #列出哪些文件没有被git 管控
.ignore 中由对应不同的编程语言的一些已经配置好的信息:github/gitignore
alias #快捷键设定,这里注意配置时候作用范围;
在下面的操作中,有可能会有下面的类型的命令,就是对于一些命令缩写的绑定;可以使用下面git cmd 来实现这个快捷键使用;
git config --global(local) --list #列出当前git的全局(当前仓库)配置信息,包含快捷键 git config --local/global/system alias.st status #快捷键设定,这里local用来选择配置信息的范围
作用范围,类似linux 系统;
a. 针对当前repo 的配置信息存储在 /.git/config , 可以在配置过程中传递 --local 来读写该文件;
b. 针对当前账户下的配置信息,linux 系统中就是在当前用户目录下: ~/.gitconfig , windows 系统是在C:\USERS\用户名\.gitconfig 文件中;可以通过git config --global 选项读写此文件,这会对当前账户下所有仓库生效;
c. 针对系统级配置文件: /etc/gitconfig (没试过), 可以传递 --system 方式读写该文件,注意权限问题;
1 amend = commit --amend 2 amendf = commit --amend --no-edit 3 br = branch 4 ct = commit 5 co = checkout 6 cp = cherry-pick 7 df = diff 8 ds = diff --staged 9 l = log 10 lg = log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(bold white)— %an%C(reset)%C(bold yellow)%d%C(reset)' --abbrev-commit --date=relative 11 sa = stash apply 12 sh = show 13 ss = stash save 14 st = status 15 lp = log --pretty=oneline --abbrev-commit --graph
常见操作:
1. 建立本地的库 与 远程库之间关联(该操作其实一般很少用,不过简单列下);
在github 建立remote 以后,我们就可以将本地库链接到这个repo 下面;github 下面也有一些基本操作教程如下:
mkdir GitLearn cd GitLearn git init git remote add LEARN git@github.com:guigym/GitLearn.git #这里将本地名字为LEARN,常见为origin, 这里设定了特殊的值; echo 'add the 1st file for new repo;' > readme_file git add --all git commit -m 'add the 1st file for new repo;' git push --set-upstream LEARN master #将LEARN 与 remote master 链接在一起,下面操作都会用到LEARN
git remote add <name> <url> #上面使用这个命令增加了remote url 到本地; git remote remove <name> #删除上面add 的name(还有rename 进行重命名) git remote -v # 此时也可以通过这个命令显示远程url 链接到本地的信息
git clone git@github.com:guigym/GitLearn.git #如果只是下载已有的repo,可以直接用repo 命令。
2. 常用的增查改删操作:
#查 git status : 查看当前branch 下的状态; # 增、改 git add : 是把当前改动添加到暂存区,
#删 git rm <filename> 可以将文件在工作区 与 暂存区删除; rm <filename> 也可以将文件直接删除,
你在后期工作中,修改了一些文件,然后要查看文件改动哪里?
#查 details git diff : 比较工作区 与 暂存区 区别, 也可以指定文件查看不同; git diff --cached : 比较暂存区 与 仓库的差别 git diff HEAD : 比较工作区 与 仓库的差别,这里HEAD 可以为指定的commit ID git difftool commitID1 commitID2 #查看两次commit的差异;
如果有文件改错了,怎么恢复;(这里对于改动文件在不同地方(工作区、暂存区,库), 有不同操作):
git reset filename/ git restore --staged filename : 取消暂存区的改动 到 工作区; git checkout HEAD filename : 暂存区 改动变为 远程库原始状态;其中HEAD 可以为之前的commit ID, delete 的文件也可以这样恢复 git reset --hard HEAD^ : 将工作区 与 暂存区 改动都丢弃,恢复至当前HEAD前一个commit 状态,(HEAD^^:前两个,HEAD~3: 前3(n)个 git restore filename / git checkout filename(早期在filename 之前加--, 以区别branch 建立/切换;)#工作区改动恢复; git reset --soft HEAD^ #对于已经commit,但是还没有push 的文件,将改动从 repo 恢复到 暂存区; git clean -fd #删除一些新增的文件; 其中 git reset 还有很多其他选项 --hard(暂存区、工作区改动都丢弃)/mixed(缓存区改动都丢弃)... git reset --help 查看帮助文件,其中有下面的这个table 展示不同option 对应的结果;
对于修改远程库内容,一般不推荐,如果只是回退某个提交,可以使用下面命令;
如果要回滚,有其他方式,一般不推荐回滚。
git revert <commit ID> #回退某个提交
工作做完了,要提交到远程时操作:
#推送remote git commit -m #标注当前推送的改动描述,方便以后查阅 #如果commit 信息错误,那么要更改commit message; git commit --ammend -m 'new message;' #更改最近一次commit message, 结果如下图所示 git push #推送到remote #查看当前branch 下过往的提交信息 git log (file) #查看过往提交信息,这里也有很多选项,下面列了一些个人常用的,可以指定文件查看该文件的修改历史 git log -p # 查看每次commit 的具体修改部分,add 的信息不会显示 git lot --stat #快速查看各个commit 的修改文件简略信息 git log --pretty=oneline --abbrev-commit # 简略显示各个修改信息,想看整体树状图,可以 --graph 选项
关于分支:
git 中主分支为 master 分支, HEAD 指向master,master 再指向最新提交;新建branch,就相当于新建一个类似master 的指针;
每次切换分支,就是将HEAD不断移动到不同的指针位置;而在合并分支过程中,最简单,直接的方式,就是将master 指向 branch 当前的提交,就可以实现合并(merge);之后删除branch ,也就相当于把当前的branch 指针删除了
在实际工作流程中,一般大家不会直接在master 中工作;下面时一个个人觉得比较适合团队合作时规范。
michael/bob:是个人完整自己工作中具体case;
dev:是个人将完成的某个case 合并的信息;
master 是某个正式release的版本的;也是比较适合对外展示项目进度的部分;
在实际工作中,常见的branch 相关的操作:
建立新的branch
git checkout -b new_branch ##建立一个新的branch,并跳转进这个branch git checkout master/ git switch master #切换到不同的branch (git 2.23 版本中引入git switch 来代替 git checkout 作为切换branch 的命令;) git branch -d new_branch ##删除本地new_branch, -D 相当于强制删除(等价-d -f) git push <remote> -d(--delete) branch_name #删除远端branch,remote是远端名字(这里是LEARN) git branch --all(-av, -rv) ##显示remote/local 所有的branch git branch new_branch commitID #基于commitID 建立new_branch, 等价(git checkout -b new_branch commitID); #完成工作,推送到remote git checkout -b 2nd_branch git status echo 'init the new file for 2nd_branch;' > 2nd_branch git status git add --all git commit -m 'add the init file for 2nd_branch;' git push --set-upstream LEARN 2nd_branch #remote 存在该branch 时候,可以直接push; 另外注意这里使用LEARN(不是origin),是因为前面在建repo 时候我们命名为LEARN;
纯粹的推送一个新的branch,没有文件变更;
工作结束,review完成后,我们就可以将当前branch merge 到dev;
git checkout dev #切换到最终被merge 的branch git merge 2nd_branch #将2nd_branch merge 到当前的branch; 这里使用fast-foward 默认方式;
从下图中,可以看出默认的merge 方式使用fast-forwad 选项,如果没有其他的改动,相当于直接把dev 转移到当前branch, 然后HEAD也还是指向dev; 这里保存了2nd_branch 中各个提交的信息;
如果不使用ff 方式merge,那么这部分作为一个branch 修改,然后再合并到dev 中, 这个过程会保留下来;
git merge --no-ff 3rd_branch #不适用fast-forward 方式;
还有中--squash 方式merge,这是相当于把某个branch 所有改动作为一个改动,然后在dev上面提交一个commit merge 到 dev 中;
这也会丢失原来branch commit 信息(原来branch 被删除后)
删除4th_branch 以后,原来branch 上面信息都会丢失;
在合并过程中,常见的conflict 处理:
一般在merge 之前最好先git pull/fetch, 这两者有所差别,注意区分使用;
git fetch LEARN #取回远程remote 所有改动到本地的repo git fetch LEARN dev #取回remote LEARN dev 中的改动到本地,但是不会改变工作区; git pull #取回remote LEARN dev 中的改动到本地,同时merge工作区内容,并把head 至于最新commit
使用fetch后的结果:
使用pull 的结果:
处理conflict, 然后用--no-ff merge ,注意观察其中brach tree 的变化过程;
其他一些命令:
git branch -v #展示local 存在的branch 相关信息; git branch --merged master #展示被merged 到master上面的branch name;
关于tag(标签)
在实际工作中,即使使用规范管理git 流程,但是每个commit ID 是随机hash 值,commit message 又不一定能完全体现变动;
所以我们一般都是用特定版本号,rev1..., 这个信息可以通过tag 与某个commit ID 绑定;
新建/删除 tag
git tag tagname (commit ID) ##新建tag,(没有commit ID, 默认当前commit); git push origin tagname ##推送当前tag 到server git push origin --tags ##推送所有tag 到server git tag -d tagname ##删除tag git push origin :refs/tags/tagname ##推送删除操作到server
其他使用技巧:
1. git 的帮助信息查找:
git --help #显示git 基本帮助信息 git help -a #显示所有的subcommand git help -g #显示git 一些定义配置相关帮助信息 git help <subcommand> #显示当前subcommand/概念相关的帮助信息
2. 查看git 历史命令对应的commit ID;
git reflog # 查看命令历史中对应的各个commit ID, 方便hard reset 以后要head 转移到新的部分;
3. git stash;
使用场景:假如突然要求fix bug,需要先切换到 其他分支,但当前分支的代码没有提交,直接切换分支,会将当前分支的新增的代码也会增加到 master 分支,而代码此时又不能commit ,这时候可以使用 git stash ;
git stash(等价git stash save) # 保存当前状态,只会保存被trach 的文件; git stash save --all "message" # 可以保存track/untrack 的文件, message 里面可以记录一些信息 git stash list #返回缓存的列表, 显示所有的保存的stash; git stash show stash@{x} #显示stash x(默认为0) 改动的文件, 选项 --all(显示改动的内容); git stash apply stash@{x} #将 stash x 中的改动,加入当前branch中改动的, 但是不会删除该stash;适合多分支应用该缓存; git stash pop stash@{x} # 与apply 差别在于,这个会将堆栈中该stash删除; git stash drop stash@{x} #将stash x 从堆栈中中删除; git stash clear #清空当前堆栈
在某些场景下,会发生恢复失败,可以考虑使用下面的命令试试
git stash show -p | git apply -3 #相当于将stash 中存储的所有改动都apply git show stash@{0}:changedFile #显示当前stash{0}中修改的changedFile 改动状态,注意该文件如果有更新;
git stash 与其他一些命令的结合
$ git diff stash@{0}^1 stash@{0} -- <filename> #显示stash 存储的第一个父级 与 stash 中内容的差别, $ git checkout stash@{0} -- <filename> (git show stash@{0}:<filename> > <new filename> #输出重定向) 从stash 中检查单个文件;
4. 其他常用命令:
git cherry-pick :可以将一个commit id 中的改动,复制到其他branch; 不过二者改动一摸一样,但是commit id 是不一样; git blame <file>: 查看file文件的每行改动的commit相关信息;
5. git cheet sheet(from Liao's blog)
6. 一个帮助学习git 的游戏网站: https://learngitbranching.js.org/
APPENDIX:
上面所使用的remote git repo link: