2021 从零开始学Git【新版本Git - 8000字详细介绍】

我写的这篇文章,主要是记录自己的学习过程,也希望帮助读者少踩坑(比如不同版本可能命令不兼容等)。本文面向git零基础初学者,建议读者按照文中命令自己全部操作一遍(注意运行环境)。

我的运行环境:win10 + VMware + ubuntu 18.04 + git 2.33.0(所有命令均在root用户下执行)

本文参考资料:

[1] 廖雪峰 Git教程
[2] 博客园 一个小时学会Git
[3] Git中文官方文档

资料在精不在多,以上3个资料足以详细解释git的各项命令,建议把这些资料都看一下。从时效性来说,[1]和[2]中的有些命令是老版本的,而[3]的官方文档能看到最新的命令使用方法;从趣味性和易懂性来说,则[1]和[2]更容易让人理解和掌握。当然,本文综合了时效性和易懂性,保证使用新版本的命令,并尽量清楚地表述各个命令的作用。

未经允许,本文禁止转载! 作者:nefu_ljw (本文所有参考资料均在对应位置标明,如有侵权,请联系我删除)

文章目录

0. Git 安装 & 更新

安装git。

apt install git

查看git版本。

git --version

若版本低于2.23,请更新git。(git 2.23版本新增了switchrestore命令,后文会用到)

add-apt-repository ppa:git-core/ppa #adding a Launchpad PPA(Personal Package Archive) repository
apt update
apt install git

1. 基本概念:工作区、暂存区、版本库、远程仓库

  • 工作区(Workspace):本地存放文件的地方。Git会知道这里的文件变更,如添加/修改/删除文件。
  • 暂存区(Stage/Index):临时存放文件变更信息,事实上它是一个名为index的文件。
  • 版本库(Repository):工作区有一个隐藏目录.git,它是Git的版本库,为了与远程仓库区分,将其明确称为本地版本库(Local Repository)。其中有一个HEAD指向当前版本库版本。
  • 远程仓库(Remote Directory):托管代码的服务器,可以简单的认为是你项目组中的一台电脑用于远程数据交换。

参考: 一个小时学会Git - 工作区域

2021 从零开始学Git【新版本Git - 8000字详细介绍】
  • Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间。

  • Workspace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间。

  • .git:存放Git管理信息的目录,初始化仓库的时候自动创建。

  • Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区。

  • Local Repo:本地仓库,一个存放在本地的版本库;HEAD只指向当前的开发分支(branch)。

  • Stash:隐藏,是一个工作状态保存栈,用于保存/恢复Workspace中的临时状态。

2. Git文件操作

在本章,介绍操作git本地仓库的一些常用命令。

2.1 创建仓库(git init)

创建本地仓库的方法有两种:一种是本地创建全新仓库,另一种是克隆远程仓库(请见第4章)。

本地创建全新仓库,初始化一个版本库:

git init

2.2 提交变更(git add, git commit)

下面介绍提交变更到本地版本库的基本流程。

准备工作:先新建一个目录learngit,并创建README.md文件,随便写入一句话。

mkdir learngit
cd learngit
touch README.md
echo "# Hello, git!" >> README.md

添加文件变更到版本库,分两步:

  • 使用命令git add <file>,可多次使用,添加多个文件。
  • 使用命令git commit -m <message>,文件从暂存区提交到版本库;<message>表示提交的信息。
git init #初始化一个git的本地仓库
git add README.md #将工作区README.md文件的变更移动到暂存区
git commit -m "add README.md" #将暂存区提交到本地仓库

2.3 管理变更(git add/rm, git restore)

理解:Git跟踪并管理的是变更,而不是文件。

  • 撤销工作区变更(撤销对工作区文件的添加/修改/删除):git restore <filename>git checkout -- <filename>

  • 撤销暂存区变更(撤销git add/rm):git restore --staged <filename>git reset HEAD <filename>

2.23版本的git,考虑到reset, checkout存在多种用法对新手来说不易学习,故引入新命令git restore方便操作。

示例:变更工作区文件后(比如修改/删除文件),使用git restore撤销工作区/暂存区的变更。

