1

I was able to sync files from one RepoA to RepoB using 'fetch' and 'checkout' to my master and handling the conflicts (if any). but this approach caused me to make a 'merge-commit' on RepoB.

Is there any way to do it without that commit id being generated? I wanted to schedule all the commits and then sync then on a regular basis through chron-task.

I also looked into git-daemon and post-commit hooks. If this can be done please provide an example.

Initial state of Repos:
Initial state of Repos

After applying merge:
After applying merge

After resetting to last commit (Create File02.txt):
After resetting to last commit (Create File02.txt)

Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • 3
    "Handling conflicts" means creating data about how the conflicts are resloved. This data has to be stored *somewhere*, and that somewhere is the merge commit. Even without conflicts, the merge commit is what makes two diverged branches converge again to incorporate both changesets. So, in the general case, you can't avoid the merge commit. – Quentin Mar 08 '17 at 13:45
  • Can **rebase** help to alter the commit history in order to conclude with the last commit instead of the final merge-commit? – Pratyush Raizada Mar 09 '17 at 04:25
  • @PratyushRaizada Yup, easiest is to simply use `git pull --rebase`, see [my answer](https://stackoverflow.com/a/42712635/321973) for details. – Tobias Kienzler Mar 10 '17 at 07:27
  • Do the two "Initial commit"s have different hashes? Then the observed behaviour is to be expected, but this [comment space is too narrow to explain ;)](https://en.wikipedia.org/wiki/Fermat%27s_Last_Theorem#Fermat.27s_conjecture "It is impossible to separate a cube into two cubes, or a fourth power into two fourth powers, or in general, any power higher than the second, into two like powers. I have discovered a truly marvelous proof of this, which this margin is too narrow to contain.") – Tobias Kienzler Mar 14 '17 at 06:50
  • The two "initial commit"s ([A](https://github.com/PratyushRaizada94/RepoA/commit/3c8afd0552651e43cf26e36be8394d01dfc42a51) and [B](https://github.com/PratyushRaizada94/RepoB/commit/99f722d74fd814a2839efc1a4e2fcec8ef28d1f6)) have indeed different hashes, so git _must_ do a merge – Tobias Kienzler Mar 14 '17 at 06:56
  • Okay now I get it, that's why it was giving an additional merge commit, but when I did the reset it overwrote A's file with B. Thanks!! – Pratyush Raizada Mar 14 '17 at 07:34

3 Answers3

2

Because of the way git stores and addresses data, a given commit ID can only possibly store one particular tree - which is to say, one set of directories with one set of files with one specific set of content in each file.

(Technically you can argue that this is not true, but realistically it will never happen that two commits with different trees have the same commit hash.)

So when you merge, whether you resolve a conflict or not, unless the result of the merge is exactly the same as one of the original commits (which generally only happens when one commit was an ancestor of the other and fast-forward is allowed), a new commit has to be created.

So when you sync two repos, if that includes syncing branches in which each repo contains changes that the other hasn't yet seen, it is not possible to avoid creating a new commit.

UPDATE - In the comments, a follow-up question was raised: "Can rebase help to alter the commit history in order to conclude with the last commit instead of the final merge-commit?"

While that question could be interpreted a couple ways, the answer is in any case "no". Setting aside warnings about using rebase if any of the repositories involved are shared with other users, it won't do what you seem to be asking of it.

First, understand that rebase does not change the order of commits. It is impossible to change the order of commits. When you do an interactive rebase and change the order of the to-do list, what happens is that completely new commits are created (with a new commit id for each) in order to apply the same changes in a different order.

In fact, by default you might not notice this, but the original commits are still left right where they were before the rebase. (Try tagging HEAD before doing this type of rebase and then run something like gitk --all to see the results.)

The reason you can't reorder commits is similar to the reason you can't merge in changes without creating a new commit ID. Everything about the commit is encoded in the commit ID. That includes its tree as I noted before; it also includes the ID of the parent commit (and so, transitively, the entire lineage of commits that led up to the current commit).

So in general:

  • If you have a repo and you incorporate new changes into it that previously it did not contian, then every commit that reflects those changes will have a new ID not previously seen in the repo.

  • If you have two repos, and each of them contains changes that are not in the other, then every commit that reflects the merged changes from both will be a new commit with a new ID not previously seen in either repo.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
  • Can **rebase** help to alter the commit history in order to conclude with the last commit instead of the final merge-commit? – Pratyush Raizada Mar 09 '17 at 04:25
  • Short answer: no. I'll update with the long answer. – Mark Adelsberger Mar 09 '17 at 14:28
  • 1
    I've provided an update to try to address this. If you want further detail or think I've misunderstood the question, I suggest trying this: draw up a diagram showing the contents of each repo before the merge, and a diagram showing what you'd like the final repo to look like. Post that, and we can directly address whether it's possible, if so how to do it, and if not why not. – Mark Adelsberger Mar 09 '17 at 14:43
  • Thanks I'll post shortly – Pratyush Raizada Mar 10 '17 at 04:44
  • Hi Mark! I found a work around for the commit removal, by restoring the repo to the last author commit I can now ignore the Merge-commit by using the command "git reset --hard fb62ef635b07afaf719ba15841579ed12e1224b0" – Pratyush Raizada Mar 10 '17 at 07:06
  • fb62ef635b07afaf719ba15841579ed12e1224b0 is the commit id of the last commit before merging, all the previous commits were present there with no merge issues – Pratyush Raizada Mar 10 '17 at 07:07
  • Uh... ok, you can reset to before the merge, but in that case you're ignoring all the changes from the other repo... so why did you try to sync them in the first place if you don't care about the other repo's changes? – Mark Adelsberger Mar 10 '17 at 13:48
  • Actually even I'm also baffled with that behaviour, but somehow that commits are not taken away. – Pratyush Raizada Mar 11 '17 at 08:13
  • suppose RepoA a has a commits as c1,c2,c3 at time T1,T2,T3 Now I sync them to RepoB c1-T1,c2-T2,c3-T3,m-T4 here m is the merge commiit when I git reset to commit c3 only m-T4 is removed rest all commits sustain as they should. Even I'm puzzled with that – Pratyush Raizada Mar 11 '17 at 08:16
  • @PratyushRaizada - If `m` is a merge, then it is merging the `c1`-`c2`-`c3` commit line *with another line of commits*. If there's no other line of commits, then `m` is not a merge. So: what makes you think you aren't losing those other commits if you reset to `c3`? (You are.) – Mark Adelsberger Mar 13 '17 at 13:07
  • c3 is the last commit (by time) , if I reset to this commit all of the previous commits are kept intact within the second repo. The merge commit is coming up because of the change in the ReadMe.md file. When I hard reset it to the last commit it actually overwrites that file as well . The only problem was the extra merge commit which is shows up when I give the 'git merge' command. On resetting it omits if the merge commit. – Pratyush Raizada Mar 14 '17 at 04:44
  • https://imageshack.com/i/pnOgarWip ---> Initial state of Repos https://imageshack.com/i/poRkcCcqp ---> After applying merge https://imageshack.com/i/pnAkkqJtp ---> After resetting to last commit (Create File02.txt) – Pratyush Raizada Mar 14 '17 at 04:47
  • In image 2 a merge commit shows up and later in third one I've added another file and merged again , but his time I've also reset it to the last commit in RepoA ie. (Create File02.txt) – Pratyush Raizada Mar 14 '17 at 04:50
1

You can use git pull --rebase, which will rebase your local commits on top of the remote head. You may of course encounter other conflicts you have to fix, but still you won't have any additional merge-commits. You can also git config pull.rebase true in order to always achieve this via git pull. Be aware of a rebase's consequences though, e.g. you shouldn't have pushed anything rebased before.

Related SO questions:

Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • Hmm... I think I read the question differently, but depending on what you're really trying to accomplish this may be a good solution. – Mark Adelsberger Mar 10 '17 at 13:52
  • @MarkAdelsberger Yes, unfortunately the question is not entirely clear without some example history. Hopefully both our answers are useful for others :) – Tobias Kienzler Mar 10 '17 at 18:52
  • @TobiasKienzler I've uploaded some images as an example to show the problem. You can find it in Mark's answer. Hope it clarifies. (I'm a new to using this platform or else I could've posted it in the question itself) – Pratyush Raizada Mar 14 '17 at 04:55
  • @PratyushRaizada You can edit your question, there is an image upload function as well - aw, I see, you need some rep first, don't worry that'll happen soon. I'll edit it in for you. – Tobias Kienzler Mar 14 '17 at 06:50
-1

After merging the repo the last merge-commit can be removed by simply restoring the Repo's state to the last commit from the parent repo.

Use command:

git log --format="%H" -n 1 ---> To get the last commit id from the parent repo

git reset --hard fb62ef635b07afaf719ba15841579ed12e1224b0 bring back the state of Repo to previous state

fb62ef635b07afaf719ba15841579ed12e1224b0 is the commit id from the previous state

  • Instead of a fixed commit id, you can use `HEAD^` for the previous commit or e.g. `HEAD~4` for the commit 4 commits ago. But a hard reset is usually a dangerous procedure making you loose all uncommitted changes; you might want to consider a `rebase` instead. Or use `git stash` first. – Tobias Kienzler Mar 10 '17 at 07:33
  • I thought of considering HEAD^ but in case there are no commits, one genuine commit will get erased. Thanks for introducing 'git stash'! – Pratyush Raizada Mar 10 '17 at 09:09
  • You're welcome - but note that your answer means not just loosing the merge commit, but also all your own commits since the last pull! Luckily for you, git does keep a reflog so you can find those commits again if you act soon. – Tobias Kienzler Mar 10 '17 at 11:35