What & How & Why

Git & GitHub

Git / GitHub Notes


Git

基础流程与相关命令

Git 分为三个区域:工作区暂存区版本库

  • 工作区(Working area):进行工作的区域,比如建立,编辑,分类文件等
  • 暂存区(Staging area):存放修改过的文件
  • 版本库(Repository):存储当前软件的某个正式版本
工作流程

Git 的基本工作流程是,在工作区创建好文件,然后将文件提交到暂存区;确认无误后将改动提交到版本库,作为新的版本发布:



相关命令
  • git init:将当前的文件夹转化为 git 项目文件夹
  • git status:查看当前文件夹的中文件的状态
  • git add:将工作区的文件添加到暂存区
  • git diff:查看指定文件在工作区与暂存区之间的不同
  • git commit:将暂存区的文件提交至版本库
  • git log:查看 commit 的历史
  • git rm:将文件删除并移除出版本库
git diff

打印信息的例子:

#git diff output example

#white, nothing changed, indicated in white
  * @Author: Codinghare
  * @Date:   2022-05-22 21:42:56
  * @Last Modified by:   hare

#start with "-", removed or replaced, indicated in red
- * @Last Modified time: 2022-05-30 00:16:57

#start with "+", changes to the file, indicated in green.
+ * @Last Modified time: 2022-05-30 00:42:45
退出使用按键 q 即可。


查看某个文件工作区与最新版本的区别:

git diff HEAD -- file

git commit

git commit 有一个非常好用的 tag amend。这个 tag 允许我们对当前最新的 commit 的进行更新,而不是创建一个新的 commit。这个命令在修改当前 commit 中的一些细小错误时非常有用。

当然,虽然看上去是”更新“了 commit,amand 实际上还是创建了一个新的 commit,并取代了旧的 commit(只是不会有新的 commit)记录。但终端会弹出界面要求我们重新书写 comment(-m 里写的东西)。如果要维持之前的 comment,使用下面的命令:

git commit --amend --no-edit

git log

打印信息的例子:

#SHA, unique id for the commit
commit 5f9c857051c2a9acc2036e90b4c965e16798bd6b
#Author
Author: hare <[email protected]>
#Date
Date:   Thu Jun 2 23:14:49 2022 -0600
#comments, generated by git commit -m " "
fixed typos.
一些相关的 tag:
git log --online 		  #shows the list of commits in one line format.
git log -S "keyword" 	  #displays a list of commits that contain the keyword in the message.
git log --oneline --graph #Displays a visual representation of how the branches and commits

git rm

直接删除版本库文件夹下的文件并不能将其移除出版本库。查看会显示该文件的状态是被删除:

On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    test.txt

  • 如果要删除记录,则应该使用 git rm <file>,然后做 git commit
  • 如果要回复记录(当前最近版本):则使用 git restore <file>

Backtracking

BackTracking 的主要内容:

  • 将不符合要求的文件移除暂存区
  • 将工作区中被错误修改的文件回滚到未修改之前的状态
  • 将当前的 commit 回滚到之前某次的 commit

关键的概念:

  • 当前最新的 commit 被成为 HEAD commit.
  • reset 命令会更改当前的 HEAD commit 到指定的 commit
相关命令
  • git show HEAD:查看当前最新的 commit 的信息。信息额外包括了文件的变动信息。
  • git checkout HEAD取消 HEAD commit 做出的改动。
  • git checkout - -:上面命令的简化版本。
  • git reset:将已经提交到 staging 区域中的某个文件移除
  • git reset commit_SHA:将 staging 区域中的文件重置为与 指定 SHA commit 中一致,也就是重新指定当前的 Head。commit_SHA 是 commit SHA 的 头7 位。重新指定当前 HEAD 之后,该 HEAD 之后的所有改动都会消失。
  • git stash:将当前未完成的工作赞存入一个临时区域(临时区域可拥有多个)
  • git stash pop:取回临时区域的内容
git reset / checkout
  • git reset 通常用于暂存区中存在着不符合要求文件的情况。比如不小心将错误的源文件送至暂存区后,可以使用 reset 命令将其移除出暂存区。
  • git reset + SHA 使得对应的 commit 会成为当前的 HEAD commit。该 commit 之后的所有内容将不再成为项目的一部分。
  • git checkout 则是对工作区的文件进行还原。当不小心修改了工作区中的文件时,可以利用该命令将其还原到 HEAD commit 时文件的状态,也就是没有被修改前的状态。
  • 新版本中,git restore 替换了 git checkoutgit reset 的部分功能:

# revert the file that has not been added to the staging area.
# git checkout -- <file>
git restore <file>

# moving the file that has been added to the staging area back to the working driectory
# git reset file
git restore --staged <file>

git stash

这个命令主要用于保存当前未完成的工作。比如当我们正在进行工作 A(brach A),然后接到要求去 B(branch B)进行另外工作的要求。此时我们就可以使用 git stash 将 A 这边的工作暂存;当完成 B 的工作后,回到 A,使用 git stash pop 就可以将之前未完成的工作取回,接着进行工作了。

