Git - 如何压缩对忽略文件的更改而不会丢失这些更改? [英] Git - How do I squash changes to ignored files without losing those changes?

查看:107
本文介绍了Git - 如何压缩对忽略文件的更改而不会丢失这些更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用git来保存一段时间内应用程序使用的实际依赖关系的历史记录,其保真度比我能从包管理器获得的要高。



我正在使用这些分支:




  • master:仅限源代码。依赖于 .gitignore

  • 构建:源代码和依赖项
  • build- $ TIMESTAMP:用于强制提交忽略文件的临时分支



此脚本 build-release.sh $ b $ $ p $ DEV_MODULES =mocha chai bower coffeelint
BUILT_FILES =node_modules build
b DATE = $(date)
TIMESTAMP = $(date +%s)
BRANCH = $(git rev-parse --abbrev-ref HEAD)

#用当前的依赖和二进制文件创建一个临时分支
npm卸载$ DEV_MODULES
git checkout -b build- $ TIMESTAMP
git add --all --force $ BUILT_FILES
git commit -m从$ BRANCH复制$ BUILT_FILES

#将临时分支合并到构建分支中
git branch build || echo构建分支已经存在
git checkout构建 - 强制
git合并构建 - $ TIMESTAMP --strategy =子树-m构建自$ DATE
git分支-D构建 - $ TIMESTAMP

#恢复原始分支
git checkout $ BRANCH
git checkout build - $ BUILT_FILES
git rm -r --cached $ BUILT_FILES

其中的一个工作原理,给我一个有用的视图,说明从一个发行版到另一个发行版对源代码,依赖项和二进制文件的更改下一个:





但需要的提交次数是其中的两倍。我希望树看起来像这样:





如何将复制构建文件提交与构建为提交结合起来?



当我尝试 git merge --squash 时,它最终会出现在 build 上的状态,而不是该状态位于 build- $ TIMESTAMP 上,这是不正确的(我想将更改导入被忽略的文件,但合并似乎没有语言来执行此操作)。当我尝试 git rebase --onto build build- $ TIMESTAMP 我失去了新提交的血统。



我只想记录我在 build-$ TIMESTAMP 分支上获得的确切文件,但是同时使用 build 然后将 build 分支指向该提交。

解决方案

这是简单直接的管道领域。您正在使用瓷器命令,这是在git内容跟踪器核心的基础上构建的源代码控制系统,它可以让瓷器变成您想做的事情,但直接与内容跟踪器直接交流会更简单。



要获得最简单的阅读内容,即希望构建分支记录当前结帐的快照以及选择什么内容$ BUILT_FILES路径/目录,它是

 #knobs 
DEV_MODULES =mocha chai bower coffeelint
BUILT_FILES =node_modules build
DATE = $(日期)

#清理我们不在乎的东西
npm卸载$ DEV_MODULES