echo "after git add" >> README.md #在工作区修改README.md,用git status可以看到被修改,但这一变更未加到暂存区
git add README.md #变更被加到暂存区
git restore --staged README.md #撤销暂存区的变更(README.md的修改),注意,此时工作区文件还是没有变
git restore README.md #撤销工作区的变更,即工作区README.md文件被还原成修改前

git rm README.md #在工作区删除README.md,并且这一变更被加到暂存区
git restore --staged README.md #撤销暂存区的变更(README.md的删除),注意,此时工作区文件还是没有变
git restore README.md #撤销工作区的变更,即工作区README.md文件被还原成删除前

git rm README.md等价于以下两句:

rm README.md #在工作区中删除README.md,但这一变更没有被加到暂存区
git add README.md #将对README.md的变更加到暂存区

2.4 版本回退(git reset)

Git允许我们在版本的历史之间穿梭,实际上就是移动HEAD指向的位置(HEAD指向的版本就是当前版本)。

git log可以查看提交历史,即历史每次提交的<commit_id>,以便确定要回退到哪个版本。

git reflog可以查看命令历史,即所有分支的所有操作记录(包括commit, reset, checkout, merge等操作)。

指定版本到<commit_id>:

git reset --hard <commit_id> #当前分支和HEAD均指向<commit_id>

回退到当前HEAD的上一个commit,撤销暂存区变更(撤销commit和add):

git reset --hard HEAD~1 #HEAD~1表示上一个commit版本,如果是往上两个版本就是HEAD~2;当前版本就是HEAD

回退到当前HEAD的上一个commit,不撤销暂存区变更(撤销commit):

git reset --soft HEAD~1

2.5 查看命令(git status, git log, git reflog, git diff)

  • git status 查看git仓库中,工作区、暂存区、版本库的当前状态。
  • git log 查看提交历史。可加选项:
    • --oneline 每行显示一个commit。例如显示9af21bf add README.md,那么<commit_id>前几个字符就是9af21bf
    • --all 显示所有分支的提交历史,否则只显示当前分支的提交历史。
    • --graph 显示分支合并,以横线形式连接曾有过合并的各个提交。
  • git reflog 查看命令历史。
  • git diff 查看工作区和暂存区差异。
  • git diff --cached 查看暂存区和版本库差异。
  • git diff -- HEAD~1 查看工作区和版本库的差异。HEAD~1可以改成HEADHEAD~2等等。
  • git diff HEAD HEAD~1 查看版本库HEAD与版本库HEAD~1之间的差异(最新的commit和上一次commit)。

2.6 小结:提交变更 + 管理变更

  • 工作区变更:任意的添加/修改/删除文件。
    • 撤销工作区变更:git restore <filename>git checkout -- <filename>
  • git add 工作区→暂存区添加文件变更。
    • 撤销add:git restore --staged <filename>git reset HEAD <filename>
  • git commit 暂存区→版本库提交文件变更。
    • 撤销commit:git reset --soft <commit_id>
    • 撤销commit和add:git reset --hard <commit_id>

本地仓库的各项管理命令可以参照下图方便记忆(从右往左看):

2021 从零开始学Git【新版本Git - 8000字详细介绍】
原图来源 ;上图由本人 nefu_ljw 修改;

3. 分支管理

本章介绍git的本地分支管理。(实际上,既有本地分支,也有远程分支,具体请见4.8章)

提醒:对于想快速上手的初学者,比如你只想推送本地仓库到远程仓库,那么可暂时跳过本章,直接去看第4章远程仓库。

3.1 查看/创建/切换/删除分支(git branch, git switch)

查看本地分支:

git branch #要查看远程分支则加上选项-r,查看所有分支则加上选项-a

创建分支:

git branch <name>

切换分支:

git switch <name> #等价于git checkout <name> (注意:switch命令是git 2.23版本的新命令)

创建+切换分支:

git switch -c <name> #等价于git checkout -b <name>

删除普通分支:

git branch -d <name>

强制删除未合并的分支:

git branch -D <name>

3.2 合并分支(git merge)

  • 快速合并(Fast-forward):合并当前分支(旧)到某分支(新)的位置。
