Git: "git rebase" fails for branches that lack the tracking information even when the upstream branch is explicitly provided

Created on 23 Mar 2016  路  27Comments  路  Source: git-for-windows/git

  • [X] I was not able to find an open
    or closed issue
    matching what I'm seeing

    Setup

  • Which version of Git for Windows are you using? 32-bit or 64-bit? Include the
    output of git version as well.

Reproduced with 64-bit installer 2.7.4.windows.1. Could not reproduce with 32-bit portable version 2.7.4.windows.1.

$ git --version
git version 2.7.4.windows.1

  • Which version of Windows are you running? 32-bit or 64-bit?

Windows 7 x64

  • What options did you set as part of the installation? Or did you choose the
    defaults?

defaults

  • Any other interesting things about your environment that might be related
    to the issue you're seeing?

no

Details

  • Which terminal/shell are you running Git from? e.g Bash/CMD/PowerShell/other

The problem is reproduced in any shell.

git init
git touch a.txt
git add a.txt
git commit -m 0
git branch work
echo 1 > a.txt
git commit -am 1
git checkout work
echo 2 > a.txt
git commit -am 2
git rebase master

  • What did you expect to occur after running these commands?

Even though the "work" branch does not track the "master" branch, git should proceed with rebase of work onto master, reporting merge conflict for a.txt because the "git rebase master" command specifies the upstream branch ("master") explicitly.

  • What actually happened instead?

git refuses to even start the rebase, complaining that "There is no tracking information for the current branch".

I know that I can add tracking information with "git branch -u master", but in my actual work I deliberately do not want to do that for a reason.

  • If the problem was occurring with a specific repository, can you provide the
    URL to that repository to help us with testing?

reproducible for a new repository created from scratch.

question

Most helpful comment

It would be nice to detect the case when $(...) does not work (either because of MacType or because of some other program). In that case, warn the user that $(...) does not work. Then, check for MacType, and if it is present, report that the issue could be due to MacType. Otherwise, the user has to find what program interferes with the evaluation of $(...).

Patches welcome.

If you do not find the time to address this wish, no worries, neither did I :grinning:

All 27 comments

git init
git touch a.txt

Are you totally certain that this git touch a.txt works for you? It does not work here, complaining with thusly:

git: 'touch' is not a git command. See 'git --help'.

git add a.txt
git commit -m 0
git branch work
echo 1 > a.txt
git commit -am 1
git checkout work
echo 2 > a.txt
git commit -am 2
git rebase master

When I call this sequence of commands, with the git touch replaced by touch (you really want to be careful to specify precisely what you have tried), it works as you expected. With Git for Windows 2.7.4 (64-bit).

Are you absolutely certain that you did not simply forget to specify master in git rebase master?

I ask because the error message is produced by the error_on_missing_default_upstream function, and that function is only called if the number of parameters passed to the rebase command is 0.

You are completely right about git touch: it does not work for me, too
:) Besides, touch doesn't work in CMD window, so my sequence needs
the following correction: replace the line git touch a.txt with line
echo 0 > a.txt.

I am absolutely certain that I did not forget to specify master in
git rebase master.

I tried my corrected sequence on three PCs (all Windows 7 64 bit). I
could not reproduce the problem on one of them (it's Windows 7
Professional), but the problem is 100% reproducible on the other two
(Windows 7 Enterprise) from both CMD and minTTY shells.

I see your point on error_on_missing_default_upstream. It could be
that by the time we execute the code you've mentioned, $# becomes 0
(e.g., thanks to previous shifts called in the while test $# != 0
statement earlier in git_rebase.sh).

It could be that by the time we execute the code you've mentioned, $# becomes 0

Not here. I tried to reproduce your problem, failing, i.e. the proposed MCVE is not quite doing the job here. Read: git rebase master does _exactly_ what you want it to do.This is on Windows 10 64-bit, Git for Windows 2.7.4.