#记录当前结帐加上$ BUILT_FILES到`build`分支
git add --all --force $ BUILT_FILES
build =` git rev-parse -q --verify build`
git update-ref refs / heads / build $(
git commit-tree $ {build:+ - p $ build} -p HEAD \
-m从$ DATE开始构建\
`git write-tree`


#重置索引到HEAD
git reset#`git read- tree HEAD`会有同样的效果,也许更安静

作为一个快速概览或提醒可能是,git的对象数据库是散列码索引的键值存储。你可以通过它的类型和散列值向git寻求任何东西,它有意地从它的对象db中回想起它。索引只不过是一个平面文件,一个路径索引清单,显示对象数据库中的内容在哪条路径上,以及用于跟踪正在进行的操作的一些元数据和注释。 git add 路径中的内容只是将对象数据库中的内容和内容的哈希放在该路径的索引条目中。



(这里有点咆哮,如果不是在鼓吹的心情就跳过)要理解的是git完全是残酷的具体。关于对象数据库之外的存储库的一切都是纯粹的约定。 git checkout 使 HEAD 指的是纯粹按照惯例签出的提交。你可以在 git read-tree -um 附近实现 git checkout HEAD 设置为您从中获取该树的提交。 git commit 使 HEAD 是纯粹按照惯例提交的内容。您可以自己实现 git commit 作为 git commit-tree 和 git write-tree ,主要的额外操作是提供 HEAD 作为父项,并且tu opdate HEAD 到新的提交。名称 HEAD 本身就是传统的。构建这些内容的内容跟踪器无需关心 HEAD ,或者分支和标签之间的区别,或者任何类型的内容。由于(a)不需要抽象化,内容模型已经完全符合要求,(b)git的整个要点是核心缺乏抽象:它是愚蠢的,愚蠢的, 。


I want to use git to keep a historical record of the actual dependencies an application has used over time, with higher fidelity than I can get from the package manager.

I am using these branches:

  • master: source code only. dependencies in .gitignore
  • build: source code and dependencies
  • build-$TIMESTAMP: temporary branch used to force commit of ignored files

And this script, build-release.sh:

DEV_MODULES="mocha chai bower coffeelint"
BUILT_FILES="node_modules build"
DATE=$(date)
TIMESTAMP=$(date +"%s")
BRANCH=$(git rev-parse --abbrev-ref HEAD)

# create a temporary branch with the current dependencies and binaries
npm uninstall $DEV_MODULES
git checkout -b build-$TIMESTAMP
git add --all --force $BUILT_FILES
git commit -m "copy $BUILT_FILES from $BRANCH"

# merge the temporary branch into the build branch
git branch build || echo "build branch already exists"
git checkout build --force
git merge build-$TIMESTAMP --strategy=subtree -m "Build as of $DATE"
git branch -D build-$TIMESTAMP

# restore the original branch
git checkout $BRANCH
git checkout build -- $BUILT_FILES
git rm -r --cached $BUILT_FILES

Which works, and gives me a useful view of changes to source, dependencies, and binaries from one release to the next:

But it takes twice as many commits as necessary. I want the tree to look like this:

How can I combine the "copy built files" commit with the "build as of" commit?

When I try to git merge --squash, it ends up with the state that was on build instead of the state that was on build-$TIMESTAMP, which is incorrect (I want to import changes to ignored files, but merge seems to have no language to do this). When I try to git rebase --onto build build-$TIMESTAMP I lose the parentage of the new commit.

I just want to record the exact files I get on the build-$TIMESTAMP branch, but with both the build and master branches as parents, then point the build branch to that commit.

解决方案

This is straightforward plumbing territory. You're using "porcelain" commands, the source-control system built on top of git's content-tracker core, in ways that happen to winkle that porcelain into doing what you want, but it's much simpler to just talk to the content tracker directly.

For the simplest reading of what's in your question, namely that you want your "build" branch to record snapshots of the current checkout along with a selection of what's in the the "$BUILT_FILES" paths/directories, it's

# knobs
DEV_MODULES="mocha chai bower coffeelint"
BUILT_FILES="node_modules build"
DATE=$(date)

# clean out stuff we don't care about
npm uninstall $DEV_MODULES

# record current checkout plus "$BUILT_FILES" to `build` branch
git add --all --force $BUILT_FILES
build=`git rev-parse -q --verify build`
git update-ref refs/heads/build $(
        git commit-tree ${build:+-p $build} -p HEAD \
                -m "build as of $DATE" \
                `git write-tree`
)

# reset index to HEAD
git reset  # `git read-tree HEAD` will have the same effect, perhaps more quietly

As a quick overview or reminder as the case may be, git's object database is a hashcode-indexed key-value store. You ask git for anything by its type and hash, it obligingly regurgitates exactly that from its object db. The index is nothing more than a flat file, a path-indexed manifest, showing what content in the object db goes at what path, along with some metadata and notes for tracking in-flight operations. git adding content at a path just puts the content in the object db and the content's hash in the index entry for that path.

(somewhat of a rant here, skip if not in the mood to be preached at) The thing to understand is that git is utterly, brutally concrete. Everything about a repository beyond the object db is pure convention. git checkout makes HEAD refer to the commit you checked out purely by convention. You can implement git checkout as an extremely thin wrapper around git read-tree -um -- the chief extra operation being to set HEAD to the commit you got that tree from . git commit makes HEAD the parent of what you're committing purely by convention. You can implement git commit yourself as an extremely thin wrapper around git commit-tree and git write-tree, the chief extra operation being to supply HEAD as a parent and tu opdate HEAD to the new commit. The name HEAD is itself purely conventional. The content tracker on which those are built couldn't care less about HEAD, or the distinction between branches and tags, or anything of the sort. The conventions are intentionally, aggressively and brutally simple, because (a) there's no need for abstraction, the content model already matches the requirements perfectly, and (b) the whole point of git is the lack of abstraction at the core: it's "stupid".

这篇关于Git - 如何压缩对忽略文件的更改而不会丢失这些更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