Git学习笔记
几个概念:
数据库 (Repository):记录文件和目录状态的地方,还存储着内容修改的历史记录。
工作树:文件、目录结构。
stash:即索引(index),stash索引暂时保存工作树和索引里还没commit提交的修改内容。可以事后取出暂存的修改,应用到原先的分支或其他的分支上。
还未提交的修改内容以及新添加的文件,留在索引区域或工作树的情况下,如切换到其他的分支,修改内容会从原来的分支移动到目标分支。
HEAD:HEAD指向的是现在使用中的分支的最后一次更新,即commit。通常默认指向master分支的最后一次更新。通过移动HEAD,就可以变更使用的分支。
一、本机用户的git设置
本机git的设置,存放在用户本地目录/home/uussrr
的.gitconfig
文件里。
$ git config --global user.name "<用户名>"
$ git config --global user.email "<电子邮件>"
以下命令能让Git以彩色显示:
$ git config --global color.ui auto
二、本地操作
(一)建立本地工作树(文件夹)
在新建的文件夹内部,使用git init
$ mkdir zzz
$ cd zzz
$ git init
(二)查看本地工作树的当前状态
使用status命令确认工作树和索引的状态:
$ git status
查看历史操作记录:
$ git log
--oneline
单行简介版本,可记录commit步骤的编号,常用。
--branches=*
显示全部分支
(三)将文件加入索引
将那些创建、或修改、但没有被删除的文件添加到索引。
$ git add <file1> <file2> <file3> <file4>
在<file>
指定加入索引的文件。用空格分割可以指定多个文件。
$ git add .
指定参数「.」
,可以把当前目录(工作树)下所有的文件加入到索引,包括子目录和其内部。
没有添加索引的文件不被commit提交。
工作树 –> 索引 –> 数据库(本地)
(四)将文件从索引删除
$ git rm --cached <file>
注意:git rm <file>
没参数时,将同时删除索引和磁盘文件!
另外:可以通过 echo "/folder" >> .gitignore
避免 git add .
把folder文件夹添加索引。
另外:可以全局设置屏蔽:新建~/.gitignore_global
文件,添加内容:
**/.DS_Store
**/._.DS_Store
**/.git
.DS_Store
._.DS_Store
.git
再运行:
$ git config --global core.excludesfile ~/.gitignore_global
(五)文件恢复
$ git checkout -- <filename>
从索引stash或者最后commit中恢复文件。
$ git checkout master~2 <filename>
从倒数第二个commit恢复文件。
(六)提交到数据库(本地)
$ git commit -m "hahahaha, this is my first commit"
强烈建议-m
写清提交的变更是什么。
执行提交后,数据库中会生成上次提交的状态与当前状态的差异记录(也被称为revision)。
(七)回到历史提交点
$ git log --oneline
$ git checkout a1e8fb5
使工作树与a1e8fb5提交时的状态一致,此时可以查看文件、编译项目、运行测试,甚至编辑文件,而不必担心丢失项目的当前状态(master)。
此时HEAD在历史提交点;然而此时commit不会改变历史和当前状态,commit被抛弃!
回到项目的“当前”状态:
$ git checkout master
(八)取消过去的提交
注意: 对当前分支取消过去提交,不需要再次commit,可直接强制 git push -f
。
- reset
$ git reset --hard a1e8fb5 # 回到a1e8fb5提交时
默认是--mixed
模式,还有--soft
和--hard
模式。通常情况用--hard
模式。
- soft模式只修改HEAD的位置;mixed模式还修改索引;hard模式还修改工作树。
- soft只取消提交;mixed复原修改过的索引的状态;hard彻底取消最近的提交。
$ git reset --hard HEAD~2
~2
代表回到倒数第二个commit时。
注意:Reset错误的时候,在ORIG_HEAD
上reset 就可以还原到reset前的状态:
$ git reset --hard ORIG_HEAD
- revert
$ git revert HEAD~3
保留已提交的,新建反向对冲操作,使之恢复到历史时的状态。
- reset还是revert的选择问题
git reset
可作为“本地”撤消方法。当不涉及远程数据库时,撤消对私有分支的更改时使用。本地撤销如可能造成与远程数据库矛盾,将影响远程其他开发者。git revert
是撤消共享历史的首选方法。它不从共享历史记录中删除任何提交。它保留需要撤消的历史,并创建一个新的提交来反转之前的提交。
三、与远程数据库交互
(0)预备:将本地SSH公共key提交给远程服务器
- 本地计算机查看:
$ cd ~/.ssh
$ ls -al
是否有 id_rsa.pub
或者 id_dsa.pub
存在。
2. 本地生成:
$ ssh-keygen -t rsa -C "xxxxx@gmail.com"
- 本地获得公钥:
打开 id_rsa.pub
文件,并且复制全部内容。
- gitlab添加公钥:
gitlab后台:点击头像–设置,点击SSH密钥;将public key粘贴到相应位置并命名密钥,保存密钥。
(一)给远程数据库取一个别名
每次push或pull的时候就不需要输入长串的远程数据库地址了。
$ git remote add <name> <url>
例如:
$ git remote add origin git@gitlab.com:nenzheng/blog.git
注意:执行push或pull时,如果省略远程数据库的名称,则默认使用名为origin的远程数据库。因此一般都会把远程数据库命名为origin。
(二)查看远程数据库的别名
$ git remote -v
注意:删除用git remote rm <name>
(三)push到远程数据库
将本地数据库的修改记录共享到远程数据库。远程数据库的修改记录就会和本地数据库的修改记录保持同步。
注意:之前别忘将新建和变更的文件添加索引,并提交。
$ git push <repository> <refspec>...
<repository>
处输入目标地址,<refspec>
处指定推送的分支。
例如:
$ git push -u origin master
首次运行push向空的远程数据库推送时,必须指定远程数据库名称和分支名称。如果指定了-u
选项,那么下一次push就可以省略目标和分支名称了;
甚至完成首次push后,再从远程数据库clone下来的另一个本地树今后也可以省略参数而直接git push
。
(四)clone复制远程数据库
执行克隆后,远程数据库的全部内容(包括变更履历)都会被下载。像原始的本地数据库一样继续操作。
$ git clone <repository> <directory>
例:如想将远程项目下载到~/projects/localname文件夹:
$ cd ~/projects
$ git clone git@xxxxxx.git ./localname
注意:将自动新建localname文件夹,并在其中配置.git,无需再git init
注意:可加参数 --depth 1
,仅克隆最近的一次commit,减少体积。
(五)从远程数据库pull
把远程数据库的内容更新到本地数据库。就是从远程数据库下载最近的变更日志,并覆盖自己本地数据库的相关内容。
$ git pull <repository> <refspec>...
(六)从远程数据库fetch
从远程数据库下载,在本地新建分支。该命令执行完后需要执行git merge
合并远程分支到你所在的分支。
pull = fetch + merge
技巧:如果要丢弃所有的本地改动与提交,想从服务器上获取最新的版本并将本地主分支指向到它:
$ git fetch origin
$ git reset --hard origin/master
四、冲突
注意看push和pull的反馈信息!
远程数据库和本地数据库的同一个地方都发生修改时,产生冲突!
-
我没改,远程改了,我push旧的,报错无法上传;先把新的pull回来,正确。
-
我改了,远程没改,我push上传OK;我pull将自动合并(Auto-merging),本地以我新的为准。
-
我改了,别人也改了,产生冲突:
git pull
回本地时,提示冲突CONFLICT
。- 打开冲突的文件,内容被添加标识:
<<<<<<< HEAD abcdefg ======= gfedcba >>>>>>> 4c0182374230cd6eaa93b30049ef2386264fe12a
- 注意:==分割线上方是本地数据库的内容;下方是远程数据库编辑的内容。
五、分支管理
分支是为了将修改记录的整体流程分叉保存。分叉后的分支不受其他分支的影响,所以在同一个数据库里可以同时进行多个修改。
A --- B --- C --->
\
----P --->
\
---X --->
分叉的分支可以合并。
分支大致可分为两大类:
- Merge分支:主线、稳定、可随时发布release的分支;不轻易修改,修改时一般分出Topic分支来修改,修改后再合并回来。
- Topic分支:了开发新功能或修复Bug等任务而建立的分支;若要同时进行多个任务,创建多个Topic分支。
Topic分支是从稳定的Merge分支创建的。完成作业后,要把Topic分支合并回Merge分支。
** master分支:数据库最初提交后,创建名为master的分支。之后的提交、在切换分支之前,都将添加到master分支里。一般作为Merge分支。
(一)一个优秀的分支管理结构
- 主分支(属于merge分支)两种:master分支和develop分支
- master:master分支只负责管理发布的状态。在提交时使用标签记录发布版本号。
- develop:develop分支是针对发布的日常开发分支。有合并分支的功用。
- feature分支(属于topic分支)
作为develop分支的下游分支。针对新功能的开发、bug修正等从develop分支分叉出来的。基本不需要上传远程数据库,不共享。 完成开发后,把分支合并回develop分支。
-
release分支
作为develop分支的上游分支。develop分支如果到了可以发布的状态,新创建release分支,为发布做最后的bug修正。 完成后,release分支合并到master分支(在合并提交时添加release版本号的标签),也要合并回develop分支。 -
hotFix分支
已经发布的版本如发现严重bug需要紧急修复。从master分支创建的分支。 完成后,合并到master分支,也要合并回develop分支。
(二)显示分支列表
$ git branch
反馈信息中,前面有*
的就是现在HEAD所在分支。
(三)新建分支
$ git branch <branchname>
例如:
$ git branch hotFix-msglost
也可以新建分支并切换到新分支:
$ git checkout -b <branchname>
(四)删除分支
$ git branch -d <branchname>
注意:合并完成后,别忘删除没用的分支。
(五)分支切换
$ git checkout <branch>
checkout之后的提交记录将被追加到目标分支。 如果checkout前没commit、之后commit,即使已经add,也追加到目标分支。
(六)提取提交(从其他分支复制指定的提交,然后导入到现在的分支)
当需要把弄错分支的提交移动到正确的地方时;或者想把其他分支的提交添加到现在的分支时:
$ git checkout issue1
$ git log --oneline
$ git checkout master
$ git cherry-pick 99daed2
把制定编号的commit导入当前HEAD的分支。
如果发生冲突,就手工修改冲突,再提交(git add
再git commit
)。
(七)分支合并
两个分支最后commit点的合并,未commit的任何修改不合并。
合并后,不用的分支,记得删除。
- merge
$ git merge <branch>
该命令将指定分支导入到当前所在的HEAD指定的分支。不需要再commit。
例如:
先切换到master分支,然后把issue1分支导入到master分支:
$ git checkout master
$ git merge issue1
- 如果master自从建立bugfix分支后就没有改动,只bugfix分支改动,此时merge:
即为fast-forward(快进)合并,即bugfix分支记录直接变成master分支记录。 - 如果master自从建立bugfix分支后有改动,bugfix分支也有改动,此时merge:
non fast-forward合并,即bugfix维持原样,master分支融合bugfix的修改,并生产一个提交。
如融合过程中产生冲突,需手动处理冲突,并手动再提交。
- rebase
$ git checkout bugfix
$ git rebase master
该命令先将HEAD从bugfix切换到master,并将bugfix分支的历史记录添加在master分支的后面。但此时master分支还没 commit !
这时移动bugfix分支中的各历史提交到master有可能会发生冲突,所以需要修改各自的提交时发生冲突的部分。
修改冲突后,再提交不是使用commit命令,而是执行git rebase --continue
。若要取消则执行git rebase --abort
。
注意:手工修改后别忘了add索引!
$ git add myfile.txt
$ git rebase --continue
rebase成功后还要merge!此时master分支才变:
$ git checkout master
$ git merge bugfix
- merge和rebase的选择问题
- 永远不要在共享分支上使用
git rebase
!造成与其他开发者的矛盾! - 如果想要一个干净的,没有merge commit的,线性历史树,那么应该选择
git rebase
;
如果想保留完整的历史记录,并且想要避免重写commit history的风险,应该选择使用git merge
。 - 实践中,
在topic分支中更新merge分支的最新代码,请使用git rebase
。
向merge分支导入topic分支的话,先使用git rebase
,再使用git merge
。
六、应用案例:删除多余commit,减少体积
$ git clone git@gitlab.demo.com/hello/aa.git # 克隆一个项目
$ cd aa
$ git checkout --orphan tmp # 创建临时分支
$ git add -A # 添加当前的全部文件
$ git commit -m "clean project"
$ git branch -D master # 删除master分支
$ git branch -m master # 更名分支为master
# 注意:gitlab项目对master有保护,此时如提交会报错!需要取消保护:
# 项目Settings -> Repository -> Protected branch -> unprotect
$ git push -f origin master # 上传分支
其他
(一)Windows系统git设置
- 下载windows git并安装
- 设置全局:
$ cd ~
$ git config --global user.name "Git账号"
$ git config --global user.email "Git邮箱"
$ ssh-keygen -t rsa -C "your_email@example.com"
- 将SSH私钥添加到ssh-agent
$ eval $(ssh-agent -s)
$ ssh-add /c/Users/username/.ssh/id_rsa
- 计算机管理-服务:开启“OpenSSH Authentiction Agent”,设为自动。
- 复制ssh key,在GitLab中添加
clip < /c/Users/username/.ssh/id_rsa.pub
(二)git挂代理
全局设置:
$ git config --global http.proxy http://172.29.176.1:9870
$ git config --global https.proxy http://172.29.176.1:9870
取消:
$ git config --global --unset http.proxy
$ git config --global --unset https.proxy
最后修改于 2024-02-24