Skip to content

Only stash changes when needed #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions PyGitUp/git_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,21 @@ def __getattr__(self, name):
###########################################################################

@contextmanager
def stash(self):
def stasher(self):
"""
A stashing contextmanager.
It stashes all changes inside and unstashed when done.
"""
stashed = False
# nonlocal for python2
stashed = [False]
clean = [False]

def stash():
if clean[0] or not self.repo.is_dirty(submodules=False):
clean[0] = True
return
if stashed[0]:
return

if self.repo.is_dirty(submodules=False):
if self.change_count > 1:
message = 'stashing {0} changes'
else:
Expand All @@ -135,11 +142,11 @@ def stash(self):
except GitError as e:
raise StashError(stderr=e.stderr, stdout=e.stdout)

stashed = True
stashed[0] = True

yield
yield stash

if stashed:
if stashed[0]:
print(colored('unstashing', 'magenta'))
try:
self._run('stash', 'pop')
Expand Down
181 changes: 86 additions & 95 deletions PyGitUp/gitup.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,7 @@ def run(self):
if self.should_fetch:
self.fetch()

with self.git.stash():
with self.returning_to_current_branch():
self.rebase_all_branches()
self.rebase_all_branches()

if self.with_bundler():
self.check_bundler()
Expand All @@ -218,83 +216,97 @@ def run(self):
def rebase_all_branches(self):
""" Rebase all branches, if possible. """
col_width = max(len(b.name) for b in self.branches) + 1
if self.repo.head.is_detached:
raise GitError("You're not currently on a branch. I'm exiting"
" in case you're in the middle of something.")
original_branch = self.repo.active_branch

for branch in self.branches:
target = self.target_map[branch.name]

# Print branch name
if branch.name == original_branch.name:
attrs = ['bold']
else:
attrs = []
print(colored(branch.name.ljust(col_width), attrs=attrs),
end=' ')
with self.git.stasher() as stasher:
for branch in self.branches:
target = self.target_map[branch.name]

# Check, if target branch exists
try:
if target.name.startswith('./'):
# Check, if local branch exists
self.git.rev_parse(target.name[2:])
# Print branch name
if branch.name == original_branch.name:
attrs = ['bold']
else:
# Check, if remote branch exists
_ = target.commit

except (ValueError, GitError):
# Remote branch doesn't exist!
print(colored('error: remote branch doesn\'t exist', 'red'))
self.states.append('remote branch doesn\'t exist')

continue

# Get tracking branch
if target.is_local:
target = find(self.repo.branches,
lambda b: b.name == target.name[2:])

# Check status and act appropriately
if target.commit.hexsha == branch.commit.hexsha:
print(colored('up to date', 'green'))
self.states.append('up to date')

continue # Do not do anything

base = self.git.merge_base(branch.name, target.name)

if base == target.commit.hexsha:
print(colored('ahead of upstream', 'cyan'))
self.states.append('ahead')

continue # Do not do anything

fast_fastforward = False
if base == branch.commit.hexsha:
print(colored('fast-forwarding...', 'yellow'), end='')
self.states.append('fast-forwarding')
# Don't fast fast-forward the currently checked-out branch
fast_fastforward = (branch.name != self.repo.active_branch.name)

elif not self.settings['rebase.auto']:
print(colored('diverged', 'red'))
self.states.append('diverged')

continue # Do not do anything
else:
print(colored('rebasing', 'yellow'), end='')
self.states.append('rebasing')
attrs = []
print(colored(branch.name.ljust(col_width), attrs=attrs),
end=' ')

# Check, if target branch exists
try:
if target.name.startswith('./'):
# Check, if local branch exists
self.git.rev_parse(target.name[2:])
else:
# Check, if remote branch exists
_ = target.commit

except (ValueError, GitError):
# Remote branch doesn't exist!
print(colored('error: remote branch doesn\'t exist', 'red'))
self.states.append('remote branch doesn\'t exist')

continue

# Get tracking branch
if target.is_local:
target = find(self.repo.branches,
lambda b: b.name == target.name[2:])

# Check status and act appropriately
if target.commit.hexsha == branch.commit.hexsha:
print(colored('up to date', 'green'))
self.states.append('up to date')

continue # Do not do anything

base = self.git.merge_base(branch.name, target.name)

if base == target.commit.hexsha:
print(colored('ahead of upstream', 'cyan'))
self.states.append('ahead')

continue # Do not do anything

fast_fastforward = False
if base == branch.commit.hexsha:
print(colored('fast-forwarding...', 'yellow'), end='')
self.states.append('fast-forwarding')
# Don't fast fast-forward the currently checked-out branch
fast_fastforward = (branch.name !=
self.repo.active_branch.name)

elif not self.settings['rebase.auto']:
print(colored('diverged', 'red'))
self.states.append('diverged')

continue # Do not do anything
else:
print(colored('rebasing', 'yellow'), end='')
self.states.append('rebasing')

if self.settings['rebase.show-hashes']:
print(' {}..{}'.format(base[0:7],
target.commit.hexsha[0:7]))
else:
print()
if self.settings['rebase.show-hashes']:
print(' {}..{}'.format(base[0:7],
target.commit.hexsha[0:7]))
else:
print()

self.log(branch, target)
if fast_fastforward:
branch.commit = target.commit
else:
self.git.checkout(branch.name)
self.git.rebase(target)
self.log(branch, target)
if fast_fastforward:
branch.commit = target.commit
else:
stasher()
self.git.checkout(branch.name)
self.git.rebase(target)

if (self.repo.head.is_detached # Only on Travis CI,
# we get a detached head after doing our rebase *confused*.
# Running self.repo.active_branch would fail.
or not self.repo.active_branch.name == original_branch.name):
print(colored('returning to {0}'.format(original_branch.name),
'magenta'))
original_branch.checkout()

def fetch(self):
"""
Expand Down Expand Up @@ -452,27 +464,6 @@ def version_info(self):
# Helpers
###########################################################################

@contextmanager
def returning_to_current_branch(self):
""" A contextmanager returning to the current branch. """
if self.repo.head.is_detached:
raise GitError("You're not currently on a branch. I'm exiting"
" in case you're in the middle of something.")

branch_name = self.repo.active_branch.name

yield

if (
self.repo.head.is_detached # Only on Travis CI,
# we get a detached head after doing our rebase *confused*.
# Running self.repo.active_branch would fail.
or
not self.repo.active_branch.name == branch_name
):
print(colored('returning to {0}'.format(branch_name), 'magenta'))
self.git.checkout(branch_name)

def load_config(self):
"""
Load the configuration from git config.
Expand Down