Skip to main content
GameDev.net gamedev.net
🔒 Locked

Please help with a merge problem.

Started by Josheir Mar 19, 2020 at 1:17 PM 21 replies 7k views
Original Post
Josheir
Josheir

I'm doing well making, “Mari Bro.” When I use Visual Studio and I have finished a feature I commit and push the new feature. After this I have made it a habit to merge that branch into the master. This works as expected, great! But, when I try to do all this with the console:

$ git checkout <branch> 
$ git add .
$ git commit -m <“my message”>
$ git push
$ git merge master
----------------Conflicts generated here!------------
$ git checkout master
$ git checkout -b <new_branch>

I get many conflicts in the files (around 30-40.) How do I get the files that are having errors to work perfectly like the Visual studio's GUI merge.

Thank you! Josheir

TheHugeManatee
TheHugeManatee

Hi Josheir,

This is because you got the merge logic the wrong way - git merges the specified branch into the currently checked out one. That means that you are trying to merge master into your new feature branch, which generates conflicts for the old files conflicting with the new ones!

So what you should do is insteas

git checkout master
git merge <branch>

Hope this helps!

Josheir
Josheir

Well, I tried all sorts of additional ways to solve this problem, but in the end I needed to get in manually and fix some conflicts. I probably solved seven or eight of them. Before, I did them by hand there was the following (please excuse my crudeness.)

source/diff

target/diff

When source or target was selected, two new files would be displayed. I selected source, target, target, and everything looks like it is working right. What is all that, anyways?

Thanx,

Josheir

SuperVGA
SuperVGA

You could also try rebasing master on top of your feature branch (it applies commits in a slightly different way, leaving you with fewer merge issues and a cleaner log):

git checkout master
git rebase <branch>

This may cause some other issues, though, but it shouldn't be a problem if you're the only one working on the master branch.
What I normally do is this:

git checkout <branch>
git rebase master
git checkout master
git merge <branch>

This way, you've already aligned the commits done on the master branch with your own, and the merge will often be painless.

Josheir
Josheir

It looks like this:

filename1 source diff

filename2 target diff

Josheir
Josheir

Source, target, and diff are hyperlinks.

Alberth
Alberth

The problem with git is there are too many things that can go wrong in an arbitrary setup that nobody can see or play with. For example, your first post starts with “checkout branch”, but that won't be possible without creating it first, and perhaps between branch creation and the checkout you are showing, things could be broken already.

I'd suggest you should try to make a reproducable very small example that breaks. Plain git at the command line is likely best, as that is common ground for everybody and can be run as a batch script.

As an example I made one, except this one works.

mkdir newdir
cd newdir

# Create repo, make initial version"
git init

echo "---------------- initial commit"
echo "x" > file1.txt
git add file1.txt
git ci -m "initial commit"

echo "--------------- make feature in a branch"
git branch feature
git checkout feature
echo "y" >> file1.txt
git add file1.txt
git ci -m "new feature"

echo "--------------- merge"
git checkout master
git merge feature

Output:

Initialized empty Git repository in ~/newdir/.git/
---------------- initial commit
[master (root-commit) 9669eb7] initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 file1.txt
--------------- make feature in a branch
Switched to branch 'feature'
[feature 875c98a] new feature
 1 file changed, 1 insertion(+)
--------------- merge
Switched to branch 'master'
Updating 9669eb7..875c98a
Fast-forward
 file1.txt | 1 +
 1 file changed, 1 insertion(+)

Now everybody can copy/paste that script, run it, verify what you're saying, and change the script to fix whatever needs fixing.

Obviously, actual file contents isn't relevant, you only need to make changes to have git pick up a new file version.

Josheir
Josheir

I thought I should know a bit about git from the command line. Alberth, you mentioned that git can 'be broken.' When this happens, can a user start over with git, or is a wrong command not always salvageable?

Josheir

Alberth
Alberth

In my experience, it's salvageable, unless you destroy information, but plain git doesn't do that unless you tell it explicitly to do such a thing. It also may destroy information on successful completion of a command, eg ‘git rebase’, but such commands are designed to rewrite history, so it's desired to happen if you use them.

