Git学习笔记
Git的入门学习笔记,体系基本完整,除了tag。

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

  1. 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
  1. revert
$ git revert HEAD~3

保留已提交的,新建反向对冲操作,使之恢复到历史时的状态。

  1. reset还是revert的选择问题
  • git reset可作为“本地”撤消方法。当不涉及远程数据库时,撤消对私有分支的更改时使用。本地撤销如可能造成与远程数据库矛盾,将影响远程其他开发者。
  • git revert是撤消共享历史的首选方法。它不从共享历史记录中删除任何提交。它保留需要撤消的历史,并创建一个新的提交来反转之前的提交。

三、与远程数据库交互

(0)预备:将本地SSH公共key提交给远程服务器

  1. 本地计算机查看:
$ cd ~/.ssh
$ ls -al

是否有 id_rsa.pub 或者 id_dsa.pub 存在。 2. 本地生成:

$ ssh-keygen -t rsa -C "xxxxx@gmail.com"
  1. 本地获得公钥:

打开 id_rsa.pub 文件,并且复制全部内容。

  1. 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),本地以我新的为准。

  • 我改了,别人也改了,产生冲突:

    1. git pull回本地时,提示冲突CONFLICT
    2. 打开冲突的文件,内容被添加标识:
    <<<<<<< HEAD
    abcdefg
    =======
    gfedcba
    >>>>>>> 4c0182374230cd6eaa93b30049ef2386264fe12a
    
    1. 注意:==分割线上方是本地数据库的内容;下方是远程数据库编辑的内容。

五、分支管理

分支是为了将修改记录的整体流程分叉保存。分叉后的分支不受其他分支的影响,所以在同一个数据库里可以同时进行多个修改。

A --- B --- C --->  
       \
        ----P --->  
        \  
         ---X --->  

分叉的分支可以合并。

分支大致可分为两大类:

  • Merge分支:主线、稳定、可随时发布release的分支;不轻易修改,修改时一般分出Topic分支来修改,修改后再合并回来。
  • Topic分支:了开发新功能或修复Bug等任务而建立的分支;若要同时进行多个任务,创建多个Topic分支。
    Topic分支是从稳定的Merge分支创建的。完成作业后,要把Topic分支合并回Merge分支。

** master分支:数据库最初提交后,创建名为master的分支。之后的提交、在切换分支之前,都将添加到master分支里。一般作为Merge分支。

(一)一个优秀的分支管理结构

  1. 主分支(属于merge分支)两种:master分支和develop分支
  • master:master分支只负责管理发布的状态。在提交时使用标签记录发布版本号。
  • develop:develop分支是针对发布的日常开发分支。有合并分支的功用。
  1. feature分支(属于topic分支)

作为develop分支的下游分支。针对新功能的开发、bug修正等从develop分支分叉出来的。基本不需要上传远程数据库,不共享。 完成开发后,把分支合并回develop分支。

  1. release分支
    作为develop分支的上游分支。develop分支如果到了可以发布的状态,新创建release分支,为发布做最后的bug修正。 完成后,release分支合并到master分支(在合并提交时添加release版本号的标签),也要合并回develop分支。

  2. 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 addgit commit)。

(七)分支合并

两个分支最后commit点的合并,未commit的任何修改不合并。
合并后,不用的分支,记得删除。

  1. 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的修改,并生产一个提交。
    如融合过程中产生冲突,需手动处理冲突,并手动再提交。
  1. 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
  1. 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设置

  1. 下载windows git并安装
  2. 设置全局:
$ cd ~
$ git config --global user.name "Git账号" 
$ git config --global user.email "Git邮箱"
$ ssh-keygen -t rsa -C "your_email@example.com"
  1. 将SSH私钥添加到ssh-agent
$ eval $(ssh-agent -s)
$ ssh-add /c/Users/username/.ssh/id_rsa
  1. 计算机管理-服务:开启“OpenSSH Authentiction Agent”,设为自动。
  2. 复制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