-1

I was working on a branch, and realized that I wanted changes from a different branch to help with my testing, but I didn't want to actually commit them to my current branch. So I used git merge --no-commit to bring those changes into my current branch. Then I unstaged the changed files with git reset and proceeded with my work and testing. Eventually I committed some changes related to my current work (but not the changes related to the other branch that I had brought in and unstaged).

I was confused to find that the commit history for my current branch now shows a commit from the "other branch" even though I never committed those changes here. I don't seem to actually have the changes, but looking through the log is confusing because it appears the changes were applied (and never removed) but they aren't here. So I think I got what I want in the end, but with a confusing history.

I realize now that what I did was presumably not a good way to accomplish my goals, but can anyone help me understand how the history works? I suppose there's some chance that I am misremembering exactly what I did too...

EDIT: Here's the most recent part of my git log graph. The part I don't understand now is why the actual changes from commit 34eaa54 do not appear at the head of my journal branch.

* 1c2e2d4 (HEAD -> journal, origin/journal) fix: make command consistent
* 4bedc14 fix: remove space
*   03499a4 fix: use subprocess instead
|\
| * 34eaa54 (origin/update, update) feat: use mock images
* | a8be334 feat: save journal
|/
*   8560394 (tag: v0.2.14, origin/master, origin/HEAD, master) Merge branch 'fix-timeout' into 'master'

EDIT2: Add reproducible example. If you follow these instructions then you can see at the end that you will have the half-aborted commit "in" branch-one but the change isn't there (the word rabbit). The included git status command explicitly mentions still being in the merge, despite the fact that I reset the changed file, so, as expected, that seems to be "why" this happens.