Unfortunately, being salvageable doesn't mean it's easy to understand how to achieve that. You can do pretty much anything you want, but with the above safeties on, it won't let you easily clean up the mess, since “mess” is also information that you cannot destroy without being explicit that you want it.

The key into git is to understand how it treats commit trees and branch labels, and what each command does to the tree. That allows you the usual checkout, branch, merge, rebase, commit commands. There is also reflog, but I never wandered into that area yet.

On a more practical point, add something like the following aliases to your config file:

[alias]
  lg = log -20 --graph --pretty=oneline --abbrev-commit --decorate --date=relative
  st = status -sb
  br = branch -v

‘git log’ is rather useless in its raw form at the command line, the above ‘git lg’ gives you a more readable 20 lines of log history in your current branch. ‘git status’ is really your friend while staging, rebasing, and resolving conflicts, but it tends to be a bit verbose, the above ‘git st’ makes it much more compact for the simple staging case. ‘git branch’ lists the branch, I found it useful to also have the commit log of the associated commit listed with ‘git br’. Likely you will find and add other useful aliases in time. (All git commands tend to have loads of options to tweak their behavior.)

For experimenting with commands, it can be useful to clone your repository locally at the disk first, ie “git clone path/to/existing/repo path/to/new/dir” (git is a distributed VCS, you can make copies of all repositories, including the ones you already have!). That way you can mess around as much as you like, and simply delete the cloned copy if you totally break it. Note that such a cloned copy is a proper git repo, its origin points back to ‘path/to/existing/repo’.

Finally, the ‘git reset’ command is the fastest way to move branch labels to any commit. It's the drop-big-bomb-on-it approach of git. and as such a potentially dangerous command. It may destroy entire branches (any commit that is not directly or indirectly attached to a branch label will eventually be deleted by git). In time, you'll find you won't need “git reset” much.

Josheir
Josheir

Alberth said:
Note that such a cloned copy is a proper git repo, its origin points back to ‘path/to/existing/repo’.

Well, I understand that cloning will allow me to make mistakes, but if the cloned copy has an origin of the original repo, than won't this cause problems because it will change this repo? For example when I push master to origin? Thanks again.

Josheir
Josheir

I have done some research. To copy the repo to protect against change, I should:

clone the remote repo into a local version.

create a new empty remote.

add the remote to the local repo and push.

Does this sound right?

Josheir

Alberth
Alberth

“push” and “fetch” are the core commands to exchange data with other repos. “pull” is a combined “fetch” and “merge”, so that exchanges data as well.

As long as you don't use those commands, no data is exchanged in my experience. Note that GUI apps for git repos typically do lots of transfers automatically in an attempt to keep both in sync, so avoid them in your experiments ?

“origin” is not black magic, it's only a convenient name map to save you from typing the full url-like address of the remote as an argument to push or fetch. Try “git remote -v”, that lists the name map. “git remote remove origin” deletes the entry, I don't know enough of git internals to know whether that fully erases all knowledge of the remote repo. (That is, does “git push” without further arguments resolve the remote destination address using the name map?)

Judging from the “.git/config” file (in the root of the repository) it should be:

[branch "master"]
        remote = origin
        merge = refs/heads/master

looks to me like master remote is resolved through the thing named “origin”, It doesn't have an url-address there.

