如何在 git 中进行“仅本地提交”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13276909/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 07:43:44  来源:igfitidea点击:

How to do a "local-only commit" in git?

git

提问by nneonneo

I'm using git, and I'd like to be able to create a commit which isn't synced with the remote repository. Such a commit would have to "float" atop all other commits in the local repository to avoid affecting the history. I could use such a commit to store local-specific changes (configuration changes, debugging flags, local workarounds, etc.).

我正在使用 git,我希望能够创建一个不与远程存储库同步的提交。这样的提交必须“浮动”在本地存储库中的所有其他提交之上,以避免影响历史记录。我可以使用这样的提交来存储特定于本地的更改(配置更改、调试标志、本地解决方法等)。

Currently, I manually rebase when I commit to reorder the commit back to the top, and I push using HEAD^to avoid pushing the local changes. I've also considered putting the changes in the stash, but that's less convenient because it precludes normal use of the stash. Another alternative is to simply leave all those local changes unstaged, and use git add -pevery time I want to commit. However, with a large number of trivial local changes, that becomes a hassle.

目前,当我提交将提交重新排序回顶部时,我手动重新设置基准,并且我推送使用HEAD^以避免推送本地更改。我也考虑过将更改放入 stash,但这不太方便,因为它妨碍了 stash 的正常使用。另一种选择是简单地保留所有这些本地更改未暂存,并在git add -p每次我想提交时使用。然而,随着大量微不足道的局部变化,这变得很麻烦。

Here's an example of my current workflow:

这是我当前工作流程的示例:

My repository initially looks like

我的存储库最初看起来像

A---B---C---F master

where "F" is my floating commit.

其中“F”是我的浮动提交。

I make a commit:

我做出承诺:

A---B---C---F---D master

then git rebase -i HEAD~2to reorder:

然后git rebase -i HEAD~2重新排序:

A---B---C---D---F master

then git push remote HEAD~1...to push everything but the local Fcommit.

然后git push remote HEAD~1...推送除本地F提交之外的所有内容。

The change F contains changes to existing versioned files, and may contain an arbitrary amount of changes. (If I can make more than one commit "floating", that would be even better, since I could then separate my local changes).

更改 F 包含对现有版本化文件的更改,并且可能包含任意数量的更改。(如果我可以使多个提交“浮动”,那就更好了,因为我可以将我的本地更改分开)。

采纳答案by koljaTM

How about putting those changes into a local branch which you rebase/merge regularly from your main dev branch? That way there wouldn't be any danger of committing them upstream.

将这些更改放入您定期从主开发分支变基/合并的本地分支怎么样?这样就不会有任何将它们提交到上游的危险。

回答by Jonathan Tomer

So, it sounds like you want two things:

所以,听起来你想要两件事:

  • Some commits should be kept private (on a local branch, for example) and never pushed or merged when you pull; they should remain "after" the shared commits.

  • This should be mostly transparent to you; you want to work on master, and have the local branch maintained automatically. You just decide which commits should be local, and commands that interact with remote repositories will ignore those.

  • 一些提交应该是私有的(例如在本地分支上),并且在你拉取时永远不要推送或合并;它们应该在共享提交“之后”保留。

  • 这对您来说应该是大部分透明的;您想在 master 上工作,并自动维护本地分支。您只需决定哪些提交应该是本地的,与远程存储库交互的命令将忽略这些。

So you want to write a script (name it git-somethingand put it on your path, so it's an extra git command) to identify and deal with these commits. You need some trigger for the script to recognize the local commits by. The easy way to do this is to put a magic word in the commit description -- one you'll never use in a real/shared commit -- for the script to recognize. (If that's too flaky-sounding for you, you could also use a special file in the commit's tree, like .THIS_COMMIT_IS_LOCAL_ONLY; I haven't done that in the examples because it's a little harder.)

所以你想写一个脚本(命名git-something并把它放在你的路径上,所以它是一个额外的 git 命令)来识别和处理这些提交。您需要一些触发器来让脚本识别本地提交。做到这一点的简单方法是在提交描述中添加一个魔术词——一个你永远不会在真实/共享提交中使用的词——以便脚本识别。(如果这对你来说太古怪,你也可以在提交树中使用一个特殊的文件,比如.THIS_COMMIT_IS_LOCAL_ONLY;我在示例中没有这样做,因为它有点难。)

You'll need a command to make a local commit from the current index/workdir; this is easy, it just calls git commit $@ -m "__LOCAL_COMMIT_ONLY__"(that's an example; the point is it does something to mark the commit being created as local-only, and then defers to git commit). You'll also need a command to temporarily pop all the local commits, do some other git command (pull, push, fetch, merge, whatever), and then reapply the local commits. You will also use this command for creating local commits that you do intend to share, so that they always appear "under" the local-only commits in your history.