printf "the\nquick\nbrown\nfox\n" > a
git init
git add a
git commit -m "initial commit"
git checkout -b branch-one
sed -i 's/quick/fast/g' a
git add a
git commit -m "fix: fast"
git checkout master
git checkout -b branch-two
sed -i 's/fox/rabbit/g' a
git add a
git commit -m "fix: rabbit"
git checkout branch-one
git merge --no-commit branch-two
git reset -- a
touch b
git add b
git status
git commit -m "feat: add b"
git reset --hard
git log --graph --oneline
cat a
sewebster
  • 5
  • 4
  • 1
    My hunch is that git _remembered_ that you were merging and so when you committed... well. – eftshift0 May 14 '21 at 17:38
  • `git show` followed by the SHA of the merge commit will tell you what changes it added. This might shed some light on what happened. – Kraigolas May 14 '21 at 18:22
  • @Kraigolas I used `git show` on the strange commit and it shows changes that don't appear at the head of my branch, which is why I am confused by the history. I added the log graph to my question. – sewebster May 14 '21 at 20:05
  • The problem is the two resets. The first reset doesn't abort the merge, so we are left in a precarious state. The second one is unaccountable; I'm not sure what you were trying to accomplish. – matt May 15 '21 at 03:28
  • @matt After the final commit, which completes the merge (perhaps unexpectedly if you don't know what you are doing, like me), the second reset was used to get rid of the changes (to the file `a`) which were brought in from `branch-two` to use for testing. – sewebster May 17 '21 at 19:58

1 Answers1

2

git merge --no-commit starts a merge operation, then stops in the middle.

A subsequent git merge --continue first verifies that you're still in the middle of the incomplete merge, then completes it by running git commit.

A subsequent git commit instead of git merge --continue ... completes the merge, as if you'd run git merge --continue, because you are still in the middle of the incomplete merge.

The git log command takes history, i.e. commits—including merge commits, which inherently make history non-linear—and shows you commits one at a time. To see how they relate, historically, use git log --graph, or git log --oneline --graph for a more compact form. See also Pretty Git branch graphs.

Fixing the mess (if it's a mess and you don't like it)

If you did not want a merge commit here, consider using git reset and git cherry-pick:

  • If you have uncommitted work, commit it.

  • Now create a branch or tag name where you are now, so that Git will remember commit hash IDs for you, using the name you create now. (If you don't do this, you have to find hash IDs, which is no fun.) For instance:

    git branch save-this
    

    will create a new branch named save-this; you'll be able to run git log save-this to see commits.

  • Then use git reset --hard with a commit hash ID, or some origin/* name, or whatever, to make your current branch no longer have the commits you've made.

  • Now you can use git cherry-pick, one commit at a time, to pick up commits from the branch named save-this. When you get to the commit that is a merge, but should not have been a merge, use git cherry-pick -m with a number (1 or 2) to cherry-pick only some of the commits. You almost always want the number 1 here (git cherry-pick -m 1 hash). For the non-merge commits, leave out the -m option and the number: -m is only useful on merge commits (where it's required); it's forbidden for non-merge commits.

Once you have the desired commits—the new ones are new, with different hash IDs, and presumably are better than the old ones since they now have what you want in them—you can delete the save-this branch. (You can leave it around as long as you like, in case you're not sure yet. Delete it when you're sufficiently sure. You can rename it, e.g., git branch -m save-this old-saved-stuff, or whatever you like, any time you like, too.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • Worth noting as well that it looks like OP intended to do [this](https://stackoverflow.com/a/501461/11659881). – Kraigolas May 14 '21 at 20:29
  • @Kraigolas I saw that question/answer, but it seems like usually `--no-ff` is needed to avoid `git merge --no-commit` from "merging unexpectedly". In my case it seemed to work as intended, in that there were the changes I wanted brought in, but they were just staged and not yet committed (and I unstaged them). – sewebster May 14 '21 at 20:34
  • @sewebster As that answer does outline, you would have to abort the merge. It is easy to make the mistake of not doing so (my first few times rebasing were very confusing and had similar problems to what you experienced here). – Kraigolas May 14 '21 at 20:42
  • 1
    @Kraigolas: Note that `git merge --abort` works by running `git reset`; you can get better control over the reset result by running `git reset` yourself. There's also `git merge --quit` now, as a nicer way to gain that same kind of control. – torek May 14 '21 at 20:44
  • 1
    @Kraigolas Yes, it seems like my issue was in never explicitly aborting the merge, which I think is basically torek's point in the bold part of his answer. Nevertheless, it still seems odd to me to end up with a commit shown in the history for which the changes do not appear in the tree. It would make sense perhaps to show that commit and then have the next commit explicitly remove those changes, but instead they just seem to silently disappear. Although, torek's recent comment here suggests that `git merge --abort` uses `git reset` which would make me think that what I did was equivalent. – sewebster May 14 '21 at 21:05
  • @torek Added a simple example to my question. It seems that just using `git reset` to unstage the changed file after `git merge --no-commit` is insufficient to entirely "cancel" the merge. – sewebster May 14 '21 at 21:51
  • 1
    Re the edit: `git reset -- a` won't stop a merge, but `git reset --soft`, `git reset --mixed`, or `git reset --hard` will. The reset command is ... overly complicated. `git merge --abort` is equivalent to `git reset --hard` while `git merge --quit` is either mixed or soft (I think soft, but have not tested it). – torek May 15 '21 at 02:37
  • @torek OK that was a really cool response. See https://stackoverflow.com/a/56605236/341994 which talks about `merge --quit`: looks like it's like `reset --soft`. I'm a little confused how that would differ from actually doing the merge and resetting soft back one. – matt May 15 '21 at 03:17
  • @matt: less internal churn, presumably. :-) Also won't update reflogs, which would be externally observable. – torek May 15 '21 at 17:59
  • Thanks @torek (and matt). I have accepted your answer. It still seems to me that it is possible to end up with a log of commits that are insufficient to recreate the current state of the repository, which I don't like... but that's a bit separate from this question. – sewebster May 17 '21 at 19:56