• Git技法:.gitignore、移除暂存与撤销修改


    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操作/修改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(如果要修改commit信息,此处改成其它的即可):

    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
    

    我们在暂存区没有任何更新的情况下,也可以使用git commit --amend命令来单纯修改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选项不能,因为它强制覆盖了工作目录中的文件。

    参考


    __EOF__

  • 本文作者: 猎户座
  • 本文链接: https://www.cnblogs.com/orion-orion/p/16303308.html
  • 关于博主: 本科CS系蒟蒻,机器学习半吊子,并行计算混子。
  • 版权声明: 欢迎您对我的文章进行转载,但请务必保留原始出处哦(*^▽^*)。
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    23种设计模式(10)——门面模式
    2023华为杯研究生数学建模竞赛选题建议+初步分析
    SpringCloud——注册中心Eureka
    一次js请求一般情况下有哪些地方会有缓存处理?
    chrome中的一些调试工具
    C++ Reference: Standard C++ Library reference: C Library: cwchar: wcsncmp
    你知道BFD是什么吗?一文详解BFD协议原理及使用场景
    7-5 改写二分搜索算法
    图像处理: ImageKit.NET 3.0.10704 Crack
    unity工具类篇 时间间隔帮助类(天、时、分、秒) 下篇
  • 原文地址:https://www.cnblogs.com/orion-orion/p/16303308.html