git merge <name> #某分支名为<name>
  • 非快速合并:合并当前分支+某分支,产生一个新的提交,合并到这个新提交的位置,这样可以保存之前的分支历史。
git merge --no-ff -m <message> #<message>表示新提交的信息

操作示例:

首先创建并切换到分支feature,并在feature分支上进行一次提交。

# 首先用git status看看是否位于分支master(如果不是就用git switch master先切换一下)
git switch -c feature #创建并切换到分支feature
echo "add merge" >> README.md #修改README.md
git add README.md 
git commit -m "add merge" #提交

则此时状态如下图(蓝线表示master分支,红线表示feature分支):

2021 从零开始学Git【新版本Git - 8000字详细介绍】

然后,用git switch master切换回master分支。此后,HEAD指向master

接下来请任选(1)或(2)的命令全部执行一遍,即选择你的合并策略(Fast-forward 或 no-fast-forward)。

(1)快速合并

git merge feature

git log --oneline --graph显示历史提交信息如下:

* f7a06a2 (HEAD -> master, feature) add merge

* e0e4ca3 conflict fixed (注:这里是你上一次的提交信息,与合并无关)

合并后的状态如下图:

2021 从零开始学Git【新版本Git - 8000字详细介绍】

(2)非快速合并

git merge feature --no-ff -m "merge with no-ff" 

git log --oneline --graph显示历史提交信息如下:

* fc7e0ee (HEAD -> master) merge with no-ff

|\

| * d4d0ec9 (feature) add merge

|/

* e0e4ca3 conflict fixed (注:这里是你上一次的提交信息,与合并无关)

合并后的状态如下图:

2021 从零开始学Git【新版本Git - 8000字详细介绍】

(选做)最后,可以用git branch -d feature删除feature分支。

(3)两种合并策略的区别

2021 从零开始学Git【新版本Git - 8000字详细介绍】
原图来源

3.3 bug分支(git stash, git cherry-pick)

假设现在有master, feature分支,你在feature分支上进行工作,写一个hello.py文件,并将其添加到了暂存区。

git switch feature #切换到feature分支,如果没有这个分支就用git switch -c feature创建并切换
touch hello.py & echo "print('hello')" >> hello.py #创建了一个python代码文件并写入了内容
git add hello.py #将变动加至暂存区

git add之后,用git status可以看到"hello.py的变更处于待提交状态"。

此时,突然发现有一个紧急的bug需要修复,但hello.py还没有写完,暂时无法commit。

于是你打算创建一个新的bug分支,但当前分支的工作状态必须先保存下来,那么可以用:

git stash

git stash可以把当前工作状态储存到stash这个栈之中,等以后出栈,即可还原现场,继续工作。

当前工作状态入栈后,再用git status可以看到"无文件要提交,干净的工作区"。

假设要在feature分支上修复bug,那么就在feature分支上创建并切换到bug分支(命名为issue-101)。假设修复bug就是修改README.md文件,修复完就commit。

#现在位于feature分支进行操作,然后创建并切换到bug分支
git switch -c issue-101 #bug分支
echo "Fixed the bug of README.md" >> README.md
git add README.md
git commit -m "fix bug 101"

然后,再切换回feature分支,将bug分支与feature分支进行非快速合并:

git switch feature
git merge issue-101 --no-ff -m "merged bug fix 101"

那么此时,已经完成了bug修复,可以重返当时储存的工作状态了:

git stash pop #将栈顶的工作状态出栈,同时删除栈顶内容

到此为止,我们就完成了feature分支上bug的修复过程,总结一下流程:

  1. feature分支,用git stash保存工作状态。
  2. 切换到bug分支,修复完bug,commit
  3. 切换回feature分支,将bug分支与feature分支合并。用git stash pop还原工作状态。

现在我们已经修复了feature分支上的bug,假设master分支上也要修复相同的bug,怎么办?

不用那么麻烦的再做一遍修复bug、合并分支的工作,可以用git cherry-pick <commit_id>,它能把bug分支的commit操作直接"复制",然后commit到当前分支:

git switch master
git stash # 如果master分支上有待提交的变更,那么必须先保存状态
git cherry-pick 8436eb8 #8436eb8是修复bug的<commit_id>,可用git log查看为8436eb8 (issue-101) fix bug 101
git stash pop