您需要一个命令来从当前索引/工作目录进行本地提交;这很简单,它只是调用git commit $@ -m "__LOCAL_COMMIT_ONLY__"(这是一个例子;关键是它做了一些事情来标记正在创建的提交为仅本地提交,然后推迟到 git commit)。您还需要一个命令来临时弹出所有本地提交,执行一些其他 git 命令(拉、推、取、合并等等),然后重新应用本地提交。您还将使用此命令来创建您打算共享的本地提交,以便它们始终显示在历史记录中仅本地提交的“下方”。

Here's one example script that gives you both in one:

这是一个示例脚本,可将两者合二为一:

#!/bin/sh
if [[  eq 'new' ]]; then
  shift
  exec git commit $@ -m "__LOCAL_COMMIT_ONLY__"
elif [[  eq

OLD_HEAD=$(git rev-parse HEAD)
OLD_REAL_HEAD="$(git rev-list HEAD --grep=__LOCAL_COMMIT_ONLY__ | tail -n1)^"
git reset --soft $OLD_REAL_HEAD
git $@
git rebase --onto HEAD $OLD_REAL_HEAD $OLD_HEAD

Now, assuming you call the script git-local, use git local newto create a new local-only commit from the index (or git local new -ato create on from modified files in the workdir), git local commit(the name is imperfect, sadly) to create a new "real" commit, git local pushto push, git local pullto pull, etc.

现在,假设您调用脚本git-local,用于git local new从索引创建新的仅本地提交(或git local new -a从工作目录中的修改文件创建),git local commit(名称不完美,遗憾的是)创建新的“真实”提交,git local push推,git local pull拉,等等。

The primary downside of this is that it requires you to remember that most commands now get prefixed with local. If you forget to do this once, you're a little bit hosed, but not too badly -- a quick git rebase -iwill allow you to easily move your local commits back to the top and you're off and running again. The biggest risk is that you accidentally use git pushinstead of git local pushand send all your private changes upstream, which will annoy everyone. For that, you might want to actually write a little wrapper script to invoke instead of git itself (call it ~/bin/gitand make sure ~/binis on your path):

这样做的主要缺点是它要求您记住大多数命令现在都以local. 如果你忘记这样做一次,你会有点受挫,但还不算太糟糕——快速git rebase -i将允许你轻松地将本地提交移回顶部,然后你又可以开始运行了。最大的风险是你不小心使用git push而不是git local push将你所有的私有更改发送到上游,这会惹恼所有人。为此,您可能希望实际编写一个小包装脚本来调用而不是 git 本身(调用它~/bin/git并确保~/bin在您的路径上):

#!/bin/sh
if [[  = 'push' ]]; then
  if /usr/bin/git rev-list HEAD --grep=__LOCAL_COMMIT_ONLY__ | grep -q .; then
    echo "Can't push with local changes still active!"
    echo "Try using `git local push' instead."
    exit 1
  fi
fi
exec /usr/bin/git "$@"

You could also make a pre-receivehook on the server that automatically rejects any commit containing __LOCAL_COMMIT_ONLY__in its message.

您还可以pre-receive在服务器上创建一个钩子,自动拒绝包含__LOCAL_COMMIT_ONLY__在其消息中的任何提交。

回答by Beyamor

At a previous job, everyone had their own local settingsbranch on which we committed our individual settings. This branch was based off master; any topic branches were branched off of settings. When topics were ready for integration, we rebased them onto master. This seems to be what @koljaTM is suggesting.

在以前的工作中,每个人都有自己的本地settings分支,我们在其中提交了我们的个人设置。这个分支是基于master; 任何主题分支都从settings. 当主题准备好集成时,我们将它们重新基于master. 这似乎是@koljaTM 的建议。

A---B---C  master
         \
          F  settings
           \
            D  topic

rebase --onto master settings topic

A---B---C  master
        |\
        | F  settings
         \
          D  topic

When new changes hit master, we would rebase settingsof master, then rebase any topics we were working on off of settings.

当新的变化打master,我们会重订settingsmaster,那么重订我们的工作就关闭任何主题settings

Admittedly, this isn't a one-step "leave this floating forever" solution, but it is clean and simple enough.

诚然,这不是一步“让这个永远漂浮”的解决方案,但它足够干净和简单。

回答by Luke

You could use a patch file, which can be generated from Git.

您可以使用补丁文件,该文件可以从 Git 生成。

# git diff > local.patch

When you want to commit, reverse patch:

当你想提交时,反向补丁:

# git apply -R local.patch

After the commit, restore patch:

提交后,恢复补丁:

# git apply local.patch

You just have to make sure only your local changes are on the working tree before creating the patch file. After you create the patch file, you could add it to .gitignoreso you don't commit it.

在创建补丁文件之前,您只需确保工作树上只有您的本地更改。创建补丁文件后,您可以将其添加到其中,.gitignore以免提交。

回答by kaeso

Assuming that you have the local-specific changes in a file that does not contain other changes that you need to commit, you can just ask git to ignore the changes in that file, via git update-index. In particular, you need either --assume-unchanged or --skip-worktree; while they basically do the same thing (mark the file as unchanged in git index), they have some very subtle peculiarities that are well described here.

假设您在一个不包含您需要提交的其他更改的文件中有特定于本地的更改,您可以通过git update-index要求 git 忽略该文件中的更改。特别是,您需要 --assume-unchanged 或 --skip-worktree;虽然它们基本上做同样的事情(在 git index 中将文件标记为未更改),但它们有一些非常微妙的特性,这里有很好的描述。

回答by Michael

If it is actual commits you are trying to prevent from going forward, you are on the right track - use

如果这是您试图阻止继续进行的实际提交,那么您就在正确的轨道上 - 使用

git rebase -i

git rebase -i

and reorder all your "personal commits" to be the latest commits, then

并将您所有的“个人提交”重新排序为最新的提交,然后

git push <remotename> <latest commit SHA you want to push>:<remotebranchname>

git push <remotename> <latest commit SHA you want to push>:<remotebranchname>

This will push all commits up to and including the SHA you provide but not the "personal commits" that come after it.

这会将所有提交推送到并包括您提供的 SHA,但不会推送其后的“个人提交”。

回答by eh9

I've come across the problem constantly, typically with splitting IDE settings and the like off from a shared code base. It's a common enough use case that you might think that it would have been solved by now, but not yet. I don't believe that gitcan handle this as you want.

我经常遇到这个问题,通常是将 IDE 设置等从共享代码库中分离出来。这是一个足够常见的用例,您可能认为它现在已经解决了,但还没有。我不相信这git可以按您的意愿处理。

The issue, boiled down to its essence, is that you want two checkouts to share a single directory. In order for this to work, there would need to be some data structure, manipulable by the user, tagging which files belong to which checkouts. And this isn't a 1-1 relationship, either; it's perfectly reasonable to have some file part of more than one checkout. In all cases you'd need some way naming the different checkouts to support disambiguation. For basic sanity, you'd need a directory lister that annotates files with their checkout associations.

归结为本质的问题是,您希望两个结帐共享一个目录。为了使其工作,需要一些可由用户操作的数据结构,标记哪些文件属于哪些结帐。这也不是一对一的关系;将某些文件部分包含在多个结帐中是完全合理的。在所有情况下,您都需要以某种方式命名不同的结帐以支持消除歧义。为了基本的理智,您需要一个目录列表器,用它们的结帐关联来注释文件。

As a particular illustration, consider .gitignore. That's a single file that applies to "all" checkouts, meaning implicitly "the unique checkout". In order to have .gitignorework well, you'd need one per checkout, a way to distinguish between the different files in each checkout. Yet there's no hint of this kind of infrastructure in git.

作为一个特殊的例子,考虑.gitignore。这是一个适用于“所有”结帐的单个文件,隐含地表示“唯一的结帐”。为了.gitignore正常工作,每次结帐都需要一个,这是一种区分每次结帐中不同文件的方法。然而,在git.

My "standard" way of solving such issues is to arrange things so it's one checkout per directory. I create a directory called "custom" (or the like). That directory goes into .gitignorein the parent. In the custom directory, I put a checkout from a different repository and/or different location. That means two commits to capture all changes; that's rarely a problem in practice.

我解决此类问题的“标准”方法是安排事情,以便每个目录结帐一次。我创建了一个名为“custom”(或类似名称)的目录。该目录进入.gitignore父目录。在自定义目录中,我从不同的存储库和/或不同的位置签出。这意味着两次提交以捕获所有更改;这在实践中很少成为问题。

If this inspires anyone to hack on git, add the option to make a "named checkout" and leave the existing behavior as an anonymous checkout. You can have [0..1] anonymous checkouts and [0..*) named ones. From that basis most everything else you might want follows fairly naturally.

如果这激发了任何人对 hack 的兴趣git,请添加选项以进行“命名结帐”并将现有行为保留为匿名结帐。您可以使用 [0..1] 匿名结帐和 [0..*) 命名结帐。在此基础上,您可能想要的大多数其他东西都会自然而然地发生。

回答by fonZ

You can ignore certain files in git so they dont get pushed to the remote repository. The way i do things like that is to keep those files locally and dont push them to the repository by ignoring them.

您可以忽略 git 中的某些文件,这样它们就不会被推送到远程存储库。我这样做的方式是将这些文件保存在本地,而不是通过忽略它们将它们推送到存储库。

Here's some explanation of gitignore.

这是对 gitignore 的一些解释。

https://help.github.com/articles/ignoring-files

https://help.github.com/articles/ignoring-files