这个命令避免了将未完成的文件进行 commit 的情形。该临时暂存的档案可以建立多个,可以通过 git stash –list 查看。

# saving the WIP
git stash

# revert the WIP but keep the saving
git stash apply
# delete the save file
git stash drop

# revert the WIP and delete the save file
git stash pop

temp

  • git alias:使用该命令可以对 git 当前命令进行 alias,允许我们使用更短的命令代替原有的命令:

$ git config --global alias.co "checkout"
$ git config --global alias.br "branch"
$ git config --global alias.glop "log --pretty=format:"%h %s" --graph"

初始设置

设置用户名和 email

#define global user name for the local repo
git config --global user.name "xxxx"
#define email for the local repo
#must match the github user email
git config --global user.email "[email protected]"

链接到 github
  • 在 github 上手动建立一个 repo
  • 链接本地 repo 和 github repo

git remote add origin https://repo.address
git push -u origin master

#check if the repo linked
git remote -v

Branching

Git 允许创建不同的 branch 来对项目进行实验;也就是说,新的 branch 是作为之前版本的改动而存在的;我们可以在新的 branch 中进行开发和实验,直到功能稳定之后再合并到主要版本中,再作为新的项目版本进行发布。新的 branch 包括两部分:

  • 之前 master branch 的内容(所有 commit)
  • 新 branch 自身拥有的内容
相关命令
  • git branch:查看当前所在的 branch。
  • git branch new_branch:建立名为 new_branch 的新 branch。
  • git branch -d branch_name:删除名为 branch_name 的 branch
  • git branch -D branch_name:功能同上。如果被删除的 branch 从未合并到 master,需要使用 -D tag
  • git checkout branch_name:切换到名为 branch_name 的 branch
  • git merge branch_name:合并名为 branch_name 的 branch 到 master

# create a new branch
git branch <new_branch_name>

# create and switch to a new branch
git switch -c <new_branch_name>

# list all branchs
git branch

# switch to a branch
# old school: git checkout <branch_name>
git switch <branch_name>

merge conflict

合并 branch 到 master 的前提是,master 在 branch 新建之后没有发生改变。如果改变发生,之后再将 branch 合并回 master,会造成合并冲突(merge conflict)。这种冲突是因为 master 的不一致导致的。

当冲突发生的时候,会产生如下的信息:

Auto-merging conflict_file_name
CONFLICT (content): Merge conflict in conflict_file_name
Automatic merge failed; fix conflicts and then commit the result.
之后我们再打开冲突发生的文件,会有如下的内容:
#master version
<<<<<<< HEAD
-content of master version 
=======
#branch version
-content of branch version
>>>>>>> branch
-------------------
这一段代表了当前 masterbranch 开始时的 master 中的内容差异。我们可以选择保留当前的 master 的修改,或是保留 branch 中对应的 master 内容。但无论如何,除了要保留的内容以外,其他所有内容(包括 git 自动产生的标记)都需要删除。

Tagging

git 中可以对指定的分支做 tag:

# switch to the branch before tagging
git tag <tag_name>

# listing exsiting tag
git tag

#deleting tag
git tag -d <tag_name>

#deleting tag on origin
git tag -d <tag_name>
git push origin :refs/tags/<tagname>
默认的 tag 是关联到当前分支的最新 commit 上的。如果希望对指定的 commit 打 tag (比如忘记打了),需要加上 commit id:
git tag <tag_name> <commit id / 7 digits>

# creating tag with extra message
git tag <tag_name> -m "your message here" <commit id>
打好 tag 以后就可以很方便根据 tag 查看 commit 了:
git show <tag_name>
tag 默认存在本地。如果要推送到远程,使用以下命令:
git push origin <tag_name>

#push all tags at once
git push origin --tags

Teamwork

在多人使用 git 进行合作中,一般会需要:

  • 项目在本地的拷贝
  • track and review 其他人工作内容的手段
  • 对项目最终版本的访问权限

所有这些内容都可以通过 Remotes 来实现。remotes 允许多个合作者(可以处于不同地方)进行同一个项目的开发。每个人都可以独立在自己的内容上工作,然后最终合并到一起。

工作流程
  1. remote 获取(fetchmerge)最新的项目内容
  2. 在本地建立新的分支进行新内容开发
  3. 提交新内容到本地分支
  4. 再次从 remote 获取最新的内容,确保不会错过在本地工作期间其他人提交的新内容
  5. push 本地内容到 origin
相关命令

# find the remote on remote_location and clone the your directiory called clone_name
# remote_location could be a website, or file path
git clone remote_location clone_name

#list all remotes
git remote - v

#updates the local copy
git fetch

#merge changes in the origin to the local copy
git merge origin/master

#push your branch feature to the remote
git push origin your_branch_name

