1. .gitignore常见项目添加

1.1 .gitignore模板

.gitignore针对每个语言都有对应的模板,在GitHub创建项目时就可以选择(你可以在GitHub提供的.gitignore模板大全中找到它)。如Python语言的.gitignore模板如下:

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class # C extensions
*.so # Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST # PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec # Installer logs
pip-log.txt
pip-delete-this-directory.txt # Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/ # Translations
*.mo
*.pot # Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal # Flask stuff:
instance/
.webassets-cache # Scrapy stuff:
.scrapy # Sphinx documentation
docs/_build/ # PyBuilder
target/ # Jupyter Notebook
.ipynb_checkpoints # IPython
profile_default/
ipython_config.py # pyenv
.python-version # pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/ # Celery stuff
celerybeat-schedule
celerybeat.pid # SageMath parsed files
*.sage.py # Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/ # Spyder project settings
.spyderproject
.spyproject # Rope project settings
.ropeproject # mkdocs documentation
/site # mypy
.mypy_cache/
.dmypy.json
dmypy.json # Pyre type checker
.pyre/

1.2 添加更多的.gitignore项目

但是这些往往是不够的的。如我们在Mac系统下用VSCode开发,那么常常还需要添加以下项目:

# IDE - VSCode
.vscode/ # OS generated files
.DS_Store

其中.vscode/表示忽略.vscode这个包含项目配置文件的隐藏目录(注意是包括目录一起忽略,这个和Linux下诸如cp test/ .这类命令的语义有区别,参加我的博客《Linux:文件解压、复制和移动的若干坑》),.DS_Store表示忽略掉Mac操作系统下存储目录自定义属性的隐藏文件。

此外,我们再以机器学习相关的项目为例子,数据(放在data目录下)和模型(放在model目录下)通常异常巨大,我们并不想将它们放到项目文件夹下,因此我们可能倾向于添加如下的项目:

# data files
data/* # model files
model/*

data/*model/*语义上表示忽视data目录下所有文件与model目录下所有文件及子目录(不包括datamodel目录本身)。但是我们会发现,实际上空的datamodel目录并没有成功git add到项目中

(base) orion-orion@MacBook-Pro Learn-Git % git add data
(base) orion-orion@MacBook-Pro Learn-Git % git add model
(base) orion-orion@MacBook-Pro Learn-Git % git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits) nothing to commit, working tree clean

这是因为空目录不会称为Git版本控制系统跟踪(track)。但是如果我们想保存datamodel的目录架构呢?很简单,我们只需要在datamodel目录下添加.gitkeep目录即可,然后将在.gitignore文件中对.gitkeep进行反选(即不忽视):

# data files
data/*
!data/.gitkeep # model files
model/*
!model/.gitkeep

可以看到由于隐藏文件的存在,现在空目录能够正常git add了:

(base) orion-orion@MacBook-Pro Learn-Git % git add data
(base) orion-orion@MacBook-Pro Learn-Git % git add model
(base) orion-orion@MacBook-Pro Learn-Git % git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits) Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: data/.gitkeep
new file: model/.gitkeep

但是需要注意,如果这样写就没用:

# data files
data/
!data/.gitkeep

因为data/表示将data目录本身也忽略了,Git根本就不会去查看该目录,以致.gitkeep文件也就不起作用了。

额外提一下,如果我们仅仅希望忽略掉data目录下的.csv文件,可以这样写:

# data files
data/*.csv

2. 移除已暂存(staged)的文件

2.1 关于跟踪与暂存

在Git中,一个文件可能在这三种区域中:工作目录(Working Directory),暂存区(Staging Area,也称索引index),Git仓库(可视为一棵提交树committed tree)。三者关系如下图所示:

当我们将文件添加到项目目录中时,我们其实是在将其添加到工作目录中。

一旦一个目录或文件被git add了一次,那么它就会被跟踪(track)并加入暂存区。此后再对其进行修改,Git会提醒你Changes not staged for commitmodified: README.md,需要再次运行git add将其暂存(staged):

(base) orion-orion@MacBook-Pro Learn-Git % echo "new version" > README.md
(base) orion-orion@MacBook-Pro Learn-Git % git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits) Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md no changes added to commit (use "git add" and/or "git commit -a")

而文件的所谓的未跟踪(untracked)、未修改(unmodified)、已修改(modified)、已暂存(staged)四种状态的关系如下所示:

2.2 清除已暂存的文件

现在假设我们搞忘了编写.gitignore,然后已经用了git add -Agit add .命令目录下所有文件及子目录都暂存了(在Git 2.0中git add -Agit add .命令等效)。而其中有很大的日志文件或一些诸如*.a的编译文件,我们如何将这些文件从暂存区域移除以取消跟踪呢?可以用git rm --cached命令完成此项工作,如:

git rm --cached README.md

注意要带上选项--cached,而不仅仅是git rmgit rm除了从暂存区域移除外,还会将磁盘上的文件也一起删了。关于参数选项可以参见我的博客《Linux:可执行程序的Shell传参格式规范 》

使用该命令效果如下:

(base) orion-orion@MacBook-Pro Learn-Git % git rm --cached README.md
rm 'README.md'
(base) orion-orion@MacBook-Pro Learn-Git % git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits) Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: README.md

注意到Changes to be committed:deleted: README.md,这说明当我们使用git rm --cached并commit后, 相关的文件还会被从committed tree中移除。如果我们只想移除出暂存区,可以使用下列命令:

 git reset HEAD README.md

该命令等同 git reset --mixed HEAD README.md(默认参数为--mixed,还有个参数为--hard,我们放在3.3节讲)。使用后效果如下:

(base) orion-orion@MacBook-Pro Learn-Git % git reset HEAD *.md
Unstaged changes after reset:
M README.md
(base) orion-orion@MacBook-Pro Learn-Git % git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits) Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md no changes added to commit (use "git add" and/or "git commit -a")

注意到Changes not staged for commit: modified: README.md。说明该命令只是将README.md移除暂存区,但是上次对README.md的commit还在(即撤销最近的一次commit之后的变化)。

如果要递归地将当前目录下的所有文件及子目录移除出暂存区(与commit tree),可以这样写:

git rm -r --cached . 

注意这个命令非常危险和暴力,一般还是建议指定具体的目录或文件名。

3. 追加与撤销git commit操作

3.1 commit历史查看

git log命令可以看到项目的git commit历史:

(base) orion-orion@MacBook-Pro Learn-Git % git log
commit 37a35d36eaf8b56c9e7b719c3c7576f3251cee36 (HEAD -> main)
Author: orion-orion <orion-orion@foxmail.com>
Date: Mon May 23 14:15:21 2022 +0800 modify .gitignore commit ab7bf6e2c400c8d775cc3bc56928c7748c63c8f8
Author: orion-orion <orion-orion@foxmail.com>
Date: Mon May 23 10:08:08 2022 +0800 add .gitignore commit 146c68e12fd2aebed8b38dd5cf95621f800fe4aa (origin/main, origin/HEAD)
Author: 猎户座 <46917784+orion-orion@users.noreply.github.com>
Date: Sun May 22 09:48:22 2022 +0800 Initial commit

默认不用任何参数的话,git log会按提交时间列出所有的更新,最近的更新排在最上面。 正如你所看到的,这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址(如果电子邮件名为<46917784+orion-orion@users.noreply.github.com>,说明你在GitHub中将邮件名设置为私有的了,需要去修改一下)、提交时间以及提交说明。

3.2 追加commit操作

现在我们又对.gitignore进行了修改。但是我们不想又commit一次,而想将其合并在最后一次的modify .gitignore里,使commit记录更为精简。我们可以用以下命令:

(base) orion-orion@MacBook-Pro Learn-Git % git add .gitignore
(base) orion-orion@MacBook-Pro Learn-Git % git commit --amend

并在commit信息的编辑界面写入modify .gitignore

modify .gitignore

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Mon May 23 14:15:21 2022 +0800
#
# On branch main
# Your branch is ahead of 'origin/main' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# modified: .gitignore
# new file: data/.gitkeep
# new file: model/.gitkeep
#
# Changes not staged for commit:
# modified: README.md
# :wq!

可以看到总的commit记录没变,所显示的最后一次commit记录的时间也没变,但新的修改已经追加进去了(SHA-1 校验和发生了变化):

(base) orion-orion@MacBook-Pro Learn-Git % git log
commit a0dfeff409494165bdff60c27b24fad2bc0ed0ad (HEAD -> main)
Author: orion-orion <orion-orion@foxmail.com>
Date: Mon May 23 14:15:21 2022 +0800 modify .gitignore commit ab7bf6e2c400c8d775cc3bc56928c7748c63c8f8
Author: orion-orion <orion-orion@foxmail.com>
Date: Mon May 23 10:08:08 2022 +0800 add .gitignore commit 146c68e12fd2aebed8b38dd5cf95621f800fe4aa (origin/main, origin/HEAD)
Author: 猎户座 <46917784+orion-orion@users.noreply.github.com>
Date: Sun May 22 09:48:22 2022 +0800 Initial commit

3.3 撤销git commit操作

现在我们想撤销git commit的操作。我们回到git reset命令。不过现在我们需要使用git reset --hard方法:

(base) orion-orion@MacBook-Pro Learn-Git %  git reset --hard HEAD^1
HEAD is now at ab7bf6e add .gitignore
(base) orion-orion@MacBook-Pro Learn-Git % git log
commit ab7bf6e2c400c8d775cc3bc56928c7748c63c8f8 (HEAD -> main)
Author: orion-orion <orion-orion@foxmail.com>
Date: Mon May 23 10:08:08 2022 +0800 add .gitignore commit 146c68e12fd2aebed8b38dd5cf95621f800fe4aa (origin/main, origin/HEAD)
Author: 猎户座 <46917784+orion-orion@users.noreply.github.com>
Date: Sun May 22 09:48:22 2022 +0800 Initial commit

命令中的HEAD^1意思为将commit记录回退到上上次提交后的状态,HEAD^2以此类推。

不过大家必须注意,--hard 标记是reset命令唯一的危险用法,它也是 Git 会真正地销毁数据的仅有的几个操作之一。 其他任何形式的reset调用都可以轻松撤消,但是--hard选项不能,因为它强制覆盖了工作目录中的文件。

参考

Git技法:.gitignore、移除暂存与撤销修改的更多相关文章

  1. 小丁带你走进git的世界二-工作区暂存区分支

    小丁带你走进git的世界二-工作区暂存区分支 一.Git基本工作流程 1.初始化一个仓库 git  init git  clone git仓库分为两种情况: 第一种是在现有项目或目录下导入所有文件到 ...

  2. 【Git】(1)---工作区、暂存区、版本库、远程仓库

    工作区.暂存区.版本库.远程仓库 一.概念 1.四个工作区域 Git本地有四个工作区域:工作目录(Working Directory).暂存区(Stage/Index).资源库(Repository或 ...

  3. Git学习(三)——暂存区、远程仓库、增删改管理

    一.工作区和暂存区 工作区(Working Directory) 就是在你的电脑里能看到的目录 版本库(Repository) 工作区中的一个隐藏目录.git,这个不算工作区,而是Git版本库.Git ...

  4. Git教程之工作区和暂存区(5)

    工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区:

  5. git教程:工作区和暂存区

    Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 先来看名词解释. 工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工 ...

  6. Git教程之工作区和暂存区

    工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区:

  7. 版本控制Git(1)——理解暂存区

    一.svn和Git的比较 我们都知道传统的源代码管理都是以服务器为中心的,每个开发者都直接连在中间服务器上, 本地修改,然后commit到svn服务器上.这种做法看似完美,但是有致命的缺陷. 1. 开 ...

  8. git学习笔记 ---工作区和暂存区

    Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 先来看名词解释. 工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工 ...

  9. [git 学习篇]工作区和暂存区

    1 工作区,就是目录/User/my./learngit 2 版本库 工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库. liuzhipeng@exdroid43:~/pad/pad- ...

随机推荐

  1. 让子弹飞,零成本让你的网站更快一点,boxopened http/3 (QUIC) 协议实战

    最近HTTP-over-QUIC 协议被正式命名为 HTTP/3,协议带来的最大改变是协议底层将采用UDP协议,而不再是TCP协议,这样的好处吗,就是更低时延,更好的拥塞控制,更精确的RTT时间,更高 ...

  2. Android CheckBox的监听事件

    1.在xml文件中定义CheckBox,一定要定义id <CheckBox android:id="@+id/beijing" android:layout_width=&q ...

  3. add jars、add external jars、add library、add class folder的区别

    add external jars = 增加工程外部的包add jars = 增加工程内包add library = 增加一个库add class folder = 增加一个类文件夹add jar是表 ...

  4. 记一次dotnet拆分包,并希望得大佬指点

    记一次dotnet拆分包,并希望得大佬指点 之前做了一个用于excel导入导出的包, 定义了一些接口, 然后基于 NPOI EPPlus MiniExcel 做了三种实现 接口大概长下面这样(现在可以 ...

  5. caioj 1000: [视频]整数运算[水题]

    题目大意:输入两个整数a和b,输出他们的和. 题解:水题不用题解,简单看一下就知道了-- 代码: #include <cstdio> int a, b; int main() { whil ...

  6. 面向对象编程-终结篇 es6新增语法

    各位,各位,终于把js完成了一个段落了,这次的章节一过我还没确定下面要学的内容可能是vue也可能是前后端交互,但无论是哪个都挺兴奋的,因为面临着终于可以做点看得过去的大点的案例项目了,先憋住激动地情绪 ...

  7. Golang 源码解读 01、深入解析 strings.Builder、strings.Join

    strings.Builder 源码解析. 存在意义. 实现原理. 常用方法. 写入方法. 扩容方法. String() 方法. 禁止复制. 线程不安全. io.Writer 接口. 代码. stri ...

  8. 从实例学习 Go 语言、"基础与进阶" 学习笔记及心得体会、Go指南

    第一轮学习 golang "基础与进阶"学习笔记,Go指南练习题目解析.使用学习资料 <Go-zh/tour tour>.记录我认为会比较容易忘记的知识点,进行补充,整 ...

  9. Dom基础(三):事件冒泡,事件委托(事件代理)和事件捕获

    javascript中的addEventListener(事件名,回调,布尔) 其中第三个参数默认为false-事件冒泡,true为事件捕获 二者区别: 事件冒泡:目标元素事件先触发,然后父元素事件触 ...

  10. C#二次开发BIMFACE系列61 File Management文件管理服务接口二次开发及实战详解

    系列目录     [已更新最新开发文章,点击查看详细] 在我的博客<C#二次开发BIMFACE系列61 File Management文件管理服务接口二次开发及实战详解>最后列出了 Fil ...