(选做)如果你需要保存多个工作状态,可以多次入栈。想恢复某个工作状态的时候,先用git stash list查看stash内容,再用git stash apply恢复到指定的工作状态:

git stash list # 查看stash内容
git stash apply stash@{0} #栈顶为stash@{0},栈顶的下一个为stash@{1}

注意:git stash apply并不会删除栈内的任何内容,但git stash pop将栈顶工作状态出栈的同时也会删除栈顶的内容。

小结:

  • git stash 暂存当前工作状态,等恢复现场后继续工作

  • git stash list 查看所有暂存的工作状态

  • git stash pop 恢复工作状态,删除栈顶内容

  • git stash apply 恢复工作状态,不删除栈内内容

  • git stash drop 删除栈顶内容

4. 远程仓库

在本章,你需要有一个GitHub账号,并且在GitHub上添加了一个远程仓库。远程仓库是指托管在互联网的版本库。

注意:远程仓库可以在你的本地主机上。 你完全可以在一个“远程”仓库上工作,而实际上它在你本地的主机上。词语“远程”未必表示仓库在互联网上的其它位置,而只是表示它在别处。为了区分这一概念,后文用远程仓库服务器/远程服务器表示GitHub托管的远程仓库。

参考:(官方文档)2.5 Git 基础 - 远程仓库的使用(博客)git远程仓库分支的各命令的具体解析

4.1 配置git

配置git的所有操作可以参考此文

设置用户名和邮箱。

git config --global user.email "your_email@example.com" # --global表示全局设置
git config --global user.name "your_name"

查看配置。

git config -l

将SSH Key添加到GitHub。

ssh-keygen -t rsa -C "your_email@example.com" # 结果显示:Your public key has been saved in /root/.ssh/id_rsa.pub
cat /root/.ssh/id_rsa.pub # 查看公钥并将其复制,之后粘贴到GitHub网页上

在GitHub网页中,点击个人头像 → Settings → SSH and GPG keys → New SSH key,添加公钥。

4.2 克隆远程仓库(git clone)

第2章提到,创建本地仓库的方法之一是克隆远程仓库。

git clone <url> #远程服务器的URL

当你克隆某个远程仓库后,默认情况下Git会自动设置本地master分支跟踪克隆的远程仓库的master分支(或其它名字的默认分支),并且默认设置远程仓库在本地的别名是origin

实际上克隆命令是git clone <repo>,表示克隆⼀个指定repo到本地,其指定repo可以是本地⽂件系统或者由HTTP或SSH指定的远程路径。

4.3 添加/删除远程仓库(git remote add/rm)

(1)添加远程仓库

实际就是给远程仓库服务器地址起一个本地的别名/简称(习惯命名为origin)。

将远程服务器地址<url> 映射为 本地对远程服务器的别名<shortname>,命令格式如下:

 git remote add <shortname> <url> #将<shortname>与<url>关联,以后就可以用<shortname>代替<url>

示例:

#进入GitHub远程仓库的网页,点击Code和SSH,查看SSH协议的<url>,例如git@github.com:nefu-ljw/ACM-Algorithm.git
git remote add origin git@github.com:nefu-ljw/ACM-Algorithm.git

这样以后,就可以在命令行中使用字符串origin代替整个git@github.com:nefu-ljw/ACM-Algorithm.git

(2)删除远程仓库

git remote rm <shortname> #删除别名<shortname>及其对应的远程服务器地址

一旦使用这种方式删除了一个远程仓库,那么所有和这个远程仓库相关的远程跟踪分支以及配置信息也会一起被删除。

4.4 推送远程仓库(git push)

将本地仓库的分支<branch>推送到远程服务器<remote>,命令格式如下:

git push <remote> <branch> # 这里的<remote>就是之前添加的远程服务器地址的别名<shortname>

如果是第一次将本地分支master推送到远程服务器origin,则使用-u选项指定一个默认服务器:

git push -u origin master #之后会产生远程分支origin/master,并且本地分支master跟踪远程分支origin/master

