Git不完全指北
本篇做一个关于 Git 的小小分享,总结了 Git 的一些日常操作。
写在前面的话
Git 的文件状态定义是 Git 的核心内容,而不同的文件状态会储存在不同的工作区域中。
Git 库所在的文件夹中的文件大致有 4 种文件状态,如下图所示:
-
Utracked 未跟踪状态:文件在文件夹中 (工作目录 Working Directory),但并未加入到 git 库 (本地仓库 Repository),不参与版本控制。通过 git add xxx 命令可将文件状态变为 Staged,加入暂存区 (Staging Area)。
-
Unmodified 未修改状态:文件已经被 commit 到 Local Repository 了,此时版本库中的文件快照内容与文件夹中完全一致。Unmodified 文件有两种可能的去处,如果被修改,将变为 Modified 状态,如果用 git rm 移除版本库,则成为 Untracked 文件。
-
Modified 已修改状态:文件被修改了,仅仅是修改,并没有进行其他操作。此时这个文件也有两个去处,通过 git add xxx 命令可将文件变为 Staged 状态,加入 Staging Area,也可 git checkout ,则丢弃修改内容,返回 Unmodified 状态,其实是从 Local Repository 中取出文件,覆盖当前修改。
Question:Modified 状态的文件是在哪个工作区中?是在 Loacal Repository 还是 Working Directory 中?
-
Staged 暂存状态:执行 git commit 可将修改同步到 Local Repository 中,此时库中的文件和本地文件又变成一致,文件状态变为 Unmodified。执行 git reset HEAD filename 取消暂存,文件变为 Modified 状态。
Git 有 4 个工作区,如下图所示:
Git 本地操作篇
1. 首次使用需要设置一下基本信息
$ git config --global user.name "Xiaowen" $ git config --global user.email
设置好之后每次 commit 将会以你设置的 name 和 email 来 commit。
2. init本地仓库,add,commit和打标签tag
初始化一个本地仓库:
$ git init
如果整个项目中含有空文件夹,而此时你也想 git 对这个文件夹进行管理,此时我们需要在这个空文件夹下生成一个 .gitkeep 文件,否则 git 是无法对空文件夹进行管理的:
$ cd path_empty_folder //先进入到你想加入 git 管理的空文件夹 $ touch .gitkeep //生成一个 .gitkeep 文件
注意,如果一个空文件夹下又有空文件夹,而你想 git 管理这个文件夹结构,此时你需要在这个文件夹结构下的每一层都生成 .gitkeep,例如:
# 原空文件夹结构 database/ train/ val/ # 在每一层都加入 .gitkeep database/ .gitkeep train/ .gitkeep val/ .gitkeep
此时所有文件处于 Untracked 状态,现在把文件 add 到暂存区,变成 Staged 状态:
$ git add . //添加所有文件 $ git add 1.py //添加指定文件
此时文件和包含文件的文件夹都已经 add 好了,已经准备好可以 commit 了,将文件 commit 到本地仓库,文件又变成 Untracked 状态:
$ git commit -m "commit 1." $ git commit -am "commit 1." // add 和 commit 合在一起
为某个提交的版本打标签, git tag 是命令, -a 0.1.3 是增加名为「0.1.3」的标签,「-m」后面跟着的是标签的注释:
$ git tag -a 0.1.3 -m "Release version 0.1.3"
删除标签:
$ git tag -d 0.1.3
3. 一些查看信息的操作
查看过去所有的操作:
$ git reflog
查看之前提交的所有更改信息:
$ git log $ git log --oneline //显示所有的 commit $ git log --oneline --graph //以图形的形式显示
查看文件状态:
$ git status $ git status -s //以缩写的形式查看状态
查看本次修改和之前 commit 的版本的细节区别:
$ git diff //必须在 Modified 状态下使用 $ git diff --cached //在 Staged 状态下使用
查看已有的标签列表:
$ git tag --list
查看分支:
$ git branch //查看本地分支 $ git branch -a //查看所有分支
4. 回到过去 の reset,实现不同的 commit 之间的穿梭
穿梭回到任意版本:
$ git reset --hard <commit_id>
穿梭回到想回到的版本后,对文件进行修改,但是再次 commit 时不想新增加 commit,即在原来的 commit 上添加:
$ git add $ git commit --amend --no-edit //这样不会新增加 commit,但是版本号会变
如果某文件已经更改完了,也已经 add 成 Staged 状态,但是还没有 commit,现在想返回 Modified 状态继续修改。也就是从 Staged 状态返回 Modified 状态:
$ git reset x.py
5. 回到过去 の checkout,针对单个文件
$ git checkout <commit_id> -- x.py
6. 分支
新建一个 dev 分支:
$ git branch dev
删除分支:
$ git branch -d dev //删除本地分支 $ git push origin --delete <BranchName> //删除远程分支
切换到 dev 分支:
$ git checkout dev
把 dev 分支合并到 master 分支上,在合并前记得要切换回 master 分支上:
$ git merge --no-ff -m "merge into master" dev
7. 分支冲突 の merge
如下图所示,branch-A 是 master 分支,branch-B 是 dev 分支,在合并分支时发现主分支已经被修改过。此时就是分支冲突。
此时合并分支会报错且合并失败,要求手动解决分支冲突。解决 merge conflict 需要在代码上删掉 `<<<<<<< HEAD`,`=======` 和 `>>>>>>> dev`,然后手动修改冲突部分,保存然后 add 和 commit 即可。
$ git commit -am "merge conflict solved"
8. 分支冲突 の rebase
比 merge 更高级,但是 rebase 是危险的。其实选用 merge 还是 rebase 取决于你到底是以什么意图来避免 merge conflict。rebase 的实质是改变当前分支 branch out 的位置,如果这个节点信息对你很重要的话,rebase 将不是你想要的。
9. 更新分支與解衝突
事情是這樣的,在這裡,我們需要嘗試搞明白一些事情:<br/>
clone, pull 和 fetch 區別;
merge 和 rebase 區別
Git 远程仓库操作篇
1. ssh key 生成:
首先要在本地创建一个 ssh key:
$ ssh-keygen -t rsa -C "xiaowen.herman@gmail.com"
三次回车即可生成 ssh key。
2. 查看并添加 ssh key:
$ cd ~/.ssh/id_rsa.pub //或者 cd /Users/hermanke/.ssh $ cat id_rsa.pub
复制里面的内容添加到 github 或 gitee 上。
3. 测试 ssh key 是否添加并连接成功:
$ ssh -T git@github.com
第一次绑定的时候输入以上代码会提示是否 continue,输入 yes 后程序会自动连接,如果要求登录,直接输入登录信息即可。再次输入上面命令,如果返回一下信息,则表示连接成功:
# gitee Welcome to Gitee.com, Your name! # github You’ve successfully authenticated ……
4. 远程仓库操作
链接绑定远程仓库:
$ git remote add origin "git@gitee.com:XiaowenHerman/STRAIT.git" //origin 是主机名
解除绑定远程仓库:
$ git remote rm "git@gitee.com:XiaowenHerman/STRAIT.git"
更改远程主机名:
$ git remote rename <原主机名> <新主机名>
把当前分支推送到服务器:
$ git push origin master
$ git push origin develop
推送标签到远程服务器上:
$ git push origin --tags //因为普通的"git push origin master"操作不会推送标签到服务器端
将远程仓库全部所有分支的更新取回本地:
$ git fetch <远程主机名>
取回远程主机某个分支的更新,再与本地的指定分支合并:
$ git pull <远程主机名> <远程分支名>:<本地分支名>
例如,取回 origin 主机的 dev 分支,与本地的 dev 分支合并:
$ git pull origin dev:dev
从远程仓库往本地仓库拉代码,有几种方式,git clone 是克隆,git pull 是拉。
Git-Flow 工作流
以上主題我們介紹了 git 的一些基本概念,利用這些概念,我們可以為我們的團隊協作建立起 “工作流” 的概念,並應用到實際的開發過程中。工作流其實不是一個初級的主題,其背後的本質問題其實是有效的項目流程管理和高效的開發協同約定。一個合理的工作流保證了你的項目在隨著開發進程愈發複雜的時候仍然能夠井然有序地推進,這正是工作流的用武之地。
以下簡單介紹一些常用的分佈式工作流:集中式工作流程,集成管理者工作流和司令官与副官工作流。
1. Centralized Workflow
集中式工作流保留了在 SVN 下的工作風格,對於每一個 repo,集中式工作流只使用 master 分支進行開發。
Vincent Driessen flow:
上面 5 個分支大體可以分為**兩類**:
1. 主要分支:
master: 主分支,永遠保持穩定狀態,切下來就能跑;
develop: 最新的開發狀態;
2. 辅助分支:
`feature`: 功能分支,用於開發新的功能,從 develop 新建分支開發,完成後合併回 develop;
`release`: 准备发布的状态,用來預發,修復 bugs,基於 develop,完成後 merge 回 develop 和 master;
`hotfix`: 緊急修復 master 上的問題,等不及 release 版本就必須馬上上線,基於 master,完成後 merge 回 master 同時也 merge 回 develop;
詳情請參考:[nvie](https://nvie.com/posts/a-successful-git-branching-model/)