1

Is there a workaround for not committing with command "git merge --continue"

I am trying to do auto-merge with command # git merge -q --no-commit --no-ff --no-edit origin/myBranch --progress -m "Auto merge" - This works if there is no conflict.

However when there is a conflict on a specific folder, which I want to exclude from merge (git reset folderName) and then issuing a command that # git merge --continue with --no-commit

git merge --continue takes no more arguments like --no-commit (fatal: --continue expects no arguments)

git merge --continue makes automatic commit and results in below.

git status

    On branch program/XYZ
    Your branch is ahead of 'origin/XYZ' by 10 commits.
      (use "git push" to publish your local commits)
2
  • Git reset would not exclude from the merge, typically that is used to rollback changes to a commit. In this case, what would you expect it to do? Maybe you want something like stash? Please clarify in your question. Commented Aug 11, 2020 at 17:57
  • Reset command - I am resetting back to its original state/commit on the current branch. The outcome I am expecting is - When I do git merge --continue it should not make the commit, where I want to manually commit. Just merge the branch, no commit locally Commented Aug 11, 2020 at 18:04

1 Answer 1

3

You cannot get what you are asking for, because the only way to merge is to commit.1

To see why this is the case, remember what a commit is and does:

  • Each commit is numbered. These aren't simple counting numbers—they don't go 1, 2, 3, etc.—but they are unique: each commit has a hash ID that no other commit may have.

  • Each commit stores two things: a full snapshot of all the files that Git knows about, and some metadata such as who made the commit, when, and why (the log message). In the metadata, Git stores the commit-number of this commit's parent or parents.

This last part—the parents of a commit—is how history works, in Git. A branch name like master simply holds the hash ID of the last commit in the branch:

... <-F <-G <-H   <-- master

Git can read this hash ID from the name master, and use that to locate commit H in its big database of all-Git-objects. Having read commit H from this database, Git finds the hash ID of previous commit G. That allows Git to read out commit G, which contains the hash ID of earlier commit F, which allows Git to read commit F, and so on. Presto: there is the history. Meanwhile each commit holds a full snapshot of every file, so by comparing the contents of the files in G to the contents of the files in H, Git can tell you what changed between G and H.

This handles simple linear chains, but what about merges? Merges, in Git, are achieved through merge commits. A merge commit is defined as any commit that has at least two parent commits.

Suppose we have the following:

          I--J   <-- branch1
         /
...--G--H
         \
          K--L   <-- branch2

You might wish to do git checkout branch1 and then git merge branch2, to merge the two branches—but Git doesn't really care about branches. Git cares about commits. The merge process works by:

  • locating the best shared commit, which in this case is H; this is called the merge base;
  • comparing the snapshot in the merge base H to the snapshot in the current commit J, to see what "we" changed;
  • comparing the snapshot in the merge base H to the snapshot in the other commit L, to see what "they" changed; and
  • combining the changes and applying the combined changes to the snapshot in H.

(You can think of this as applying their changes on top of our changes, or vice versa, but internally, Git just combines the two sets of changes, which means it needs to apply the combined changes to the base version, H.)

Having done all of this combining successfully, Git normally goes on to make a new commit like this:

          I--J
         /    \
...--G--H      M   <-- branch1 (HEAD)
         \    /
          K--L   <-- branch2

Note that new commit M points back to both the previous current commit J and the other commit L. The branch name that gets updated is the same as always: the current branch name. The name branch1 now points to new merge commit M.

Using --no-commit, you instruct Git to stop after combining, or failing to combine, the two sets of changes. That leaves you in this state:

          I--J   <-- branch1 (HEAD)
         /
...--G--H
         \
          K--L   <-- branch2

Git's attempt at applying the combined changes to H is found in one or both of two places:

  • If Git succeeded at combining the changes for some file F, file F is at stage zero in Git's index, and is modified in your work-tree to match the index copy.

  • If Git failed at combining the changes for F, file F is at a nonzero stage in Git's index. In most cases,2 there are now three copies of F in the index: the one at stage 1 is from commit H, the one at stage 2 is from commit J, and the one at stage 3 is from commit L. Meanwhile, your work-tree contains Git's attempt at combining the changes, but with conflict markers added.

A git commit command will, at this point:

  • fail if any index entries are not at stage zero, or
  • make a new merge commit (commit M as usual) if all index entries are at stage zero.

As always, Git will make the new commit using whatever is in the index at this time. So git reset --mixed, which resets index files, could be something you could do. Usually though it's better to use git checkout (or in Git 2.23 or later, git restore) to update both the index and your work-tree from some commit, if the idea is to keep one particular commit's version of those files.

You wrote this in a comment:

Reset command - I am resetting back to its original state/commit on the current branch

To do this, it's better to use:

git checkout HEAD -- path

or:

git restore -iw --source HEAD path

which will update both the index and your work-tree copy of the named path from HEAD, and in the process, clear out the higher-stage (merge conflict) entries for the given path.

When I do git merge --continue it should not make the commit ...

The only thing a successful git merge --continue can do is make the commit. Either the merge is now done—the index is ready to commit because all higher-stage entries have been cleared out, replaced with stage-zero entries ready to be committed—and git merge --continue will run git commit which will make the commit, or one of these two conditions holds:

  • There are still some higher-stage entries, and the commit cannot be made (git commit will also fail). Or:
  • You aborted the merge entirely, so that there is no merge to continue (git merge --continue will fail, but git commit will attempt to make a new commit).3

1A fast-forward merge is not a merge at all, plus you're excluding it with --no-ff anyway.

2This glosses over cases where there are modify/delete, rename/delete, and other such high-level conflicts, hence the phrase most cases.

3Note that git commit, whether it is committing a merge or not, can fail for other reasons. For instance, you might have a pre-commit hook that rejects the commit, or your disk drive might be on fire. A regular (non-merge) commit will typically fail if the current index matches the current commit: Git calls this an attempt to make an empty commit. Note that you can force the commit with --allow-empty, and such a commit isn't actually empty: it still has a full snapshot of every file. It's just that the snapshot in the new commit exactly matches the snapshot in the previous commit, which makes Git wonder why you're bothering.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.