#fetch and merge the newest master
git pull origin master

git rebase

我们在 push 的时候,如果 origin 对应分支已经发生了改变(别人提交过了),那么需要用 fetch/merge 或是 pull 来同步 origin 中的新内容到本地分支。但这样做的问题在于,我们在本地的 commits 实际上处于 pull commit 之后了,那么从路径上来说我们的 commit 之于 master 的新内容之前。这样顺序是不对的。

git rebase 会将本地 commit 的提交历史止于 pull(origin 内容更新之后),等于重新顺理了一遍提交顺序。这样看使提交历史起来更加直观。

# before
|\
| * d662d56 (origin/master) Update readme.md.txt
* | 30bcffe modified hello2.py
* | 9091cc7 add hello2.py
|/

# after git rebase

* 280680e (HEAD -> master) modified hello2.py
* 8337019 add hello2.py
* d662d56 (origin/master) Update readme.md.txt

#after push
* 280680e (HEAD -> master, origin/master) modified hello2.py
* 8337019 add hello2.py
* d662d56 Update readme.md.txt

git clone

clone 命令是将指定的共享项目拷贝到本地的项目中。当共享项目被拷贝后,git 会自动将其与 origin 关联。

git remote

# list all origins
git remove -v
# add origin
git remote add origin <address>
# remove origin (link)
git remote rm origin

git fetch

如果本地版本没有及时更新(本地使用的版本已经在 origin 中被别人更新),可以使用 fetch 命令更新本地版本到最新。这个更新指的是信息的更新。用户在查看更新后可以自行决定是否合并 origin 的新内容到本地的对应 branch 中。

git merge origin/master

fetch 只会从 origin 下载 objects。这些 object 会存储在 origin/branch_name 中等待被合并。如果希望应用所有 origin 的新内容到本地,需要使用 merge 命令;也就是将 origin/branch_name 中的内容转移到 branch_name 中。

git push

push 命令允许我们将本地完成的内容推送到 origin 中。

# first time push 
# -u: push the new data and link the local branch to the cloud branch
git push -u origin master

Tips

误回滚回复

如果知道想回滚版本的 hash:

git reset --hard hash
如果不知道:
#find the hash
git reflog
#backtrack
git reset --hard hash

将 master 上修复的 bug 同步合并到 dev 上

这种情况一般出现在:

  • 你正在开发新的东西
  • 当前的 master 分支出现了 Bug 并被修复了

这种情况下,无需去 dev 分支上再把 bug 修改一遍,只需在 dev 分支上使用如下命令即可合并 Bug 修复内容到 dev:

git cherry-pick <bug-fix-commit-code-7-digits>

Git Hub

GitHub 的简单工作流程

  1. 建立 新的 branch
  2. commit
  3. 建立 pull request
  4. 等待 reviewer 的审查通过
  5. 合并当前 branch 到 master branch,并删除当前 branch



新建 branch 的必要性
  • 防止冲突
  • 保证主项目(master branch)不受开发进程的影响
branch 的命名方式

#author + branch type + short branch description 
hare_feature_dashboard_notifications

pull request 的注意事项
  • 提供足够的细节以便节省 review 的时间
  • PR 较小的更新,让 review 更快更容易
review 的注意事项
  • comment 应该包括需要修改的内容和原因
  • 清晰的表达自身的意思
  • 指出潜在的问题

添加 github 远程仓库

首先查找用户主目录下有没有 id_rsaid_rsa.pub 的文件,如果没有,使用以下命令生成 key(命令中的邮箱使用自己的邮箱):

ssh-keygen -t rsa -C "[email protected]"
运行以上命令后访问 .ssh 目录,会找到 id_rsaid_rsa.pub 两个文件,前者是私有密钥,需要妥善保存;后者是公有密钥,可以放心告诉别人。

接下来到 Github 中的 seting 中找到 SSH and GPG keys,将刚才生成的 id_rsa.pub 中的内容作为一个新的 SSH key 添加到 GitHub中。

添加完成之后可以输入如下命令测试是否连接成功:
ssh -T [email protected]
如果显示以下文本就证明连接成功了:
Hi XXXX! You've successfully authenticated, but GitHub does not provide shell access.
到此就可以使用 git 命令对远程库操作了。

Trouble shooting

GitHub Error Message - Permission denied (publickey)

可能的解决方案


  • 不使用 sudo 进行 Git 命令的使用

  • 如果出现以下错误:

Git XXX: fatal: could not set 'core.filemode' to 'false'
尝试修改 /etc/wsl.conf,添加以下代码(没有则创建):
[automount]
options = "metadata"
之后运行:
wsl --shutdown
重启即可


  • 如果是多用户,并出现以下错误:

fatal: Could not read from remote repository.
尝试将密钥文件夹复制给 root 一份:
sudo cp ~/.ssh/* /root/.ssh/

让 git 忽略文件权限改变带来的改动

git config core.filemode false

References