So here is a suggestion how to diagnose things further on your end: install a PortableGit (so that you can edit files without admin privileges), edit mingw64/libexec/git-core/git-rebase by inserting set -x after the first line, then try the git rebase again (but this time in PortableGit's git-bash.exe, of course).

It will most likely spit out a ton of text. Now you need to look through this log, and you need to determine where that master argument gets shifted away.

I diagnosed things as you suggested. I found that when the execution of
git-rebase script reaches the while test $# != 0 statement, $* is
master and $# is 1. Then, in the above statement we shift that
single argument away by executing the shift statement that follows the
case "$1" in statement inside that while loop.

Then, I repeated the same diagnosis for 32-bit portable git version
(where the problem is not reproducible) so as to see how things should
work. Here, by the time we reach while test $# != 0, $# becomes 2
and $* becomes -- master. So, we proceed with shifting -- which
leaves the master argument in place, and everything works as expected.

By adding traces I confirmed that in both cases, the git-rebase
script is executed with $*=master and $#=1. Then, for 32-bit
version, executing the . git-sh-setup line results in $#=2 and
$*=-- master. In contrast, for 64-bit version, . git-sh-setup does
not change $# and $*, thus causing the problem.

Is . git-sh-setup expected to insert the -- argument? Where does
this insertion happen, exactly (I tried to find that out,
unsuccessfully)?

Thanks.

I suspect that the parseopt call in git-sh-setup has the explanation. The set -x setting should still be active when entering git-sh-setup, do you see the parseopt in your log?

In git-sh-setup, the code

eval "$(
    echo "$OPTIONS_SPEC" |
        git rev-parse --parseopt $parseopt_extra -- "$@" ||
    echo exit $?
)"

is executed differently in 32- and 64-bit portable versions.
In the 32-bit case, I see in my log

+++ git rev-parse --parseopt --stuck-long -- master
++ eval 'set -- -- '\''master'\'''
+++ set -- -- master

but in the 64-bit case, I see only

++ eval ''

Here is where the master command-line argument gets lost in 64-bit version.

I've verified that the problem is still reproducible with the new 64-bit version, 2.8.1.windows.1.

Are you sure that there is no `+++ git rev-parse [...]``` line _at all_ in the 64-bit version?

Yes, I'm sure that in the 64-bit case, there is no
git rev-parse --parseopt [...] line _at all_.

I triple-checked that. Specifically, I added the "before EVAL" and
"after EVAL" markers around the eval statement in git-sh-setup:

echo "OG> before EVAL"

eval "$(
    echo "$OPTIONS_SPEC" |
        git rev-parse --parseopt $parseopt_extra -- "$@" ||
    echo exit $?
)"

echo "OG> after EVAL"   

Here is what I get in the 64-bit case:

++ echo 'OG> before EVAL'
++ eval ''
++ echo 'OG> after EVAL'

In contrast, in the 32-bit case I get this:

++ echo 'OG> before EVAL'
+++ echo 'git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git-rebase --continue | --abort | --skip | --edit-todo
--
 Available options are
v,verbose!         display a diffstat of what changed upstream
q,quiet!           be quiet. implies --no-stat
autostash          automatically stash/stash pop before and after
fork-point         use '\''merge-base --fork-point'\'' to refine upstream
onto=!             rebase onto given branch instead of upstream
p,preserve-merges! try to recreate merges instead of ignoring them
s,strategy=!       use the given merge strategy
no-ff!             cherry-pick all commits, even if unchanged
m,merge!           use merging strategies to rebase
i,interactive!     let the user edit the list of commits to rebase
x,exec=!           add exec lines after each commit of the editable list
k,keep-empty       preserve empty commits during rebase
f,force-rebase!    force rebase even if branch is up to date
X,strategy-option=! pass the argument through to the merge strategy
stat!              display a diffstat of what changed upstream
n,no-stat!         do not show diffstat of what changed upstream
verify             allow pre-rebase hook to run
rerere-autoupdate  allow rerere to update index with resolved conflicts
root!              rebase all reachable commits up to the root(s)
autosquash         move commits that begin with squash!/fixup! under -i
committer-date-is-author-date! passed to '\''git am'\''
ignore-date!       passed to '\''git am'\''
whitespace=!       passed to '\''git apply'\''
ignore-whitespace! passed to '\''git apply'\''
C=!                passed to '\''git apply'\''
S,gpg-sign?        GPG-sign commits
 Actions:
continue!          continue
abort!             abort and check out the original branch
skip!              skip current patch and continue
edit-todo!         edit the todo list during an interactive rebase
'
+++ git rev-parse --parseopt --stuck-long -- master
++ eval 'set -- -- '\''master'\'''
+++ set -- -- master
++ echo 'OG> after EVAL'