如果当前分支与多个服务器存在跟踪关系,可以使用-u选项指定一个默认服务器,以后就可以不加任何参数直接使用git push

当你和其他人在同一时间克隆,但他们先推送然后你再推送,那么你的推送会被拒绝,因为你要推送的本地分支落后于远程服务器的分支。你必须先抓取(fetch)他们的工作并将其合并(merge)进你的工作后才能推送。或者用-force选项强行推送你的本地库,但是这样会使得远程服务器丢失其他人的变更,请慎重使用。

注:git push需要输入用户名和密码,输入密码时可能出现要求输入token的问题,可参考此文解决。

4.5 抓取/拉取远程仓库(git fetch, git pull)

如果远程服务器的数据有更新,要将这些新数据下载到本地,可以用git fetchgit pull

(1)抓取远程仓库(fetch

a. 访问远程服务器,从中抓取所有所有分支的更新:

git fetch <remote> #从远程仓库服务器<remote>中抓取所有分支的更新

b. 如果只想取回特定分支的更新,可以指定分支名:

git fetch <remote> <branch> #例如git fetch origin master

(2)拉取远程仓库(pull,即fetch+merge

a. 取回远程服务器<remote>某个分支<remote_branch>的更新,再与本地的指定分支<local_branch>合并:

git pull <remote> <remote_branch>:<local_branch>

示例,取回远程服务器origin的分支feature,与本地分支master合并:

git pull origin feature:master

b. 如果远程分支是与本地当前分支合并,则可省略:<local_branch>,如下:

git pull origin feature #等价于先fetch,再merge

这会让featureFETCH_HEAD中临时保留一个副本,并更新远程跟踪分支origin/feature

等价于,先fetch,再merge

git fetch origin #将远程服务器origin的所有分支取回到本地,比如远程分支feature的本地副本为origin/feature
git merge origin/feature #将远程分支origin/feature合并到当前分支

c. 如果当前分支与远程分支存在跟踪关系,则可省略<remote_branch>:<local_branch>,如下:

git pull origin #自动尝试合并 当前分支跟踪的远程分支→当前分支

经常有人在不给任何参数的情况下使用git pull,这一般相当于git pull origin

(3)抓取(fetch)与拉取(pull)的区别

  • git fetch:获取远程服务器的更新到本地,不自动合并到本地分支。

  • git pull:获取远程服务器的更新到本地,自动合并到本地分支。

4.6 查看远程仓库(git remote -v/show)

如果想查看已经配置的远程仓库服务器,可以用git remote列出已指定的每一个远程服务器的简写。

你也可以指定选项-v,将会显示远程服务器使用的简写<shortname>与其对应的<url>

git remote -v

运行命令后,将会显示可以进行抓取(fetch)和推送(push)的地址:

origin git@github.com:nefu-ljw/ACM-Algorithm.git (fetch)

origin git@github.com:nefu-ljw/ACM-Algorithm.git (push) (注:如果没有推送权限,则看不到push的地址)

如果你已经克隆了仓库,那么至少能看到origin,这是Git给你克隆的远程服务器的默认别名。

如果想要查看某一远程仓库<remote>的更多信息,可以用:

git remote show <remote>

这个命令列出了当你在特定的分支上执行git push会自动地推送到哪一个远程分支。 它也同样地列出了哪些远程分支不在你的本地,哪些远程分支已经从服务器上移除了, 还有当你执行git pull时哪些本地分支可以与它跟踪的远程分支自动合并。

4.7 查看/跟踪/删除 本地分支与远程分支(git branch -a/-vv/–track/–set-upstream-to/-dr, git push origin --delete)

(1)查看本地分支和远程分支:

git branch -a

例如,显示有本地分支feature、远程分支origin/feature,则表示你在本地库中拥有masterorigin/master,其中origin/master代表远程库中master分支的本地副本。

(2)查看本地分支和远程分支的跟踪关系:

git branch -vv

(3)设置跟踪关系,要使得本地分支feature跟踪远程分支origin/feature,有以下方法:

a. git branch(注意:新版本已弃用选项--set-upstream,请使用--track--set-upstream-to

  • 选项--track。适合用于还没有本地分支,它会自动创建一个本地分支并指定它跟踪指定远程分支,完整命令如下:
git branch --track feature origin/feature 

运行以上命令后,会创建分支feature(HEAD位置不变),并且feature的位置到远程分支origin/feature

  • 选项--set-upstream-to。适合用于已有本地分支,它能指定一个已经存在的本地分支跟踪指定远程分支,完整命令如下:
#指定本地分支feature跟踪远程分支origin/feature
git branch --set-upstream-to=origin/feature feature #指定分支feature可省略,若省略则默认为当前分支

如果先用git switch -c feature创建并切换到分支feature(HEAD位置改变);再运行以上命令,则feature的位置不会到远程分支origin/feature

b. git switch

#可以用-c自动从同名的远程分支创建新分支,并且建立跟踪;--track是默认选项,可省略
git switch -c feature --track origin/feature 
#或:git switch -c feature origin/feature
#或:git checkout -b feature origin/feature
#或:git switch feature
#或:git checkout feature

运行以上命令后,会创建并切换到分支feature(HEAD位置改变),并且feature的位置到远程分支origin/feature

(4)删除远程服务器上的远程分支origin/feature:

git push origin --delete feature #之后可在Github远程仓库上看到无feature分支

(5)删除本地跟踪的远程分支origin/feature:

git branch -dr origin/feature #删除本地的远程分支副本

(6)删除本地分支feature:

git branch -d feature

4.8 多人协作(push被拒绝、pull自动合并失败、解决冲突)

本节对多人协作的操作过程进行示例。事先说明,本节会多次用到查看命令:

  • 查看提交历史和分支情况:git log --oneline --all
  • 查看本地分支的跟踪关系:git branch -vv

假设已经用git remote add origin git@github.com:nefu-ljw/ACM-Algorithm.git添加了远程服务器origin。

假设现在有两台主机(用两个工作路径模拟),A和B,最开始只有A上有本地分支master和远程分支origin/master,并且它们已经建立了跟踪关系。

Step 1: 在A上操作(工作路径/opt/workspace/learngit):

git switch -c feature #之后就会多一个本地分支feature
touch env.txt & echo "env" >> env.txt
git add env.txt
git commit env.txt -m "add env.txt"
git push -u origin feature #之后就会多一个远程分支origin/feature;加上-u选项,则feature将会跟踪origin/feature

查看提交历史和分支情况:

1f776ca (HEAD -> feature, origin/feature) add env.txt

f7a06a2 (origin/master, master) add merge

查看本地分支的跟踪关系:

* feature 1f776ca [origin/feature] add env.txt

  master  f7a06a2 [origin/master] add merge

Step 2: 在B上操作(工作路径/opt/workspace/ACM-Algorithm):

git clone git@github.com:nefu-ljw/ACM-Algorithm.git /opt/workspace/ACM-Algorithm #指定克隆到本地路径(自选)
cd /opt/workspace/ACM-Algorithm #进入刚才克隆指定的git工作路径,把它作为B的工作路径

查看提交历史和分支情况:

1f776ca (origin/feature) add env.txt

f7a06a2 (HEAD -> master, origin/master, origin/HEAD) add merge

查看本地分支的跟踪关系:

* master f7a06a2 [origin/master] add merge

可以看到,与A的本地仓库相比,B少了一个本地分支feature,多了一个远程分支origin/HEAD,并且当前HEAD指向本地分支master。本地分支master已经跟踪了远程分支origin/master。


关于origin/HEAD的解释:

origin/HEAD表示远程服务器上的默认分支,即你正在调用的远程库origin中的HEAD。**origin/HEAD是一个本地引用,代表远程库中HEAD的本地副本。**你在本地仓库中切换分支时,只是本地HEAD的移动,不会影响origin/HEAD。

参考:Stack Overflow - How does origin/HEAD get set?

origin/HEAD可用git remote set-head origin -d删掉,也可用git remote set-head origin master设置origin/HEAD指向origin/master


克隆之后,远程有origin/feature分支,本地却没有feature分支,那么创建和切换到feature分支,并建立跟踪:

git switch -c feature origin/feature

查看提交历史和分支情况(成功创建和切换feature分支):

1f776ca (HEAD -> feature, origin/feature) add env.txt

f7a06a2 (origin/master, origin/HEAD, master) add merge

查看本地分支的跟踪关系(成功建立跟踪feature→origin/feature):

* feature 1f776ca [origin/feature] add env.txt
master f7a06a2 [origin/master] add merge

在B上继续操作,修改env.txt,并进行commitpush

echo "B changed env.txt" >> env.txt
git add env.txt
git commit env.txt -m "B changed env.txt"
git push origin feature #-u可以不写,因为feature已经跟踪origin/feature

Step 3: 在A上操作,恰好也修改了env.txt,进行了commitpush,但是A的push在刚才B的push之后:

echo "A changed env.txt" >> env.txt
git add env.txt
git commit env.txt -m "A changed env.txt"
git push origin feature #push被拒绝

然而,push更新被拒绝:

! [rejected] feature -> feature (fetch first)

error: 推送一些引用到 ‘github.com:nefu-ljw/ACM-Algorithm.git’ 失败

提示:更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外一个仓库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更(如 ‘git pull …’)。详见 ‘git push --help’ 中的 ‘Note about fast-forwards’ 小节。

那么按照提示,用 ‘git pull …’ 取得远程服务器origin的所有更新到本地,并且自动合并origin/feature到当前分支feature:

git pull origin feature #等价于:git fetch origin 且 git merge origin/feature

来自 github.com:nefu-ljw/ACM-Algorithm
* branch feature -> FETCH_HEAD
自动合并 env.txt
冲突(内容):合并冲突于 env.txt
自动合并失败,修正冲突然后提交修正的结果。

提示我们"合并冲突于env.txt",那查看一下env.txt:

env

<<<<<<< HEAD

A changed env.txt

=======

B changed env.txt

>>>>>>> 435cb9a75b999d9e127cc9cad13a048674dbb6cb

提示我们"自动合并失败",那么手动解决冲突,修改env.txt为:

env

First, B changed env.txt

Second, A changed env.txt

然后再提交修正的结果,进行add, commit, push即可:

git add env.txt
git commit -m "fixed the conflict of A and B on env.txt"
git push origin feature #这次就push成功了

git log --oneline --all --graph查看历史提交和分支合并情况如下:

* 0055c62 (HEAD -> feature, origin/feature) fixed the conflict of A and B on env.txt

|\

| * 435cb9a B changed env.txt

* | 8488b5e A changed env.txt

|/

* 1f776ca add env.txt

* f7a06a2 (origin/master, master) add merge

最后,我们总结一下多人协作的操作过程:

  1. A创建env.txt,并进行commitpush
  2. B克隆A刚才push的远程服务器,然后修改了env.txt,并进行commitpush
  3. A恰好也修改了env.txt,并进行commitpush,然而push失败。用pull获取更新的数据和尝试自动合并,然而自动合并失败,需要解决冲突。手动解决冲突后,即可顺利进行commitpush

其他操作

配置别名(偷懒神器)

参考

如果你觉得git log --color --graph --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit这个命令太长了,那么可以将log之后的那一长串配置为别名lg

git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

之后,你就可以用git lg来直接代替那一长串的命令了!(注意,命令还是要以git开头的,lg代替的只是后面那一长串)

配置Git的时候,加上--global是针对当前用户(终端用户)起作用的,如果不加,那只针对当前的仓库起作用。

怎么查看配置文件,它们放在哪?

  • 查看当前用户的配置信息: git config --global --list ,当前用户的global配置在用户主目录下的.gitconfig

  • 查看当前仓库的配置信息:git config --local --list ,当前仓库的local配置在仓库路径下的 .git/config

每个仓库的Git配置文件都放在.git/config文件中。别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。

当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中。配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。

附赠资料

Git Cheat Sheet(常用命令的小册子):https://liaoxuefeng.gitee.io/resource.liaoxuefeng.com/git/git-cheat-sheet.pdf

标签(tag)、变基(rebase)这两章本文就不写了,大家参考廖雪峰教程即可。


2021.10.24 程序员节快乐!

上一篇:13运动状态-程序编写


下一篇:stm32生成bin文件并添加CRC校验到文件末尾