Other options exist as well, you can back-up your checked-out repository copy to an archive file (it's just a directory tree, although it has symlinks at my system, so your archiver should be able to handle those), I always use ‘tar’ (tar czf repobackup20200323a.tgz path/to/repo).

If you make a mess, simply move the mess out of the way (or remove it) and restore by unpacking the archive.

Josheir
Josheir

Alberth, I have been under the weather, but I have just made a script that is broken in a way, but still works. I think I need to change my #s to REMs for example. Also, the merge is checking out the master branch. Just wanted to say thanks!

Josheir

Alberth
Alberth

You're welcome.

I used a Linux shell for that script, if you use a different command language, it likely needs a few changes. Comment isn't quite needed, but it is nice to read what it is supposed to do ?

Josheir
Josheir

Sure, assumption is on branch four with a change:

# to work, open with right click and left click open
# this works, however not reading # correctly and master is already checked out during merge
# must be checked out on new branch already that has a commit
# on branch and branch already exists, so:
# git checkout branch4
# consider changing # to REM
echo ------initial commit
git add .
git commit -m "creates this commit message."
git push --set-upstream origin branch4

# (on branch4)
echo ------merging master
git merge master
# (resolve any merge conflicts if there are any)
# already checked out
git checkout master
git merge branch4 
#(there won't be any conflicts now)
git push


# on old master branch: 
git checkout -b branch5 master
PAUSE
 
Josheir
Josheir

Edit comment, doesn't have a commit!

Alberth
Alberth

Commits are always useful to have ?

Let me add some comments for fun and profits.

# to work, open with right click and left click open

Alternatively, you can fold the script in a “doit.BAT” file, start a command(?) program, and type “doit”. (I think, I haven't touched a Windows system in a few decades.)

# this works, however not reading # correctly and master is already checked out during merge

I use Linux, where shells use “#” as comment character. BAT files indeed use REM.

git add .
git commit -m "creates this commit message."

This adds everything new or modified under “.”. A more subtle way of creating commits is to list the files and directories to add explicitly, ie “git add file1 file2 dir1 ../otherdir/file3”. In that way, you can just hack away until it all works, and at the end create a nice series of commits where you incrementally create the new feature, instead of pushing everything in one big blob of changes. It takes a bit of practice though, and it depends on how important you consider a readable commit history.

You may find that you want to add only some of the changes in a file (and keep the other changes in that file for a next commit) every now and then. git provides a -e (--edit) flag for that purpose which pops up an editor with the full diff of the file, and then you can delete everything but the changes you want to have in the commit that you are creating. When you try this, you'll find “git status” of much use.

git push --set-upstream origin branch4

Nice! I always forget the “--set-upstream” option, so I simply always type “git push origin branch4” instead.

As you are pushing, this creates a “branch4” label at the remote. You don't seem to delete that label at all (both locally and remotely), so you're creating a nice collection of labels.

# (on branch4)
echo "------merging master"
git merge master
# (resolve any merge conflicts if there are any)

This brings in new changes that appeared in local master since you created ‘branch4’. In a setting where others change the remote master as well, you would first update the local master with the remote changes. (git checkout master; git fetch origin; git merge --fast-forward origin/master).

Note that “git fetch origin; git merge --fast-forward origin/master” isn't the fastest way to do this, “git pull” combines these commands, although I have no clue how “pull” works as I always use the above “fetch” / “merge --fast-forward” sequence.

Instead of merging, you can also do a rebase, as in “git checkout branch4; git rebase master”. Basically, it moves entire ‘branch4’ on top of ‘master’, ie like you started writing ‘branch4’ after the new changes were brought in (there is no “merge” commit" in the branch then).

# already checked out
git checkout master
git merge branch4
#(there won't be any conflicts now)
git push

I don't understand your “already checked out” comment, you're on ‘branch4’. You do need to “git checkout master” to change branches.

The “merge” will be conflict-free indeed, as the merge you did above unified the starting point of ‘branch4’ with the top of ‘master’. Theoretically, “git push” could fail here, if someone else changed the remote master in the mean time.

# on old master branch:
git checkout -b branch5 master

I am not sure what “old master branch” you are referring to. You just merged “branch4” into it. Sure you can start “branch5” from the moment before you merged “branch4”, but why would you want to do that?

Josheir
Josheir

Alberth said:
I am not sure what “old master branch” you are referring to. You just merged “branch4” into it. Sure you can start “branch5” from the moment before you merged “branch4”, but why would you want to do that?

Old master branch is the local master branch. My idea was to use numbers with the branches to keep them in order and than letters…

Branch1 - Branch 9

Brach9a

brach9b, etc.

I thought this was great because each branch would be an in order feature.

Josheir

Josheir
Josheir

For diff and reset and checking history, etc.

Josheir
Josheir

Well, I suppose it is all in the master history. The next branch has to be called something, though?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.