3

So if I do in master:

git checkout -b my-branch

and do several commits + pushes there. Then I do:

git checkout master
git pull

Can I now somehow get those changes in my-branch to master as local changes just like using git stash apply after doing git stash?

EDIT: By local changes I mean if I edit something or take from stash the files in workspace are edited, pending changes but they are not committed. Local changes have no commit history or anything. The local files are simply changed. If i commit and push the "local" changes to master there won't be any commit history from my-branch.

char m
  • 7,840
  • 14
  • 68
  • 117
  • What do you mean "as local changes"? It's important to understand that commits in git don't "belong to" any particular branch. Possibly what you're looking for is a "fast-forward merge"; see https://stackoverflow.com/questions/9069061/what-is-the-difference-between-git-merge-and-git-merge-no-ff – IMSoP Jun 08 '21 at 12:09
  • @IMSoP uncommitted pending changes. – char m Jun 08 '21 at 16:33

3 Answers3

3

The command git stash apply takes the stashed changes and applies them on top of the current working tree state. Note that no commit is made or referenced at this point. I assume that by "local changes" you mean such changes in your working tree. This behaviour can be replicated for branches with git restore as follows:

git restore --source=my-branch --worktree -- .

Let's see the two commands in comparison with the following graph:

$ git log --oneline --graph --all
* e7cea52 (my-branch) Same as in stash
| *   b3d1317 (refs/stash) WIP on master: df7538f Initial commit
| |\  
|/ /  
| * 0f7231b index on master: df7538f Initial commit
|/  
* df7538f (HEAD -> master) Initial commit

The proposed solution with git-restore applies the deletion and modification. The new file is added and left untracked (not added to the index):

$ git restore --source=my-branch --worktree -- .
$ git status -s
 D old.txt
 M readme.md
?? new.txt

When using git-stash, the deletion and modification is applied as well. The only difference is that the new file is staged already (added to the index):

$ git stash apply
$ git status -s  
A  new.txt
 D old.txt
 M readme.md

If you want to get the actual commits (not just the changes in the worktree) you can use git merge my-branch as suggested by @joanis.

Matt
  • 12,848
  • 2
  • 31
  • 53
  • 2
    I definitely don't want the commits, just the changes i've made in all the commits. Thanks for detailed description. – char m Jun 08 '21 at 16:39
  • what is the difference with this and @TTT's reply? Does both do the same thing? – char m Jun 08 '21 at 17:30
  • My answer does explicitly what you want in one command (put the changes in the worktree). @TTT's answer does a merge first (which creates a commit) and then removes the commit from the history (with reset), leaving the changes in the worktree. This is more error-prone IMHO and might not work properly when there is more than one commit on `my-branch` (see my [comment](https://stackoverflow.com/questions/67886393/is-it-possible-to-get-changes-from-a-branch-created-from-master-back-to-master-a/67887915?noredirect=1#comment120001233_67890521) on the answer). – Matt Jun 08 '21 at 17:49
  • @Matt FYI, I updated my answer to force the merge commit so that it is no longer error prone in that case. ;) – TTT Jun 08 '21 at 19:51
  • @charm I disagree that this is the same as my answer. As far as I can tell this answer also undoes the new changes on `master` that weren't in the other branch. – TTT Jun 08 '21 at 20:02
2

One way to achieve this is to simply merge in your branch and then reset mixed right back to where you just were. So, while you're on master, after the pull:

git merge --no-ff --no-edit my-branch
git reset @^1

This works because the merge brings in all of the changes from my-branch, and then the reset (which is mixed by default if you don't specify a type) brings you back to where you were, but with all of the changes from my-branch as pending, just like they would be with git stash apply.

Note @^1 is short for HEAD^1 which means the first parent of the merge commit. Read more about the meaning of caret and tilde here.

Side note: although this works in your scenario, as Matt pointed out in a comment, it wouldn't work if you were able to fast-forward merge the other branch in and that branch had multiple commits. By adding --no-ff to the merge statement, a merge commit will be created regardless, and then this works in the more general case too. Adding --no-edit skips the prompt to write the merge commit message, which you don't need since you're going to throw that commit away in the next command.

TTT
  • 22,611
  • 8
  • 63
  • 69
  • Thanks! I don't know what mixed means or what is the meaning of 1 in reset. However your explanation makes sense if it works like that. – char m Jun 08 '21 at 16:34
  • 1
    @charm I added some links with some explanations of the syntax. – TTT Jun 08 '21 at 16:43
  • This does not work when the merge is fast-forward with several commits. In this case, `git reset @^1` just goes back one commit. – Matt Jun 08 '21 at 17:35
  • @Matt agreed! It doesn't apply to this question, but that's a great point for the general case. I edited it to force a merge commit. Thx! – TTT Jun 08 '21 at 19:49
1

There are several routes you can go here, I'll spell out the two that make the most sense to me.

Assumption: "local changes" is ill defined: do you mean local commits at the tip of master, or local modifications that are not even committed yet? I'm going to assume the former since it makes more sense: you want to bring those commits into branch master.

Option 1: rebase my-branch at the tip of master

If you want the my-branch commits at the tip of master, you need rebase:

git checkout my-branch
git rebase master

Then if you want to continue working on master, merge my-branch in:

git checkout master
git merge my-branch

Option 2: just merge my-branch into master

If you don't mind bubbles in your history, skip the rebase and just merge my-branch into master, with those same two commands:

git checkout master
git merge my-branch
joanis
  • 10,635
  • 14
  • 30
  • 40
  • i refer to local changes as pending changes is in my workspace. it also means that if I create another branch (or commit them directly to master) from the "local changes" there won't be any commit history from my-branch. – char m Jun 08 '21 at 16:42
  • If I understand your comment correctly, you were looking for what Matt's and TTT's answers do, then. So my answer is more-or-less off topic, is that correct? – joanis Jun 08 '21 at 17:16
  • yes, that is correct. sorry for not being clear and using incorrect terms. I use git but i just use the super basics and have never bothered to look beyond what i need because of git's complexity. – char m Jun 08 '21 at 17:27
  • No worries, there's a lot to learn about Git. I'm a fairly advanced user, yet keep learning new things about it here as questions go by. Glad someone understood you and gave you an answer. – joanis Jun 08 '21 at 22:25