Whoa! That code should _at least_ print "echo 1" or some such to evaluate.

Could you also echo $OPTIONS_SPEC and $SHELL before the eval?

(For some reason, the _entire_ block inside the `"$( ... )" is not executed _at all_, it seems, and I am currently at a loss even to suspect any culprit...)

I changed the "before EVAL" marker to

echo "OG> before EVAL: OPTIONS_SPEC=$OPTIONS_SPEC; SHELL=$SHELL."

and got this:

++ echo 'OG> before EVAL: OPTIONS_SPEC=git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git-rebase --continue | --abort | --skip | --edit-todo
--
 Available options are
v,verbose!         display a diffstat of what changed upstream
q,quiet!           be quiet. implies --no-stat
autostash          automatically stash/stash pop before and after
fork-point         use '\''merge-base --fork-point'\'' to refine upstream
onto=!             rebase onto given branch instead of upstream
p,preserve-merges! try to recreate merges instead of ignoring them
s,strategy=!       use the given merge strategy
no-ff!             cherry-pick all commits, even if unchanged
m,merge!           use merging strategies to rebase
i,interactive!     let the user edit the list of commits to rebase
x,exec=!           add exec lines after each commit of the editable list
k,keep-empty       preserve empty commits during rebase
f,force-rebase!    force rebase even if branch is up to date
X,strategy-option=! pass the argument through to the merge strategy
stat!              display a diffstat of what changed upstream
n,no-stat!         do not show diffstat of what changed upstream
verify             allow pre-rebase hook to run
rerere-autoupdate  allow rerere to update index with resolved conflicts
root!              rebase all reachable commits up to the root(s)
autosquash         move commits that begin with squash!/fixup! under -i
committer-date-is-author-date! passed to '\''git am'\''
ignore-date!       passed to '\''git am'\''
whitespace=!       passed to '\''git apply'\''
ignore-whitespace! passed to '\''git apply'\''
C=!                passed to '\''git apply'\''
S,gpg-sign?        GPG-sign commits
 Actions:
continue!          continue
abort!             abort and check out the original branch
skip!              skip current patch and continue
edit-todo!         edit the todo list during an interactive rebase
; SHELL=/usr/bin/bash.'
OG> before EVAL: OPTIONS_SPEC=git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git-rebase --continue | --abort | --skip | --edit-todo
--
 Available options are
v,verbose!         display a diffstat of what changed upstream
q,quiet!           be quiet. implies --no-stat
autostash          automatically stash/stash pop before and after
fork-point         use 'merge-base --fork-point' to refine upstream
onto=!             rebase onto given branch instead of upstream
p,preserve-merges! try to recreate merges instead of ignoring them
s,strategy=!       use the given merge strategy
no-ff!             cherry-pick all commits, even if unchanged
m,merge!           use merging strategies to rebase
i,interactive!     let the user edit the list of commits to rebase
x,exec=!           add exec lines after each commit of the editable list
k,keep-empty       preserve empty commits during rebase
f,force-rebase!    force rebase even if branch is up to date
X,strategy-option=! pass the argument through to the merge strategy
stat!              display a diffstat of what changed upstream
n,no-stat!         do not show diffstat of what changed upstream
verify             allow pre-rebase hook to run
rerere-autoupdate  allow rerere to update index with resolved conflicts
root!              rebase all reachable commits up to the root(s)
autosquash         move commits that begin with squash!/fixup! under -i
committer-date-is-author-date! passed to 'git am'
ignore-date!       passed to 'git am'
whitespace=!       passed to 'git apply'
ignore-whitespace! passed to 'git apply'
C=!                passed to 'git apply'
S,gpg-sign?        GPG-sign commits
 Actions:
continue!          continue
abort!             abort and check out the original branch
skip!              skip current patch and continue
edit-todo!         edit the todo list during an interactive rebase
; SHELL=/usr/bin/bash.
++ eval ''
++ echo 'OG> after EVAL'
OG> after EVAL

The same values of $OPTIONS_SPEC and $SHELL I get in 32-bit case.

That looks correct.

This gets more and more curious. I am really clueless why your $( ... ) block does not get executed.

@guban This could be related to #710.

Can you try @tanmatra's example:
count=$(cat "/etc/fstab" | wc -l); echo count=$count

What is the language of the Windows you are testing with?

In my 64-bit git-bash.exe, executing

count=$(cat "/etc/fstab" | wc -l); echo count=$count

results in

count=

In 32-bit git-bash.exe, executing the above commands results in

count=6

So, it definitely looks like a bug in the 64-bit mingw shell.

I'm testing with English version of Windows.

@guban Next attempt to bring light into this issue: would you please run Process Monitor while executing this statement? Hopefully the log will contain something interesting that makes us understand what's going wrong.

ping

While trying to reduce the amount of information captured by Process Monitor, I decided to temporarily disable MacType (https://code.google.com/archive/p/mactype), a program that fine-tunes the font rendering for me. I found that the problem is reproduced only if I have the MacType service running (it's a windows service). As soon as I stop the MacType service and run count=$(cat "/etc/fstab" | wc -l); echo count=$count again (without restarting the gib-bash console), I get the expected result count=6. So, the best-known root cause is a subtle interaction between MacType and git-bash shell.

@guban oh wow... Font rendering interfering with POSIX tools, that sounds rather bad...

Well, I have MacType too... Therefore, I can confirm what this bug and #710 is caused by it. But I love this program and don't want to work without it. So I just added bash.exe to MacType exclusion list. It's in [UnloadDll] section of corresponding .ini file. Now all is working good, substitution runs correctly.

@tanmatra is there a standardized location for this exclusion file? If so, it would be awesome if you could look into teaching our installer to detect if the file exists and is missing that entry (the GetIniString() function makes that easy), and to issue a warning in that case.

@tanmatra ra,I have a MacType too,when i added bash.exe to MacType exclusion list,now i can edit the git-rebase-todo window,when i save the window ,then give me some waring messages;
qq 20160412145851

qq 20160412143756
qq 20160412143706
qq 20160412143734

last,when i exit the mactype, then all work good,so it's not perfectly.the git version is git-for-window 2.8.1 64-bit,the os system is win7
but git-for-window 2.7.0 64-bit has no this issue,when i use it with the mactype program,it's still all good.

@yunyu2019 another exclusion should be sh.exe. Sorry for not tested it thoroughly.

@tanmatra,thanks,now i'm sure,it's perfectly,all is working good,is there another better answer,if too many people use mactype or the git version upgrade,then the answer is not friendly,it's just my worry,i'm sorry

@dscho Though it's possible to make workarounds in installer, but I doubt you should bother about it because this application is rare enough, and also detection can be a little complicated. MacType can has several profiles, located in C:\Program Files (x86)\MacType\ini folder, so many files may need to be changed. But currently selected profile is located in C:\Program Files (x86)\MacType\MacType.ini, section [General], entry key AlternativeFile.

It may be a better way to list MacType as "slightly incompatible" somewhere in docs.

Sounds like MacType is doing bad things with the Unicode causing issues with parsing scripts, etc. That is bad. Apple's version of Unicode is not UCS-2 (one of the original, pre-UTF16 versions) and Windows is. I do wonder how much impact this is having, especially if the local Conhost isn't in latin-1 mode.

Regardless, it is an unsupported scenario in my opinion. Besides, I'm not sure who would really want their Windows fonts to looks like Mac fonts :stuck_out_tongue_winking_eye:

MacType can tweak only text rendered by the GDI graphical subsystem. As more and more apps and system tools use WPF, MacType's value diminishes with time. I've chosen to completely remove MacType to avoid any potential issues with git, and I admit that making that choice wasn't too difficult.

It would be nice to detect the case when $(...) does not work (either because of MacType or because of some other program). In that case, warn the user that $(...) does not work. Then, check for MacType, and if it is present, report that the issue could be due to MacType. Otherwise, the user has to find what program interferes with the evaluation of $(...).

The above detection could be done in installer to cover cases when a "MacType-like program" is installed before git. Besides, to cover cases when a "MacType-like program" is installed after git, the detection could be part of a git command that is not used routinely but is certainly used when troubleshooting (e.g., git --version).

It would be nice to detect the case when $(...) does not work (either because of MacType or because of some other program). In that case, warn the user that $(...) does not work. Then, check for MacType, and if it is present, report that the issue could be due to MacType. Otherwise, the user has to find what program interferes with the evaluation of $(...).

Patches welcome.

If you do not find the time to address this wish, no worries, neither did I :grinning:

Was this page helpful?
0 / 5 - 0 ratings